gitee.com/liuxuezhan/go-micro-v1.18.0@v1.0.0/config/source/cli/cli.go (about) 1 package cli 2 3 import ( 4 "flag" 5 "io/ioutil" 6 "os" 7 "strings" 8 "time" 9 10 "github.com/imdario/mergo" 11 "github.com/micro/cli" 12 "gitee.com/liuxuezhan/go-micro-v1.18.0/config/cmd" 13 "gitee.com/liuxuezhan/go-micro-v1.18.0/config/source" 14 ) 15 16 type cliSource struct { 17 opts source.Options 18 ctx *cli.Context 19 } 20 21 func (c *cliSource) Read() (*source.ChangeSet, error) { 22 var changes map[string]interface{} 23 24 for _, name := range c.ctx.GlobalFlagNames() { 25 tmp := toEntry(name, c.ctx.GlobalGeneric(name)) 26 mergo.Map(&changes, tmp) // need to sort error handling 27 } 28 29 for _, name := range c.ctx.FlagNames() { 30 tmp := toEntry(name, c.ctx.Generic(name)) 31 mergo.Map(&changes, tmp) // need to sort error handling 32 } 33 34 b, err := c.opts.Encoder.Encode(changes) 35 if err != nil { 36 return nil, err 37 } 38 39 cs := &source.ChangeSet{ 40 Format: c.opts.Encoder.String(), 41 Data: b, 42 Timestamp: time.Now(), 43 Source: c.String(), 44 } 45 cs.Checksum = cs.Sum() 46 47 return cs, nil 48 } 49 50 func toEntry(name string, v interface{}) map[string]interface{} { 51 n := strings.ToLower(name) 52 keys := strings.FieldsFunc(n, split) 53 reverse(keys) 54 tmp := make(map[string]interface{}) 55 for i, k := range keys { 56 if i == 0 { 57 tmp[k] = v 58 continue 59 } 60 61 tmp = map[string]interface{}{k: tmp} 62 } 63 return tmp 64 } 65 66 func reverse(ss []string) { 67 for i := len(ss)/2 - 1; i >= 0; i-- { 68 opp := len(ss) - 1 - i 69 ss[i], ss[opp] = ss[opp], ss[i] 70 } 71 } 72 73 func split(r rune) bool { 74 return r == '-' || r == '_' 75 } 76 77 func (c *cliSource) Watch() (source.Watcher, error) { 78 return source.NewNoopWatcher() 79 } 80 81 func (c *cliSource) String() string { 82 return "cli" 83 } 84 85 // NewSource returns a config source for integrating parsed flags from a micro/cli.Context. 86 // Hyphens are delimiters for nesting, and all keys are lowercased. The assumption is that 87 // command line flags have already been parsed. 88 // 89 // Example: 90 // cli.StringFlag{Name: "db-host"}, 91 // 92 // 93 // { 94 // "database": { 95 // "host": "localhost" 96 // } 97 // } 98 func NewSource(opts ...source.Option) source.Source { 99 options := source.NewOptions(opts...) 100 101 var ctx *cli.Context 102 103 c, ok := options.Context.Value(contextKey{}).(*cli.Context) 104 if ok { 105 ctx = c 106 } 107 108 // no context 109 if ctx == nil { 110 // get the default app/flags 111 app := cmd.App() 112 flags := app.Flags 113 114 // create flagset 115 set := flag.NewFlagSet(app.Name, flag.ContinueOnError) 116 117 // apply flags to set 118 for _, f := range flags { 119 f.Apply(set) 120 } 121 122 // parse flags 123 set.SetOutput(ioutil.Discard) 124 set.Parse(os.Args[1:]) 125 126 // normalise flags 127 normalizeFlags(app.Flags, set) 128 129 // create context 130 ctx = cli.NewContext(app, set, nil) 131 } 132 133 return &cliSource{ 134 ctx: ctx, 135 opts: options, 136 } 137 } 138 139 // WithContext returns a new source with the context specified. 140 // The assumption is that Context is retrieved within an app.Action function. 141 func WithContext(ctx *cli.Context, opts ...source.Option) source.Source { 142 return &cliSource{ 143 ctx: ctx, 144 opts: source.NewOptions(opts...), 145 } 146 }