Skip to content

Commit 7920d7d

Browse files
committed
feat: add Close functions
This change adds a `Close` function to both the `maybe` and `result` packages. These funcitions call the Close method on their encapsulated values if there is a value and that value implements `io.Closer`. These are convience functions that could be implemented by users but are so common as to warrent inclusion. An `As` function is also added to the `maybe` package to mirror the `result` package's `As` function. Signed-off-by: m-d-key <[email protected]>
1 parent 3c23fec commit 7920d7d

File tree

5 files changed

+141
-1
lines changed

5 files changed

+141
-1
lines changed

pkg/maybe/maybe.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
package maybe
55

6+
import (
7+
"io"
8+
)
9+
610
// Getter defines a function that gets the default value for a Maybe that
711
// encapsulates an absent value.
812
type Getter[T any] func() T
@@ -24,6 +28,37 @@ type Maybe[T any] interface {
2428
OrElseGet(getter Getter[T]) T
2529
}
2630

31+
// As encapsulates the value in the given Maybe in a new Maybe of the target
32+
// type. If the given Maybe encapsulates nothing, or the encapsulated value does
33+
// not implement the requested type, then a Nothing Maybe will be returned of
34+
// the target type.
35+
func As[T, S any](src Maybe[S]) Maybe[T] {
36+
if !src.IsJust() {
37+
return Nothing[T]()
38+
}
39+
var v any = src.MustGet()
40+
val, ok := v.(T)
41+
if !ok {
42+
return Nothing[T]()
43+
}
44+
return Just(val)
45+
}
46+
47+
// Close calls close on the encapsulated value if that value implements
48+
// io.Closer. It is a no-op if the value encapsulates nothing or if the
49+
// encasuplated value does not implement io.Closer.
50+
func Close[T any](src Maybe[T]) error {
51+
if !src.IsJust() {
52+
return nil
53+
}
54+
v := any(src.MustGet())
55+
closer, ok := v.(io.Closer)
56+
if !ok {
57+
return nil
58+
}
59+
return closer.Close()
60+
}
61+
2762
// MapperNoError defines a function with one argument of any type that returns a
2863
// single value.
2964
type MapperNoError[T, R any] func(

pkg/maybe/maybe_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,59 @@ func Test_Just_Is_Just(t *testing.T) {
3636
instance := maybe.Just(10)
3737
require.True(t, maybe.IsJust(instance))
3838
}
39+
40+
type named interface {
41+
GetName() string
42+
}
43+
44+
type instance struct{}
45+
46+
func (i *instance) GetName() string { return "my name" }
47+
48+
func Test_As(t *testing.T) {
49+
m := maybe.Just(&instance{})
50+
a := maybe.As[named](m)
51+
require.True(t, a.IsJust())
52+
require.Equal(t, "my name", a.MustGet().GetName())
53+
}
54+
55+
func Test_As_NotSupported(t *testing.T) {
56+
m := maybe.Just("string")
57+
a := maybe.As[int](m)
58+
require.False(t, a.IsJust())
59+
}
60+
61+
func Test_As_FromNothing(t *testing.T) {
62+
m := maybe.Nothing[string]()
63+
a := maybe.As[int](m)
64+
require.False(t, a.IsJust())
65+
}
66+
67+
type closer struct {
68+
called bool
69+
}
70+
71+
func (c *closer) Close() error {
72+
c.called = true
73+
return nil
74+
}
75+
76+
func Test_CloseCloser(t *testing.T) {
77+
c := &closer{}
78+
closerMaybe := maybe.Just(c)
79+
err := maybe.Close(closerMaybe)
80+
require.NoError(t, err)
81+
require.True(t, c.called)
82+
}
83+
84+
func Test_CloseNonCloser(t *testing.T) {
85+
closerMaybe := maybe.Just(99)
86+
err := maybe.Close(closerMaybe)
87+
require.NoError(t, err)
88+
}
89+
90+
func Test_CloseError(t *testing.T) {
91+
closerMaybe := maybe.Nothing[int]()
92+
err := maybe.Close(closerMaybe)
93+
require.NoError(t, err)
94+
}

pkg/result/ok.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
package result
55

6-
import "fmt"
6+
import (
7+
"fmt"
8+
)
79

810
// Ok returns a Result encapsulating the given value.
911
func Ok[T any](value T) Result[T] {
@@ -45,6 +47,7 @@ func (o *ok[T]) get() T {
4547
return o.value
4648
}
4749

50+
// String returns a string representation of this Result.
4851
func (o *ok[T]) String() string {
4952
return fmt.Sprintf("{Ok: %#v}", o.value)
5053
}

pkg/result/result.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
package result
55

66
import (
7+
"io"
8+
79
"github.com/sassoftware/sas-ggdk/pkg/errors"
810
"github.com/sassoftware/sas-ggdk/pkg/maybe"
911
)
@@ -59,6 +61,21 @@ func As[T, S any](src Result[S]) Result[T] {
5961
return Ok(val)
6062
}
6163

64+
// Close calls close on the encapsulated value if that value implements
65+
// io.Closer. It is a no-op if the value does not implement io.Closer or the
66+
// result encapsulates an error.
67+
func Close[T any](src Result[T]) error {
68+
if src.IsError() {
69+
return nil
70+
}
71+
v := any(src.MustGet())
72+
closer, ok := v.(io.Closer)
73+
if !ok {
74+
return nil
75+
}
76+
return closer.Close()
77+
}
78+
6279
// ErrorMapper defines a function that takes an error and returns an error.
6380
// This can be used to convert between errors or add additional context to an
6481
// error.

pkg/result/result_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,32 @@ func Test_FromMaybe(t *testing.T) {
104104
require.Error(t, rErr.Error())
105105
require.ErrorContains(t, rErr.Error(), "failed")
106106
}
107+
108+
type closer struct {
109+
called bool
110+
}
111+
112+
func (c *closer) Close() error {
113+
c.called = true
114+
return nil
115+
}
116+
117+
func Test_CloseCloser(t *testing.T) {
118+
c := &closer{}
119+
closerResult := result.Ok(c)
120+
err := result.Close(closerResult)
121+
require.NoError(t, err)
122+
require.True(t, c.called)
123+
}
124+
125+
func Test_CloseNonCloser(t *testing.T) {
126+
closerResult := result.Ok(99)
127+
err := result.Close(closerResult)
128+
require.NoError(t, err)
129+
}
130+
131+
func Test_CloseError(t *testing.T) {
132+
closerResult := result.Error[int](errors.New("failed"))
133+
err := result.Close(closerResult)
134+
require.NoError(t, err)
135+
}

0 commit comments

Comments
 (0)