diff options
| author | Alex Flint <[email protected]> | 2024-11-04 12:58:37 -0500 | 
|---|---|---|
| committer | GitHub <[email protected]> | 2024-11-04 12:58:37 -0500 | 
| commit | 438bbfff1edeb3e65a0af4d385151788a5306644 (patch) | |
| tree | 1b4fe82e4194d7c95a823d62384a1a75bc51b624 | |
| parent | efb1be7122ca536544533937a831b54111eff19e (diff) | |
| parent | cb7e5c190570d24d0768224f08a11ce8bf607b41 (diff) | |
Merge pull request #258 from hhromic/implement-204
Add support for setting a global env var prefix
| -rw-r--r-- | README.md | 50 | ||||
| -rw-r--r-- | parse.go | 13 | ||||
| -rw-r--r-- | parse_test.go | 52 | 
3 files changed, 78 insertions, 37 deletions
@@ -64,7 +64,7 @@ fmt.Println("Input:", args.Input)  fmt.Println("Output:", args.Output)  ``` -``` +```shell  $ ./example src.txt x.out y.out z.out  Input: src.txt  Output: [x.out y.out z.out] @@ -80,12 +80,12 @@ arg.MustParse(&args)  fmt.Println("Workers:", args.Workers)  ``` -``` +```shell  $ WORKERS=4 ./example  Workers: 4  ``` -``` +```shell  $ WORKERS=4 ./example --workers=6  Workers: 6  ``` @@ -100,7 +100,7 @@ arg.MustParse(&args)  fmt.Println("Workers:", args.Workers)  ``` -``` +```shell  $ NUM_WORKERS=4 ./example  Workers: 4  ``` @@ -115,7 +115,7 @@ arg.MustParse(&args)  fmt.Println("Workers:", args.Workers)  ``` -``` +```shell  $ WORKERS='1,99' ./example  Workers: [1 99]  ``` @@ -130,14 +130,35 @@ arg.MustParse(&args)  fmt.Println("Workers:", args.Workers)  ``` -``` +```shell  $ NUM_WORKERS=6 ./example  Workers: 6  $ NUM_WORKERS=6 ./example --count 4  Workers: 4  ``` +Configuring a global environment variable name prefix is also possible: + +```go +var args struct { +	Workers int `arg:"--count,env:NUM_WORKERS"` +} + +p, err := arg.NewParser(arg.Config{ +    EnvPrefix: "MYAPP_", +}, &args) + +p.MustParse(os.Args[1:]) +fmt.Println("Workers:", args.Workers) +``` + +```shell +$ MYAPP_NUM_WORKERS=6 ./example +Workers: 6 +``` +  ### Usage strings +  ```go  var args struct {  	Input    string   `arg:"positional"` @@ -185,6 +206,7 @@ arg.MustParse(&args)  ```  #### Ignoring environment variables and/or default values +  ```go  var args struct {      Test  string `arg:"-t,env:TEST" default:"something"` @@ -195,10 +217,11 @@ p, err := arg.NewParser(arg.Config{      IgnoreDefault: true,  }, &args) -err = p.Parse(os.Args) +err = p.Parse(os.Args[1:])  ```  ### Arguments with multiple values +  ```go  var args struct {  	Database string @@ -214,6 +237,7 @@ Fetching the following IDs from foo: [1 2 3]  ```  ### Arguments that can be specified multiple times, mixed with positionals +  ```go  var args struct {      Commands  []string `arg:"-c,separate"` @@ -231,6 +255,7 @@ Databases [db1 db2 db3]  ```  ### Arguments with keys and values +  ```go  var args struct {  	UserIDs map[string]int @@ -245,6 +270,7 @@ map[john:123 mary:456]  ```  ### Version strings +  ```go  type args struct {  	... @@ -269,6 +295,7 @@ someprogram 4.3.0  > If a `--version` flag is defined in `args` or any subcommand, it overrides the built-in versioning.  ### Custom validation +  ```go  var args struct {  	Foo string @@ -310,13 +337,11 @@ Options:    --help, -h             display this help and exit  ``` -  ### Embedded structs  The fields of embedded structs are treated just like regular fields:  ```go -  type DatabaseOptions struct {  	Host     string  	Username string @@ -384,6 +409,7 @@ func main() {  	fmt.Printf("%#v\n", args.Name)  }  ``` +  ```shell  $ ./example --name=foo.bar  main.NameDotName{Head:"foo", Tail:"bar"} @@ -420,6 +446,7 @@ func main() {  	fmt.Printf("%#v\n", args.Name)  }  ``` +  ```shell  $ ./example --help  Usage: test [--name NAME] @@ -445,6 +472,7 @@ var args struct {  }  arg.MustParse(&args)  ``` +  ```shell  $ ./example -h  Usage: example [--optimize LEVEL] [--maxjobs N] SRC [DST [DST ...]] @@ -581,7 +609,6 @@ if p.Subcommand() == nil {  }  ``` -  ### Custom handling of --help and --version  The following reproduces the internal logic of `MustParse` for the simple case where @@ -722,7 +749,8 @@ func main() {  		p.WriteUsageForSubcommand(os.Stdout, p.SubcommandNames()...)  		os.Exit(1)  	} -}``` +} +```  ```shell  $ ./example --version @@ -131,6 +131,9 @@ type Config struct {  	// subcommand  	StrictSubcommands bool +	// EnvPrefix instructs the library to use a name prefix when reading environment variables. +	EnvPrefix string +  	// Exit is called to terminate the process with an error code (defaults to os.Exit)  	Exit func(int) @@ -235,7 +238,7 @@ func NewParser(config Config, dests ...interface{}) (*Parser, error) {  			panic(fmt.Sprintf("%s is not a pointer (did you forget an ampersand?)", t))  		} -		cmd, err := cmdFromStruct(name, path{root: i}, t) +		cmd, err := cmdFromStruct(name, path{root: i}, t, config.EnvPrefix)  		if err != nil {  			return nil, err  		} @@ -285,7 +288,7 @@ func NewParser(config Config, dests ...interface{}) (*Parser, error) {  	return &p, nil  } -func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) { +func cmdFromStruct(name string, dest path, t reflect.Type, envPrefix string) (*command, error) {  	// commands can only be created from pointers to structs  	if t.Kind() != reflect.Ptr {  		return nil, fmt.Errorf("subcommands must be pointers to structs but %s is a %s", @@ -372,9 +375,9 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) {  			case key == "env":  				// Use override name if provided  				if value != "" { -					spec.env = value +					spec.env = envPrefix + value  				} else { -					spec.env = strings.ToUpper(field.Name) +					spec.env = envPrefix + strings.ToUpper(field.Name)  				}  			case key == "subcommand":  				// decide on a name for the subcommand @@ -389,7 +392,7 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) {  				}  				// parse the subcommand recursively -				subcmd, err := cmdFromStruct(cmdnames[0], subdest, field.Type) +				subcmd, err := cmdFromStruct(cmdnames[0], subdest, field.Type, envPrefix)  				if err != nil {  					errs = append(errs, err.Error())  					return false diff --git a/parse_test.go b/parse_test.go index 7e9cbf9..249cbf3 100644 --- a/parse_test.go +++ b/parse_test.go @@ -28,11 +28,11 @@ func parse(cmdline string, dest interface{}) error {  }  func pparse(cmdline string, dest interface{}) (*Parser, error) { -	return parseWithEnv(cmdline, nil, dest) +	return parseWithEnv(Config{}, cmdline, nil, dest)  } -func parseWithEnv(cmdline string, env []string, dest interface{}) (*Parser, error) { -	p, err := NewParser(Config{}, dest) +func parseWithEnv(config Config, cmdline string, env []string, dest interface{}) (*Parser, error) { +	p, err := NewParser(config, dest)  	if err != nil {  		return nil, err  	} @@ -231,7 +231,7 @@ func TestRequiredWithEnvOnly(t *testing.T) {  	var args struct {  		Foo string `arg:"required,--,-,env:FOO"`  	} -	_, err := parseWithEnv("", []string{}, &args) +	_, err := parseWithEnv(Config{}, "", []string{}, &args)  	require.Error(t, err, "environment variable FOO is required")  } @@ -720,7 +720,7 @@ func TestEnvironmentVariable(t *testing.T) {  	var args struct {  		Foo string `arg:"env"`  	} -	_, err := parseWithEnv("", []string{"FOO=bar"}, &args) +	_, err := parseWithEnv(Config{}, "", []string{"FOO=bar"}, &args)  	require.NoError(t, err)  	assert.Equal(t, "bar", args.Foo)  } @@ -729,7 +729,7 @@ func TestEnvironmentVariableNotPresent(t *testing.T) {  	var args struct {  		NotPresent string `arg:"env"`  	} -	_, err := parseWithEnv("", nil, &args) +	_, err := parseWithEnv(Config{}, "", nil, &args)  	require.NoError(t, err)  	assert.Equal(t, "", args.NotPresent)  } @@ -738,7 +738,7 @@ func TestEnvironmentVariableOverrideName(t *testing.T) {  	var args struct {  		Foo string `arg:"env:BAZ"`  	} -	_, err := parseWithEnv("", []string{"BAZ=bar"}, &args) +	_, err := parseWithEnv(Config{}, "", []string{"BAZ=bar"}, &args)  	require.NoError(t, err)  	assert.Equal(t, "bar", args.Foo)  } @@ -747,7 +747,7 @@ func TestEnvironmentVariableOverrideArgument(t *testing.T) {  	var args struct {  		Foo string `arg:"env"`  	} -	_, err := parseWithEnv("--foo zzz", []string{"FOO=bar"}, &args) +	_, err := parseWithEnv(Config{}, "--foo zzz", []string{"FOO=bar"}, &args)  	require.NoError(t, err)  	assert.Equal(t, "zzz", args.Foo)  } @@ -756,7 +756,7 @@ func TestEnvironmentVariableError(t *testing.T) {  	var args struct {  		Foo int `arg:"env"`  	} -	_, err := parseWithEnv("", []string{"FOO=bar"}, &args) +	_, err := parseWithEnv(Config{}, "", []string{"FOO=bar"}, &args)  	assert.Error(t, err)  } @@ -764,7 +764,7 @@ func TestEnvironmentVariableRequired(t *testing.T) {  	var args struct {  		Foo string `arg:"env,required"`  	} -	_, err := parseWithEnv("", []string{"FOO=bar"}, &args) +	_, err := parseWithEnv(Config{}, "", []string{"FOO=bar"}, &args)  	require.NoError(t, err)  	assert.Equal(t, "bar", args.Foo)  } @@ -773,7 +773,7 @@ func TestEnvironmentVariableSliceArgumentString(t *testing.T) {  	var args struct {  		Foo []string `arg:"env"`  	} -	_, err := parseWithEnv("", []string{`FOO=bar,"baz, qux"`}, &args) +	_, err := parseWithEnv(Config{}, "", []string{`FOO=bar,"baz, qux"`}, &args)  	require.NoError(t, err)  	assert.Equal(t, []string{"bar", "baz, qux"}, args.Foo)  } @@ -782,7 +782,7 @@ func TestEnvironmentVariableSliceEmpty(t *testing.T) {  	var args struct {  		Foo []string `arg:"env"`  	} -	_, err := parseWithEnv("", []string{`FOO=`}, &args) +	_, err := parseWithEnv(Config{}, "", []string{`FOO=`}, &args)  	require.NoError(t, err)  	assert.Len(t, args.Foo, 0)  } @@ -791,7 +791,7 @@ func TestEnvironmentVariableSliceArgumentInteger(t *testing.T) {  	var args struct {  		Foo []int `arg:"env"`  	} -	_, err := parseWithEnv("", []string{`FOO=1,99`}, &args) +	_, err := parseWithEnv(Config{}, "", []string{`FOO=1,99`}, &args)  	require.NoError(t, err)  	assert.Equal(t, []int{1, 99}, args.Foo)  } @@ -800,7 +800,7 @@ func TestEnvironmentVariableSliceArgumentFloat(t *testing.T) {  	var args struct {  		Foo []float32 `arg:"env"`  	} -	_, err := parseWithEnv("", []string{`FOO=1.1,99.9`}, &args) +	_, err := parseWithEnv(Config{}, "", []string{`FOO=1.1,99.9`}, &args)  	require.NoError(t, err)  	assert.Equal(t, []float32{1.1, 99.9}, args.Foo)  } @@ -809,7 +809,7 @@ func TestEnvironmentVariableSliceArgumentBool(t *testing.T) {  	var args struct {  		Foo []bool `arg:"env"`  	} -	_, err := parseWithEnv("", []string{`FOO=true,false,0,1`}, &args) +	_, err := parseWithEnv(Config{}, "", []string{`FOO=true,false,0,1`}, &args)  	require.NoError(t, err)  	assert.Equal(t, []bool{true, false, false, true}, args.Foo)  } @@ -818,7 +818,7 @@ func TestEnvironmentVariableSliceArgumentWrongCsv(t *testing.T) {  	var args struct {  		Foo []int `arg:"env"`  	} -	_, err := parseWithEnv("", []string{`FOO=1,99\"`}, &args) +	_, err := parseWithEnv(Config{}, "", []string{`FOO=1,99\"`}, &args)  	assert.Error(t, err)  } @@ -826,7 +826,7 @@ func TestEnvironmentVariableSliceArgumentWrongType(t *testing.T) {  	var args struct {  		Foo []bool `arg:"env"`  	} -	_, err := parseWithEnv("", []string{`FOO=one,two`}, &args) +	_, err := parseWithEnv(Config{}, "", []string{`FOO=one,two`}, &args)  	assert.Error(t, err)  } @@ -834,7 +834,7 @@ func TestEnvironmentVariableMap(t *testing.T) {  	var args struct {  		Foo map[int]string `arg:"env"`  	} -	_, err := parseWithEnv("", []string{`FOO=1=one,99=ninetynine`}, &args) +	_, err := parseWithEnv(Config{}, "", []string{`FOO=1=one,99=ninetynine`}, &args)  	require.NoError(t, err)  	assert.Len(t, args.Foo, 2)  	assert.Equal(t, "one", args.Foo[1]) @@ -845,11 +845,21 @@ func TestEnvironmentVariableEmptyMap(t *testing.T) {  	var args struct {  		Foo map[int]string `arg:"env"`  	} -	_, err := parseWithEnv("", []string{`FOO=`}, &args) +	_, err := parseWithEnv(Config{}, "", []string{`FOO=`}, &args)  	require.NoError(t, err)  	assert.Len(t, args.Foo, 0)  } +func TestEnvironmentVariableWithPrefix(t *testing.T) { +	var args struct { +		Foo string `arg:"env"` +	} + +	_, err := parseWithEnv(Config{EnvPrefix: "MYAPP_"}, "", []string{"MYAPP_FOO=bar"}, &args) +	require.NoError(t, err) +	assert.Equal(t, "bar", args.Foo) +} +  func TestEnvironmentVariableIgnored(t *testing.T) {  	var args struct {  		Foo string `arg:"env"` @@ -882,7 +892,7 @@ func TestRequiredEnvironmentOnlyVariableIsMissing(t *testing.T) {  		Foo string `arg:"required,--,env:FOO"`  	} -	_, err := parseWithEnv("", []string{""}, &args) +	_, err := parseWithEnv(Config{}, "", []string{""}, &args)  	assert.Error(t, err)  } @@ -891,7 +901,7 @@ func TestOptionalEnvironmentOnlyVariable(t *testing.T) {  		Foo string `arg:"env:FOO"`  	} -	_, err := parseWithEnv("", []string{}, &args) +	_, err := parseWithEnv(Config{}, "", []string{}, &args)  	assert.NoError(t, err)  }  | 
