You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

120 lines
3.3 KiB

  1. package cmd
  2. import (
  3. "fmt"
  4. "os"
  5. "strings"
  6. "github.com/pkg/errors"
  7. "github.com/spf13/cobra"
  8. "github.com/spf13/viper"
  9. "github.com/tendermint/go-data/base58"
  10. data "github.com/tendermint/go-wire/data"
  11. )
  12. /*******
  13. TODO
  14. This file should move into go-common or the like as a basis for all cli tools.
  15. It is here for experimentation of re-use between go-keys and light-client.
  16. *********/
  17. const (
  18. RootFlag = "root"
  19. OutputFlag = "output"
  20. EncodingFlag = "encoding"
  21. )
  22. func PrepareMainCmd(cmd *cobra.Command, envPrefix, defautRoot string) func() {
  23. cobra.OnInitialize(func() { initEnv(envPrefix) })
  24. cmd.PersistentFlags().StringP(RootFlag, "r", defautRoot, "root directory for config and data")
  25. cmd.PersistentFlags().StringP(EncodingFlag, "e", "hex", "Binary encoding (hex|b64|btc)")
  26. cmd.PersistentFlags().StringP(OutputFlag, "o", "text", "Output format (text|json)")
  27. cmd.PersistentPreRunE = multiE(bindFlags, setEncoding, validateOutput, cmd.PersistentPreRunE)
  28. return func() { execute(cmd) }
  29. }
  30. // initEnv sets to use ENV variables if set.
  31. func initEnv(prefix string) {
  32. // env variables with TM prefix (eg. TM_ROOT)
  33. viper.SetEnvPrefix(prefix)
  34. viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
  35. viper.AutomaticEnv()
  36. }
  37. // execute adds all child commands to the root command sets flags appropriately.
  38. // This is called by main.main(). It only needs to happen once to the rootCmd.
  39. func execute(cmd *cobra.Command) {
  40. if err := cmd.Execute(); err != nil {
  41. fmt.Println(err)
  42. os.Exit(-1)
  43. }
  44. }
  45. type wrapE func(cmd *cobra.Command, args []string) error
  46. func multiE(fs ...wrapE) wrapE {
  47. return func(cmd *cobra.Command, args []string) error {
  48. for _, f := range fs {
  49. if f != nil {
  50. if err := f(cmd, args); err != nil {
  51. return err
  52. }
  53. }
  54. }
  55. return nil
  56. }
  57. }
  58. func bindFlags(cmd *cobra.Command, args []string) error {
  59. // cmd.Flags() includes flags from this command and all persistent flags from the parent
  60. if err := viper.BindPFlags(cmd.Flags()); err != nil {
  61. return err
  62. }
  63. // rootDir is command line flag, env variable, or default $HOME/.tlc
  64. rootDir := viper.GetString("root")
  65. viper.SetConfigName("config") // name of config file (without extension)
  66. viper.AddConfigPath(rootDir) // search root directory
  67. // If a config file is found, read it in.
  68. if err := viper.ReadInConfig(); err == nil {
  69. // stderr, so if we redirect output to json file, this doesn't appear
  70. // fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
  71. } else if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
  72. // we ignore not found error, only parse error
  73. // stderr, so if we redirect output to json file, this doesn't appear
  74. fmt.Fprintf(os.Stderr, "%#v", err)
  75. }
  76. return nil
  77. }
  78. // setEncoding reads the encoding flag
  79. func setEncoding(cmd *cobra.Command, args []string) error {
  80. // validate and set encoding
  81. enc := viper.GetString("encoding")
  82. switch enc {
  83. case "hex":
  84. data.Encoder = data.HexEncoder
  85. case "b64":
  86. data.Encoder = data.B64Encoder
  87. case "btc":
  88. data.Encoder = base58.BTCEncoder
  89. default:
  90. return errors.Errorf("Unsupported encoding: %s", enc)
  91. }
  92. return nil
  93. }
  94. func validateOutput(cmd *cobra.Command, args []string) error {
  95. // validate output format
  96. output := viper.GetString(OutputFlag)
  97. switch output {
  98. case "text", "json":
  99. default:
  100. return errors.Errorf("Unsupported output format: %s", output)
  101. }
  102. return nil
  103. }