package cmd import ( "fmt" "os" "strings" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/tendermint/go-data/base58" data "github.com/tendermint/go-wire/data" ) /******* TODO This file should move into go-common or the like as a basis for all cli tools. It is here for experimentation of re-use between go-keys and light-client. *********/ const ( RootFlag = "root" OutputFlag = "output" EncodingFlag = "encoding" ) func PrepareMainCmd(cmd *cobra.Command, envPrefix, defautRoot string) func() { cobra.OnInitialize(func() { initEnv(envPrefix) }) cmd.PersistentFlags().StringP(RootFlag, "r", defautRoot, "root directory for config and data") cmd.PersistentFlags().StringP(EncodingFlag, "e", "hex", "Binary encoding (hex|b64|btc)") cmd.PersistentFlags().StringP(OutputFlag, "o", "text", "Output format (text|json)") cmd.PersistentPreRunE = multiE(bindFlags, setEncoding, validateOutput, cmd.PersistentPreRunE) return func() { execute(cmd) } } // initEnv sets to use ENV variables if set. func initEnv(prefix string) { // env variables with TM prefix (eg. TM_ROOT) viper.SetEnvPrefix(prefix) viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) viper.AutomaticEnv() } // execute adds all child commands to the root command sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func execute(cmd *cobra.Command) { if err := cmd.Execute(); err != nil { fmt.Println(err) os.Exit(-1) } } type wrapE func(cmd *cobra.Command, args []string) error func multiE(fs ...wrapE) wrapE { return func(cmd *cobra.Command, args []string) error { for _, f := range fs { if f != nil { if err := f(cmd, args); err != nil { return err } } } return nil } } func bindFlags(cmd *cobra.Command, args []string) error { // cmd.Flags() includes flags from this command and all persistent flags from the parent if err := viper.BindPFlags(cmd.Flags()); err != nil { return err } // rootDir is command line flag, env variable, or default $HOME/.tlc rootDir := viper.GetString("root") viper.SetConfigName("config") // name of config file (without extension) viper.AddConfigPath(rootDir) // search root directory // If a config file is found, read it in. if err := viper.ReadInConfig(); err == nil { // stderr, so if we redirect output to json file, this doesn't appear // fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) } else if _, ok := err.(viper.ConfigFileNotFoundError); !ok { // we ignore not found error, only parse error // stderr, so if we redirect output to json file, this doesn't appear fmt.Fprintf(os.Stderr, "%#v", err) } return nil } // setEncoding reads the encoding flag func setEncoding(cmd *cobra.Command, args []string) error { // validate and set encoding enc := viper.GetString("encoding") switch enc { case "hex": data.Encoder = data.HexEncoder case "b64": data.Encoder = data.B64Encoder case "btc": data.Encoder = base58.BTCEncoder default: return errors.Errorf("Unsupported encoding: %s", enc) } return nil } func validateOutput(cmd *cobra.Command, args []string) error { // validate output format output := viper.GetString(OutputFlag) switch output { case "text", "json": default: return errors.Errorf("Unsupported output format: %s", output) } return nil }