Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for setting a global env var prefix #258

Merged
merged 2 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 39 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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
```
Expand All @@ -100,7 +100,7 @@ arg.MustParse(&args)
fmt.Println("Workers:", args.Workers)
```

```
```shell
$ NUM_WORKERS=4 ./example
Workers: 4
```
Expand All @@ -115,7 +115,7 @@ arg.MustParse(&args)
fmt.Println("Workers:", args.Workers)
```

```
```shell
$ WORKERS='1,99' ./example
Workers: [1 99]
```
Expand All @@ -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"`
Expand Down Expand Up @@ -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"`
Expand All @@ -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
Expand All @@ -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"`
Expand All @@ -231,6 +255,7 @@ Databases [db1 db2 db3]
```

### Arguments with keys and values

```go
var args struct {
UserIDs map[string]int
Expand All @@ -245,6 +270,7 @@ map[john:123 mary:456]
```

### Version strings

```go
type args struct {
...
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -384,6 +409,7 @@ func main() {
fmt.Printf("%#v\n", args.Name)
}
```

```shell
$ ./example --name=foo.bar
main.NameDotName{Head:"foo", Tail:"bar"}
Expand Down Expand Up @@ -420,6 +446,7 @@ func main() {
fmt.Printf("%#v\n", args.Name)
}
```

```shell
$ ./example --help
Usage: test [--name NAME]
Expand All @@ -445,6 +472,7 @@ var args struct {
}
arg.MustParse(&args)
```

```shell
$ ./example -h
Usage: example [--optimize LEVEL] [--maxjobs N] SRC [DST [DST ...]]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -722,7 +749,8 @@ func main() {
p.WriteUsageForSubcommand(os.Stdout, p.SubcommandNames()...)
os.Exit(1)
}
}```
}
```

```shell
$ ./example --version
Expand Down
13 changes: 8 additions & 5 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
Loading
Loading