diff --git a/example_comand_config_test.go b/example_comand_config_test.go new file mode 100644 index 0000000..6b33402 --- /dev/null +++ b/example_comand_config_test.go @@ -0,0 +1,102 @@ +package cmder_test + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "os" + + "github.com/brandon1024/cmder" + "github.com/brandon1024/cmder/getopt" +) + +// This example demonstrates how to configure your application from a configuration file, environment variables, and +// flags. +func ExampleCommand_config() { + // lowest precedence + json.Unmarshal([]byte(MultiConfSettings), &multiconf.settings) + + // higher precedence + os.Setenv("MULTICONF_COUNT", "15") + os.Setenv("MULTICONF_ARGS", "arg-1,arg-2") + + // highest precedence + args := []string{"--count", "12", "--args", "arg-3"} + + ops := []cmder.ExecuteOption{ + cmder.WithArgs(args), + cmder.WithEnvironmentBinding(), + } + + if err := cmder.Execute(context.Background(), multiconf, ops...); err != nil { + fmt.Printf("unexpected error occurred: %v", err) + } + // Output: + // format: pretty + // count: 12 + // args: [arg-0 arg-1 arg-2 arg-3] +} + +const MultiConfSettings = `{ + "format": "pretty", + "count": 10, + "args": ["arg-0"] +} +` + +const MultiConfDesc = ` +'multi-conf' desmonstrates how you can setup configuration from a configuration file (json), environment variables, and +command-line flags. In this example, configuration is evaluated in this order, from lowest to highest precedence: + + 1. Configuration File (/etc/multi.conf) + 2. Environment Variables (MULTICONF_*) + 3. Command Flags +` + +const MultiConfExamples = ` +$ MULTICONF_COUNT=15 MULTICONF_ARGS=arg-1,arg-2 multi-conf --count 12 --args arg-3 +format: pretty +count: 12 +args: [arg-0 arg-1 arg-2 arg-3] +` + +type MultiConfConfig struct { + Format string `json:"format"` + Count int `json:"count"` + Args []string `json:"args"` +} + +type MultiConf struct { + cmder.CommandDocumentation + + settings MultiConfConfig +} + +var ( + multiconf = &MultiConf{ + CommandDocumentation: cmder.CommandDocumentation{ + Usage: "multi-conf [flags]", + ShortHelp: "Simple demonstration of application configuration.", + Help: MultiConfDesc, + Examples: MultiConfExamples, + }, + } +) + +func (m *MultiConf) Name() string { + return "multi-conf" +} + +func (m *MultiConf) InitializeFlags(fs *flag.FlagSet) { + fs.StringVar(&m.settings.Format, "format", m.settings.Format, "specify a format") + fs.IntVar(&m.settings.Count, "count", m.settings.Count, "specify a count") + fs.Var((*getopt.StringsVar)(&m.settings.Args), "args", "provide arguments") +} + +func (m *MultiConf) Run(ctx context.Context, args []string) error { + fmt.Printf("format: %s\n", m.settings.Format) + fmt.Printf("count: %d\n", m.settings.Count) + fmt.Printf("args: %v\n", m.settings.Args) + return nil +} diff --git a/flags.go b/flags.go index 685d7d2..62ebc2b 100644 --- a/flags.go +++ b/flags.go @@ -10,11 +10,11 @@ import ( // FlagInitializer is an interface implemented by commands that need to register flags. // -// InitializeFlags will be invoked during [Execute], prior to any lifecycle routines. You can use this to register -// flags for your command. +// InitializeFlags will be invoked during [Execute], prior to Initialize/Run/Destroy routines. You can use this to +// register flags for your command. // -// Help flags '-h' and '--help' are registered automatically and will instruct [Execute] to render usage information -// to the [UsageOutputWriter]. +// If the command does not define help flags '-h' and '--help', they will be registered automatically and will instruct +// [Execute] to render command usage. type FlagInitializer interface { InitializeFlags(*flag.FlagSet) } diff --git a/getopt/example_stringsvar_test.go b/getopt/example_stringsvar_test.go index 69f62b7..3ff6d29 100644 --- a/getopt/example_stringsvar_test.go +++ b/getopt/example_stringsvar_test.go @@ -7,29 +7,34 @@ import ( "github.com/brandon1024/cmder/getopt" ) -// This example demonstrates usage of [getopt.StringsVar] for string slice flags. You'll often find string sliceee flags +// This example demonstrates usage of [getopt.StringsVar] for string slice flags. You'll often find string slice flags // on commands that accept IP addresses, for example. func ExampleStringsVar() { - hosts := getopt.StringsVar{} - fs := flag.NewFlagSet("stringsvar", flag.ContinueOnError) + + // option 1: use StringsVar directly + var hosts getopt.StringsVar fs.Var(&hosts, "broker", "connect to a broker") - fs.Var(&hosts, "b", "connect to a broker") - args := []string{ - "--broker", "tcp://127.0.0.1", - "-b", "tls://broker-1.domain.example.com,tls://broker-2.domain.example.com", - } + // option 2: wrap an existing slice + var args []string + fs.Var((*getopt.StringsVar)(&args), "a", "provide args") - if err := fs.Parse(args); err != nil { - panic(err) - } + fs.Parse([]string{ + "--broker", "tls://broker-1.domain.example.com,tls://broker-2.domain.example.com", + "-a", "CLIENT_USER", + "-a", "CLIENT_PASS", + }) for _, host := range hosts { - fmt.Printf("'%s'\n", host) + fmt.Printf("broker: '%s'\n", host) + } + for _, arg := range args { + fmt.Printf("arg: '%s'\n", arg) } // Output: - // 'tcp://127.0.0.1' - // 'tls://broker-1.domain.example.com' - // 'tls://broker-2.domain.example.com' + // broker: 'tls://broker-1.domain.example.com' + // broker: 'tls://broker-2.domain.example.com' + // arg: 'CLIENT_USER' + // arg: 'CLIENT_PASS' }