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