diff options
Diffstat (limited to 'refined')
| -rw-r--r-- | refined/condition.go | 24 | ||||
| -rw-r--r-- | refined/refined.go | 59 | ||||
| -rw-r--r-- | refined/refined_test.go | 10 | ||||
| -rw-r--r-- | refined/value.go | 47 |
4 files changed, 76 insertions, 64 deletions
diff --git a/refined/condition.go b/refined/condition.go new file mode 100644 index 0000000..af3b12c --- /dev/null +++ b/refined/condition.go @@ -0,0 +1,24 @@ +/******************************************************************************* +* Copyright 2025 Stefan Majewsky <majewsky@gmx.net> +* SPDX-License-Identifier: Apache-2.0 +* Refer to the file "LICENSE" for details. +*******************************************************************************/ + +package refined + +import ( + "errors" + "regexp" +) + +type Condition[T any] interface { + MatchesValue(T) error +} + +// Building block for writing MatchesValue() implementations. +func RegexpMatch(rx *regexp.Regexp, value string) error { + if !rx.MatchString(value) { + return errors.New("TODO: error message") + } + return nil +} diff --git a/refined/refined.go b/refined/refined.go deleted file mode 100644 index b40fd34..0000000 --- a/refined/refined.go +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* -* Copyright 2025 Stefan Majewsky <majewsky@gmx.net> -* SPDX-License-Identifier: Apache-2.0 -* Refer to the file "LICENSE" for details. -*******************************************************************************/ - -package refined - -import ( - "encoding/json" - "errors" - "regexp" - - . "github.com/majewsky/gg/option" -) - -// NOTE: The zero value is illegal and will panic on use. -type Refined[Self Condition[T], T any] struct { - value Option[T] -} - -type Condition[T any] interface { - MatchesValue(T) error -} - -func Refine[C Condition[T], T any](value T) (Refined[C, T], error) { - var c C - err := c.MatchesValue(value) - if err != nil { - return Refined[C, T]{None[T]()}, err - } - return Refined[C, T]{Some(value)}, nil -} - -func (r Refined[Self, T]) GetValue() T { - return r.value.UnwrapOrPanicf("illegal use of zero-valued instance of Refined type") -} - -func (r *Refined[Self, T]) UnmarshalJSON(buf []byte) error { - var value T - err := json.Unmarshal(buf, &value) - if err != nil { - return err - } - *r, err = Refine[Self](value) - return err -} - -func (r Refined[Self, T]) MarshalJSON() ([]byte, error) { - return json.Marshal(r.GetValue()) -} - -// Building block for writing MatchesValue() implementations. -func RegexpMatch(rx *regexp.Regexp, value string) error { - if !rx.MatchString(value) { - return errors.New("TODO: error message") - } - return nil -} diff --git a/refined/refined_test.go b/refined/refined_test.go index 8faad1f..c6e98e8 100644 --- a/refined/refined_test.go +++ b/refined/refined_test.go @@ -20,7 +20,7 @@ var accountNameRx = regexp.MustCompile(`^[a-z_][a-z0-9_]*$`) // Full demonstration of a refinement type for the test. type AccountName struct { - refined.Refined[AccountName, string] + refined.Value[AccountName, string] } // Demonstration of a struct containing a refinement type. @@ -29,8 +29,8 @@ type AccountData struct { } func NewAccountName(value string) (AccountName, error) { - r, err := refined.Refine[AccountName](value) - return AccountName{r}, err + v, err := refined.NewValue[AccountName](value) + return AccountName{v}, err } // MatchesValue implements the refined.Condition interface. @@ -40,7 +40,7 @@ func (AccountName) MatchesValue(value string) error { // Example for how to access the contained value in computations. func (n AccountName) ContainerName() string { - return fmt.Sprintf("container-for-%s", n.GetValue()) + return fmt.Sprintf("container-for-%s", n.Get()) } func TestAccountName(t *testing.T) { @@ -48,7 +48,7 @@ func TestAccountName(t *testing.T) { var d1 AccountData err := json.Unmarshal(buf1, &d1) AssertEqual(t, err, error(nil)) - AssertEqual(t, d1.Name.GetValue(), "foo") + AssertEqual(t, d1.Name.Get(), "foo") // TODO: fails because we need specialized unmarshaling logic on type AccountData buf2 := []byte(`{}`) diff --git a/refined/value.go b/refined/value.go new file mode 100644 index 0000000..f9cb626 --- /dev/null +++ b/refined/value.go @@ -0,0 +1,47 @@ +/******************************************************************************* +* Copyright 2025 Stefan Majewsky <majewsky@gmx.net> +* SPDX-License-Identifier: Apache-2.0 +* Refer to the file "LICENSE" for details. +*******************************************************************************/ + +package refined + +import ( + "encoding/json" + + . "github.com/majewsky/gg/option" +) + +// TODO: how do we express a literal constructor, preferably in a generic way? e.g. `var demoAccountName = refined.Literal[AccountName]("demo")` + +// NOTE: The zero value is illegal and will panic on use. +type Value[Self Condition[T], T any] struct { + value Option[T] +} + +func NewValue[C Condition[T], T any](value T) (Value[C, T], error) { + var c C + err := c.MatchesValue(value) + if err != nil { + return Value[C, T]{None[T]()}, err + } + return Value[C, T]{Some(value)}, nil +} + +func (v Value[Self, T]) Get() T { + return v.value.UnwrapOrPanic("illegal use of zero-valued instance of Refined type") +} + +func (v *Value[Self, T]) UnmarshalJSON(buf []byte) error { + var value T + err := json.Unmarshal(buf, &value) + if err != nil { + return err + } + *v, err = NewValue[Self](value) + return err +} + +func (v Value[Self, T]) MarshalJSON() ([]byte, error) { + return json.Marshal(v.Get()) +} |
