Как осуществляется доступ к флагу Cobra типа StringToStringVar с помощью Viper?

Я пытаюсь разработать приложение на Go, которое принимает ввод в командной строке в виде строки пар ключ-значение. Для этого я использую StrngToStringVar из библиотеки Cobra.

Я также использую Viper для привязки этих флагов к конфигурации, однако какое бы значение я ни поставил, я не могу получить его от Viper.

Это код

package main

import (
    "fmt"

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

    "github.com/davecgh/go-spew/spew"
)

func main() {
    var items map[string]string
    var name string

    rootCmd := &cobra.Command{
        Use:   "cobra",
        Short: "Test options on the command line",
        Long:  ``,
        Run: func(ccmd *cobra.Command, args []string) {

            spew.Dump(viper.GetString("name"))
            spew.Println("")

            fmt.Println("GetStringMap")
            spew.Dump(viper.GetStringMap("items"))

            fmt.Println("")

            fmt.Println("GetStringMapString")
            spew.Dump(viper.GetStringMapString("items"))
        },
    }

    rootCmd.Flags().StringVar(&name, "name", "", "Name of the list")
    rootCmd.Flags().StringToStringVar(&items, "item", nil, "Map stating the items to be included")

    viper.BindPFlag("name", rootCmd.Flags().Lookup("name"))
    viper.BindPFlag("items", rootCmd.Flags().Lookup("item"))

    rootCmd.Execute()
}

Если я запустил это с помощью команды go run .\main.go --item shopping=apple,banana --name foobar, я получу следующий результат

(string) (len=6) "foobar"

GetStringMap
(map[string]interface {}) {
}

GetStringMapString
(map[string]string) {
}

Как можно видеть, он не содержит ничего для элементов, хотя я правильно установил ввод (я считаю). Я пытался использовать PR https://github.com/spf13/pflag/pull/133, чтобы понять, как это сделать, но мне не повезло.

Мне интересно, неверна ли привязка, но я успешно использовал CObra в других проектах, поэтому я не понимаю, как ссылаться на сгенерированный map[string]string из Cobra.


person Russell Seymour    schedule 18.10.2019    source источник
comment
Вы пытались использовать переменные items и names в качестве переменных пакета?   -  person Eduardo Pereira    schedule 18.10.2019


Ответы (1)


Что касается обязательной части, то это правильно. Однако перед обработкой вы можете проверить, существует ли конкретный ключ, используя vipver.IsSet (), распечатайте все доступные ключи, используя viper.AllKeys () или распечатайте все, используя viper.AllSettings ().

Вот пример (test.go):

package main

import (
    "fmt"

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

func main() {
    var items map[string]string

    rootCmd := &cobra.Command{
        Use: "app",
        Run: func(ccmd *cobra.Command, args []string) {

            fmt.Println("item exists?", viper.IsSet("item"))
            fmt.Println("GetString  :", viper.GetString("item"))
            fmt.Println("Keys       :", viper.AllKeys())
            fmt.Println("Settings   :", viper.AllSettings())
        },
    }

    rootCmd.Flags().StringToStringVarP(&items, "item", "i", nil, "Map stating the items to be included")
    viper.BindPFlag("item", rootCmd.Flags().Lookup("item"))
    rootCmd.Execute()
}

Выход:

$ ./test.exe -i 'k=v,"a=b,c"'
item exists? true
GetString  : [k=v,"a=b,c"]
Keys       : [item]
Settings   : map[item:[k=v,"a=b,c"]]

Использование GetStringMap() и GetStringMapString() имеет больше смысла с другими форматами, такими как JSON, YAML и т. Д. Вы можете отслеживать выполнение этих функций по немаршаллингу вызовов JSON в режиме отладки.

Для преобразования карты вы можете написать свою собственную функцию, подобную этой (live):

func getStringMap(s string) map[string]string {
    entries := strings.Split(s, ",")

    m := make(map[string]string)
    for _, e := range entries {
        tokens := strings.Split(e, "=")
        k := strings.TrimSpace(tokens[0])
        v := strings.TrimSpace(tokens[1])
        m[k] = v
    }

    return m
}
person Azeem    schedule 20.10.2019
comment
спасибо за это, распечатка настроек viper действительно помогла мне разобраться в происходящем. У меня возникла проблема, когда ./text.exe -i 'k=v,"a=b,c" приводит к ошибке Error: invalid argument "k=v,b=c,d" for "-i, --item" flag: d must be formatted as key=value, однако я понял, что могу использовать несколько -i, чтобы добавить больше элементов. - person Russell Seymour; 21.10.2019
comment
@RussellSeymour: Добро пожаловать! :) Да, вы можете использовать флаг несколько раз, чтобы добиться того же эффекта. Но он должен работать и для нескольких значений с одним флагом, например. ./test.exe -i 'k=v,"a=b,c"'. В вашем комментарии отсутствует завершающая цитата '. Это была опечатка? - person Azeem; 21.10.2019
comment
да, это была опечатка, у меня был конечный ' в командной строке, когда я его тестировал. Я не против, что могу использовать -i несколько раз, на самом деле я думаю, что это упрощает документирование :-). Спасибо за вашу помощь. - person Russell Seymour; 22.10.2019
comment
@RussellSeymour: Верно. Не за что. Рад, что помог. :) - person Azeem; 22.10.2019