简体   繁体   中英

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.

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:

虚拟应用程序输出

So I know that the line viper.BindPFlag("project.0.name", rootCmd.Flags().Lookup(("project"))) is not working. If I change this to project[0].name I get a stack trace. 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?

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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