diff --git a/README.md b/README.md index 45c6708..9db706e 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ You set the validation rules following the "validate:" tag according to the rule |-------------------|---------------------------------------------------| | alpha | Check whether value is alphabetic or not | | alphanumeric | Check whether value is alphanumeric 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 | diff --git a/csv_test.go b/csv_test.go index 5c4595b..0ce6549 100644 --- a/csv_test.go +++ b/csv_test.go @@ -2,6 +2,7 @@ package csv import ( "bytes" + "fmt" "os" "path/filepath" "testing" @@ -498,4 +499,33 @@ ABC } } }) + + 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) + } + } + } + }) } diff --git a/errors.go b/errors.go index 8b77c7d..47f0dac 100644 --- a/errors.go +++ b/errors.go @@ -94,4 +94,6 @@ var ( 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" ) diff --git a/i18n/en.yaml b/i18n/en.yaml index b32bc9f..c6f6ba6 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -63,3 +63,6 @@ - id: "ErrUppercase" translation: "target is not an uppercase character" + +- id: "ErrASCII" + translation: "target is not an ASCII character" diff --git a/i18n/ja.yaml b/i18n/ja.yaml index 1506ff4..c4024e4 100644 --- a/i18n/ja.yaml +++ b/i18n/ja.yaml @@ -69,3 +69,6 @@ - id: "ErrUppercase" translation: "値が大文字ではありません" + +- id: "ErrASCII" + translation: "値がASCII文字ではありません" diff --git a/i18n/ru.yaml b/i18n/ru.yaml index 15ff4db..201c334 100644 --- a/i18n/ru.yaml +++ b/i18n/ru.yaml @@ -63,3 +63,6 @@ - id: "ErrUppercase" translation: "целевое значение не является заглавным символом" + +- id: "ErrASCII" + translation: "целевое значение не является ASCII символом" diff --git a/parser.go b/parser.go index 2ec273d..789d530 100644 --- a/parser.go +++ b/parser.go @@ -131,6 +131,8 @@ func (c *CSV) parseValidateTag(tags string) (validators, error) { validatorList = append(validatorList, newLowercaseValidator()) case strings.HasPrefix(t, uppercaseTagValue.String()): validatorList = append(validatorList, newUppercaseValidator()) + case strings.HasPrefix(t, asciiTagValue.String()): + validatorList = append(validatorList, newASCIIValidator()) } } return validatorList, nil diff --git a/tag.go b/tag.go index fc7f977..d43fe2e 100644 --- a/tag.go +++ b/tag.go @@ -46,6 +46,8 @@ const ( lowercaseTagValue tagValue = "lowercase" // uppercaseTagValue is the struct tag name for uppercase fields. uppercaseTagValue tagValue = "uppercase" + // asciiTagValue is the struct tag name for ascii fields. + asciiTagValue tagValue = "ascii" ) // String returns the string representation of the tag. diff --git a/validation.go b/validation.go index b7cdfe5..48f2b98 100644 --- a/validation.go +++ b/validation.go @@ -450,3 +450,26 @@ func (u *uppercaseValidator) Do(localizer *i18n.Localizer, target any) error { } return nil } + +// asciiValidator is a struct that contains the validation rules for an ASCII column. +type asciiValidator struct{} + +// newASCIIValidator returns a new asciiValidator. +func newASCIIValidator() *asciiValidator { + return &asciiValidator{} +} + +// Do validates the target is an ASCII string. +func (a *asciiValidator) Do(localizer *i18n.Localizer, target any) error { + v, ok := target.(string) + if !ok { + return NewError(localizer, ErrASCIIID, fmt.Sprintf("value=%v", target)) + } + + for _, r := range v { + if r > 127 { + return NewError(localizer, ErrASCIIID, fmt.Sprintf("value=%v", target)) + } + } + return nil +}