diff --git a/afterset.go b/afterset.go new file mode 100644 index 0000000..c7a803b --- /dev/null +++ b/afterset.go @@ -0,0 +1,44 @@ +package jaws + +import "time" + +type AfterSetter[T comparable] struct { + Binding[T] + Func func() +} + +func (as *AfterSetter[T]) Set(value T) (err error) { + if err = as.Binding.Set(value); err == nil { + as.Func() + } + return +} + +func (as *AfterSetter[T]) JawsSet(elem *Element, value T) (err error) { + return as.Set(value) +} + +func (as *AfterSetter[T]) JawsSetString(e *Element, val string) (err error) { + return as.JawsSet(e, any(val).(T)) +} + +func (as *AfterSetter[T]) JawsSetFloat(e *Element, val float64) (err error) { + return as.JawsSet(e, any(val).(T)) +} + +func (as *AfterSetter[T]) JawsSetBool(e *Element, val bool) (err error) { + return as.JawsSet(e, any(val).(T)) +} + +func (as *AfterSetter[T]) JawsSetTime(elem *Element, value time.Time) error { + return as.JawsSet(elem, any(value).(T)) +} + +// AfterSet returns a wrapped Binding with a function to call after a +// successful Set. +func AfterSet[T comparable](bind Binding[T], fn func()) *AfterSetter[T] { + return &AfterSetter[T]{ + Binding: bind, + Func: fn, + } +} diff --git a/afterset_test.go b/afterset_test.go new file mode 100644 index 0000000..c067129 --- /dev/null +++ b/afterset_test.go @@ -0,0 +1,88 @@ +package jaws + +import ( + "sync" + "testing" + "time" +) + +func TestAfterSet_String(t *testing.T) { + var mu sync.Mutex + var value string + var called int + + fn := func() { + called++ + } + + as := AfterSet(Bind(&mu, &value), fn) + if err := as.JawsSetString(nil, "foo"); err != nil { + t.Error(err) + } + if called != 1 { + t.Error(called) + } + if tag := as.JawsGetTag(nil); tag != &value { + t.Error(tag) + } + if err := as.JawsSetString(nil, "foo"); err != ErrValueUnchanged { + t.Error(err) + } + if called != 1 { + t.Error(called) + } +} + +func TestAfterSet_Float(t *testing.T) { + var mu sync.Mutex + var value float64 + var called bool + + fn := func() { + called = true + } + + as := AfterSet(Bind(&mu, &value), fn) + if err := as.JawsSetFloat(nil, 1); err != nil { + t.Error(err) + } + if !called { + t.Error(called) + } +} + +func TestAfterSet_Bool(t *testing.T) { + var mu sync.Mutex + var value bool + var called bool + + fn := func() { + called = true + } + + as := AfterSet(Bind(&mu, &value), fn) + if err := as.JawsSetBool(nil, !value); err != nil { + t.Error(err) + } + if !called { + t.Error(called) + } +} + +func TestAfterSet_Time(t *testing.T) { + var mu sync.Mutex + var value time.Time + var called bool + + fn := func() { + called = true + } + + as := AfterSet(Bind(&mu, &value), fn) + if err := as.JawsSetTime(nil, time.Now()); err != nil { + t.Error(err) + } + if !called { + t.Error(called) + } +} diff --git a/bind.go b/bind.go index bdaf6a7..6260f65 100644 --- a/bind.go +++ b/bind.go @@ -85,7 +85,7 @@ func (bind Binding[T]) JawsGetTime(elem *Element) time.Time { } func (bind Binding[T]) JawsSetTime(elem *Element, value time.Time) error { - return bind.Set(any(value).(T)) + return bind.JawsSet(elem, any(value).(T)) } // Bind returns a Binding[T] with the given sync.Locker (or RWLocker) and a pointer to the underlying value of type T.