From 876b3b930dc8c90391082ee5b629f847fbb5d337 Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Wed, 2 Jul 2025 17:18:25 +0200 Subject: refined: UnmarshalJSON needs a PreScalar type after all --- refined/value.go | 70 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 31 deletions(-) (limited to 'refined/value.go') diff --git a/refined/value.go b/refined/value.go index cfaa412..a15e4f1 100644 --- a/refined/value.go +++ b/refined/value.go @@ -4,9 +4,8 @@ package refined import ( - "cmp" + "encoding/json" "errors" - "regexp" . "github.com/majewsky/gg/option" ) @@ -21,52 +20,61 @@ func (s Scalar[S, V]) Raw() V { func New[S IsAScalar[S, V], V any](value V) (S, error) { var empty S - return empty.Refine(Challenge[S, V]{Value: value, valid: true}) + if empty.RefinedMatch(value) { + return empty.RefinedBuild(PreScalar[S, V]{value: Some(value)}), nil + } else { + return empty, errors.New("TODO 2") + } } func Literal[S IsAScalar[S, V], V any](value V) S { - var empty S - s, err := empty.Refine(Challenge[S, V]{Value: value, valid: true}) + s, err := New[S, V](value) if err != nil { - panic("TODO 2") + panic(err.Error()) } return s } -type IsAScalar[S any, V any] interface { - Refine(Challenge[S, V]) (S, error) +func (s Scalar[S, V]) MarshalJSON() ([]byte, error) { + return json.Marshal(s.Raw()) } -type Challenge[S any, V any] struct { - Value V - valid bool -} +func (s *Scalar[S, V]) UnmarshalJSON(buf []byte) error { + var value V + err := json.Unmarshal(buf, &value) + if err != nil { + return err + } -func (c Challenge[S, V]) Accept() Scalar[S, V] { - if !c.valid { - panic("broken Challenge object") + // We cannot directly call `New[S, V](value)` here because we cannot prove statically + // that `S` satisfies `IsAScalar[S, V]`. + var empty S + if r, ok := any(empty).(IsAScalar[S, V]); ok { + if r.RefinedMatch(value) { + *s = Scalar[S, V]{value: Some(value)} + return nil + } else { + return errors.New("TODO 3") + } + } else { + return errors.New("TODO 4") } - return Scalar[S, V]{value: Some(c.Value)} } -func RangeCheck[S any, V cmp.Ordered](c Challenge[S, V], minimum, maximum V) (Scalar[S, V], error) { - if minimum <= maximum && c.Value >= minimum && c.Value <= maximum { - return c.Accept(), nil - } - return Scalar[S, V]{}, errors.New("TODO 3") +type IsAScalar[S any, V any] interface { + // We need both steps separately. New() wants to have an S at the end, which goes through both steps. + // But Unmarshal...() wants to obtain a Scalar[S, V], so it only wants to use the first step. + RefinedMatch(V) bool + RefinedBuild(PreScalar[S, V]) S } -func RegexpCheck[S any, V ~string](c Challenge[S, V], rx *regexp.Regexp) (Scalar[S, V], error) { - if rx.MatchString(string(c.Value)) { - return c.Accept(), nil - } - return Scalar[S, V]{}, errors.New("TODO 4") +type PreScalar[S any, V any] struct { + value Option[V] } -func NotZeroCheck[S any, V comparable](c Challenge[S, V]) (Scalar[S, V], error) { - var zero V - if c.Value != zero { - return c.Accept(), nil +func (p PreScalar[S, V]) Into() Scalar[S, V] { + if p.value.IsNone() { + panic("broken PreScalar object") } - return Scalar[S, V]{}, errors.New("TODO 5") + return Scalar[S, V](p) } -- cgit v1.2.3