diff --git a/VERSION b/VERSION index 9949c17..4e93974 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.13 \ No newline at end of file +v2.0.14 \ No newline at end of file diff --git a/assure.go b/assure.go index 0824514..ada64f4 100644 --- a/assure.go +++ b/assure.go @@ -274,7 +274,7 @@ var AssureIntPositive = func(value interface{}) error { v := figFlesh{value, nil} if v.IsInt() { if v.ToInt() < 0 { - return fmt.Errorf("value must be positive, got %d", v.ToInt()) + return ErrValue{ErrWayBePositive, v.ToInt(), 0} } return nil } @@ -287,7 +287,7 @@ var AssureIntNegative = func(value interface{}) error { v := figFlesh{value, nil} if v.IsInt() { if v.ToInt() > 0 { - return fmt.Errorf("value must be negative, got %d", v.ToInt()) + return ErrValue{ErrWayBeNegative, v.ToInt(), 0} } return nil } @@ -304,7 +304,7 @@ var AssureIntGreaterThan = func(above int) FigValidatorFunc { } i := v.ToInt() if i < above { - return fmt.Errorf("value must be below %d", i) + return ErrValue{ErrWayBeBelow, i, above} } return nil } @@ -320,7 +320,7 @@ var AssureIntLessThan = func(below int) FigValidatorFunc { } i := v.ToInt() if i > below { - return fmt.Errorf("value must be below %d", i) + return ErrValue{ErrWayBeBelow, i, below} } return nil } @@ -336,7 +336,7 @@ var AssureIntInRange = func(min, max int) FigValidatorFunc { } i := v.ToInt() if i < min || i > max { - return fmt.Errorf("value must be between %d and %d, got %d", min, max, i) + return ErrValue{fmt.Sprintf(ErrWayBeBetweenFmt, min, max), i, nil} } return nil } @@ -352,7 +352,7 @@ var AssureInt64GreaterThan = func(above int64) FigValidatorFunc { } i := v.ToInt64() if i < above { - return fmt.Errorf("value must be below %d", i) + return ErrValue{ErrWayBeAbove, i, above} } return nil } @@ -368,7 +368,7 @@ var AssureInt64LessThan = func(below int64) FigValidatorFunc { } i := v.ToInt64() if i > below { - return fmt.Errorf("value must be below %d", i) + return ErrValue{ErrWayBeBelow, i, below} } return nil } @@ -383,7 +383,7 @@ var AssureInt64Positive = func(value interface{}) error { } i := v.ToInt64() if i <= 0 { - return fmt.Errorf("value must be positive, got %d", i) + return ErrValue{ErrWayBePositive, i, 0} } return nil } @@ -398,7 +398,7 @@ var AssureInt64InRange = func(min, max int64) FigValidatorFunc { } i := v.ToInt64() if i < min || i > max { - return fmt.Errorf("value must be between %d and %d, got %d", min, max, i) + return ErrValue{fmt.Sprintf(ErrWayBeBetweenFmt, min, max), i, nil} } return nil } @@ -413,7 +413,7 @@ var AssureFloat64Positive = func(value interface{}) error { } f := v.ToFloat64() if f <= 0 { - return fmt.Errorf("value must be positive, got %f", f) + return ErrValue{ErrWayBePositive, f, 0} } return nil } @@ -428,7 +428,7 @@ var AssureFloat64InRange = func(min, max float64) FigValidatorFunc { } f := v.ToFloat64() if f < min || f > max { - return fmt.Errorf("value must be between %f and %f, got %f", min, max, f) + return ErrValue{fmt.Sprintf(ErrWayBeBetweenFmt, min, max), f, nil} } return nil } @@ -444,7 +444,7 @@ var AssureFloat64GreaterThan = func(above float64) FigValidatorFunc { } f := v.ToFloat64() if f < above { - return fmt.Errorf("value must be below %f", f) + return ErrValue{ErrWayBeBelow, f, above} } return nil } @@ -460,7 +460,7 @@ var AssureFloat64LessThan = func(below float64) FigValidatorFunc { } f := v.ToFloat64() if f > below { - return fmt.Errorf("value must be below %f", f) + return ErrValue{ErrWayBeBelow, f, below} } return nil } @@ -475,7 +475,7 @@ var AssureFloat64NotNaN = func(value interface{}) error { } n := v.ToFloat64() if math.IsNaN(n) { - return fmt.Errorf("value must not be NaN, got %f", n) + return ErrValue{ErrWayBeNotNaN, n, nil} } return nil } @@ -490,7 +490,7 @@ var AssureDurationGreaterThan = func(above time.Duration) FigValidatorFunc { } t := v.ToDuration() if t < above { - return fmt.Errorf("value must be above %v, got = %v", above, t) + return ErrValue{ErrWayBeAbove, t, above} } return nil } @@ -506,7 +506,7 @@ var AssureDurationLessThan = func(below time.Duration) FigValidatorFunc { } t := v.ToDuration() if t > below { - return fmt.Errorf("value must be below %v, got = %v", below, t) + return ErrValue{ErrWayBeBelow, t, below} } return nil } diff --git a/conversions.go b/conversions.go index d4a0d54..d8fac4b 100644 --- a/conversions.go +++ b/conversions.go @@ -2,6 +2,7 @@ package figtree import ( "fmt" + "math" "strconv" "strings" "time" @@ -21,6 +22,12 @@ func toInt(value interface{}) (int, error) { case *int: return *v, nil case int64: + if v > math.MaxInt32 { + return 0, fmt.Errorf("max int32 %d exceeded by %d", math.MaxInt32, v-math.MaxInt32) + } + if v < math.MinInt32 { + return 0, fmt.Errorf("min int32 %d exceeded by %d", math.MinInt32, math.MinInt32-v) + } return int(v), nil case *int64: return int(*v), nil @@ -29,13 +36,13 @@ func toInt(value interface{}) (int, error) { case float64: return int(v), nil case *string: - if f, err := strconv.ParseFloat(*v, 64); err == nil { - return int(f), nil + if f, err := strconv.ParseInt(*v, 10, 32); err == nil { + return toInt(f) } return strconv.Atoi(*v) case string: - if f, err := strconv.ParseFloat(v, 64); err == nil { - return int(f), nil + if f, err := strconv.ParseInt(v, 10, 64); err == nil { + return toInt(f) } return strconv.Atoi(v) default: diff --git a/conversions_test.go b/conversions_test.go index 418d907..e486d29 100644 --- a/conversions_test.go +++ b/conversions_test.go @@ -176,8 +176,8 @@ func Test_toInt(t *testing.T) { { name: "String float truncated", args: args{value: "45.6"}, - want: 45, - wantErr: assert.NoError, + want: 0, + wantErr: assert.Error, }, { name: "Invalid string", diff --git a/errors.go b/errors.go index 5cce4a8..ad24871 100644 --- a/errors.go +++ b/errors.go @@ -15,6 +15,10 @@ func (tree *figTree) ErrorFor(name string) error { return fruit.Error } +func (fig *figFruit) Unwrap() error { + return fig.Error +} + type ErrInvalidType struct { Wanted Mutagenesis Got any @@ -46,3 +50,50 @@ func (e ErrInvalidValue) Error() string { func (e ErrInvalidValue) Unwrap() error { return e.Err } + +const ( + ErrWayBeBelow string = "be below" + ErrWayBeAbove string = "be above" + ErrWayBeBetweenFmt string = "be between %v and %v" + ErrWayBePositive string = "be positive" + ErrWayBeNegative string = "be negative" + ErrWayBeNotNaN string = "not be NaN" +) + +type ErrValue struct { + Way string + Value any + Than any +} + +func (e ErrValue) Error() string { + if e.Than != nil { + return fmt.Sprintf("invalid value ; must be %s than %v ; got %v", e.Way, e.Value, e.Than) + } + return fmt.Sprintf("invalid value ; must be %s ; got %v", e.Way, e.Value) +} + +type ErrLoadFailure struct { + What string + Err error +} + +func (e ErrLoadFailure) Error() string { + return fmt.Sprintf("failed to load %s: %s", e.What, e.Err.Error()) +} + +func (e ErrLoadFailure) Unwrap() error { + return e.Err +} + +type ErrValidationFailure struct { + Err error +} + +func (e ErrValidationFailure) Error() string { + return fmt.Sprintf("failed to validateAll with err: %v", e.Err) +} + +func (e ErrValidationFailure) Unwrap() error { + return e.Err +} diff --git a/figtree_test.go b/figtree_test.go index 473cd02..2d46323 100644 --- a/figtree_test.go +++ b/figtree_test.go @@ -81,7 +81,6 @@ func TestParse_InvalidFlagInput(t *testing.T) { argValue string }{ {"IntFlag_InvalidString", "port", zeroInt, "port number", "not-a-number"}, - {"BoolFlag_InvalidString", "debug", zeroBool, "debug mode", "maybe"}, {"Float64Flag_InvalidString", "ratio", zeroFloat64, "ratio value", "bad-float"}, {"DurationFlag_InvalidString", "timeout", zeroDuration, "timeout duration", "invalid-duration"}, {"ListFlag_MalformedItem", "tags", []string{"a"}, "list of tags", "item1,item2=val"}, @@ -221,7 +220,6 @@ func TestEmptyStringInput(t *testing.T) { usage string }{ {"IntFlag_EmptyString", "count", zeroInt, "count"}, - {"BoolFlag_EmptyString", "enabled", zeroBool, "enabled"}, {"Float64Flag_EmptyString", "ratio", zeroFloat64, "ratio"}, {"DurationFlag_EmptyString", "interval", zeroDuration, "interval"}, {"ListFlag_EmptyString", "items", zeroList, "items"}, diff --git a/fruit.go b/fruit.go index c93e259..ba086ad 100644 --- a/fruit.go +++ b/fruit.go @@ -1,8 +1,6 @@ package figtree import ( - "errors" - "fmt" "strings" "time" ) @@ -17,6 +15,10 @@ func (v *Value) Raw() interface{} { return v.Value } +func (v *Value) IsBoolFlag() bool { + return v.Mutagensis == tBool +} + func (v *Value) Set(in string) error { switch v.Mutagensis { case tString: @@ -27,7 +29,7 @@ func (v *Value) Set(in string) error { } val, err := toBool(in) if err != nil { - v.Err = fmt.Errorf("failed to set bool value from string %q: %w", in, err) + v.Err = ErrInvalidValue{in, err} return v.Err } v.Value = val @@ -37,7 +39,7 @@ func (v *Value) Set(in string) error { } val, err := toInt(in) if err != nil { - v.Err = fmt.Errorf("failed to set int value from string %q: %w", in, err) + v.Err = ErrInvalidValue{in, err} return v.Err } v.Value = val @@ -47,7 +49,7 @@ func (v *Value) Set(in string) error { } val, err := toInt64(in) if err != nil { - v.Err = fmt.Errorf("failed to set int64 value from string %q: %w", in, err) + v.Err = ErrInvalidValue{in, err} return v.Err } v.Value = val @@ -57,7 +59,7 @@ func (v *Value) Set(in string) error { } val, err := toFloat64(in) if err != nil { - v.Err = fmt.Errorf("failed to set float64 value from string %q: %w", in, err) + v.Err = ErrInvalidValue{in, err} return v.Err } v.Value = val @@ -65,11 +67,12 @@ func (v *Value) Set(in string) error { if len(in) == 0 { err := v.Assign(zeroDuration) if err != nil { - v.Err = fmt.Errorf("failed to set duration value from string %q: %w", in, err) + v.Err = ErrInvalidValue{in, err} return v.Err } return nil } + va, er := time.ParseDuration(in) if er == nil { v.Value = va @@ -79,18 +82,23 @@ func (v *Value) Set(in string) error { _val, err := ParseCustomDuration(in) if err != nil { if !strings.Contains(err.Error(), "invalid duration format") { - v.Err = fmt.Errorf("failed to set duration value from string %q: %w", in, err) + v.Err = ErrInvalidValue{in, err} val, err := toInt64(in) if err != nil { - v.Err = errors.Join(v.Err, fmt.Errorf("failed to set duration value from string %q", in)) + v.Err = ErrInvalidValue{in, err} return v.Err } v.Value = time.Duration(val) return nil } + vi, err := toInt64(in) + if err == nil { + v.Value = time.Duration(vi) + return nil + } _val, er := time.ParseDuration(in) if er != nil { - v.Err = fmt.Errorf("failed to set duration value from string %q: %w", in, err) + v.Err = ErrInvalidValue{in, err} return v.Err } v.Value = _val @@ -103,20 +111,20 @@ func (v *Value) Set(in string) error { if len(in) == 0 { err := v.Assign(zeroList) if err != nil { - v.Err = fmt.Errorf("failed to set list value from string %q: %w", in, err) + v.Err = ErrInvalidValue{in, err} return v.Err } return nil } val, err := toStringSlice(in) if err != nil { - v.Err = fmt.Errorf("failed to set list value from string %q: %w", in, err) + v.Err = ErrInvalidValue{in, err} return v.Err } if PolicyListAppend { vl, er := toStringSlice(v.Value) if er != nil { - v.Err = fmt.Errorf("failed to set list value from string %q: %w", in, er) + v.Err = ErrInvalidValue{in, er} return v.Err } for _, x := range val { @@ -132,14 +140,14 @@ func (v *Value) Set(in string) error { if len(in) == 0 { err := v.Assign(zeroMap) if err != nil { - v.Err = fmt.Errorf("failed to set map value from string %q: %w", in, err) + v.Err = ErrInvalidValue{in, err} return v.Err } return nil } val, err := toStringMap(in) if err != nil { - v.Err = fmt.Errorf("failed to set map value from string %q: %w", in, err) + v.Err = ErrInvalidValue{in, err} return v.Err } if PolicyMapAppend { @@ -154,7 +162,7 @@ func (v *Value) Set(in string) error { default: err := v.Assign(in) if err != nil { - v.Err = fmt.Errorf("failed to set value from string %q: %w", in, err) + v.Err = ErrInvalidValue{in, err} return v.Err } } @@ -164,9 +172,19 @@ func (v *Value) Set(in string) error { func (v *Value) Assign(as interface{}) error { switch as := as.(type) { case *ListFlag: - v.Value = as.values + vValue := make([]string, len(as.values)) + copy(vValue, as.values) + v.Value = vValue + case ListFlag: + vValue := make([]string, len(as.values)) + copy(vValue, as.values) + v.Value = vValue case *MapFlag: - v.Value = as.values + vValue := make(map[string]string, len(as.values)) + for k, v := range as.values { + vValue[k] = v + } + v.Value = vValue case *Value: v.Value = as.Value default: diff --git a/fruit_test.go b/fruit_test.go new file mode 100644 index 0000000..d5ea012 --- /dev/null +++ b/fruit_test.go @@ -0,0 +1,227 @@ +package figtree + +import ( + "fmt" + "math" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestValue_Set(t *testing.T) { + type test struct { + In string + Out interface{} + WantErr bool + } + t.Run("String", func(t *testing.T) { + testNo := 0 + tests := []test{ + {t.Name(), t.Name(), false}, + {"", "", false}, + {"abc", "abc", false}, + } + for _, test := range tests { + testNo++ + t.Run(fmt.Sprintf("TEST-%d", testNo), func(t *testing.T) { + v := Value{Mutagensis: tString} + if test.WantErr { + assert.Error(t, v.Set(test.In)) + } else { + assert.NoError(t, v.Set(test.In)) + } + assert.Equal(t, test.Out, v.Value) + }) + } + }) + t.Run("Bool", func(t *testing.T) { + testNo := 0 + tests := []test{ + {t.Name(), true, true}, + {"", false, false}, + {"true", true, false}, + {"1", true, false}, + {"false", false, false}, + {"0", false, false}, + {"9", false, true}, + {"[]", false, true}, + } + for _, test := range tests { + testNo++ + t.Run(fmt.Sprintf("TEST-%d", testNo), func(t *testing.T) { + v := Value{Mutagensis: tBool} + if test.WantErr { + assert.Error(t, v.Set(test.In)) + assert.NotEqual(t, test.Out, v.Value) + } else { + assert.NoError(t, v.Set(test.In)) + assert.Equal(t, test.Out, v.Value) + } + }) + } + }) + t.Run("Int", func(t *testing.T) { + testNo := 0 + tests := []test{ + {t.Name(), 0, true}, + {"", 0, false}, + {"3.14", 3, true}, + {"abc", 0, true}, + {"-3.14", -3, true}, + {fmt.Sprintf("%d", math.MaxInt64), 0, true}, + } + for _, test := range tests { + testNo++ + t.Run(fmt.Sprintf("TEST-%d", testNo), func(t *testing.T) { + v := Value{Mutagensis: tInt} + if test.WantErr { + assert.Error(t, v.Set(test.In)) + assert.NotEqual(t, test.Out, v.Value) + } else { + assert.NoError(t, v.Set(test.In)) + assert.Equal(t, test.Out, v.Value) + } + }) + } + }) + t.Run("Int64", func(t *testing.T) { + testNo := 0 + tests := []test{ + {t.Name(), int64(0), true}, + {"", int64(0), false}, + {"abc", int64(0), true}, + {"42", int64(42), false}, + {"3.14", int64(3), false}, + {"-3.14", int64(-3), false}, + } + for _, test := range tests { + testNo++ + t.Run(fmt.Sprintf("TEST-%d", testNo), func(t *testing.T) { + v := Value{Mutagensis: tInt64} + if test.WantErr { + assert.Error(t, v.Set(test.In)) + assert.NotEqual(t, test.Out, v.Value) + } else { + assert.NoError(t, v.Set(test.In)) + assert.Equal(t, test.Out, v.Value) + } + }) + } + }) + t.Run("Float64", func(t *testing.T) { + testNo := 0 + tests := []test{ + {t.Name(), 0.0, true}, + {"", 0.0, false}, + {"3.14", 3.14, false}, + {"3", 3.0, false}, + {"3.3.3", 0.0, true}, + {"-3.3", -3.3, false}, + } + for _, test := range tests { + testNo++ + t.Run(fmt.Sprintf("TEST-%d", testNo), func(t *testing.T) { + v := Value{Mutagensis: tFloat64} + if test.WantErr { + assert.Error(t, v.Set(test.In)) + assert.NotEqual(t, test.Out, v.Value) + } else { + assert.NoError(t, v.Set(test.In)) + assert.Equal(t, test.Out, v.Value) + } + }) + } + }) + t.Run("Duration", func(t *testing.T) { + testNo := 0 + tests := []test{ + {t.Name(), nil, true}, + {"", time.Duration(0), false}, + {"1", time.Duration(1), false}, + {"1h", time.Hour, false}, + {"1w", time.Hour * 168, false}, + {"1d7h76s", time.Duration(31)*time.Hour + time.Duration(76)*time.Second, false}, + {"abc", nil, true}, + } + for _, test := range tests { + testNo++ + t.Run(fmt.Sprintf("TEST-%d", testNo), func(t *testing.T) { + v := Value{Mutagensis: tDuration} + if test.WantErr { + assert.Error(t, v.Set(test.In)) + } else { + assert.NoError(t, v.Set(test.In)) + assert.Equal(t, test.Out, v.Value) + } + }) + } + }) + t.Run("List", func(t *testing.T) { + testNo := 0 + tests := []test{ + {t.Name(), []string{t.Name()}, false}, + {"", []string{}, false}, + {"yes,no", []string{"yes", "no"}, false}, + {"test", []string{"test"}, false}, + {"abc|cde", []string{"abc|cde"}, false}, + } + for _, test := range tests { + testNo++ + t.Run(fmt.Sprintf("TEST-%d", testNo), func(t *testing.T) { + v := Value{Mutagensis: tList} + if test.WantErr { + assert.Error(t, v.Set(test.In)) + assert.Equal(t, test.Out, v.Value) + } else { + assert.NoError(t, v.Set(test.In)) + assert.Equal(t, test.Out, v.Value) + } + }) + } + tests = []test{ + {"abc|def", []string{"abc", "def"}, false}, + {"abc,def", []string{"abc,def"}, false}, + } + for _, test := range tests { + testNo++ + t.Run(fmt.Sprintf("TEST-%d", testNo), func(t *testing.T) { + originalListSeparator := strings.Clone(ListSeparator) + ListSeparator = "|" + v := Value{Mutagensis: tList} + if test.WantErr { + assert.Error(t, v.Set(test.In)) + assert.Equal(t, test.Out, v.Value) + } else { + assert.NoError(t, v.Set(test.In)) + assert.Equal(t, test.Out, v.Value) + } + ListSeparator = originalListSeparator + }) + } + }) + t.Run("Map", func(t *testing.T) { + testNo := 0 + tests := []test{ + {t.Name(), t.Name(), true}, + {"", map[string]string{}, false}, + {"name=yeshua,age=33", map[string]string{"name": "yeshua", "age": "33"}, false}, + {"invalid=single=format=set", map[string]string{"invalid": "single=format=set"}, false}, + {"nothing-something", map[string]string{}, true}, + } + for _, test := range tests { + testNo++ + t.Run(fmt.Sprintf("TEST-%d", testNo), func(t *testing.T) { + v := Value{Mutagensis: tMap} + if test.WantErr { + assert.Error(t, v.Set(test.In)) + assert.NotEqual(t, test.Out, v.Value) + } else { + assert.NoError(t, v.Set(test.In)) + assert.Equal(t, test.Out, v.Value) + } + }) + } + }) +} diff --git a/loading.go b/loading.go index 29f545b..d536d3f 100644 --- a/loading.go +++ b/loading.go @@ -54,7 +54,7 @@ func (tree *figTree) Load() (err error) { if err2 != nil { err = errors.Join(err, err2) } - return fmt.Errorf("failed to Load() due to err: %w", err) + return ErrLoadFailure{"flags", err} } err = tree.loadFlagSet() if err != nil { @@ -79,7 +79,7 @@ func (tree *figTree) Load() (err error) { } if err := check.File(f, file.Options{Exists: true}); err == nil { if err := tree.loadFile(f); err != nil { - return fmt.Errorf("failed to load %s: %w", f, err) + return ErrLoadFailure{f, err} } } } @@ -115,7 +115,7 @@ func (tree *figTree) LoadFile(path string) (err error) { var loadErr error if loadErr = check.File(path, file.Options{Exists: true}); loadErr == nil { if err2 := tree.loadFile(path); err2 != nil { - return fmt.Errorf("failed to loadFile %s: %w", path, err2) + return ErrLoadFailure{path, err2} } tree.readEnv() err3 := tree.loadFlagSet() @@ -124,7 +124,7 @@ func (tree *figTree) LoadFile(path string) (err error) { } err4 := tree.validateAll() if err4 != nil { - return fmt.Errorf("failed to validateAll: %w", err4) + return ErrValidationFailure{err4} } return nil } @@ -139,9 +139,9 @@ func (tree *figTree) LoadFile(path string) (err error) { } err5 := tree.validateAll() if err5 != nil { - return fmt.Errorf("failed to validateAll: %w", err5) + return ErrValidationFailure{err5} } - return fmt.Errorf("failed to LoadFile %s due to err %v", path, loadErr) + return ErrLoadFailure{path, loadErr} } func (tree *figTree) loadFlagSet() (e error) { @@ -164,7 +164,7 @@ func (tree *figTree) loadFlagSet() (e error) { } value, err := tree.from(flagName) if err != nil || value == nil { - e = fmt.Errorf("loadFlagSet(): failed to load %s: %w", flagName, err) + e = ErrLoadFailure{flagName, err} return } switch value.Mutagensis { @@ -174,7 +174,7 @@ func (tree *figTree) loadFlagSet() (e error) { witheredValue := withered.Value.Flesh().ToMap() flagged, err := toStringMap(f.Value) if err != nil { - e = fmt.Errorf("failed to load %s: %w", flagName, err) + e = ErrLoadFailure{flagName, err} return } result := make(map[string]string) @@ -191,18 +191,18 @@ func (tree *figTree) loadFlagSet() (e error) { } err = value.Assign(result) if err != nil { - e = fmt.Errorf("failed to load %s: %w", flagName, err) + e = ErrLoadFailure{flagName, err} return } case tList: merged, err := toStringSlice(value.Value) if err != nil { - e = fmt.Errorf("failed to load %s: %w", flagName, err) + e = ErrLoadFailure{flagName, err} return } flagged, err := toStringSlice(f.Value) if err != nil { - e = fmt.Errorf("failed to load %s: %w", flagName, err) + e = ErrLoadFailure{flagName, err} return } unique := make(map[string]bool) @@ -218,13 +218,14 @@ func (tree *figTree) loadFlagSet() (e error) { } err = value.Assign(newValue) if err != nil { - e = fmt.Errorf("failed to load %s: %w", flagName, err) + e = ErrLoadFailure{flagName, err} return } default: - err := value.Set(f.Value.String()) + v := f.Value.String() + err := value.Set(v) if err != nil { - e = fmt.Errorf("failed to value.Set(%s) due to err: %w", f.Value.String(), err) + e = ErrLoadFailure{flagName, fmt.Errorf("failed to value.Set(%s): %w", f.Value.String(), err)} return } } diff --git a/mutations_new.go b/mutations_new.go index 9d7fefc..d3d0ef8 100644 --- a/mutations_new.go +++ b/mutations_new.go @@ -46,11 +46,11 @@ func (tree *figTree) NewBool(name string, value bool, usage string) Plant { tree.activateFlagSet() name = strings.ToLower(name) v := &Value{ - Value: value, + Value: &value, // Store a persistent pointer to the bool value Mutagensis: tBool, } + tree.flagSet.BoolVar(v.Value.(*bool), name, value, usage) // Use the persistent pointer directly tree.values.Store(name, v) - tree.flagSet.Var(v, name, usage) def := &figFruit{ name: name, usage: usage,