Skip to content

Commit 653493a

Browse files
authored
Refine attribute dictionary methods (#2496)
* refine attribute dict methods * fix Unknown checker bug
1 parent 51b5663 commit 653493a

2 files changed

Lines changed: 212 additions & 10 deletions

File tree

pkg/attribute/attribute.go

Lines changed: 113 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,56 +56,159 @@ type Description struct {
5656
}
5757

5858
// Int declares an attribute of int-typed in Dictionary d.
59-
func (d Dictionary) Int(name string, value int, doc string, checker func(int) error) Dictionary {
59+
func (d Dictionary) Int(name string, value interface{}, doc string, checker func(int) error) Dictionary {
60+
interfaceChecker := func(v interface{}) error {
61+
if intValue, ok := v.(int); ok {
62+
if checker != nil {
63+
return checker(intValue)
64+
}
65+
return nil
66+
}
67+
return fmt.Errorf("attribute %s must be of type int, but got %T", name, v)
68+
}
69+
70+
if value != nil {
71+
err := interfaceChecker(value)
72+
if err != nil {
73+
log.Panicf("default value of attribute %s is invalid, error is: %s", name, err)
74+
}
75+
}
76+
6077
d[name] = &Description{
6178
Type: Int,
6279
Default: value,
6380
Doc: doc,
64-
Checker: func(x interface{}) error { return checker(x.(int)) },
81+
Checker: interfaceChecker,
6582
}
6683
return d
6784
}
6885

6986
// Float declares an attribute of float32-typed in Dictionary d.
70-
func (d Dictionary) Float(name string, value float32, doc string, checker func(float32) error) Dictionary {
87+
func (d Dictionary) Float(name string, value interface{}, doc string, checker func(float32) error) Dictionary {
88+
interfaceChecker := func(v interface{}) error {
89+
if floatValue, ok := v.(float32); ok {
90+
if checker != nil {
91+
return checker(floatValue)
92+
}
93+
return nil
94+
}
95+
return fmt.Errorf("attribute %s must be of type float, but got %T", name, v)
96+
}
97+
98+
if value != nil {
99+
err := interfaceChecker(value)
100+
if err != nil {
101+
log.Panicf("default value of attribute %s is invalid, error is: %s", name, err)
102+
}
103+
}
104+
71105
d[name] = &Description{
72106
Type: Float,
73107
Default: value,
74108
Doc: doc,
75-
Checker: func(x interface{}) error { return checker(x.(float32)) },
109+
Checker: interfaceChecker,
76110
}
77111
return d
78112
}
79113

80114
// Bool declares an attribute of bool-typed in Dictionary d.
81-
func (d Dictionary) Bool(name string, value bool, doc string, checker func(bool) error) Dictionary {
115+
func (d Dictionary) Bool(name string, value interface{}, doc string, checker func(bool) error) Dictionary {
116+
interfaceChecker := func(v interface{}) error {
117+
if boolValue, ok := v.(bool); ok {
118+
if checker != nil {
119+
return checker(boolValue)
120+
}
121+
return nil
122+
}
123+
return fmt.Errorf("attribute %s must be of type bool, but got %T", name, v)
124+
}
125+
126+
if value != nil {
127+
err := interfaceChecker(value)
128+
if err != nil {
129+
log.Panicf("default value of attribute %s is invalid, error is: %s", name, err)
130+
}
131+
}
132+
82133
d[name] = &Description{
83134
Type: Bool,
84135
Default: value,
85136
Doc: doc,
86-
Checker: func(x interface{}) error { return checker(x.(bool)) },
137+
Checker: interfaceChecker,
87138
}
88139
return d
89140
}
90141

91142
// String declares an attribute of string-typed in Dictionary d.
92-
func (d Dictionary) String(name string, value string, doc string, checker func(string) error) Dictionary {
143+
func (d Dictionary) String(name string, value interface{}, doc string, checker func(string) error) Dictionary {
144+
interfaceChecker := func(v interface{}) error {
145+
if stringValue, ok := v.(string); ok {
146+
if checker != nil {
147+
return checker(stringValue)
148+
}
149+
return nil
150+
}
151+
return fmt.Errorf("attribute %s must be of type string, but got %T", name, v)
152+
}
153+
154+
if value != nil {
155+
err := interfaceChecker(value)
156+
if err != nil {
157+
log.Panicf("default value of attribute %s is invalid, error is: %s", name, err)
158+
}
159+
}
160+
93161
d[name] = &Description{
94162
Type: String,
95163
Default: value,
96164
Doc: doc,
97-
Checker: func(x interface{}) error { return checker(x.(string)) },
165+
Checker: interfaceChecker,
98166
}
99167
return d
100168
}
101169

102170
// IntList declares an attribute of []int-typed in Dictionary d.
103-
func (d Dictionary) IntList(name string, value []int, doc string, checker func([]int) error) Dictionary {
171+
func (d Dictionary) IntList(name string, value interface{}, doc string, checker func([]int) error) Dictionary {
172+
interfaceChecker := func(v interface{}) error {
173+
if intListValue, ok := v.([]int); ok {
174+
if checker != nil {
175+
return checker(intListValue)
176+
}
177+
return nil
178+
}
179+
return fmt.Errorf("attribute %s must be of type []int, but got %T", name, v)
180+
}
181+
182+
if value != nil {
183+
err := interfaceChecker(value)
184+
if err != nil {
185+
log.Panicf("default value of attribute %s is invalid, error is: %s", name, err)
186+
}
187+
}
188+
104189
d[name] = &Description{
105190
Type: IntList,
106191
Default: value,
107192
Doc: doc,
108-
Checker: func(x interface{}) error { return checker(x.([]int)) },
193+
Checker: interfaceChecker,
194+
}
195+
return d
196+
}
197+
198+
// Unknown declares an attribute of dynamically determined type
199+
func (d Dictionary) Unknown(name string, value interface{}, doc string, checker func(interface{}) error) Dictionary {
200+
if value != nil && checker != nil {
201+
err := checker(value)
202+
if err != nil {
203+
log.Panicf("default value of attribute %s is invalid, error is: %s", name, err)
204+
}
205+
}
206+
207+
d[name] = &Description{
208+
Type: Unknown,
209+
Default: value,
210+
Doc: doc,
211+
Checker: checker,
109212
}
110213
return d
111214
}

pkg/attribute/attribute_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,110 @@ package attribute
1616
import (
1717
"fmt"
1818
"reflect"
19+
"strings"
1920
"testing"
2021

2122
"github.com/stretchr/testify/assert"
2223
)
2324

25+
func TestDictionaryNamedTypeChecker(t *testing.T) {
26+
a := assert.New(t)
27+
name := "any_attr_name"
28+
29+
assertFunc := func(d Dictionary, value interface{}, ok bool) {
30+
err := d.Validate(map[string]interface{}{name: value})
31+
if ok {
32+
a.NoError(err)
33+
} else {
34+
a.Error(err)
35+
}
36+
}
37+
38+
boolDictWithoutChecker := Dictionary{}.Bool(name, false, "", nil)
39+
assertFunc(boolDictWithoutChecker, "abc", false)
40+
assertFunc(boolDictWithoutChecker, false, true)
41+
assertFunc(boolDictWithoutChecker, true, true)
42+
boolDictWithChecker := Dictionary{}.Bool(name, nil, "", func(v bool) error {
43+
if v {
44+
return fmt.Errorf("attribute %s must be false", name)
45+
}
46+
return nil
47+
})
48+
assertFunc(boolDictWithChecker, "abc", false)
49+
assertFunc(boolDictWithChecker, false, true)
50+
assertFunc(boolDictWithChecker, true, false)
51+
52+
intDictWithoutChecker := Dictionary{}.Int(name, 0, "", nil)
53+
assertFunc(intDictWithoutChecker, "abc", false)
54+
assertFunc(intDictWithoutChecker, 3, true)
55+
intDictWithChecker := Dictionary{}.Int(name, 0, "", func(v int) error {
56+
if v == 3 {
57+
return fmt.Errorf("attribute %s cannot be 3", name)
58+
}
59+
return nil
60+
})
61+
assertFunc(intDictWithChecker, "abc", false)
62+
assertFunc(intDictWithChecker, 3, false)
63+
assertFunc(intDictWithChecker, 0, true)
64+
65+
floatDictWithoutChecker := Dictionary{}.Float(name, nil, "", nil)
66+
assertFunc(floatDictWithoutChecker, "abc", false)
67+
assertFunc(floatDictWithoutChecker, float32(-1.5), true)
68+
floatDictWithChecker := Dictionary{}.Float(name, float32(0), "", func(v float32) error {
69+
if v <= float32(-1.0) {
70+
return fmt.Errorf("attribute %s must larger than -1.0", name)
71+
}
72+
return nil
73+
})
74+
assertFunc(floatDictWithChecker, "abc", false)
75+
assertFunc(floatDictWithChecker, float32(-2.0), false)
76+
assertFunc(floatDictWithChecker, float32(7.5), true)
77+
78+
stringDictWithoutChecker := Dictionary{}.String(name, "", "", nil)
79+
assertFunc(stringDictWithoutChecker, 1, false)
80+
assertFunc(stringDictWithoutChecker, "abc", true)
81+
stringDictWithChecker := Dictionary{}.String(name, nil, "", func(v string) error {
82+
if !strings.HasPrefix(v, "valid") {
83+
return fmt.Errorf("attribute %s must have prefix valid", name)
84+
}
85+
return nil
86+
})
87+
assertFunc(stringDictWithChecker, 1, false)
88+
assertFunc(stringDictWithChecker, "invalidString", false)
89+
assertFunc(stringDictWithChecker, "validString", true)
90+
91+
intListDictWithoutChecker := Dictionary{}.IntList(name, []int{}, "", nil)
92+
assertFunc(intListDictWithoutChecker, "abc", false)
93+
assertFunc(intListDictWithoutChecker, []int{1}, true)
94+
intListDictWithChecker := Dictionary{}.IntList(name, []int{}, "", func(v []int) error {
95+
if len(v) > 2 {
96+
return fmt.Errorf("length of attribute %s must be less than or equal to 2", name)
97+
}
98+
return nil
99+
})
100+
assertFunc(intListDictWithChecker, "abc", false)
101+
assertFunc(intListDictWithChecker, []int{1, 2, 3}, false)
102+
assertFunc(intListDictWithChecker, []int{1, 2}, true)
103+
104+
unknownTypeDictWithoutChecker := Dictionary{}.Unknown(name, nil, "", nil)
105+
assertFunc(unknownTypeDictWithoutChecker, 1, true)
106+
assertFunc(unknownTypeDictWithoutChecker, float32(-0.5), true)
107+
assertFunc(unknownTypeDictWithoutChecker, "abc", true)
108+
109+
unknownTypeDictWithChecker := Dictionary{}.Unknown(name, 1, "", func(v interface{}) error {
110+
if _, ok := v.(int); ok {
111+
return nil
112+
}
113+
if _, ok := v.(string); ok {
114+
return nil
115+
}
116+
return fmt.Errorf("attribute %s must be of type int or string", name)
117+
})
118+
assertFunc(unknownTypeDictWithChecker, 1, true)
119+
assertFunc(unknownTypeDictWithChecker, "abc", true)
120+
assertFunc(unknownTypeDictWithChecker, float32(1.5), false)
121+
}
122+
24123
func TestDictionaryValidate(t *testing.T) {
25124
a := assert.New(t)
26125

0 commit comments

Comments
 (0)