Add support for setting a global env var prefix
This commit is contained in:
parent
b218ad854d
commit
9b5c76b1c4
13
parse.go
13
parse.go
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
||||
|
@ -711,7 +711,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)
|
||||
}
|
||||
|
@ -720,7 +720,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)
|
||||
}
|
||||
|
@ -729,7 +729,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)
|
||||
}
|
||||
|
@ -738,7 +738,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)
|
||||
}
|
||||
|
@ -747,7 +747,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)
|
||||
}
|
||||
|
||||
|
@ -755,7 +755,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)
|
||||
}
|
||||
|
@ -764,7 +764,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)
|
||||
}
|
||||
|
@ -773,7 +773,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)
|
||||
}
|
||||
|
@ -782,7 +782,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)
|
||||
}
|
||||
|
@ -791,7 +791,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)
|
||||
}
|
||||
|
@ -800,7 +800,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)
|
||||
}
|
||||
|
@ -809,7 +809,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)
|
||||
}
|
||||
|
||||
|
@ -817,7 +817,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)
|
||||
}
|
||||
|
||||
|
@ -825,7 +825,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])
|
||||
|
@ -836,11 +836,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"`
|
||||
|
@ -873,7 +883,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)
|
||||
}
|
||||
|
||||
|
@ -882,7 +892,7 @@ func TestOptionalEnvironmentOnlyVariable(t *testing.T) {
|
|||
Foo string `arg:"env:FOO"`
|
||||
}
|
||||
|
||||
_, err := parseWithEnv("", []string{}, &args)
|
||||
_, err := parseWithEnv(Config{}, "", []string{}, &args)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue