Skip to content

Commit

Permalink
Add value.UpdatedBy to values
Browse files Browse the repository at this point in the history
Still need to actually use it...
  • Loading branch information
bbkane committed Dec 29, 2024
1 parent 17cdd42 commit 2bbf4e9
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 33 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
Note that I update this changelog as I make changes, so the top version (right
below this description) is likely unreleased.

# v0.0.26

## Changed

- Moved `SetBy` into the `Value` interface - this allows `Flag` to be readonly and we need to update `SetBy` anyway

# v0.0.25

## Added
Expand Down
10 changes: 5 additions & 5 deletions app_parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func resolveFlag(
continue
}
canUpdate = true
err := fl.Value.Update(v)
err := fl.Value.Update(v, value.UpdatedByFlag)
if err != nil {
return fmt.Errorf("error updating flag %v from passed flag value %v: %w", name, v, err)
}
Expand All @@ -238,7 +238,7 @@ func resolveFlag(
}
if fpr != nil {
if !fpr.IsAggregated {
err := fl.Value.ReplaceFromInterface(fpr.IFace)
err := fl.Value.ReplaceFromInterface(fpr.IFace, value.UpdatedByConfig)
if err != nil {
return fmt.Errorf(
"could not replace container type value:\nval:\n%#v\nreplacement:\n%#v\nerr: %w",
Expand All @@ -259,7 +259,7 @@ func resolveFlag(
return fmt.Errorf("expected []interface{}, got: %#v", under)
}
for _, e := range under {
err = v.AppendFromInterface(e)
err = v.AppendFromInterface(e, value.UpdatedByConfig)
if err != nil {
return fmt.Errorf("could not update container type value: err: %w", err)
}
Expand All @@ -277,7 +277,7 @@ func resolveFlag(
for _, e := range fl.EnvVars {
val, exists := lookupEnv(e)
if exists {
err := fl.Value.Update(val)
err := fl.Value.Update(val, value.UpdatedByEnvVar)
if err != nil {
return fmt.Errorf("error updating flag %v from envvar %v: %w", name, val, err)
}
Expand All @@ -292,7 +292,7 @@ func resolveFlag(
// update from default
{
if canUpdate && fl.SetBy == "" && fl.Value.HasDefault() {
fl.Value.ReplaceFromDefault()
fl.Value.ReplaceFromDefault(value.UpdatedByDefault)
fl.SetBy = "appdefault"
}
}
Expand Down
19 changes: 13 additions & 6 deletions value/dict/dict.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@ type dictValue[T comparable] struct {
hasDefault bool
inner contained.TypeInfo[T]
vals map[string]T
updatedBy value.UpdatedBy
}

type DictOpt[T comparable] func(*dictValue[T])

// TODO: TEST THIS, but lets try to get more sleep

func New[T comparable](inner contained.TypeInfo[T], opts ...DictOpt[T]) value.EmptyConstructor {
return func() value.Value {
dv := dictValue[T]{
Expand All @@ -28,6 +27,7 @@ func New[T comparable](inner contained.TypeInfo[T], opts ...DictOpt[T]) value.Em
hasDefault: false,
inner: inner,
vals: make(map[string]T),
updatedBy: value.UpdatedByUnset,
}
for _, opt := range opts {
opt(&dv)
Expand Down Expand Up @@ -77,7 +77,7 @@ func (v *dictValue[_]) HasDefault() bool {
return v.hasDefault
}

func (v *dictValue[T]) ReplaceFromInterface(iFace interface{}) error {
func (v *dictValue[T]) ReplaceFromInterface(iFace interface{}, u value.UpdatedBy) error {
under, ok := iFace.(map[string]interface{})
if !ok {
return contained.ErrIncompatibleInterface // TODO: should ErrIncompatibleInterface be in value?
Expand All @@ -93,6 +93,7 @@ func (v *dictValue[T]) ReplaceFromInterface(iFace interface{}) error {
newVals[k] = underE
}
v.vals = newVals
v.updatedBy = u
return nil
}

Expand Down Expand Up @@ -125,7 +126,7 @@ func (v *dictValue[T]) update(key string, val T) error {
return nil
}

func (v *dictValue[_]) Update(s string) error {
func (v *dictValue[_]) Update(s string, u value.UpdatedBy) error {
key, strValue, found := strings.Cut(s, "=")
if !found {
return fmt.Errorf("Could not parse key=value for %v", s)
Expand All @@ -134,11 +135,17 @@ func (v *dictValue[_]) Update(s string) error {
if err != nil {
return err
}
return v.update(key, val)
err = v.update(key, val)
if err != nil {
return err
}
v.updatedBy = u
return nil
}

func (v *dictValue[_]) ReplaceFromDefault() {
func (v *dictValue[_]) ReplaceFromDefault(u value.UpdatedBy) {
if v.hasDefault {
v.vals = v.defaultVals
v.updatedBy = u
}
}
4 changes: 2 additions & 2 deletions value/dict/dict_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func TestDict_ReplaceFromInterface(t *testing.T) {
v := constructor()
dictVal := v.(value.DictValue)

actualErr := dictVal.ReplaceFromInterface(tt.update)
actualErr := dictVal.ReplaceFromInterface(tt.update, value.UpdatedByFlag)
require.Equal(t, tt.expectedErr, actualErr)
require.Equal(t, tt.expectedValue, dictVal.Get())
})
Expand All @@ -73,7 +73,7 @@ func TestDict_Update(t *testing.T) {
v := constructor()
dictVal := v.(value.DictValue)

err := dictVal.Update("key=1.1.1.1")
err := dictVal.Update("key=1.1.1.1", value.UpdatedByFlag)
require.Nil(t, err)
expected := map[string]netip.Addr{
"key": netip.MustParseAddr("1.1.1.1"),
Expand Down
11 changes: 8 additions & 3 deletions value/scalar/scalar.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type scalarValue[T comparable] struct {
defaultVal *T
inner contained.TypeInfo[T]
val T
updatedBy value.UpdatedBy
}

type ScalarOpt[T comparable] func(*scalarValue[T])
Expand All @@ -25,6 +26,7 @@ func newScalarValue[T comparable](
defaultVal: nil,
inner: inner,
val: inner.Empty(),
updatedBy: value.UpdatedByUnset,
}
for _, opt := range opts {
opt(&sv)
Expand Down Expand Up @@ -82,12 +84,13 @@ func (v *scalarValue[_]) HasDefault() bool {
return v.defaultVal != nil
}

func (v *scalarValue[_]) ReplaceFromInterface(iFace interface{}) error {
func (v *scalarValue[_]) ReplaceFromInterface(iFace interface{}, u value.UpdatedBy) error {
val, err := v.inner.FromIFace(iFace)
if err != nil {
return err
}
v.val = val
v.updatedBy = u
return nil
}

Expand All @@ -108,7 +111,7 @@ func withinChoices[T comparable](val T, choices []T) bool {
return false
}

func (v *scalarValue[T]) Update(s string) error {
func (v *scalarValue[T]) Update(s string, u value.UpdatedBy) error {
val, err := v.inner.FromString(s)
if err != nil {
return err
Expand All @@ -117,11 +120,13 @@ func (v *scalarValue[T]) Update(s string) error {
return value.ErrInvalidChoice[T]{Choices: v.choices}
}
v.val = val
v.updatedBy = u
return nil
}

func (v *scalarValue[_]) ReplaceFromDefault() {
func (v *scalarValue[_]) ReplaceFromDefault(u value.UpdatedBy) {
if v.defaultVal != nil {
v.updatedBy = u
v.val = *v.defaultVal
}
}
26 changes: 20 additions & 6 deletions value/slice/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type sliceValue[T comparable] struct {
hasDefault bool
inner contained.TypeInfo[T]
vals []T
updatedBy value.UpdatedBy
}

type SliceOpt[T comparable] func(*sliceValue[T])
Expand All @@ -25,6 +26,7 @@ func New[T comparable](hc contained.TypeInfo[T], opts ...SliceOpt[T]) value.Empt
hasDefault: false,
inner: hc,
vals: nil,
updatedBy: value.UpdatedByUnset,
}
for _, opt := range opts {
opt(&sv)
Expand Down Expand Up @@ -76,7 +78,7 @@ func (v *sliceValue[_]) HasDefault() bool {
return v.hasDefault
}

func (v *sliceValue[T]) ReplaceFromInterface(iFace interface{}) error {
func (v *sliceValue[T]) ReplaceFromInterface(iFace interface{}, u value.UpdatedBy) error {
under, ok := iFace.([]interface{})
if !ok {
return contained.ErrIncompatibleInterface
Expand All @@ -91,6 +93,7 @@ func (v *sliceValue[T]) ReplaceFromInterface(iFace interface{}) error {
}
newVals = append(newVals, underE)
}
v.updatedBy = u
v.vals = newVals
return nil
}
Expand Down Expand Up @@ -124,24 +127,35 @@ func (v *sliceValue[T]) update(val T) error {
return nil
}

func (v *sliceValue[_]) Update(s string) error {
func (v *sliceValue[_]) Update(s string, u value.UpdatedBy) error {
val, err := v.inner.FromString(s)
if err != nil {
return err
}
return v.update(val)
err = v.update(val)
if err != nil {
return err
}
v.updatedBy = u
return nil
}

func (v *sliceValue[_]) ReplaceFromDefault() {
func (v *sliceValue[_]) ReplaceFromDefault(u value.UpdatedBy) {
if v.hasDefault {
v.vals = v.defaultVals
v.updatedBy = u
}
}

func (v *sliceValue[_]) AppendFromInterface(iFace interface{}) error {
func (v *sliceValue[_]) AppendFromInterface(iFace interface{}, u value.UpdatedBy) error {
val, err := v.inner.FromIFace(iFace)
if err != nil {
return err
}
return v.update(val)
err = v.update(val)
if err != nil {
return err
}
v.updatedBy = u
return nil
}
19 changes: 14 additions & 5 deletions value/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ import (
"fmt"
)

type UpdatedBy string

const (
UpdatedByUnset UpdatedBy = ""
UpdatedByDefault UpdatedBy = "default"
UpdatedByEnvVar UpdatedBy = "envvar"
UpdatedByFlag UpdatedBy = "flag"
UpdatedByConfig UpdatedBy = "config"
)

// Value is a "generic" type to store different types into flags
// Inspired by https://golang.org/src/flag/flag.go .
// There are two underlying "type" families designed to fit in Value:
Expand All @@ -25,14 +35,14 @@ type Value interface {

// ReplaceFromInterface replaces a value with one found in an interface.
// Useful to update a Value from a config.
ReplaceFromInterface(interface{}) error
ReplaceFromInterface(interface{}, UpdatedBy) error

// Update appends to container type Values from a string (useful for CLI flags, env vars, default values)
// and replaces scalar Values
Update(string) error
Update(string, UpdatedBy) error

// ReplaceFromDefault updates the Value from a pre-set default, if one exists. use HasDefault to check whether a default exists
ReplaceFromDefault()
ReplaceFromDefault(u UpdatedBy)
}

type ScalarValue interface {
Expand All @@ -55,12 +65,11 @@ type SliceValue interface {
StringSlice() []string

// AppendFromInterface updates a container type Value from an interface (useful for configs)
// and replaces scalar values (for scalar values, AppendFromInterface is the same as ReplaceFromInterface).
// Note that AppendFromInterface must be called with the "contained" type for container type Values
// For example, the StringSlice.AppendFromInterface
// must be called with a string, not a []string
// It returns ErrIncompatibleInterface if the interface can't be decoded
AppendFromInterface(interface{}) error
AppendFromInterface(interface{}, UpdatedBy) error
}

type DictValue interface {
Expand Down
14 changes: 8 additions & 6 deletions value/value_ext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/stretchr/testify/require"
"go.bbkane.com/warg/value"
"go.bbkane.com/warg/value/scalar"
"go.bbkane.com/warg/value/slice"
)
Expand All @@ -14,15 +15,15 @@ func TestIntScalar(t *testing.T) {
scalar.Default(2),
)()

err := v.Update("1")
err := v.Update("1", value.UpdatedByFlag)
require.Nil(t, err)
require.Equal(t, v.Get().(int), 1)

err = v.Update("-1")
err = v.Update("-1", value.UpdatedByFlag)
require.NotNil(t, err)
require.Equal(t, v.Get().(int), 1)

v.ReplaceFromDefault()
v.ReplaceFromDefault(value.UpdatedByDefault)
require.Equal(t, v.Get().(int), 2)
}

Expand All @@ -33,15 +34,15 @@ func TestIntSlice(t *testing.T) {
slice.Default([]int{1, 1, 1}),
)()

err := v.Update("1")
err := v.Update("1", value.UpdatedByFlag)
require.Nil(t, err)
require.Equal(
t,
[]int{1},
v.Get().([]int),
)

err = v.Update("-1")
err = v.Update("-1", value.UpdatedByFlag)
require.NotNil(t, err)
require.Equal(
t,
Expand All @@ -51,6 +52,7 @@ func TestIntSlice(t *testing.T) {

err = v.ReplaceFromInterface(
[]interface{}{1, 2},
value.UpdatedByFlag,
)
require.Nil(t, err)
require.Equal(
Expand All @@ -59,7 +61,7 @@ func TestIntSlice(t *testing.T) {
v.Get().([]int),
)

v.ReplaceFromDefault()
v.ReplaceFromDefault(value.UpdatedByFlag)
require.Equal(
t,
[]int{1, 1, 1},
Expand Down

0 comments on commit 2bbf4e9

Please sign in to comment.