Skip to content

Commit f7c3ff3

Browse files
committed
Add guard conditions to enforce how mapper func should be passed
1 parent 7079ddc commit f7c3ff3

File tree

2 files changed

+111
-4
lines changed

2 files changed

+111
-4
lines changed

map.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,32 @@ import (
77

88
// Map applies mapperFn on each item of in and puts it in out
99
func Map(in, out, mapperFn interface{}) error {
10-
mapper := reflect.ValueOf(mapperFn)
11-
if mapper.Kind() != reflect.Func {
12-
return fmt.Errorf("mapperFn has to be a function")
13-
}
1410
input := reflect.ValueOf(in)
1511
output := reflect.ValueOf(out)
12+
13+
zeroValue := reflect.Value{}
14+
if output == zeroValue {
15+
return fmt.Errorf("output is nil. Pass a reference to set output")
16+
}
17+
18+
if output.IsNil() {
19+
return fmt.Errorf("output is nil. Pass a reference to set output")
20+
}
21+
1622
if !output.Elem().CanSet() {
1723
return fmt.Errorf("cannot set out. Pass a reference to set output")
1824
}
1925

26+
mapper := reflect.ValueOf(mapperFn)
27+
if err := validateMapperFunction(mapper); err != nil {
28+
return err
29+
}
30+
2031
if input.Kind() == reflect.Slice {
32+
if input.Type().Elem().Kind() != mapper.Type().In(0).Kind() {
33+
return fmt.Errorf("mapper function's first argument has to be the type of element of input slice")
34+
}
35+
2136
result := reflect.MakeSlice(output.Elem().Type(), 0, input.Len())
2237
for i := 0; i < input.Len(); i++ {
2338
arg := input.Index(i)
@@ -32,3 +47,20 @@ func Map(in, out, mapperFn interface{}) error {
3247
}
3348
return fmt.Errorf("not implemented")
3449
}
50+
51+
func validateMapperFunction(mapper reflect.Value) error {
52+
if mapper.Kind() != reflect.Func {
53+
return fmt.Errorf("mapperFn has to be a function")
54+
}
55+
56+
mapperFnType := mapper.Type()
57+
if mapperFnType.NumIn() != 1 {
58+
return fmt.Errorf("mapper function has to take only one argument")
59+
}
60+
61+
if mapperFnType.NumOut() != 1 {
62+
return fmt.Errorf("mapper function should return only one return value")
63+
}
64+
65+
return nil
66+
}

map_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,79 @@ func TestMap(t *testing.T) {
4040
assert.NoError(t, err)
4141
assert.Equal(t, expected, out)
4242
})
43+
44+
squared := func(element int) int {
45+
return element * element
46+
}
47+
48+
t.Run("should not panic if output is nil", func(t *testing.T) {
49+
in := []int{1, 2, 3}
50+
{
51+
var out []int
52+
53+
err := godash.Map(in, out, squared)
54+
55+
assert.EqualError(t, err, "output is nil. Pass a reference to set output")
56+
}
57+
58+
{
59+
err := godash.Map(in, nil, squared)
60+
61+
assert.EqualError(t, err, "output is nil. Pass a reference to set output")
62+
}
63+
})
64+
65+
t.Run("should not accept mapper function that are not functions", func(t *testing.T) {
66+
in := []int{1, 2, 3}
67+
var out []int
68+
69+
err := godash.Map(in, &out, 7)
70+
71+
assert.EqualError(t, err, "mapperFn has to be a function")
72+
})
73+
74+
t.Run("should not accept mapper function that do not take exactly one argument", func(t *testing.T) {
75+
in := []int{1, 2, 3}
76+
var out []int
77+
78+
{
79+
err := godash.Map(in, &out, func() int { return 0 })
80+
assert.EqualError(t, err, "mapper function has to take only one argument")
81+
}
82+
83+
{
84+
err := godash.Map(in, &out, func(int, int) int { return 0 })
85+
assert.EqualError(t, err, "mapper function has to take only one argument")
86+
}
87+
})
88+
89+
t.Run("should not accept mapper function that do not return exactly one value", func(t *testing.T) {
90+
in := []int{1, 2, 3}
91+
var out []int
92+
93+
{
94+
err := godash.Map(in, &out, func(int) {})
95+
assert.EqualError(t, err, "mapper function should return only one return value")
96+
}
97+
98+
{
99+
err := godash.Map(in, &out, func(int) (int, int) { return 0, 0 })
100+
assert.EqualError(t, err, "mapper function should return only one return value")
101+
}
102+
})
103+
104+
t.Run("should accept mapper function whose argument's kind should be slice's element kind", func(t *testing.T) {
105+
in := []int{1, 2, 3}
106+
var out []int
107+
108+
{
109+
err := godash.Map(in, &out, func(string) string { return "" })
110+
assert.EqualError(t, err, "mapper function's first argument has to be the type of element of input slice")
111+
}
112+
113+
{
114+
err := godash.Map(in, &out, func(int) int { return 0 })
115+
assert.NoError(t, err)
116+
}
117+
})
43118
}

0 commit comments

Comments
 (0)