aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--refined/condition.go24
-rw-r--r--refined/refined.go59
-rw-r--r--refined/refined_test.go10
-rw-r--r--refined/value.go47
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())
+}