Skip to content

Commit

Permalink
Merge pull request #67 from steamrolla/master
Browse files Browse the repository at this point in the history
enable a short option for flags
  • Loading branch information
Geoffrey J. Teale authored Sep 2, 2019
2 parents 31dcf3b + 7bebbcd commit 1fd65b7
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 23 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,29 @@ type Config struct {

The `flags` backend allows to load individual configuration keys from the command line. The default values are extracted from the struct fields values.

A `short` option is also supported.

```go
type Config struct {
Host string `config:"host,short=h"`
Port uint32 `config:"port,short=p"`
Timeout time.Duration `config:"timeout"`
}
```


```sh
./bin -h

Usage of ./bin:
-host string
(default "127.0.0.1")
-h string
(default "127.0.0.1")
-port int
(default 5656)
-p int
(default 5656)
-timeout duration
(default 10s)
```
Expand Down
53 changes: 34 additions & 19 deletions backend/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,48 +32,63 @@ func (b *Backend) LoadStruct(ctx context.Context, cfg *confita.StructConfig) err
k := f.Value.Kind()
switch {
case f.Value.Type().String() == "time.Duration":
// define the flag and its default value
v := flag.Duration(f.Key, time.Duration(f.Default.Int()), "")
var val time.Duration
flag.DurationVar(&val, f.Key, time.Duration(f.Default.Int()), "")
if f.Short != "" {
flag.DurationVar(&val, f.Short, time.Duration(f.Default.Int()), "")
}
// this function must be executed after the flag.Parse call.
defer func() {
// if the user has set the flag, save the value in the field.
if isFlagSet(f.Key) {
f.Value.SetInt(int64(*v))
if isFlagSet(f) {
f.Value.SetInt(int64(val))
}
}()
case k == reflect.Bool:
v := flag.Bool(f.Key, f.Default.Bool(), "")
var val bool
flag.BoolVar(&val, f.Key, f.Default.Bool(), "")
if f.Short != "" {
flag.BoolVar(&val, f.Short, f.Default.Bool(), "")
}
defer func() {
if isFlagSet(f.Key) {
f.Value.SetBool(*v)
if isFlagSet(f) {
f.Value.SetBool(val)
}
}()
case k >= reflect.Int && k <= reflect.Int64:
v := flag.Int(f.Key, int(f.Default.Int()), "")
var val int
flag.IntVar(&val, f.Key, int(f.Default.Int()), "")
if f.Short != "" {
flag.IntVar(&val, f.Short, int(f.Default.Int()), "")
}
defer func() {
if isFlagSet(f.Key) {
f.Value.SetInt(int64(*v))
if isFlagSet(f) {
f.Value.SetInt(int64(val))
}
}()
case k >= reflect.Uint && k <= reflect.Uint64:
v := flag.Uint(f.Key, uint(f.Default.Uint()), "")
defer func() {
if isFlagSet(f.Key) {
if isFlagSet(f) {
f.Value.SetUint(uint64(*v))
}
}()
case k >= reflect.Float32 && k <= reflect.Float64:
v := flag.Float64(f.Key, f.Default.Float(), "")
defer func() {
if isFlagSet(f.Key) {
if isFlagSet(f) {
f.Value.SetFloat(*v)
}
}()
case k == reflect.String:
v := flag.String(f.Key, f.Default.String(), "")
var val string
flag.StringVar(&val, f.Key, f.Default.String(), "")
if f.Short != "" {
flag.StringVar(&val, f.Short, f.Default.String(), "")
}
defer func() {
if isFlagSet(f.Key) {
f.Value.SetString(*v)
if isFlagSet(f) {
f.Value.SetString(val)
}
}()
default:
Expand Down Expand Up @@ -112,10 +127,10 @@ func (b *Backend) Name() string {
return "flags"
}

func isFlagSet(name string) bool {
flagset := make(map[string]bool)
flag.Visit(func(f *flag.Flag) { flagset[f.Name] = true })
func isFlagSet(config *confita.FieldConfig) bool {
flagset := make(map[*confita.FieldConfig]bool)
flag.Visit(func(f *flag.Flag) { flagset[config] = true })

_, ok := flagset[name]
_, ok := flagset[config]
return ok
}
24 changes: 20 additions & 4 deletions backend/flags/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ import (

type Config struct {
A string `config:"a"`
Adef string `config:"a-def"`
Adef string `config:"a-def,short=ad"`
B bool `config:"b"`
Bdef bool `config:"b-def"`
Bdef bool `config:"b-def,short=bd"`
C time.Duration `config:"c"`
Cdef time.Duration `config:"c-def"`
Cdef time.Duration `config:"c-def,short=cd"`
D int `config:"d"`
Ddef int `config:"d-def"`
Ddef int `config:"d-def,short=dd"`
}

func runHelper(t *testing.T, args ...string) *Config {
Expand Down Expand Up @@ -65,6 +65,22 @@ func TestFlags(t *testing.T) {
})
}

func TestFlagsShort(t *testing.T) {
cfg := runHelper(t, "-ad=hello", "-bd=true", "-cd=20s", "-dd=500")
require.Equal(t, "hello", cfg.Adef)
require.Equal(t, true, cfg.Bdef)
require.Equal(t, 20*time.Second, cfg.Cdef)
require.Equal(t, 500, cfg.Ddef)
}

func TestFlagsMixed(t *testing.T) {
cfg := runHelper(t, "-ad=hello", "-b-def=true", "-cd=20s", "-d-def=500")
require.Equal(t, "hello", cfg.Adef)
require.Equal(t, true, cfg.Bdef)
require.Equal(t, 20*time.Second, cfg.Cdef)
require.Equal(t, 500, cfg.Ddef)
}

func TestHelperProcess(t *testing.T) {
if os.Getenv("GO_HELPER_PROCESS") != "1" {
return
Expand Down
11 changes: 11 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ func (l *Loader) parseStruct(ref reflect.Value) *StructConfig {
for _, opt := range opts {
if opt == "required" {
f.Required = true
continue
}

if strings.HasPrefix(opt, "short=") {
f.Short = opt[len("short="):]
continue
}

if strings.HasPrefix(opt, "backend=") {
Expand Down Expand Up @@ -169,6 +175,10 @@ func (l *Loader) resolve(ctx context.Context, s *StructConfig) error {
default:
}

if len(foundFields) == len(s.Fields) {
break
}

if u, ok := b.(Unmarshaler); ok {
err := u.Unmarshal(ctx, s.S)
if err != nil {
Expand Down Expand Up @@ -233,6 +243,7 @@ type StructConfig struct {
// FieldConfig holds informations about a struct field.
type FieldConfig struct {
Name string
Short string
Key string
Value reflect.Value
Default reflect.Value
Expand Down

0 comments on commit 1fd65b7

Please sign in to comment.