Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/go_modules/golang.org/x/text-0.18.0
Browse files Browse the repository at this point in the history
  • Loading branch information
nao1215 authored Sep 5, 2024
2 parents eb949a2 + 8be9264 commit fd1dc49
Show file tree
Hide file tree
Showing 11 changed files with 368 additions and 96 deletions.
36 changes: 25 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,40 +111,54 @@ a,Yulia,25
}

// Output:
// line:2 column age: ターゲットがしきい値より大きくありません: threshold=24, value=23
// line:3 column id: ターゲットが数字ではありません: value=a
// line:4 column name: ターゲットがアルファベット文字ではありません: value=Den1s
// line:2 column age: 値がしきい値より大きくありません: threshold=24, value=23
// line:3 column id: 値が数字ではありません: value=a
// line:4 column name: 値がアルファベット文字ではありません: value=Den1s
}
```

### Struct tags

You set the validation rules following the "validate:" tag according to the rules in the table below. If you need to set multiple rules, please enumerate them separated by commas.

#### Validation rule without arguments
#### Strings

| Tag Name | Description |
|-------------------|---------------------------------------------------|
| boolean | Check whether value is boolean or not. |
| alpha | Check whether value is alphabetic or not |
| numeric | Check whether value is numeric or not |
| alphanumeric | Check whether value is alphanumeric or not |
| required | Check whether value is empty or not |
| ascii | Check whether value is ASCII or not |
| boolean | Check whether value is boolean or not. |
| lowercase | Check whether value is lowercase or not |
| numeric | Check whether value is numeric or not |
| uppercase | Check whether value is uppercase or not |

#### Validation rule with numeric argument
#### Format

| Tag Name | Description |
|-------------------|---------------------------------------------------|
| email | Check whether value is an email address or not |

#### Comparisons

| Tag Name | Description |
|-------------------|---------------------------------------------------|
| eq | Check whether value is equal to the specified value.<br> e.g. `validate:"eq=1"` |
| ne | Check whether value is not equal to the specified value <br> e.g. `validate:"ne=1"` |
| gt | Check whether value is greater than the specified value <br> e.g. `validate:"gt=1"` |
| gte | Check whether value is greater than or equal to the specified value <br> e.g. `validate:"gte=1"` |
| lt | Check whether value is less than the specified value <br> e.g. `validate:"lt=1"` |
| lte | Check whether value is less than or equal to the specified value <br> e.g. `validate:"lte=1"` |
| min | Check whether value is greater than or equal to the specified value <br> e.g. `validate:"min=1"` |
| max | Check whether value is less than or equal to the specified value <br> e.g. `validate:"max=100"` |
| ne | Check whether value is not equal to the specified value <br> e.g. `validate:"ne=1"` |

#### Other

| Tag Name | Description |
|-------------------|---------------------------------------------------|
| len | Check whether the length of the value is equal to the specified value <br> e.g. `validate:"len=10"` |
| max | Check whether value is less than or equal to the specified value <br> e.g. `validate:"max=100"` |
| min | Check whether value is greater than or equal to the specified value <br> e.g. `validate:"min=1"` |
| oneof | Check whether value is included in the specified values <br> e.g. `validate:"oneof=male female prefer_not_to"` |
| required | Check whether value is empty or not |

## License
[MIT License](./LICENSE)
Expand Down
145 changes: 145 additions & 0 deletions csv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package csv

import (
"bytes"
"fmt"
"os"
"path/filepath"
"testing"
Expand Down Expand Up @@ -420,4 +421,148 @@ func Test_ErrCheck(t *testing.T) {
}
}
})

t.Run("validate lowercase", func(t *testing.T) {
t.Parallel()

input := `name
Abc
abc
ABC
あいう
`

c, err := NewCSV(bytes.NewBufferString(input))
if err != nil {
t.Fatal(err)
}

type person struct {
Name string `validate:"lowercase"`
}

persons := make([]person, 0)
errs := c.Decode(&persons)
for i, err := range errs {
switch i {
case 0:
if err.Error() != "line:2 column name: target is not a lowercase character: value=Abc" {
t.Errorf("CSV.Decode() got errors: %v", err)
}
case 1:
if err.Error() != "line:4 column name: target is not a lowercase character: value=ABC" {
t.Errorf("CSV.Decode() got errors: %v", err)
}
case 2:
if err.Error() != "line:5 column name: target is not a lowercase character: value=あいう" {
t.Errorf("CSV.Decode() got errors: %v", err)
}
}
}
})

t.Run("validate uppercase", func(t *testing.T) {
t.Parallel()

input := `name
Abc
abc
ABC
あいう
`

c, err := NewCSV(bytes.NewBufferString(input))
if err != nil {
t.Fatal(err)
}

type person struct {
Name string `validate:"uppercase"`
}

persons := make([]person, 0)
errs := c.Decode(&persons)
for i, err := range errs {
switch i {
case 0:
if err.Error() != "line:2 column name: target is not an uppercase character: value=Abc" {
t.Errorf("CSV.Decode() got errors: %v", err)
}
case 1:
if err.Error() != "line:3 column name: target is not an uppercase character: value=abc" {
t.Errorf("CSV.Decode() got errors: %v", err)
}
case 2:
if err.Error() != "line:5 column name: target is not an uppercase character: value=あいう" {
t.Errorf("CSV.Decode() got errors: %v", err)
}
}
}
})

t.Run("validate ascii", func(t *testing.T) {
t.Parallel()

input := fmt.Sprintf(
"name\n%s\n%s\n",
"\"!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\`]^_abcdefghijklmnopqrstuvwxyz{|}~\"",
"あいう",
)

c, err := NewCSV(bytes.NewBufferString(input))
if err != nil {
t.Fatal(err)
}
type ascii struct {
Name string `validate:"ascii"`
}

asciis := make([]ascii, 0)
errs := c.Decode(&asciis)
for i, err := range errs {
switch i {
case 0:
if err.Error() != "line:3 column name: target is not an ASCII character: value=あいう" {
t.Errorf("CSV.Decode() got errors: %v", err)
}
}
}
})

t.Run("validate email", func(t *testing.T) {
t.Parallel()

input := `email
[email protected]
[email protected]
[email protected]
[email protected]
admin@mailserver1
badあ@example.com
`

c, err := NewCSV(bytes.NewBufferString(input))
if err != nil {
t.Fatal(err)
}

type email struct {
Email string `validate:"email"`
}

emails := make([]email, 0)
errs := c.Decode(&emails)
for i, err := range errs {
switch i {
case 0:
if err.Error() != "line:6 column email: target is not a valid email address: value=admin@mailserver1" {
t.Errorf("CSV.Decode() got errors: %v", err)
}
case 1:
if err.Error() != "line:7 column email: target is not a valid email address: value=badあ@example.com" {
t.Errorf("CSV.Decode() got errors: %v", err)
}
}
}
})
}
50 changes: 8 additions & 42 deletions errors.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package csv

import (
"errors"
"fmt"

"github.com/nicksnyder/go-i18n/v2/i18n"
Expand Down Expand Up @@ -91,45 +90,12 @@ var (
ErrInvalidStructID = "ErrInvalidStruct"
// ErrUnsupportedTypeID is the error ID used when the target is an unsupported type.
ErrUnsupportedTypeID = "ErrUnsupportedType"
)

var (
// ErrStructSlicePointer is returned when the value is not a pointer to a struct slice.
ErrStructSlicePointer = errors.New("value is not a pointer to a struct slice")
// ErrInvalidOneOfFormat is returned when the target is not one of the values.
ErrInvalidOneOfFormat = errors.New("target is not one of the values")
// ErrInvalidThresholdFormat is returned when the threshold value is not an integer.
ErrInvalidThresholdFormat = errors.New("threshold format is invalid")
// ErrInvalidBoolean is returned when the target is not a boolean.
ErrInvalidBoolean = errors.New("target is not a boolean")
// ErrInvalidAlphabet is returned when the target is not an alphabetic character.
ErrInvalidAlphabet = errors.New("target is not an alphabetic character")
// ErrInvalidNumeric is returned when the target is not a numeric character.
ErrInvalidNumeric = errors.New("target is not a numeric character")
// ErrInvalidAlphanumeric is returned when the target is not an alphanumeric character.
ErrInvalidAlphanumeric = errors.New("target is not an alphanumeric character")
// ErrRequired is returned when the target is required but is empty.
ErrRequired = errors.New("target is required but is empty")
// ErrEqual is returned when the target is not equal to the value.
ErrEqual = errors.New("target is not equal to the threshold value")
// ErrInvalidThreshold is returned when the target is not greater than the value.
ErrInvalidThreshold = errors.New("threshold value is invalid")
// ErrNotEqual is returned when the target is equal to the value.
ErrNotEqual = errors.New("target is equal to threshold the value")
// ErrGreaterThan is returned when the target is not greater than the value.
ErrGreaterThan = errors.New("target is not greater than the threshold value")
// ErrGreaterThanEqual is returned when the target is not greater than or equal to the value.
ErrGreaterThanEqual = errors.New("target is not greater than or equal to the threshold value")
// ErrLessThan is returned when the target is not less than the value.
ErrLessThan = errors.New("target is not less than the threshold value")
// ErrLessThanEqual is returned when the target is not less than or equal to the value.
ErrLessThanEqual = errors.New("target is not less than or equal to the threshold value")
// ErrMin is returned when the target is less than the minimum value.
ErrMin = errors.New("target is less than the minimum value")
// ErrMax is returned when the target is greater than the maximum value.
ErrMax = errors.New("target is greater than the maximum value")
// ErrLength is returned when the target length is not equal to the value.
ErrLength = errors.New("target length is not equal to the threshold value")
// ErrOneOf is returned when the target is not one of the values.
ErrOneOf = errors.New("target is not one of the values")
// ErrLowercaseID is the error ID used when the target is not a lowercase character.
ErrLowercaseID = "ErrLowercase"
// ErrUppercaseID is the error ID used when the target is not an uppercase character.
ErrUppercaseID = "ErrUppercase"
// ErrASCIIID is the error ID used when the target is not an ASCII character.
ErrASCIIID = "ErrASCII"
// ErrEmailID is the error ID used when the target is not an email.
ErrEmailID = "ErrEmail"
)
6 changes: 3 additions & 3 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ a,Yulia,25
}

// Output:
// line:2 column age: ターゲットがしきい値より大きくありません: threshold=24, value=23
// line:3 column id: ターゲットが数字ではありません: value=a
// line:4 column name: ターゲットがアルファベット文字ではありません: value=Den1s
// line:2 column age: 値がしきい値より大きくありません: threshold=24, value=23
// line:3 column id: 値が数字ではありません: value=a
// line:4 column name: 値がアルファベット文字ではありません: value=Den1s
}

func ExampleWithRussianLanguage() {
Expand Down
12 changes: 12 additions & 0 deletions i18n/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,15 @@

- id: "ErrLoadMessageFile"
translation: "failed to load message file"

- id: "ErrLowercase"
translation: "target is not a lowercase character"

- id: "ErrUppercase"
translation: "target is not an uppercase character"

- id: "ErrASCII"
translation: "target is not an ASCII character"

- id: "ErrEmail"
translation: "target is not a valid email address"
Loading

0 comments on commit fd1dc49

Please sign in to comment.