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  }