你可以使用Cobra和Viper将一个值绑定为配置数组中的第一项。

huangapple go评论70阅读模式
英文:

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

问题

我有以下配置模型:

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

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

我希望能够使用配置文件和命令行选项来配置它。我知道如何通过传递正确格式的配置文件并进行解组来实现配置文件。

然而,我无法弄清楚如何使用Cobra在命令行上设置项目名称,然后让Viper将该值绑定为Project数组中的第一个项目。

以下是我编写的一个简单程序,用于展示我遇到的问题:

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()
}

当我使用命令go run .\binding.go -n Russell -p Turtle运行此程序时,我得到以下输出:

你可以使用Cobra和Viper将一个值绑定为配置数组中的第一项。

所以我知道viper.BindPFlag("project.0.name", rootCmd.Flags().Lookup(("project")))这一行不起作用。如果我将其更改为project[0].name,我会得到一个堆栈跟踪。问题是如何将这个(和其他属性)添加为复杂对象数组中的第一个项目?我可以有第二个Viper读取另一个对象并添加到主配置中吗?还是还有其他方法?

英文:

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:

你可以使用Cobra和Viper将一个值绑定为配置数组中的第一项。

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?

答案1

得分: 1

在玩弄了一番之后,我找到了答案。

尽管我已经设置了配置,使其具有项目Project []Project的切片,但Viper足够聪明,可以解决这个问题。

因此,要将项目名称绑定到切片的第一个元素,只需简单地使用:

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

不需要索引。但是,我可以使用以下代码打印该值:

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

你可以使用Cobra和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.

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)

你可以使用Cobra和Viper将一个值绑定为配置数组中的第一项。

I was over thinking this

huangapple
  • 本文由 发表于 2021年7月30日 19:04:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/68589913.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定