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>
## 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
## 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
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
### Initialize
A new flag set is created using the `xflags.New()` function. The passed
structure is used type for the flags.
A new flag set is created using the `xflags.New()` function.
The structure passed is used as the type for the flags.
```go
package main
......
......@@ -17,7 +17,7 @@ func New[C any](name string, definitions C) *setting[C] {
errorHandling: flag.ContinueOnError,
},
}
if reflect.TypeOf(definitions).Kind() != reflect.Struct {
s.errors = append(s.errors, newUnsupportedReflectKindError(reflect.TypeOf(definitions)))
return s
......@@ -40,3 +40,24 @@ func (s *setting[C]) FlagOutput() string {
func (s *setting[C]) Args() []string {
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 (
"reflect"
)
const (
tagIgnore = "ignore"
tagCall = "call"
tagCommand = "command"
tagShort = "short"
tagLong = "long"
tagDescription = "description"
)
type cmd[C any] struct {
name string
flagSet *flag.FlagSet
mapping map[string]string
commands []*cmd[C]
settings *setting[C]
valuePath []string
functionName string
name string
flagSet *flag.FlagSet
tagMapping map[string]string
shadowMapping map[string]string
commands []*cmd[C]
settings *setting[C]
valuePath []string
functionName string
}
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] {
cc := &cmd[C]{
name: name,
flagSet: flag.NewFlagSet(name, errorHandling),
commands: []*cmd[C]{},
settings: s,
mapping: map[string]string{},
valuePath: path,
functionName: fkt,
name: name,
flagSet: flag.NewFlagSet(name, errorHandling),
commands: []*cmd[C]{},
settings: s,
tagMapping: map[string]string{},
shadowMapping: map[string]string{},
valuePath: path,
functionName: fkt,
}
cc.flagSet.SetOutput(s.flagOutput)
......@@ -155,18 +148,22 @@ func (c *cmd[C]) parseStruct(dta any) {
}
if m[tagShort] != "" {
c.mapping[m[tagShort]] = v.Type().Field(i).Name
c.tagMapping[m[tagShort]] = v.Type().Field(i).Name
}
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)
} else if m[tagCommand] != "" {
//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)
} else if m[tagIgnore] != "" {
......
......@@ -25,6 +25,7 @@ func (s *setting[C]) Errors() []error {
var WatchListNotInitializedError = errors.New("watch list not initialized")
var MissingCommandError = errors.New("missing command")
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
type UnsupportedReflectKindError error
......
......@@ -9,11 +9,12 @@ import (
type testExecutionStruct struct {
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"`
Command1 struct {
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 {
Command2Flag1 bool `short:"e" long:"command2flag1" description:"Command 2 Flag 1"`
Command2Flag2 bool `short:"f" long:"command2flag2" description:"Command 2 Flag 2"`
......
......@@ -5,35 +5,6 @@ import (
"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]) {
flgs := c.flagSet
flgs.Visit(func(f *flag.Flag) {
......@@ -41,7 +12,7 @@ func (s *setting[C]) assignValues(c cmd[C]) {
name := f.Name
value := f.Value.String()
k, ok := c.mapping[name]
k, ok := c.tagMapping[name]
if !ok {
s.errors = append(s.errors, newUnknownFlagError(name))
return
......@@ -50,7 +21,12 @@ func (s *setting[C]) assignValues(c cmd[C]) {
pa := append(c.valuePath, k)
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 {
s.errors = append(s.errors, err)
}
......@@ -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) {
}
// 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, ".")
v := reflect.ValueOf(obj)
......
......@@ -55,7 +55,7 @@ func TestPathFindSetValueString(t *testing.T) {
for k, v := range testData {
s := &PathfindTestStruct1{}
err := setTheValueOverPath[*PathfindTestStruct1](s, k, v)
err := setValueUsingPath[*PathfindTestStruct1](s, k, v)
if err != nil {
t.Error(err)
}
......@@ -195,10 +195,10 @@ func TestPathFindGetValueFrom(t *testing.T) {
func TestPathFindSetValueFrom(t *testing.T) {
s := &PathfindTestStruct1{}
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.B", "true")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Bi", "2")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Bs", "3")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Bf", "4.0")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.B", "true")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Bi", "2")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Bs", "3")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Bf", "4.0")
if s.Sub1.B != true {
t.Error("s.Sub1.B != true")
......@@ -217,10 +217,10 @@ func TestPathFindSetValueFrom(t *testing.T) {
t.Error("s.Sub1.Bf != 4.0")
}
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.C", "true")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.Ci", "2")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.Cs", "3")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.Cf", "4.0")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.C", "true")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Ci", "2")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Cs", "3")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Cf", "4.0")
if s.Sub1.Sub2.C != true {
t.Error("s.Sub1.Sub2.C != true")
......@@ -247,10 +247,10 @@ func TestPathFindSetValueFrom(t *testing.T) {
}
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.D", "true")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Di", "2")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Ds", "3")
setTheValueOverPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Df", "4.0")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.D", "true")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Di", "2")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Ds", "3")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Df", "4.0")
if 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 {
args []string
config config
shadow any
}
func (s *setting[C]) GetValues() C {
......
......@@ -21,5 +21,5 @@ func TestInitCommands(t *testing.T) {
assert.Equal(t, 0, len(s.errors))
assert.Equal(t, "test", s.command.name)
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 (
"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) {
tagValues := map[string]string{}
......