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

Target

Select target project
  • oss/libraries/go/application/xflags
1 result
Select Git revision
Show changes
Commits on Source (3)
<a name="v1.9.0"></a>
## [v1.9.0] - 2022-10-15
### Add Features
- feat implements proxy interface [#2](https://gitlab.schukai.com/oss/libraries/go/application/xflags/issues/2)
<a name="v1.8.3"></a> <a name="v1.8.3"></a>
## [v1.8.3] - 2022-10-15 ## [v1.8.3] - 2022-10-15
### Bug Fixes ### Bug Fixes
...@@ -102,6 +108,7 @@ ...@@ -102,6 +108,7 @@
<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.9.0]: https://gitlab.schukai.com/oss/libraries/go/application/xflags/compare/v1.8.3...v1.9.0
[v1.8.3]: https://gitlab.schukai.com/oss/libraries/go/application/xflags/compare/v1.8.2...v1.8.3 [v1.8.3]: https://gitlab.schukai.com/oss/libraries/go/application/xflags/compare/v1.8.2...v1.8.3
[v1.8.2]: https://gitlab.schukai.com/oss/libraries/go/application/xflags/compare/v1.8.1...v1.8.2 [v1.8.2]: https://gitlab.schukai.com/oss/libraries/go/application/xflags/compare/v1.8.1...v1.8.2
[v1.8.1]: https://gitlab.schukai.com/oss/libraries/go/application/xflags/compare/v1.8.0...v1.8.1 [v1.8.1]: https://gitlab.schukai.com/oss/libraries/go/application/xflags/compare/v1.8.0...v1.8.1
......
...@@ -146,14 +146,16 @@ The function `Execute()` executes the command. See the section ...@@ -146,14 +146,16 @@ The function `Execute()` executes the command. See the section
setting.Execute() setting.Execute()
``` ```
### Shadow ### Proxy
The shadow structure is used to copy the values of the flags to the The proxy structure is used to copy the values of the flags to the
shadow structure. The shadow structure is set using the `SetShadow()` proxy structure. The proxy structure is used to access the values of the
and configured using the tag `shadow`. flags.
The proxy structure must implement the `Proxy` interface.
```go ```go
type Shadow struct { type MyObj struct {
Verbose bool Verbose bool
Serve struct { Serve struct {
Host string Host string
...@@ -161,12 +163,14 @@ type Shadow struct { ...@@ -161,12 +163,14 @@ type Shadow struct {
} }
} }
func (m *MyObj) Copy(_ map[string]any) {
}
func main() { func main() {
setting := New(os.Args[0], Definition{}) setting := New(os.Args[0], Definition{})
setting.SetShadow(Shadow{}) setting.SetProxy(&MyObj{})
setting.Parse(os.Args[1:]) setting.Parse(os.Args[1:])
setting.Execute() setting.Execute()
fmt.Printf("Shadow: %+v", setting.GetShadow())
} }
``` ```
......
...@@ -12,18 +12,22 @@ import ( ...@@ -12,18 +12,22 @@ import (
"reflect" "reflect"
) )
// ExecuteWithShadow executes the command line arguments and calls the functions. type dummyCopyArg struct{}
func ExecuteWithShadow[C any, D Copyable[D]](cmd C, cnf D) *Settings[C] {
return execute(cmd, cnf, os.Args[0], os.Args[1:])
}
type noShadow struct{}
func (n noShadow) Copy(a noShadow) {} func (n dummyCopyArg) Copy(_ map[string]any) {}
// Execute executes the command line arguments and calls the functions. // Execute executes the command line arguments and calls the functions.
func Execute[C any](cmd C) *Settings[C] { func Execute[C any](cmd C, cpy ...Proxy) *Settings[C] {
return execute(cmd, noShadow{}, os.Args[0], os.Args[1:])
if cpy == nil {
return execute(cmd, dummyCopyArg{}, os.Args[0], os.Args[1:])
}
if len(cpy) > 1 {
panic("too many arguments")
}
return execute(cmd, cpy[0], os.Args[0], os.Args[1:])
} }
// PrintFlagOutput prints the flag output to the standard output. // PrintFlagOutput prints the flag output to the standard output.
...@@ -36,15 +40,15 @@ func (s *Settings[C]) GetFlagOutput() { ...@@ -36,15 +40,15 @@ func (s *Settings[C]) GetFlagOutput() {
fmt.Println(s.command.flagSet.Output()) fmt.Println(s.command.flagSet.Output())
} }
// execute is the internal implementation of ExecuteWithShadow. // execute is the internal implementation of Execute.
func execute[C any, D Copyable[D]](cmd C, cnf D, name string, args []string) *Settings[C] { func execute[C any, D Proxy](cmd C, proxy D, name string, args []string) *Settings[C] {
instance := New(name, cmd) instance := New(name, cmd)
if instance.HasErrors() { if instance.HasErrors() {
return instance return instance
} }
if (reflect.ValueOf(&cnf).Elem().Type() != reflect.TypeOf(noShadow{})) { if (reflect.ValueOf(&proxy).Elem().Type() != reflect.TypeOf(dummyCopyArg{})) {
instance.SetShadow(cnf) instance.SetProxy(proxy)
if instance.HasErrors() { if instance.HasErrors() {
return instance return instance
} }
...@@ -59,10 +63,6 @@ func execute[C any, D Copyable[D]](cmd C, cnf D, name string, args []string) *Se ...@@ -59,10 +63,6 @@ func execute[C any, D Copyable[D]](cmd C, cnf D, name string, args []string) *Se
return instance return instance
} }
if instance.shadow != nil {
cnf.Copy(instance.shadow.(D))
}
instance.Execute() instance.Execute()
if instance.HasErrors() { if instance.HasErrors() {
return instance return instance
...@@ -87,6 +87,8 @@ func New[C any](name string, definitions C) *Settings[C] { ...@@ -87,6 +87,8 @@ func New[C any](name string, definitions C) *Settings[C] {
return s return s
} }
s.mapping = make(map[string]any)
buf := bytes.NewBufferString("") buf := bytes.NewBufferString("")
s.flagOutput = io.Writer(buf) s.flagOutput = io.Writer(buf)
s.definitions = definitions s.definitions = definitions
......
...@@ -25,7 +25,7 @@ func TestUsage(t *testing.T) { ...@@ -25,7 +25,7 @@ func TestUsage(t *testing.T) {
type TestDataStruct struct { type TestDataStruct struct {
} }
func (s *TestDataStruct) Copy(x *TestDataStruct) { func (s *TestDataStruct) Copy(_ map[string]any) {
} }
...@@ -44,8 +44,7 @@ func TestExecuteTypeStringIsNotSupported(t *testing.T) { ...@@ -44,8 +44,7 @@ func TestExecuteTypeStringIsNotSupported(t *testing.T) {
func TestExecuteHelp(t *testing.T) { func TestExecuteHelp(t *testing.T) {
instance := execute(CmdTest1{}, &TestDataStruct{}, "test", []string{"-h"}) instance := execute(CmdTest1{}, &TestDataStruct{}, "test", []string{"-h"})
assert.False(t, instance.HasErrors()) assert.True(t, instance.HelpRequested())
} }
func TestNewIntWithError(t *testing.T) { func TestNewIntWithError(t *testing.T) {
......
...@@ -9,14 +9,13 @@ import ( ...@@ -9,14 +9,13 @@ import (
) )
type cmd[C any] struct { type cmd[C any] struct {
name string name string
flagSet *flag.FlagSet flagSet *flag.FlagSet
tagMapping map[string]string tagMapping map[string]string
shadowMapping map[string]string commands []*cmd[C]
commands []*cmd[C] settings *Settings[C]
settings *Settings[C] valuePath []string
valuePath []string functionName string
functionName string
} }
func (c *cmd[C]) parse(args []string) { func (c *cmd[C]) parse(args []string) {
...@@ -56,14 +55,13 @@ func (c *cmd[C]) parse(args []string) { ...@@ -56,14 +55,13 @@ func (c *cmd[C]) parse(args []string) {
func buildCommandStruct[C any](s *Settings[C], name, fkt string, errorHandling flag.ErrorHandling, path []string) *cmd[C] { func buildCommandStruct[C any](s *Settings[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,
tagMapping: map[string]string{}, tagMapping: map[string]string{},
shadowMapping: map[string]string{}, valuePath: path,
valuePath: path, functionName: fkt,
functionName: fkt,
} }
cc.flagSet.SetOutput(s.flagOutput) cc.flagSet.SetOutput(s.flagOutput)
...@@ -157,15 +155,8 @@ func (c *cmd[C]) parseStruct(dta any) { ...@@ -157,15 +155,8 @@ func (c *cmd[C]) parseStruct(dta any) {
if m[tagLong] != "" { if m[tagLong] != "" {
c.tagMapping[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.tagMapping[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)
......
...@@ -126,3 +126,9 @@ type MissingFunctionError error ...@@ -126,3 +126,9 @@ type MissingFunctionError error
func newMissingFunctionError(missing string) MissingFunctionError { func newMissingFunctionError(missing string) MissingFunctionError {
return MissingFunctionError(errors.New("missing function " + missing)) return MissingFunctionError(errors.New("missing function " + missing))
} }
type InvalidTypeForPathError error
func newInvalidTypeForPathError(path string, pt string, nt string) InvalidTypeForPathError {
return InvalidTypeForPathError(errors.New("invalid type for path " + path + ": expected " + pt + ", got " + nt))
}
...@@ -96,3 +96,9 @@ func TestMissingFunctionError(t *testing.T) { ...@@ -96,3 +96,9 @@ func TestMissingFunctionError(t *testing.T) {
_, ok := err.(MissingFunctionError) _, ok := err.(MissingFunctionError)
assert.True(t, ok) assert.True(t, ok)
} }
func TestInvalidTypeForPathError(t *testing.T) {
err := newInvalidTypeForPathError("test", "test", "test")
_, ok := err.(InvalidTypeForPathError)
assert.True(t, ok)
}
...@@ -6,8 +6,10 @@ package xflags ...@@ -6,8 +6,10 @@ package xflags
import ( import (
"flag" "flag"
"fmt" "fmt"
"github.com/stretchr/testify/assert"
"reflect" "reflect"
"strings" "strings"
"testing"
) )
func (s *Settings[C]) Execute() *Settings[C] { func (s *Settings[C]) Execute() *Settings[C] {
...@@ -31,7 +33,6 @@ func (s *Settings[C]) Execute() *Settings[C] { ...@@ -31,7 +33,6 @@ func (s *Settings[C]) Execute() *Settings[C] {
func callCmdFunctions[C any](settings *Settings[C], commands []*cmd[C]) bool { func callCmdFunctions[C any](settings *Settings[C], commands []*cmd[C]) bool {
//result := false
wasExecuted := false wasExecuted := false
shouldExecute := false shouldExecute := false
...@@ -117,3 +118,33 @@ func (s *Settings[C]) MissingCommand() bool { ...@@ -117,3 +118,33 @@ func (s *Settings[C]) MissingCommand() bool {
func (s *Settings[C]) WasExecuted() bool { func (s *Settings[C]) WasExecuted() bool {
return s.wasExecuted return s.wasExecuted
} }
func TestWrongDefinitionType(t *testing.T) {
c := New("root", 2)
c.Parse([]string{"test"})
c.Execute()
assert.True(t, c.HasErrors())
}
type testExecuteCommandStruct struct {
Command1 struct {
} `command:"command1" description:"Command 1" callback:"command1Callback" `
Command2 struct {
Command3 struct {
} `command:"command3" description:"Command 3" callback:"command3Callback" call:"DoCmd3"`
} `command:"command2" description:"Command 2" callback:"command2Callback" `
}
func (c *testExecuteCommandStruct) DoCmd3(s *Settings[testExecuteCommandStruct]) {
}
func (c *testExecuteCommandStruct) command1Callback(args []string) {
fmt.Println("command1Callback", args)
}
func TestExecute1(t *testing.T) {
c := New("root", testExecuteCommandStruct{})
c.Parse([]string{"command2", "command3", "commandX"})
c.Execute()
assert.False(t, c.HasErrors())
}
// Copyright 2022 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0
package xflags
// Copyright 2022 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0
package xflags
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
)
func TestWrongDefinitionType(t *testing.T) {
c := New("root", 2)
c.Parse([]string{"test"})
c.Execute()
assert.True(t, c.HasErrors())
}
type testExecuteCommandStruct struct {
Command1 struct {
} `command:"command1" description:"Command 1" callback:"command1Callback" `
Command2 struct {
Command3 struct {
} `command:"command3" description:"Command 3" callback:"command3Callback" call:"DoCmd3"`
} `command:"command2" description:"Command 2" callback:"command2Callback" `
}
func (c *testExecuteCommandStruct) DoCmd3(s *Settings[testExecuteCommandStruct]) {
}
func (c *testExecuteCommandStruct) command1Callback(args []string) {
fmt.Println("command1Callback", args)
}
func TestExecute1(t *testing.T) {
c := New("root", testExecuteCommandStruct{})
c.Parse([]string{"command2", "command3", "commandX"})
c.Execute()
assert.False(t, c.HasErrors())
}
...@@ -60,7 +60,7 @@ func TestIssue1TestCallCMD4(tp *testing.T) { ...@@ -60,7 +60,7 @@ func TestIssue1TestCallCMD4(tp *testing.T) {
for i, tt := range testData { for i, tt := range testData {
tp.Run(strconv.Itoa(i)+":"+strings.Join(tt.args, ","), func(t *testing.T) { tp.Run(strconv.Itoa(i)+":"+strings.Join(tt.args, ","), func(t *testing.T) {
s := execute(testCmdStructIssue1{}, noShadow{}, "test", tt.args) s := execute(testCmdStructIssue1{}, dummyCopyArg{}, "test", tt.args)
assert.Equal(t, tt.hasErrors, s.HasErrors()) assert.Equal(t, tt.hasErrors, s.HasErrors())
assert.Equal(t, tt.hasHint, s.HasHint()) assert.Equal(t, tt.hasHint, s.HasHint())
...@@ -75,27 +75,27 @@ func TestIssue1TestCallCMD4(tp *testing.T) { ...@@ -75,27 +75,27 @@ func TestIssue1TestCallCMD4(tp *testing.T) {
} }
func TestIssue1TestNoCallback(t *testing.T) { func TestIssue1TestNoCallback(t *testing.T) {
s := execute(testCmdStructIssue1{}, noShadow{}, "test", []string{"cmd1", "cmd6", "cmd7"}) s := execute(testCmdStructIssue1{}, dummyCopyArg{}, "test", []string{"cmd1", "cmd6", "cmd7"})
assert.Equal(t, 3, len(s.Errors())) assert.Equal(t, 3, len(s.Errors()))
assert.False(t, s.WasExecuted()) assert.False(t, s.WasExecuted())
} }
func TestIssue1TestToMuchCommands(t *testing.T) { func TestIssue1TestToMuchCommands(t *testing.T) {
s := execute(testCmdStructIssue1{}, noShadow{}, "test", []string{"cmd1", "cmd9"}) s := execute(testCmdStructIssue1{}, dummyCopyArg{}, "test", []string{"cmd1", "cmd9"})
assert.True(t, s.MissingCommand()) assert.True(t, s.MissingCommand())
assert.False(t, s.WasExecuted()) assert.False(t, s.WasExecuted())
} }
// here it is tested whether the last and defined callback is called // here it is tested whether the last and defined callback is called
func TestIssue1TestCallCMD3(t *testing.T) { func TestIssue1TestCallCMD3(t *testing.T) {
s := execute(testCmdStructIssue1{}, noShadow{}, "test", []string{"cmd1", "cmd2", "cmd3"}) s := execute(testCmdStructIssue1{}, dummyCopyArg{}, "test", []string{"cmd1", "cmd2", "cmd3"})
assert.Equal(t, 0, len(s.Errors())) assert.Equal(t, 0, len(s.Errors()))
assert.True(t, s.WasExecuted()) assert.True(t, s.WasExecuted())
} }
// NoShadow is an internal Struct for testing // NoShadow is an internal Struct for testing
func TestIssue1MessageWithNoShadow(t *testing.T) { func TestIssue1MessageWithNoShadow(t *testing.T) {
s := execute(testCmdStructIssue1{}, noShadow{}, "test", []string{"cmd1", "cmd2", "cmd3", "-v3"}) s := execute(testCmdStructIssue1{}, dummyCopyArg{}, "test", []string{"cmd1", "cmd2", "cmd3", "-v3"})
assert.Equal(t, 0, len(s.Errors())) assert.Equal(t, 0, len(s.Errors()))
} }
...@@ -139,7 +139,7 @@ func TestIssue1Summary(tp *testing.T) { ...@@ -139,7 +139,7 @@ func TestIssue1Summary(tp *testing.T) {
for _, tt := range data { for _, tt := range data {
tp.Run(tt.args[0], func(t *testing.T) { tp.Run(tt.args[0], func(t *testing.T) {
s := execute(testCmdStructIssue1{}, noShadow{}, "test", tt.args) s := execute(testCmdStructIssue1{}, dummyCopyArg{}, "test", tt.args)
assert.Equal(t, tt.helpRequested, s.HelpRequested()) assert.Equal(t, tt.helpRequested, s.HelpRequested())
assert.Equal(t, tt.wasExecuted, s.WasExecuted()) assert.Equal(t, tt.wasExecuted, s.WasExecuted())
assert.Equal(t, tt.missingCommand, s.MissingCommand()) assert.Equal(t, tt.missingCommand, s.MissingCommand())
......
...@@ -9,25 +9,26 @@ import ( ...@@ -9,25 +9,26 @@ import (
"strings" "strings"
) )
// SetShadow sets the shadow struct for the flag configuration. // SetProxy sets the shadow struct for the flag configuration.
func (s *Settings[C]) SetShadow(shadow any) *Settings[C] { func (s *Settings[C]) SetProxy(proxy Proxy) *Settings[C] {
if reflect.TypeOf(shadow).Kind() != reflect.Ptr { if reflect.TypeOf(proxy).Kind() != reflect.Ptr {
s.errors = append(s.errors, ShadowMustBePointerError) s.errors = append(s.errors, ShadowMustBePointerError)
return s return s
} }
if reflect.TypeOf(shadow).Elem().Kind() != reflect.Struct { if reflect.TypeOf(proxy).Elem().Kind() != reflect.Struct {
s.errors = append(s.errors, ShadowMustBePointerError) s.errors = append(s.errors, ShadowMustBePointerError)
return s return s
} }
s.shadow = shadow s.proxy = proxy
return s return s
} }
type Copyable[C any] interface { // Proxy is the interface for the proxy struct.
Copy(data C) type Proxy interface {
Copy(map[string]any)
} }
func (s *Settings[C]) assignValues(c cmd[C]) { func (s *Settings[C]) assignValues(c cmd[C]) {
...@@ -46,33 +47,15 @@ func (s *Settings[C]) assignValues(c cmd[C]) { ...@@ -46,33 +47,15 @@ func (s *Settings[C]) assignValues(c cmd[C]) {
pa := append(c.valuePath, k) pa := append(c.valuePath, k)
p := strings.Join(pa, ".") p := strings.Join(pa, ".")
err := setValueUsingPath(&s.definitions, p, value) err := SetValueUsingPath(&s.definitions, p, value)
if err != nil { if err != nil {
s.errors = append(s.errors, err) s.errors = append(s.errors, err)
} }
err = c.setShadowValue(s.shadow, k, value) s.mapping[p] = value
if err != nil {
s.errors = append(s.errors, err)
}
return return
}) })
} }
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)
}
...@@ -20,13 +20,6 @@ type ConfigStruct6 struct { ...@@ -20,13 +20,6 @@ type ConfigStruct6 struct {
ValSub ConfigStruct6Sub1 ValSub ConfigStruct6Sub1
} }
func TestFlagSetShadowError(t *testing.T) {
settings := New("test", testExecutionStruct{})
settings.SetShadow(3)
assert.True(t, settings.HasErrors())
}
func TestFlagCopyToShadow(t *testing.T) { func TestFlagCopyToShadow(t *testing.T) {
c := ConfigStruct6{} c := ConfigStruct6{}
...@@ -35,7 +28,7 @@ func TestFlagCopyToShadow(t *testing.T) { ...@@ -35,7 +28,7 @@ func TestFlagCopyToShadow(t *testing.T) {
settings := New("test", testExecutionStruct{}) settings := New("test", testExecutionStruct{})
assert.NotNil(t, settings) assert.NotNil(t, settings)
settings.SetShadow(&c) settings.SetProxy(&c)
assert.False(t, settings.HasErrors()) assert.False(t, settings.HasErrors())
settings.Parse([]string{"-a", "command1", "-d"}) settings.Parse([]string{"-a", "command1", "-d"})
...@@ -45,8 +38,9 @@ func TestFlagCopyToShadow(t *testing.T) { ...@@ -45,8 +38,9 @@ func TestFlagCopyToShadow(t *testing.T) {
} }
func (s *ConfigStruct6) Copy(p *ConfigStruct6) { func (s *ConfigStruct6) Copy(m map[string]any) {
SetValueUsingPath(s, "ValGlobal1", (m["Global1"]))
SetValueUsingPath(s, "ValCommand1Flag2", (m["Command1.Command1Flag2"]))
} }
func TestCopyable(t *testing.T) { func TestCopyable(t *testing.T) {
......
...@@ -38,5 +38,9 @@ func (s *Settings[C]) Parse(args []string) *Settings[C] { ...@@ -38,5 +38,9 @@ func (s *Settings[C]) Parse(args []string) *Settings[C] {
s.command.parse(r) s.command.parse(r)
if s.mapping != nil && s.proxy != nil {
s.proxy.Copy(s.mapping)
}
return s return s
} }
...@@ -4,13 +4,14 @@ ...@@ -4,13 +4,14 @@
package xflags package xflags
import ( import (
"fmt"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
) )
// This function returns the value of a field in a struct, given a path to the field. // This function returns the value of a field in a struct, given a path to the field.
func getValueFrom[D any](obj D, keyWithDots string) (interface{}, error) { func GetValueFrom[D any](obj D, keyWithDots string) (any, error) {
keySlice := strings.Split(keyWithDots, ".") keySlice := strings.Split(keyWithDots, ".")
v := reflect.ValueOf(obj) v := reflect.ValueOf(obj)
...@@ -49,7 +50,7 @@ func getValueFrom[D any](obj D, keyWithDots string) (interface{}, error) { ...@@ -49,7 +50,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 setValueUsingPath[D any](obj D, keyWithDots string, newValue string) error { func SetValueUsingPath[D any](obj D, keyWithDots string, newValue any) error {
keySlice := strings.Split(keyWithDots, ".") keySlice := strings.Split(keyWithDots, ".")
v := reflect.ValueOf(obj) v := reflect.ValueOf(obj)
...@@ -94,29 +95,83 @@ func setValueUsingPath[D any](obj D, keyWithDots string, newValue string) error ...@@ -94,29 +95,83 @@ func setValueUsingPath[D any](obj D, keyWithDots string, newValue string) error
return newCannotSetError(keyWithDots) return newCannotSetError(keyWithDots)
} }
switch v.Kind() {
case reflect.Ptr:
if newValue == nil {
v.Set(reflect.Zero(v.Type()))
} else {
v.Set(reflect.ValueOf(&newValue))
}
return nil
}
newValueType := reflect.TypeOf(newValue)
if newValueType == nil {
return newUnsupportedTypePathError(keyWithDots, v.Type())
}
newValueKind := reflect.TypeOf(newValue).Kind()
switch v.Kind() { switch v.Kind() {
case reflect.String: case reflect.String:
v.SetString(newValue) if newValueKind == reflect.String {
case reflect.Int: v.SetString(newValue.(string))
} else {
v.SetString(fmt.Sprintf("%v", newValue))
}
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
s, err := strconv.Atoi(newValue) if newValueKind == reflect.Int {
if err != nil { v.SetInt(int64(newValue.(int)))
return err } else {
s, err := strconv.ParseInt(newValue.(string), 10, 64)
if err != nil {
return err
}
v.SetInt(s)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if newValueKind == reflect.Int {
v.SetUint(uint64(newValue.(int)))
} else {
s, err := strconv.ParseInt(newValue.(string), 10, 64)
if err != nil {
return err
}
v.SetUint(uint64(s))
} }
v.SetInt(int64(s))
case reflect.Bool: case reflect.Bool:
v.SetBool(newValue == "true")
case reflect.Float64:
s, err := strconv.ParseFloat(newValue, 64) if newValueKind == reflect.Bool {
if err != nil { v.SetBool(newValue.(bool))
return err } else {
b, err := strconv.ParseBool(newValue.(string))
if err != nil {
return err
}
v.SetBool(b)
}
case reflect.Float64, reflect.Float32:
if newValueKind == reflect.Float64 {
v.SetFloat(newValue.(float64))
} else {
s, err := strconv.ParseFloat(newValue.(string), 64)
if err != nil {
return err
}
v.SetFloat(s)
} }
v.SetFloat(s)
default: default:
return newUnsupportedTypeAtTopOfPathError(keyWithDots, v.Type()) return newInvalidTypeForPathError(keyWithDots, v.Type().String(), newValueKind.String())
} }
return nil return nil
......
...@@ -32,7 +32,7 @@ func TestPathFindError(t *testing.T) { ...@@ -32,7 +32,7 @@ func TestPathFindError(t *testing.T) {
s := PathfindTestStruct1{} s := PathfindTestStruct1{}
_, err := getValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.XX") _, err := GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.XX")
if err == nil { if err == nil {
t.Error("err == nil") t.Error("err == nil")
} }
...@@ -58,7 +58,7 @@ func TestPathFindSetValueString(t *testing.T) { ...@@ -58,7 +58,7 @@ func TestPathFindSetValueString(t *testing.T) {
for k, v := range testData { for k, v := range testData {
s := &PathfindTestStruct1{} s := &PathfindTestStruct1{}
err := setValueUsingPath[*PathfindTestStruct1](s, k, v) err := SetValueUsingPath[*PathfindTestStruct1](s, k, v)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
...@@ -74,7 +74,7 @@ func TestPathFindGetValueFrom(t *testing.T) { ...@@ -74,7 +74,7 @@ func TestPathFindGetValueFrom(t *testing.T) {
s.Sub1.Bs = "3" s.Sub1.Bs = "3"
s.Sub1.Bf = 4.0 s.Sub1.Bf = 4.0
v, err := getValueFrom[PathfindTestStruct1](s, "Sub1.B") v, err := GetValueFrom[PathfindTestStruct1](s, "Sub1.B")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
...@@ -83,7 +83,7 @@ func TestPathFindGetValueFrom(t *testing.T) { ...@@ -83,7 +83,7 @@ func TestPathFindGetValueFrom(t *testing.T) {
t.Error("v != true") t.Error("v != true")
} }
v, err = getValueFrom[PathfindTestStruct1](s, "Sub1.Bi") v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Bi")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
...@@ -92,7 +92,7 @@ func TestPathFindGetValueFrom(t *testing.T) { ...@@ -92,7 +92,7 @@ func TestPathFindGetValueFrom(t *testing.T) {
t.Error("v != 2") t.Error("v != 2")
} }
v, err = getValueFrom[PathfindTestStruct1](s, "Sub1.Bs") v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Bs")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
...@@ -101,7 +101,7 @@ func TestPathFindGetValueFrom(t *testing.T) { ...@@ -101,7 +101,7 @@ func TestPathFindGetValueFrom(t *testing.T) {
t.Error("v != 3") t.Error("v != 3")
} }
v, err = getValueFrom[PathfindTestStruct1](s, "Sub1.Bf") v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Bf")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
...@@ -115,7 +115,7 @@ func TestPathFindGetValueFrom(t *testing.T) { ...@@ -115,7 +115,7 @@ func TestPathFindGetValueFrom(t *testing.T) {
s.Sub1.Sub2.Cs = "3" s.Sub1.Sub2.Cs = "3"
s.Sub1.Sub2.Cf = 4.0 s.Sub1.Sub2.Cf = 4.0
v, err = getValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.C") v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.C")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
...@@ -124,7 +124,7 @@ func TestPathFindGetValueFrom(t *testing.T) { ...@@ -124,7 +124,7 @@ func TestPathFindGetValueFrom(t *testing.T) {
t.Error("v != true") t.Error("v != true")
} }
v, err = getValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Ci") v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Ci")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
...@@ -133,7 +133,7 @@ func TestPathFindGetValueFrom(t *testing.T) { ...@@ -133,7 +133,7 @@ func TestPathFindGetValueFrom(t *testing.T) {
t.Error("v != 2") t.Error("v != 2")
} }
v, err = getValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Cs") v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Cs")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
...@@ -142,7 +142,7 @@ func TestPathFindGetValueFrom(t *testing.T) { ...@@ -142,7 +142,7 @@ func TestPathFindGetValueFrom(t *testing.T) {
t.Error("v != 3") t.Error("v != 3")
} }
v, err = getValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Cf") v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Cf")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
...@@ -156,7 +156,7 @@ func TestPathFindGetValueFrom(t *testing.T) { ...@@ -156,7 +156,7 @@ func TestPathFindGetValueFrom(t *testing.T) {
s.Sub1.Sub2.Sub3.Ds = "3" s.Sub1.Sub2.Sub3.Ds = "3"
s.Sub1.Sub2.Sub3.Df = 4.0 s.Sub1.Sub2.Sub3.Df = 4.0
v, err = getValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.D") v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.D")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
...@@ -166,7 +166,7 @@ func TestPathFindGetValueFrom(t *testing.T) { ...@@ -166,7 +166,7 @@ func TestPathFindGetValueFrom(t *testing.T) {
t.Error("v != true") t.Error("v != true")
} }
v, err = getValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Di") v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Di")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
...@@ -175,7 +175,7 @@ func TestPathFindGetValueFrom(t *testing.T) { ...@@ -175,7 +175,7 @@ func TestPathFindGetValueFrom(t *testing.T) {
t.Error("v != 2") t.Error("v != 2")
} }
v, err = getValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Ds") v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Ds")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
...@@ -184,7 +184,7 @@ func TestPathFindGetValueFrom(t *testing.T) { ...@@ -184,7 +184,7 @@ func TestPathFindGetValueFrom(t *testing.T) {
t.Error("v != 3") t.Error("v != 3")
} }
v, err = getValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Df") v, err = GetValueFrom[PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Df")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
...@@ -198,10 +198,10 @@ func TestPathFindGetValueFrom(t *testing.T) { ...@@ -198,10 +198,10 @@ func TestPathFindGetValueFrom(t *testing.T) {
func TestPathFindSetValueFrom(t *testing.T) { func TestPathFindSetValueFrom(t *testing.T) {
s := &PathfindTestStruct1{} s := &PathfindTestStruct1{}
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.B", "true") SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.B", "true")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Bi", "2") SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Bi", "2")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Bs", "3") SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Bs", "3")
setValueUsingPath[*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")
...@@ -220,10 +220,10 @@ func TestPathFindSetValueFrom(t *testing.T) { ...@@ -220,10 +220,10 @@ func TestPathFindSetValueFrom(t *testing.T) {
t.Error("s.Sub1.Bf != 4.0") t.Error("s.Sub1.Bf != 4.0")
} }
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.C", "true") SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.C", "true")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Ci", "2") SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Ci", "2")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Cs", "3") SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Cs", "3")
setValueUsingPath[*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")
...@@ -250,10 +250,10 @@ func TestPathFindSetValueFrom(t *testing.T) { ...@@ -250,10 +250,10 @@ func TestPathFindSetValueFrom(t *testing.T) {
} }
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.D", "true") SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.D", "true")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Di", "2") SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Di", "2")
setValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Ds", "3") SetValueUsingPath[*PathfindTestStruct1](s, "Sub1.Sub2.Sub3.Ds", "3")
setValueUsingPath[*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")
......
...@@ -25,6 +25,13 @@ func TestReadMeInit(t *testing.T) { ...@@ -25,6 +25,13 @@ func TestReadMeInit(t *testing.T) {
setting.Parse([]string{"-v", "serve", "-h", "localhost", "-p", "8080"}) setting.Parse([]string{"-v", "serve", "-h", "localhost", "-p", "8080"})
setting.Execute() setting.Execute()
assert.True(t, setting.definitions.Verbose) assert.True(t, setting.definitions.Verbose)
if setting.HasErrors() {
for _, err := range setting.Errors() {
t.Log(err)
}
}
assert.False(t, setting.HasErrors()) assert.False(t, setting.HasErrors())
} }
......
{"version":"1.8.3"} {"version":"1.9.0"}
...@@ -17,6 +17,7 @@ type config struct { ...@@ -17,6 +17,7 @@ type config struct {
errorHandling flag.ErrorHandling errorHandling flag.ErrorHandling
} }
// Settings[C] is the main struct for the xflags package.
type Settings[C any] struct { type Settings[C any] struct {
definitions C definitions C
...@@ -29,7 +30,8 @@ type Settings[C any] struct { ...@@ -29,7 +30,8 @@ type Settings[C any] struct {
config config config config
shadow any mapping map[string]any
proxy Proxy
wasExecuted bool wasExecuted bool
hint string hint string
......
...@@ -15,7 +15,6 @@ const ( ...@@ -15,7 +15,6 @@ const (
tagShort = "short" tagShort = "short"
tagLong = "long" tagLong = "long"
tagDescription = "description" tagDescription = "description"
tagShadow = "shadow"
) )
func getTagMap(field reflect.StructField) (value map[string]string) { func getTagMap(field reflect.StructField) (value map[string]string) {
......