简体   繁体   English

如何使用 Cobra 和 Viper 将值绑定为配置中数组中的第一项?

[英]How can I use Cobra and Viper to bind a value as the first item in an array in the configuration?

I have the following configuration model:我有以下配置模型:

type Config struct {
  Project []Project `mapstructure:"project"`
}

type Project struct {
  Name string `mapstructure:"name"`
}

I want to be able to configure this using a configuration file as well as options on the command line.我希望能够使用配置文件以及命令行上的选项进行配置。 I know how to do the config file by passing it in the correct format and then unmarshalling it.我知道如何通过以正确的格式传递配置文件然后对其进行解组来执行配置文件。

However what I cannot work out how to do is have the name of the project set on the command line using Cobra and then have Viper bind that value as the first item in the Project array.但是,我无法弄清楚如何使用 Cobra 在命令行上设置项目的名称,然后让 Viper 将该值绑定为项目数组中的第一项。

The following is a simple program I put together to show the problem I have:以下是我放在一起的一个简单程序,用于显示我遇到的问题:

package main

import (
    "fmt"
    "log"

    "github.com/spf13/cobra"
    "github.com/spf13/viper"
)

type Config struct {
    Project []Project `mapstructure:"project"`
    Name    string    `mapstructure:"name"`
}

type Project struct {
    Name string `mapstructure:"name"`
}

var (
    config Config

    rootCmd = &cobra.Command{
        Use:              "rjsdummy",
        Short:            "Dummy app to understand Viper BindPFlags",
        Long:             "",
        PersistentPreRun: preRun,
        Run:              executeRun,
    }
)

func init() {

    var name string
    var project_name string

    cobra.OnInitialize()

    // configure the flags on the command line
    rootCmd.Flags().StringVarP(&name, "name", "n", "", "Your name")
    rootCmd.Flags().StringVarP(&project_name, "project", "p", "", "Project name")

    // bind the flags to the configuration
    viper.BindPFlag("name", rootCmd.Flags().Lookup(("name")))
    viper.BindPFlag("project.0.name", rootCmd.Flags().Lookup(("project")))
}

func preRun(ccmd *cobra.Command, args []string) {
    err := viper.Unmarshal(&config)
    if err != nil {
        log.Fatalf("Unable to read Viper options into configuration: %v", err)
    }
}

func executeRun(ccmd *cobra.Command, args []string) {
    fmt.Printf("Your name: %s\n", config.Name)
    fmt.Printf("Project name: %s\n", config.Project[0].Name)
}

func main() {
    rootCmd.Execute()
}

When I run this with the command go run .\\binding.go -n Russell -p Turtle I get the following output:当我使用命令go run .\\binding.go -n Russell -p Turtle运行它时,我得到以下输出:

虚拟应用程序输出

So I know that the line viper.BindPFlag("project.0.name", rootCmd.Flags().Lookup(("project"))) is not working.所以我知道viper.BindPFlag("project.0.name", rootCmd.Flags().Lookup(("project")))不起作用。 If I change this to project[0].name I get a stack trace.如果我将其更改为project[0].name我会得到一个堆栈跟踪。 The question is how do I add this (and other attributes) as an the first item in array of complex objects?问题是如何将这个(和其他属性)添加为复杂对象数组中的第一项? Can I have a second Viper that would read into another object and then add to the main config or is there another way?我可以有第二个 Viper 来读入另一个对象然后添加到主配置中还是有其他方法?

After playing around with this I have the answer.在玩弄这个之后,我有了答案。

Even though I have set the configuation so that it has a slice of project Project []Project , Viper is clever enough to work this out.即使我已经设置了配置,以便它有一部分项目Project []Project ,但 Viper 足够聪明来解决这个问题。

So to bind the project name to the first element of the slice, it is as simple as using:所以要将项目名称绑定到切片的第一个元素,就像使用一样简单:

viper.BindPFlag("project.name", runCmd.Flags().Lookup("name"))

No index is required.不需要索引。 However I can print the value with:但是我可以打印值:

fmt.Println(Config.Project[0].Name)

在此处输入图片说明

I was over thinking this我想太多了

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM