Skip to content

Commit

Permalink
add uniq and more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dedalqq committed Aug 15, 2021
1 parent e929ec5 commit f3d72b6
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 24 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ Here are some the tag examples:
package main

type data struct {
F1 string `json:"f1,notEmpty"` // the field must be not empty
F2 string `json:"f2,notEmpty,required"` // the field is required and must be not empty but may be the null value
F3 string `json:"f3,notEmpty,notNull"` // the field must be not empty and not null but may not exist
F4 []string `json:"f4,notNull,min:3"` // the field must be not null and contains 3 or more items but may not exist
F1 string `json:"f1,notEmpty"` // the field must be not empty but can be "null" or may not exist
F2 string `json:"f2,notEmpty,required"` // the field is required and must be not empty but may be the "null" value
F3 string `json:"f3,notEmpty,notNull"` // the field must be not empty and not "null" but may not exist
F4 []string `json:"f4,notNull,min:3"` // the field must be not "null" and contains 3 or more items but may not exist
}
```

Expand All @@ -62,6 +62,7 @@ type data struct {
| `notEmpty` | any | The field can be not exist but if exist value must be not zero value but can be `null` |
| `notEmpty` | slice | The field must have one element or more but may be `null` or not exist |
| `notNull` | any | The field should not be null, but may not exist |
| `uniq` | []string | The strings slice must contains only unique strings |
| `min:n` | slice | The slice must have `n` items or more |
| `max:n` | slice | The slice must have `n` items or less |
| `min:n` | string | The string must have `n` runes or more |
Expand Down
37 changes: 17 additions & 20 deletions json.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const (
optRequired = "required"
optNotEmpty = "notEmpty"
optNotNull = "notNull"
optUniq = "uniq"
optMin = "min:"
optMax = "max:"
)
Expand All @@ -23,6 +24,7 @@ type fieldOpt struct {
required bool
notEmpty bool
notNull bool
uniq bool
min *int
max *int
}
Expand Down Expand Up @@ -57,6 +59,8 @@ func parseTag(data string) (string, fieldOpt) {
opt.notEmpty = true
case optNotNull:
opt.notNull = true
case optUniq:
opt.uniq = true
}

if strings.HasPrefix(o, optMin) {
Expand Down Expand Up @@ -146,22 +150,8 @@ func getInt(v reflect.Value) (int64, bool) {
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int(), true
default:
return 0, false
}
}
}

func getUint(v reflect.Value) (uint64, bool) {
for {
if v.Kind() == reflect.Ptr {
v = v.Elem()
continue
}

switch v.Kind() {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return v.Uint(), true
return (int64)(v.Uint()), true
default:
return 0, false
}
Expand Down Expand Up @@ -253,6 +243,18 @@ func parseJsonSlice(r io.Reader, prefix string, opt fieldOpt, val reflect.Value)
val = val.Elem()
}

if opt.uniq {
if val.Type().Elem().Kind() == reflect.String {
for i, v := range data {
for j := i + 1; j < len(data); j++ {
if bytes.Compare(v, data[j]) == 0 {
return newError("contains repeated values", prefix)
}
}
}
}
}

for i, d := range data {
newVal := reflect.New(val.Type().Elem())
val.Set(reflect.Append(val, newVal.Elem()))
Expand Down Expand Up @@ -284,11 +286,6 @@ func parseJsonValue(r io.Reader, prefix string, opt fieldOpt, val reflect.Value)
if err != nil {
return err
}
} else if ui, ok := getUint(tempVal); ok {
err := validateMinMax(opt, prefix, (int)(ui), "value")
if err != nil {
return err
}
}

val.Elem().Set(tempVal.Elem())
Expand Down
83 changes: 83 additions & 0 deletions json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ func TestParseJson(t *testing.T) {
nil,
`{"a":"b"}`,
},
testData{
`{"A": "b"}`,
&struct {
A string
}{},
nil,
`{"A":"b"}`,
},
testData{
`{"a": "b"}`,
&struct {
Expand Down Expand Up @@ -294,6 +302,14 @@ func TestParsPointer(t *testing.T) {
nil,
`{"a":123}`,
},
testData{
`{"a": 123}`,
&struct {
A *uint `json:"a"`
}{},
nil,
`{"a":123}`,
},
)
}

Expand Down Expand Up @@ -502,3 +518,70 @@ func TestIncorrectType(t *testing.T) {
},
)
}

func TestUniq(t *testing.T) {
test(
t,
testData{
`{"a": []}`,
&struct {
A []string `json:"a,uniq"`
}{},
nil,
`{"a":null}`,
},
testData{
`{"a": ["b"]}`,
&struct {
A []string `json:"a,uniq"`
}{},
nil,
`{"a":["b"]}`,
},
testData{
`{"a": ["b", "c"]}`,
&struct {
A []string `json:"a,uniq"`
}{},
nil,
`{"a":["b","c"]}`,
},
testData{
`{"a": ["b", "c", "d"]}`,
&struct {
A []string `json:"a,uniq"`
}{},
nil,
`{"a":["b","c","d"]}`,
},
testData{
`{"a": ["a", "a"]}`,
&struct {
A []string `json:"a,uniq"`
}{},
fmt.Errorf("value [a.] contains repeated values"),
`{"a":null}`,
},
testData{
`{"a": ["b", "c", "d", "c"]}`,
&struct {
A []string `json:"a,uniq"`
}{},
fmt.Errorf("value [a.] contains repeated values"),
`{"a":null}`,
},
)
}

func TestUnmarshal(t *testing.T) {
data := []byte(`{"a": "b"}`)

st := struct {
A string `json:"a"`
}{}

err := Unmarshal(data, &st)
if err != nil {
t.Fail()
}
}

0 comments on commit f3d72b6

Please sign in to comment.