Skip to content

Commit 3b76b34

Browse files
committed
the basics with generics
Signed-off-by: Aaron Schlesinger <[email protected]>
1 parent 9dd2b6e commit 3b76b34

18 files changed

+104
-341
lines changed

bool.go

Lines changed: 0 additions & 16 deletions
This file was deleted.

builder.go

Lines changed: 0 additions & 38 deletions
This file was deleted.

builder_test.go

Lines changed: 0 additions & 11 deletions
This file was deleted.

decode.go

Lines changed: 5 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,11 @@
11
package dcode
22

3-
import (
4-
"fmt"
5-
"reflect"
6-
)
7-
83
// Decode decodes b into i using d
9-
func Decode(b []byte, d Decoder, i interface{}) error {
10-
ret, err := d.call(b)
4+
func Decode[T any](b []byte, d Decoder[T]) (T, error) {
5+
var zero T
6+
ret, err := d.Decode(b)
117
if err != nil {
12-
return err
13-
}
14-
inputType := reflect.TypeOf(i)
15-
if inputType.Kind() != reflect.Ptr {
16-
return fmt.Errorf("Given %s type is not a pointer", inputType.Name())
17-
}
18-
inputTypeName := inputType.Elem().Name()
19-
returnedType := reflect.TypeOf(ret)
20-
if inputTypeName != returnedType.Name() {
21-
return fmt.Errorf(
22-
"Got type '%s', but expected '%s'",
23-
returnedType.Name(),
24-
inputType.Name(),
25-
)
26-
}
27-
// Need to call Elem to traverse the pointer
28-
inputVal := reflect.ValueOf(i).Elem()
29-
retVal := reflect.ValueOf(ret)
30-
if !inputVal.CanSet() {
31-
return fmt.Errorf("Couldn't set the decoded value")
32-
}
33-
inputVal.Set(retVal)
34-
return nil
35-
}
36-
37-
// MapPair is a value to pass into the Map function. Create one
38-
// of these with the Pair function, and see the documentation
39-
// under the Map function for where this is used
40-
type MapPair struct {
41-
decoder Decoder
42-
iface interface{}
43-
}
44-
45-
// Pair returns a new MapPair
46-
func Pair(d Decoder, iface interface{}) MapPair {
47-
return MapPair{decoder: d, iface: iface}
48-
}
49-
50-
// Map decodes separate JSON fields into separate values,
51-
// using separate decoders, all from the same JSON.
52-
//
53-
// For example, if you have the following JSON:
54-
//
55-
// json := `{"field1": 123, "field2": "456"}`
56-
//
57-
// And you have the following decoders:
58-
//
59-
// dcode1 := Field("field1", Int())
60-
// dcode2 := Field("field2", String())
61-
//
62-
// You can do the following:
63-
//
64-
// stucco := struct{
65-
// field1 int
66-
// field2 string
67-
// }
68-
//
69-
// // check the error here!
70-
// Map(
71-
// []byte(json),
72-
// Pair(dcode1, &stucco.field1),
73-
// Pair(dcode2, &stucco.field2),
74-
// )
75-
//
76-
// Note: you can decode JSON into structs using this function,
77-
// but if you're trying to decode tons of fields from JSON
78-
// to struct fields, you might be better off using
79-
// encoding/json!
80-
func Map(
81-
b []byte,
82-
pairs ...MapPair,
83-
) error {
84-
for _, pair := range pairs {
85-
if err := Decode(b, pair.decoder, pair.iface); err != nil {
86-
return err
87-
}
88-
}
89-
return nil
90-
}
91-
92-
// OneOf tries to decode b into i using every decoder in dList.
93-
//
94-
// It returns nil after the first decoder succeeds. If none
95-
// succeeded, returns an error
96-
func OneOf(b []byte, dList []Decoder, i interface{}) error {
97-
for _, d := range dList {
98-
if err := Decode(b, d, i); err == nil {
99-
return nil
100-
}
8+
return zero, err
1019
}
102-
return fmt.Errorf("Tried %d decoders, none decoded", len(dList))
10+
return ret, nil
10311
}

decode_test.go

Lines changed: 0 additions & 9 deletions
This file was deleted.

decoder.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ package dcode
88
// First(...).Then(...).Into(...).
99
//
1010
// Check out the documentation for Field() or Builder for more information
11-
type Decoder struct {
12-
call func([]byte) (interface{}, error)
11+
type Decoder[T any] interface {
12+
Decode([]byte) (T, error)
1313
}
1414

15-
func newDecoder(fn func([]byte) (interface{}, error)) Decoder {
16-
return Decoder{call: fn}
15+
type DecoderFunc[T any] func([]byte) (T, error)
16+
17+
func (d DecoderFunc[T]) Decode(b []byte) (T, error) {
18+
return d(b)
1719
}

decoder_test.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,40 @@ package dcode
33
import (
44
"fmt"
55
"strconv"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
69
)
710

8-
func (t *TheSuite) TestSimpleInt() {
9-
r := t.Require()
11+
func TestSimpleInt(t *testing.T) {
12+
r := require.New(t)
1013
ints := []int{1, 2, 3, 4, 5, 1000}
1114
decoder := Int()
1215
for i, expected := range ints {
1316
expectedBytes := []byte(strconv.Itoa(expected))
14-
actual, err := decoder.call(expectedBytes)
17+
actual, err := decoder.Decode(expectedBytes)
1518
r.NoError(err, "for iteration %d, int %d", i, expected)
1619
r.Equal(expected, actual, "expected int %d", actual)
1720
}
1821

1922
notInts := []string{`"abc"`, `"dev"`}
2023
for _, notInt := range notInts {
21-
actual, err := decoder.call([]byte(notInt))
24+
actual, err := decoder.Decode([]byte(notInt))
2225
r.True(err != nil)
2326
r.Equal(0, actual)
2427
}
2528
}
2629

27-
func (t *TheSuite) TestSimpleString() {
28-
r := t.Require()
30+
func TestSimpleString(t *testing.T) {
31+
r := require.New(t)
2932
decoder := String()
3033
strings := []string{
3134
`"this is a thing"`,
3235
`"this is another thing"`,
3336
}
3437
for i, expected := range strings {
3538
b := []byte(expected)
36-
actual, err := decoder.call(b)
39+
actual, err := decoder.Decode(b)
3740
r.NoError(err, "for iteration %d", i)
3841
actualJSONStr := fmt.Sprintf(`"%s"`, actual)
3942
r.Equal(expected, actualJSONStr, "for iteration %d", i)

decoders.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package dcode
2+
3+
import (
4+
"encoding/json"
5+
)
6+
7+
// Float64 returns a Decoder that can decode JSON directly
8+
// into a float64
9+
func Float64() Decoder[float64] {
10+
var ret float64
11+
return DecoderFunc[float64](
12+
func(b []byte) (float64, error) {
13+
if err := json.Unmarshal(b, &ret); err != nil {
14+
return ret, nil
15+
}
16+
return ret, nil
17+
},
18+
)
19+
}
20+
21+
// Bool returns a decoder that can decode JSON
22+
// into a bool
23+
func Bool() Decoder[bool] {
24+
return DecoderFunc[bool](func(b []byte) (bool, error) {
25+
var ret bool
26+
if err := json.Unmarshal(b, &ret); err != nil {
27+
return false, err
28+
}
29+
return ret, nil
30+
})
31+
}
32+
33+
// Int returns a Decoder that can decode any JSON
34+
// into an integer
35+
func Int() Decoder[int] {
36+
var zero int
37+
return DecoderFunc[int](
38+
func(b []byte) (int, error) {
39+
var ret int
40+
if err := json.Unmarshal(b, &ret); err != nil {
41+
return zero, err
42+
}
43+
return ret, nil
44+
},
45+
)
46+
}
47+
48+
type MapT map[string]interface{}
49+
50+
func Map() Decoder[MapT] {
51+
zero := map[string]interface{}{}
52+
return DecoderFunc[MapT](
53+
func(b []byte) (MapT, error) {
54+
var ret map[string]interface{}
55+
if err := json.Unmarshal(b, &ret); err != nil {
56+
return zero, nil
57+
}
58+
return ret, nil
59+
},
60+
)
61+
}
62+
63+
// String returns a Decoder that can decode JSON
64+
// into a string
65+
func String() Decoder[string] {
66+
var zero string
67+
return DecoderFunc[string](
68+
func(b []byte) (string, error) {
69+
var ret string
70+
if err := json.Unmarshal(b, &ret); err != nil {
71+
return zero, err
72+
}
73+
return ret, nil
74+
},
75+
)
76+
}

exists.go

Lines changed: 0 additions & 10 deletions
This file was deleted.

field.go

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)