Add support for environment variables

This commit is contained in:
brettlangdon 2016-01-18 13:42:04 -05:00
parent c9155bb0c3
commit 8dd29d34bf
4 changed files with 107 additions and 2 deletions

View File

@ -54,6 +54,41 @@ Input: src.txt
Output: [x.out y.out z.out]
```
### Environment variables
```go
var args struct {
Workers int `arg:"env"`
}
arg.MustParse(&args)
fmt.Println("Workers:", args.Workers)
```
```
$ WORKERS=4 ./example
Workers: 4
```
```
$ WORKERS=4 ./example --workers=6
Workers: 6
```
You can also override the name of the environment variable:
```go
var args struct {
Workers int `arg:"env:NUM_WORKERS"`
}
arg.MustParse(&args)
fmt.Println("Workers:", args.Workers)
```
```
$ NUM_WORKERS=4 ./example
Workers: 4
```
### Usage strings
```go
var args struct {

View File

@ -18,6 +18,7 @@ type spec struct {
required bool
positional bool
help string
env string
wasPresent bool
isBool bool
}
@ -130,6 +131,13 @@ func NewParser(dests ...interface{}) (*Parser, error) {
spec.positional = true
case key == "help":
spec.help = value
case key == "env":
// Use override name if provided
if value != "" {
spec.env = value
} else {
spec.env = strings.ToUpper(field.Name)
}
default:
return nil, fmt.Errorf("unrecognized tag '%s' on field %s", key, tag)
}
@ -179,6 +187,15 @@ func process(specs []*spec, args []string) error {
if spec.short != "" {
optionMap[spec.short] = spec
}
if spec.env != "" {
if value, found := os.LookupEnv(spec.env); found {
err := setScalar(spec.dest, value)
if err != nil {
return fmt.Errorf("error processing environment variable %s: %v", spec.env, err)
}
spec.wasPresent = true
}
}
}
// process each string from the command line

View File

@ -357,3 +357,53 @@ func TestMustParse(t *testing.T) {
assert.Equal(t, "bar", args.Foo)
assert.NotNil(t, parser)
}
func TestEnvironmentVariable(t *testing.T) {
var args struct {
Foo string `arg:"env"`
}
os.Setenv("FOO", "bar")
os.Args = []string{"example"}
MustParse(&args)
assert.Equal(t, "bar", args.Foo)
}
func TestEnvironmentVariableOverrideName(t *testing.T) {
var args struct {
Foo string `arg:"env:BAZ"`
}
os.Setenv("BAZ", "bar")
os.Args = []string{"example"}
MustParse(&args)
assert.Equal(t, "bar", args.Foo)
}
func TestEnvironmentVariableOverrideArgument(t *testing.T) {
var args struct {
Foo string `arg:"env"`
}
os.Setenv("FOO", "bar")
os.Args = []string{"example", "--foo", "baz"}
MustParse(&args)
assert.Equal(t, "baz", args.Foo)
}
func TestEnvironmentVariableError(t *testing.T) {
var args struct {
Foo int `arg:"env"`
}
os.Setenv("FOO", "bar")
os.Args = []string{"example"}
err := Parse(&args)
assert.Error(t, err)
}
func TestEnvironmentVariableRequired(t *testing.T) {
var args struct {
Foo string `arg:"env,required"`
}
os.Setenv("FOO", "bar")
os.Args = []string{"example"}
MustParse(&args)
assert.Equal(t, "bar", args.Foo)
}

View File

@ -10,9 +10,9 @@ import (
)
func TestWriteUsage(t *testing.T) {
expectedUsage := "usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] INPUT [OUTPUT [OUTPUT ...]]\n"
expectedUsage := "usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--workers WORKERS] INPUT [OUTPUT [OUTPUT ...]]\n"
expectedHelp := `usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] INPUT [OUTPUT [OUTPUT ...]]
expectedHelp := `usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--workers WORKERS] INPUT [OUTPUT [OUTPUT ...]]
positional arguments:
input
@ -26,6 +26,8 @@ options:
--optimize OPTIMIZE, -O OPTIMIZE
optimization level
--ids IDS Ids
--workers WORKERS, -w WORKERS
number of workers to start
--help, -h display this help and exit
`
var args struct {
@ -37,6 +39,7 @@ options:
Dataset string `arg:"help:dataset to use"`
Optimize int `arg:"-O,help:optimization level"`
Ids []int64 `arg:"help:Ids"`
Workers int `arg:"-w,env:WORKERS,help:number of workers to start"`
}
args.Name = "Foo Bar"
args.Value = 42