diff --git a/bind.go b/bind.go index a757ff0..bdaf6a7 100644 --- a/bind.go +++ b/bind.go @@ -1,7 +1,6 @@ package jaws import ( - "fmt" "sync" "time" ) @@ -21,18 +20,14 @@ var ( _ TimeSetter = Binding[time.Time]{} ) -func (bind Binding[T]) getLocked() T { - return *bind.P -} - func (bind Binding[T]) Get() (value T) { if rl, ok := bind.L.(RLocker); ok { rl.RLock() - value = bind.getLocked() + value = *bind.P rl.RUnlock() } else { bind.L.Lock() - value = bind.getLocked() + value = *bind.P bind.L.Unlock() } return @@ -49,10 +44,6 @@ func (bind Binding[T]) Set(value T) (err error) { return } -func (bind Binding[T]) jawsGetLocked(*Element) T { - return bind.getLocked() -} - func (bind Binding[T]) JawsGet(*Element) T { return bind.Get() } @@ -62,45 +53,15 @@ func (bind Binding[T]) JawsSet(elem *Element, value T) error { } func (bind Binding[T]) JawsGetTag(*Request) any { - if x, ok := any(*bind.P).(fmt.Stringer); ok { - return x - } - if x, ok := any(bind.P).(fmt.Stringer); ok { - return x - } return bind.P } func (bind Binding[T]) JawsSetString(e *Element, val string) (err error) { - defer func() { - if e := recover(); e != nil { - if _, ok := any(*bind.P).(fmt.Stringer); ok { - err = ErrValueNotSettable - } else if _, ok := any(bind.P).(fmt.Stringer); ok { - err = ErrValueNotSettable - } else { - panic(e) - } - } - }() return bind.JawsSet(e, any(val).(T)) } func (bind Binding[T]) JawsGetString(e *Element) string { - if rl, ok := bind.L.(RLocker); ok { - rl.RLock() - defer rl.RUnlock() - } else { - bind.L.Lock() - defer bind.L.Unlock() - } - if x, ok := any(*bind.P).(fmt.Stringer); ok { - return x.String() - } - if x, ok := any(bind.P).(fmt.Stringer); ok { - return x.String() - } - return any(bind.jawsGetLocked(e)).(string) + return any(bind.JawsGet(e)).(string) } func (bind Binding[T]) JawsSetFloat(e *Element, val float64) (err error) { @@ -132,10 +93,7 @@ func (bind Binding[T]) JawsSetTime(elem *Element, value time.Time) error { // It implements Setter[T]. It also implements BoolSetter, FloatSetter, StringSetter and TimeSetter, but will panic // if the underlying type T is not correct. // -// It has special support for fmt.Stringer, and will call T.String() for JawsGetString() -// and return ErrValueNotSettable for JawsSetString(). -// -// The pointer (or fmt.Stringer if applicable) will be used as the UI tag. +// The pointer will be used as the UI tag. func Bind[T comparable](l sync.Locker, p *T) Binding[T] { return Binding[T]{L: l, P: p} } diff --git a/stringer.go b/stringer.go index 7c9a61e..c1b4462 100644 --- a/stringer.go +++ b/stringer.go @@ -2,6 +2,7 @@ package jaws import ( "fmt" + "sync" ) type stringizer[T any] struct { @@ -16,14 +17,34 @@ func (s stringizer[T]) JawsGetTag(*Request) any { return s.v } -// Stringer returns a fmt.Stringer using fmt.Sprint(*T) -// unless *T or T implements fmt.Stringer, in which case that will be returned directly. -func Stringer[T any](v *T) fmt.Stringer { - if x, ok := any(*v).(fmt.Stringer); ok { - return x +type lockedstringer struct { + l sync.Locker + s fmt.Stringer +} + +func (s lockedstringer) String() (value string) { + if rl, ok := s.l.(RLocker); ok { + rl.RLock() + defer rl.RUnlock() + } else { + s.l.Lock() + defer s.l.Unlock() + } + return s.s.String() +} + +func (s lockedstringer) JawsGetTag(*Request) any { + return s.s +} + +// Stringer returns a lock protected fmt.Stringer using fmt.Sprint(*T) +// unless *T or T implements fmt.Stringer, in which case that will be used. +func Stringer[T any](l sync.Locker, p *T) fmt.Stringer { + if x, ok := any(*p).(fmt.Stringer); ok { + return lockedstringer{l, x} } - if x, ok := any(v).(fmt.Stringer); ok { - return x + if x, ok := any(p).(fmt.Stringer); ok { + return lockedstringer{l, x} } - return stringizer[T]{v} + return lockedstringer{l, stringizer[T]{p}} } diff --git a/stringer_test.go b/stringer_test.go index 808ca6c..8b9c764 100644 --- a/stringer_test.go +++ b/stringer_test.go @@ -23,8 +23,8 @@ func TestStringer(t *testing.T) { var mu2 sync.RWMutex txt := "text" - stringer := Stringer(&txt) - if s := Bind(&mu, &stringer).JawsGetString(nil); s != "text" { + stringer := Stringer(&mu, &txt) + if s := stringer.String(); s != "text" { t.Error(s) } if tags := MustTagExpand(nil, stringer); !reflect.DeepEqual(tags, []any{&txt}) { @@ -32,8 +32,8 @@ func TestStringer(t *testing.T) { } num := int(123) - stringer = Stringer(&num) - if s := Bind(&mu, &stringer).JawsGetString(nil); s != "123" { + stringer = Stringer(&mu, &num) + if s := stringer.String(); s != "123" { t.Error(s) } if tags := MustTagExpand(nil, stringer); !reflect.DeepEqual(tags, []any{&num}) { @@ -41,52 +41,21 @@ func TestStringer(t *testing.T) { } teststringer := testStringer{} - stringer = Stringer(&teststringer) - if !reflect.DeepEqual(stringer, teststringer) { - t.Errorf("%#v != %#v", stringer, teststringer) - } - if tags := MustTagExpand(nil, stringer); !reflect.DeepEqual(tags, []any{teststringer}) { - t.Errorf("%#v", tags) - } - b1 := Bind(&mu, &stringer) - if s := b1.JawsGetString(nil); s != (testStringer{}).String() { + stringer = Stringer(&mu, &teststringer) + if s := stringer.String(); s != (testStringer{}).String() { t.Error(s) } - if tags := MustTagExpand(nil, b1); !reflect.DeepEqual(tags, []any{teststringer}) { + if tags := MustTagExpand(nil, stringer); !reflect.DeepEqual(tags, []any{teststringer}) { t.Errorf("%#v", tags) } - if err := b1.JawsSetString(nil, "x"); err != ErrValueNotSettable { - t.Error(err) - } testptrstringer := &testPtrStringer{} - stringer = Stringer(testptrstringer) - if !reflect.DeepEqual(stringer, testptrstringer) { - t.Errorf("%#v != %#v", stringer, testptrstringer) - } - if tags := MustTagExpand(nil, stringer); !reflect.DeepEqual(tags, []any{testptrstringer}) { - t.Errorf("%#v", tags) - } - - b2 := Bind(&mu2, &stringer) - if s := b2.JawsGetString(nil); s != (&testPtrStringer{}).String() { + stringer = Stringer(&mu2, testptrstringer) + if s := stringer.String(); s != (&testPtrStringer{}).String() { t.Error(s) } - if tags := MustTagExpand(nil, b2); !reflect.DeepEqual(tags, []any{testptrstringer}) { + if tags := MustTagExpand(nil, stringer); !reflect.DeepEqual(tags, []any{testptrstringer}) { t.Errorf("%#v", tags) } - if err := b2.JawsSetString(nil, "x"); err != ErrValueNotSettable { - t.Error(err) - } - b3 := Bind(&mu2, testptrstringer) - if s := b3.JawsGetString(nil); s != (&testPtrStringer{}).String() { - t.Error(s) - } - if tags := MustTagExpand(nil, b3); !reflect.DeepEqual(tags, []any{testptrstringer}) { - t.Errorf("%#v", tags) - } - if err := b3.JawsSetString(nil, "x"); err != ErrValueNotSettable { - t.Error(err) - } }