Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • master
  • v1.0.0
  • v1.1.0
  • v1.1.1
  • v1.10.0
  • v1.10.1
  • v1.10.2
  • v1.11.0
  • v1.12.0
  • v1.13.0
  • v1.13.1
  • v1.13.2
  • v1.14.0
  • v1.15.0
  • v1.16.0
  • v1.16.1
  • v1.16.2
  • v1.16.3
  • v1.16.4
  • v1.16.5
  • v1.2.0
  • v1.2.1
  • v1.2.2
  • v1.2.3
  • v1.3.0
  • v1.3.1
  • v1.4.0
  • v1.5.0
  • v1.6.0
  • v1.7.0
  • v1.8.0
  • v1.8.1
  • v1.8.2
  • v1.8.3
  • v1.9.0
35 results

Target

Select target project
  • oss/libraries/go/application/xflags
1 result
Select Git revision
  • master
  • v1.0.0
  • v1.1.0
  • v1.1.1
  • v1.10.0
  • v1.10.1
  • v1.10.2
  • v1.11.0
  • v1.12.0
  • v1.13.0
  • v1.13.1
  • v1.13.2
  • v1.14.0
  • v1.15.0
  • v1.16.0
  • v1.16.1
  • v1.16.2
  • v1.16.3
  • v1.16.4
  • v1.16.5
  • v1.2.0
  • v1.2.1
  • v1.2.2
  • v1.2.3
  • v1.3.0
  • v1.3.1
  • v1.4.0
  • v1.5.0
  • v1.6.0
  • v1.7.0
  • v1.8.0
  • v1.8.1
  • v1.8.2
  • v1.8.3
  • v1.9.0
35 results
Show changes
Commits on Source (3)
<a name="v1.1.0"></a>
## [v1.1.0] - 2022-10-05
<a name="v1.0.0"></a> <a name="v1.0.0"></a>
## v1.0.0 - 2022-10-04 ## v1.0.0 - 2022-10-04
[v1.1.0]: https://gitlab.schukai.com/oss/libraries/go/application/configuration/compare/v1.0.0...v1.1.0
## X-Flags ## X-Flags
## What do this library? ## What does this library?
This library provides a simple way to use flags in your application. It extends the standard library This library provides a simple way to use flags in your application. It extends the standard library
to be able to define and use a structure with flags. to be able to define and use a structure with flags.
...@@ -25,8 +25,8 @@ go get gitlab.schukai.com/oss/libraries/go/network/xflags ...@@ -25,8 +25,8 @@ go get gitlab.schukai.com/oss/libraries/go/network/xflags
### Initialize ### Initialize
A new flag set is created using the `xflags.New()` function. The passed A new flag set is created using the `xflags.New()` function.
structure is used type for the flags. The structure passed is used as the type for the flags.
```go ```go
package main package main
......
...@@ -17,7 +17,7 @@ func New[C any](name string, definitions C) *setting[C] { ...@@ -17,7 +17,7 @@ func New[C any](name string, definitions C) *setting[C] {
errorHandling: flag.ContinueOnError, errorHandling: flag.ContinueOnError,
}, },
} }
if reflect.TypeOf(definitions).Kind() != reflect.Struct { if reflect.TypeOf(definitions).Kind() != reflect.Struct {
s.errors = append(s.errors, newUnsupportedReflectKindError(reflect.TypeOf(definitions))) s.errors = append(s.errors, newUnsupportedReflectKindError(reflect.TypeOf(definitions)))
return s return s
...@@ -40,3 +40,24 @@ func (s *setting[C]) FlagOutput() string { ...@@ -40,3 +40,24 @@ func (s *setting[C]) FlagOutput() string {
func (s *setting[C]) Args() []string { func (s *setting[C]) Args() []string {
return s.args return s.args
} }
//type ConfigAny[C any] struct {
// path string
// ConfigurationAdapter[C]
//}
//
//type ConfigurationAdapter[RT any] interface {
// InitFromFlagSet(flagSet *flag.FlagSet) RT
//}
//func (s *setting[C]) CopyValuesToStruct(path string, adapter any) *setting[C] {
//
// if s.command == nil {
// s.errors = append(s.errors, MissingCommandError)
// return s
// }
//
// s.command.copyValuesToStruct(path, adapter)
//
// return s
//}
...@@ -5,23 +5,15 @@ import ( ...@@ -5,23 +5,15 @@ import (
"reflect" "reflect"
) )
const (
tagIgnore = "ignore"
tagCall = "call"
tagCommand = "command"
tagShort = "short"
tagLong = "long"
tagDescription = "description"
)
type cmd[C any] struct { type cmd[C any] struct {
name string name string
flagSet *flag.FlagSet flagSet *flag.FlagSet
mapping map[string]string tagMapping map[string]string
commands []*cmd[C] shadowMapping map[string]string
settings *setting[C] commands []*cmd[C]
valuePath []string settings *setting[C]
functionName string valuePath []string
functionName string
} }
func (c *cmd[C]) parse(args []string) { func (c *cmd[C]) parse(args []string) {
...@@ -61,13 +53,14 @@ func (c *cmd[C]) parse(args []string) { ...@@ -61,13 +53,14 @@ func (c *cmd[C]) parse(args []string) {
func buildCommandStruct[C any](s *setting[C], name, fkt string, errorHandling flag.ErrorHandling, path []string) *cmd[C] { func buildCommandStruct[C any](s *setting[C], name, fkt string, errorHandling flag.ErrorHandling, path []string) *cmd[C] {
cc := &cmd[C]{ cc := &cmd[C]{
name: name, name: name,
flagSet: flag.NewFlagSet(name, errorHandling), flagSet: flag.NewFlagSet(name, errorHandling),
commands: []*cmd[C]{}, commands: []*cmd[C]{},
settings: s, settings: s,
mapping: map[string]string{}, tagMapping: map[string]string{},
valuePath: path, shadowMapping: map[string]string{},
functionName: fkt, valuePath: path,
functionName: fkt,
} }
cc.flagSet.SetOutput(s.flagOutput) cc.flagSet.SetOutput(s.flagOutput)
...@@ -155,18 +148,22 @@ func (c *cmd[C]) parseStruct(dta any) { ...@@ -155,18 +148,22 @@ func (c *cmd[C]) parseStruct(dta any) {
} }
if m[tagShort] != "" { if m[tagShort] != "" {
c.mapping[m[tagShort]] = v.Type().Field(i).Name c.tagMapping[m[tagShort]] = v.Type().Field(i).Name
} }
if m[tagLong] != "" { if m[tagLong] != "" {
c.mapping[m[tagLong]] = v.Type().Field(i).Name c.tagMapping[m[tagLong]] = v.Type().Field(i).Name
}
if m[tagShadow] != "" {
c.shadowMapping[v.Type().Field(i).Name] = m[tagShadow]
} }
c.initFlags(x, m) c.initFlags(x, m)
} else if m[tagCommand] != "" { } else if m[tagCommand] != "" {
//c.valuePath = append(c.valuePath, ) //c.valuePath = append(c.valuePath, )
c.mapping[m[tagCommand]] = v.Type().Field(i).Name c.tagMapping[m[tagCommand]] = v.Type().Field(i).Name
c.initCommands(x, m, v.Type().Field(i).Name) c.initCommands(x, m, v.Type().Field(i).Name)
} else if m[tagIgnore] != "" { } else if m[tagIgnore] != "" {
......
...@@ -25,6 +25,7 @@ func (s *setting[C]) Errors() []error { ...@@ -25,6 +25,7 @@ func (s *setting[C]) Errors() []error {
var WatchListNotInitializedError = errors.New("watch list not initialized") var WatchListNotInitializedError = errors.New("watch list not initialized")
var MissingCommandError = errors.New("missing command") var MissingCommandError = errors.New("missing command")
var NotParsedError = errors.New("flag set not parsed") var NotParsedError = errors.New("flag set not parsed")
var ShadowMustBePointerError = errors.New("shadow must be a pointer to a struct")
// At the reflect level, some types are not supported // At the reflect level, some types are not supported
type UnsupportedReflectKindError error type UnsupportedReflectKindError error
......
...@@ -9,11 +9,12 @@ import ( ...@@ -9,11 +9,12 @@ import (
type testExecutionStruct struct { type testExecutionStruct struct {
callbackCounter int `ignore:"true"` callbackCounter int `ignore:"true"`
Global1 bool `short:"a" long:"global1" description:"Global 1"` // for tag shadow see TestFlagCopyToShadow
Global1 bool `short:"a" long:"global1" description:"Global 1" shadow:"ValGlobal1"`
Global2 bool `short:"b" long:"global2" description:"Global 2"` Global2 bool `short:"b" long:"global2" description:"Global 2"`
Command1 struct { Command1 struct {
Command1Flag1 bool `short:"c" long:"command1flag1" description:"Command 1 Flag 1"` Command1Flag1 bool `short:"c" long:"command1flag1" description:"Command 1 Flag 1"`
Command1Flag2 bool `short:"d" long:"command1flag2" description:"Command 1 Flag 2"` Command1Flag2 bool `short:"d" long:"command1flag2" description:"Command 1 Flag 2" shadow:"ValCommand1Flag2"`
Command2 struct { Command2 struct {
Command2Flag1 bool `short:"e" long:"command2flag1" description:"Command 2 Flag 1"` Command2Flag1 bool `short:"e" long:"command2flag1" description:"Command 2 Flag 1"`
Command2Flag2 bool `short:"f" long:"command2flag2" description:"Command 2 Flag 2"` Command2Flag2 bool `short:"f" long:"command2flag2" description:"Command 2 Flag 2"`
......
...@@ -5,35 +5,6 @@ import ( ...@@ -5,35 +5,6 @@ import (
"strings" "strings"
) )
// Parse parses the command line arguments and assigns the values to the settings.
func (s *setting[C]) Parse(args []string) *setting[C] {
if len(s.errors) > 0 {
return s
}
if s.command == nil {
s.errors = append(s.errors, MissingCommandError)
return s
}
err := s.command.flagSet.Parse(args[1:])
if err != nil {
s.errors = append(s.errors, err)
return s
}
s.assignValues(*s.command)
r := s.command.flagSet.Args()
if len(r) == 0 {
return s
}
s.command.parse(r)
return s
}
func (s *setting[C]) assignValues(c cmd[C]) { func (s *setting[C]) assignValues(c cmd[C]) {
flgs := c.flagSet flgs := c.flagSet
flgs.Visit(func(f *flag.Flag) { flgs.Visit(func(f *flag.Flag) {
...@@ -41,7 +12,7 @@ func (s *setting[C]) assignValues(c cmd[C]) { ...@@ -41,7 +12,7 @@ func (s *setting[C]) assignValues(c cmd[C]) {
name := f.Name name := f.Name
value := f.Value.String() value := f.Value.String()
k, ok := c.mapping[name] k, ok := c.tagMapping[name]
if !ok { if !ok {
s.errors = append(s.errors, newUnknownFlagError(name)) s.errors = append(s.errors, newUnknownFlagError(name))
return return
...@@ -50,7 +21,12 @@ func (s *setting[C]) assignValues(c cmd[C]) { ...@@ -50,7 +21,12 @@ func (s *setting[C]) assignValues(c cmd[C]) {
pa := append(c.valuePath, k) pa := append(c.valuePath, k)
p := strings.Join(pa, ".") p := strings.Join(pa, ".")
err := setTheValueOverPath(&s.definitions, p, value) err := setValueUsingPath(&s.definitions, p, value)
if err != nil {
s.errors = append(s.errors, err)
}
err = c.setShadowValue(s.shadow, k, value)
if err != nil { if err != nil {
s.errors = append(s.errors, err) s.errors = append(s.errors, err)
} }
...@@ -60,3 +36,18 @@ func (s *setting[C]) assignValues(c cmd[C]) { ...@@ -60,3 +36,18 @@ func (s *setting[C]) assignValues(c cmd[C]) {
}) })
} }
func (c cmd[C]) setShadowValue(obj any, k string, value string) error {
if obj == nil {
return nil
}
// set shadow
n, ok := c.shadowMapping[k]
if !ok {
return nil
}
return setValueUsingPath(obj, n, value)
}
package xflags
// Parse parses the command line arguments and assigns the values to the settings.
func (s *setting[C]) Parse(args []string) *setting[C] {
if len(s.errors) > 0 {
return s
}
if s.command == nil {
s.errors = append(s.errors, MissingCommandError)
return s
}
err := s.command.flagSet.Parse(args[1:])
if err != nil {
s.errors = append(s.errors, err)
return s
}
s.assignValues(*s.command)
r := s.command.flagSet.Args()
if len(r) == 0 {
return s
}
s.command.parse(r)
return s
}
...@@ -42,7 +42,7 @@ func getValueFrom[D any](obj D, keyWithDots string) (interface{}, error) { ...@@ -42,7 +42,7 @@ func getValueFrom[D any](obj D, keyWithDots string) (interface{}, error) {
} }
// This function sets the value of a field in a struct, given a path to the field. // This function sets the value of a field in a struct, given a path to the field.
func setTheValueOverPath[D any](obj D, keyWithDots string, newValue string) error { func setValueUsingPath[D any](obj D, keyWithDots string, newValue string) error {
keySlice := strings.Split(keyWithDots, ".") keySlice := strings.Split(keyWithDots, ".")
v := reflect.ValueOf(obj) v := reflect.ValueOf(obj)
......
...@@ -55,7 +55,7 @@ func TestPathFindSetValueString(t *testing.T) { ...@@ -55,7 +55,7 @@ func TestPathFindSetValueString(t *testing.T) {
for k, v := range testData { for k, v := range testData {
s := &PathfindTestStruct1{} s := &PathfindTestStruct1{}
err := setTheValueOverPath[*PathfindTestStruct1](s, k, v) err := setValueUsingPath[*PathfindTestStruct1](s, k, v)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
...@@ -195,10 +195,10 @@ func TestPathFindGetValueFrom(t *testing.T) { ...@@ -195,10 +195,10 @@ func TestPathFindGetValueFrom(t *testing.T) {
func TestPathFindSetValueFrom(t *testing.T) { func TestPathFindSetValueFrom(t *testing.T) {
s := &PathfindTestStruct1{} s := &PathfindTestStruct1{}
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.B", "true") setValueUsingPath[*PathfindTestStruct1](s, "Sub1.B", "true")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Bi", "2") setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Bi", "2")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Bs", "3") setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Bs", "3")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Bf", "4.0") setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Bf", "4.0")
if s.Sub1.B != true { if s.Sub1.B != true {
t.Error("s.Sub1.B != true") t.Error("s.Sub1.B != true")
...@@ -217,10 +217,10 @@ func TestPathFindSetValueFrom(t *testing.T) { ...@@ -217,10 +217,10 @@ func TestPathFindSetValueFrom(t *testing.T) {
t.Error("s.Sub1.Bf != 4.0") t.Error("s.Sub1.Bf != 4.0")
} }
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.C", "true") setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.C", "true")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.Ci", "2") setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Ci", "2")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.Cs", "3") setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Cs", "3")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.Cf", "4.0") setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Cf", "4.0")
if s.Sub1.Sub2.C != true { if s.Sub1.Sub2.C != true {
t.Error("s.Sub1.Sub2.C != true") t.Error("s.Sub1.Sub2.C != true")
...@@ -247,10 +247,10 @@ func TestPathFindSetValueFrom(t *testing.T) { ...@@ -247,10 +247,10 @@ func TestPathFindSetValueFrom(t *testing.T) {
} }
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.D", "true") setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.D", "true")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Di", "2") setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Di", "2")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Ds", "3") setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Ds", "3")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Df", "4.0") setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Df", "4.0")
if s.Sub1.Sub2.Sub3.D != true { if s.Sub1.Sub2.Sub3.D != true {
t.Error("s.Sub1.Sub2.Sub3.D != true") t.Error("s.Sub1.Sub2.Sub3.D != true")
......
{"version":"1.0.0"} {"version":"1.1.0"}
...@@ -25,6 +25,8 @@ type setting[C any] struct { ...@@ -25,6 +25,8 @@ type setting[C any] struct {
args []string args []string
config config config config
shadow any
} }
func (s *setting[C]) GetValues() C { func (s *setting[C]) GetValues() C {
......
...@@ -21,5 +21,5 @@ func TestInitCommands(t *testing.T) { ...@@ -21,5 +21,5 @@ func TestInitCommands(t *testing.T) {
assert.Equal(t, 0, len(s.errors)) assert.Equal(t, 0, len(s.errors))
assert.Equal(t, "test", s.command.name) assert.Equal(t, "test", s.command.name)
assert.Equal(t, 1, len(s.command.commands)) assert.Equal(t, 1, len(s.command.commands))
assert.Equal(t, 3, len(s.command.mapping)) assert.Equal(t, 3, len(s.command.tagMapping))
} }
package xflags
import "reflect"
// SetShadow sets the shadow struct for the flag configuration.
func (s *setting[C]) SetShadow(shadow any) *setting[C] {
if reflect.TypeOf(shadow).Kind() != reflect.Ptr {
s.errors = append(s.errors, ShadowMustBePointerError)
return s
}
if reflect.TypeOf(shadow).Elem().Kind() != reflect.Struct {
s.errors = append(s.errors, ShadowMustBePointerError)
return s
}
s.shadow = shadow
return s
}
package xflags
import (
"github.com/stretchr/testify/assert"
"testing"
)
type ConfigStruct6Sub1 struct {
Command3Flag1 bool
}
type ConfigStruct6 struct {
ValGlobal1 bool
ValGlobal2 bool
ValCommand1Flag1 bool
ValCommand1Flag2 bool
ValSub ConfigStruct6Sub1
}
func TestFlagSetShadowError(t *testing.T) {
settings := New("test", testExecutionStruct{})
settings.SetShadow(3)
assert.True(t, settings.HasErrors())
}
func TestFlagCopyToShadow(t *testing.T) {
c := ConfigStruct6{}
c.ValSub.Command3Flag1 = true
settings := New("test", testExecutionStruct{})
assert.NotNil(t, settings)
settings.SetShadow(&c)
assert.False(t, settings.HasErrors())
settings.Parse([]string{"test", "-a", "command1", "-d"})
assert.True(t, c.ValGlobal1)
assert.True(t, c.ValCommand1Flag2)
}
...@@ -5,6 +5,16 @@ import ( ...@@ -5,6 +5,16 @@ import (
"strconv" "strconv"
) )
const (
tagIgnore = "ignore"
tagCall = "call"
tagCommand = "command"
tagShort = "short"
tagLong = "long"
tagDescription = "description"
tagShadow = "shadow"
)
func getTagMap(field reflect.StructField) (value map[string]string) { func getTagMap(field reflect.StructField) (value map[string]string) {
tagValues := map[string]string{} tagValues := map[string]string{}
......