修改包路径
This commit is contained in:
parent
142c4d88bc
commit
2e2ba084bf
|
@ -0,0 +1,62 @@
|
||||||
|
package b
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gin-valid/gin/binding"
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ValidError struct {
|
||||||
|
ErrString string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ValidError) Error() string {
|
||||||
|
return e.ErrString
|
||||||
|
}
|
||||||
|
|
||||||
|
func ShouldBind(req *http.Request, obj interface{}) error {
|
||||||
|
content, err := contentType(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b := binding.Default(req.Method, content)
|
||||||
|
err = ShouldBindWith(req, obj, b)
|
||||||
|
errs, ok := err.(validator.ValidationErrors)
|
||||||
|
if !ok {
|
||||||
|
// 非validator.ValidationErrors类型错误直接返回
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return errs.Translate(binding.ValidTrans)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ShouldBindWith(req *http.Request, obj interface{}, b binding.Binding) error {
|
||||||
|
return b.Bind(req, obj)
|
||||||
|
}
|
||||||
|
func ShouldBindJSON(req *http.Request, obj interface{}) error {
|
||||||
|
return ShouldBindWith(req, obj, binding.JSON)
|
||||||
|
}
|
||||||
|
func ShouldBindHeader(req *http.Request, obj interface{}) error {
|
||||||
|
return ShouldBindWith(req, obj, binding.Header)
|
||||||
|
}
|
||||||
|
func ShouldBindQuery(req *http.Request, obj interface{}) error {
|
||||||
|
return ShouldBindWith(req, obj, binding.Query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func contentType(r *http.Request) (string, error) {
|
||||||
|
ct := r.Header.Get("Content-Type")
|
||||||
|
if ct == "" {
|
||||||
|
ct = "application/octet-stream"
|
||||||
|
}
|
||||||
|
ct, _, err := mime.ParseMediaType(ct)
|
||||||
|
return ct, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterFlags(content string) string {
|
||||||
|
for i, char := range content {
|
||||||
|
if char == ' ' || char == ';' {
|
||||||
|
return content[:i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !nomsgpack
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// Content-Type MIME of the most common data formats.
|
||||||
|
const (
|
||||||
|
MIMEJSON = "application/json"
|
||||||
|
MIMEHTML = "text/html"
|
||||||
|
MIMEXML = "application/xml"
|
||||||
|
MIMEXML2 = "text/xml"
|
||||||
|
MIMEPlain = "text/plain"
|
||||||
|
MIMEPOSTForm = "application/x-www-form-urlencoded"
|
||||||
|
MIMEMultipartPOSTForm = "multipart/form-data"
|
||||||
|
MIMEPROTOBUF = "application/x-protobuf"
|
||||||
|
MIMEMSGPACK = "application/x-msgpack"
|
||||||
|
MIMEMSGPACK2 = "application/msgpack"
|
||||||
|
MIMEYAML = "application/x-yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Binding describes the interface which needs to be implemented for binding the
|
||||||
|
// data present in the request such as JSON request body, query parameters or
|
||||||
|
// the form POST.
|
||||||
|
type Binding interface {
|
||||||
|
Name() string
|
||||||
|
Bind(*http.Request, interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
|
||||||
|
// but it reads the body from supplied bytes instead of req.Body.
|
||||||
|
type BindingBody interface {
|
||||||
|
Binding
|
||||||
|
BindBody([]byte, interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
|
||||||
|
// but it read the Params.
|
||||||
|
type BindingUri interface {
|
||||||
|
Name() string
|
||||||
|
BindUri(map[string][]string, interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// StructValidator is the minimal interface which needs to be implemented in
|
||||||
|
// order for it to be used as the validator engine for ensuring the correctness
|
||||||
|
// of the request. Gin provides a default implementation for this using
|
||||||
|
// https://github.com/go-playground/validator/tree/v8.18.2.
|
||||||
|
type StructValidator interface {
|
||||||
|
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
|
||||||
|
// If the received type is not a struct, any validation should be skipped and nil must be returned.
|
||||||
|
// If the received type is a struct or pointer to a struct, the validation should be performed.
|
||||||
|
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
|
||||||
|
// Otherwise nil must be returned.
|
||||||
|
ValidateStruct(interface{}) error
|
||||||
|
|
||||||
|
// Engine returns the underlying validator engine which powers the
|
||||||
|
// StructValidator implementation.
|
||||||
|
Engine() interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validator is the default validator which implements the StructValidator
|
||||||
|
// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
|
||||||
|
// under the hood.
|
||||||
|
var Validator StructValidator = &defaultValidator{}
|
||||||
|
|
||||||
|
// These implement the Binding interface and can be used to bind the data
|
||||||
|
// present in the request to struct instances.
|
||||||
|
var (
|
||||||
|
JSON = jsonBinding{}
|
||||||
|
XML = xmlBinding{}
|
||||||
|
Form = formBinding{}
|
||||||
|
Query = queryBinding{}
|
||||||
|
FormPost = formPostBinding{}
|
||||||
|
FormMultipart = formMultipartBinding{}
|
||||||
|
ProtoBuf = protobufBinding{}
|
||||||
|
MsgPack = msgpackBinding{}
|
||||||
|
YAML = yamlBinding{}
|
||||||
|
Uri = uriBinding{}
|
||||||
|
Header = headerBinding{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Default returns the appropriate Binding instance based on the HTTP method
|
||||||
|
// and the content type.
|
||||||
|
func Default(method, contentType string) Binding {
|
||||||
|
if method == http.MethodGet {
|
||||||
|
return Form
|
||||||
|
}
|
||||||
|
|
||||||
|
switch contentType {
|
||||||
|
case MIMEJSON:
|
||||||
|
return JSON
|
||||||
|
case MIMEXML, MIMEXML2:
|
||||||
|
return XML
|
||||||
|
case MIMEPROTOBUF:
|
||||||
|
return ProtoBuf
|
||||||
|
case MIMEMSGPACK, MIMEMSGPACK2:
|
||||||
|
return MsgPack
|
||||||
|
case MIMEYAML:
|
||||||
|
return YAML
|
||||||
|
case MIMEMultipartPOSTForm:
|
||||||
|
return FormMultipart
|
||||||
|
default: // case MIMEPOSTForm:
|
||||||
|
return Form
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validate(obj interface{}) error {
|
||||||
|
if Validator == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return Validator.ValidateStruct(obj)
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
// Copyright 2020 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build nomsgpack
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// Content-Type MIME of the most common data formats.
|
||||||
|
const (
|
||||||
|
MIMEJSON = "application/json"
|
||||||
|
MIMEHTML = "text/html"
|
||||||
|
MIMEXML = "application/xml"
|
||||||
|
MIMEXML2 = "text/xml"
|
||||||
|
MIMEPlain = "text/plain"
|
||||||
|
MIMEPOSTForm = "application/x-www-form-urlencoded"
|
||||||
|
MIMEMultipartPOSTForm = "multipart/form-data"
|
||||||
|
MIMEPROTOBUF = "application/x-protobuf"
|
||||||
|
MIMEYAML = "application/x-yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Binding describes the interface which needs to be implemented for binding the
|
||||||
|
// data present in the request such as JSON request body, query parameters or
|
||||||
|
// the form POST.
|
||||||
|
type Binding interface {
|
||||||
|
Name() string
|
||||||
|
Bind(*http.Request, interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
|
||||||
|
// but it reads the body from supplied bytes instead of req.Body.
|
||||||
|
type BindingBody interface {
|
||||||
|
Binding
|
||||||
|
BindBody([]byte, interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
|
||||||
|
// but it read the Params.
|
||||||
|
type BindingUri interface {
|
||||||
|
Name() string
|
||||||
|
BindUri(map[string][]string, interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// StructValidator is the minimal interface which needs to be implemented in
|
||||||
|
// order for it to be used as the validator engine for ensuring the correctness
|
||||||
|
// of the request. Gin provides a default implementation for this using
|
||||||
|
// https://github.com/go-playground/validator/tree/v8.18.2.
|
||||||
|
type StructValidator interface {
|
||||||
|
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
|
||||||
|
// If the received type is not a struct, any validation should be skipped and nil must be returned.
|
||||||
|
// If the received type is a struct or pointer to a struct, the validation should be performed.
|
||||||
|
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
|
||||||
|
// Otherwise nil must be returned.
|
||||||
|
ValidateStruct(interface{}) error
|
||||||
|
|
||||||
|
// Engine returns the underlying validator engine which powers the
|
||||||
|
// StructValidator implementation.
|
||||||
|
Engine() interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validator is the default validator which implements the StructValidator
|
||||||
|
// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
|
||||||
|
// under the hood.
|
||||||
|
var Validator StructValidator = &defaultValidator{}
|
||||||
|
|
||||||
|
// These implement the Binding interface and can be used to bind the data
|
||||||
|
// present in the request to struct instances.
|
||||||
|
var (
|
||||||
|
JSON = jsonBinding{}
|
||||||
|
XML = xmlBinding{}
|
||||||
|
Form = formBinding{}
|
||||||
|
Query = queryBinding{}
|
||||||
|
FormPost = formPostBinding{}
|
||||||
|
FormMultipart = formMultipartBinding{}
|
||||||
|
ProtoBuf = protobufBinding{}
|
||||||
|
YAML = yamlBinding{}
|
||||||
|
Uri = uriBinding{}
|
||||||
|
Header = headerBinding{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Default returns the appropriate Binding instance based on the HTTP method
|
||||||
|
// and the content type.
|
||||||
|
func Default(method, contentType string) Binding {
|
||||||
|
if method == "GET" {
|
||||||
|
return Form
|
||||||
|
}
|
||||||
|
|
||||||
|
switch contentType {
|
||||||
|
case MIMEJSON:
|
||||||
|
return JSON
|
||||||
|
case MIMEXML, MIMEXML2:
|
||||||
|
return XML
|
||||||
|
case MIMEPROTOBUF:
|
||||||
|
return ProtoBuf
|
||||||
|
case MIMEYAML:
|
||||||
|
return YAML
|
||||||
|
case MIMEMultipartPOSTForm:
|
||||||
|
return FormMultipart
|
||||||
|
default: // case MIMEPOSTForm:
|
||||||
|
return Form
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validate(obj interface{}) error {
|
||||||
|
if Validator == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return Validator.ValidateStruct(obj)
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
ut "github.com/go-playground/universal-translator"
|
||||||
|
zhTrans "go-playground/validator/v10/translations/zh"
|
||||||
|
"go-playground/locales/zh"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
type defaultValidator struct {
|
||||||
|
once sync.Once
|
||||||
|
validate *validator.Validate
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ StructValidator = &defaultValidator{} //这是啥情况?yang
|
||||||
|
|
||||||
|
// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
|
||||||
|
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
||||||
|
value := reflect.ValueOf(obj)
|
||||||
|
valueType := value.Kind()
|
||||||
|
if valueType == reflect.Ptr {
|
||||||
|
valueType = value.Elem().Kind()
|
||||||
|
}
|
||||||
|
if valueType == reflect.Struct {
|
||||||
|
v.lazyinit()
|
||||||
|
if err := v.validate.Struct(obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Engine returns the underlying validator engine which powers the default
|
||||||
|
// Validator instance. This is useful if you want to register custom validations
|
||||||
|
// or struct level validations. See validator GoDoc for more info -
|
||||||
|
// https://godoc.org/gopkg.in/go-playground/validator.v8
|
||||||
|
func (v *defaultValidator) Engine() interface{} {
|
||||||
|
v.lazyinit()
|
||||||
|
return v.validate
|
||||||
|
}
|
||||||
|
|
||||||
|
var ValidTrans ut.Translator
|
||||||
|
|
||||||
|
func (v *defaultValidator) lazyinit() {
|
||||||
|
v.once.Do(func() {
|
||||||
|
v.validate = validator.New()
|
||||||
|
zh := zh.New()
|
||||||
|
uni := ut.New(zh, zh)
|
||||||
|
|
||||||
|
// this is usually know or extracted from http 'Accept-Language' header
|
||||||
|
// also see uni.FindTranslator(...)
|
||||||
|
ValidTrans, _ = uni.GetTranslator("zh")
|
||||||
|
|
||||||
|
zhTrans.RegisterDefaultTranslations(v.validate, ValidTrans) // 为gin的校验 注册翻译
|
||||||
|
v.validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
|
||||||
|
name := strings.SplitN(fld.Tag.Get("description"), ",", 2)[0]
|
||||||
|
//if name == "-" {
|
||||||
|
// return ""
|
||||||
|
//}
|
||||||
|
return name
|
||||||
|
})
|
||||||
|
// 设置 tag 的名字
|
||||||
|
v.validate.SetTagName("binding")
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultMemory = 32 << 20
|
||||||
|
|
||||||
|
type formBinding struct{}
|
||||||
|
type formPostBinding struct{}
|
||||||
|
type formMultipartBinding struct{}
|
||||||
|
|
||||||
|
func (formBinding) Name() string {
|
||||||
|
return "form"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (formBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
if err := req.ParseForm(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := req.ParseMultipartForm(defaultMemory); err != nil {
|
||||||
|
if err != http.ErrNotMultipart {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := mapForm(obj, req.Form); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (formPostBinding) Name() string {
|
||||||
|
return "form-urlencoded"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (formPostBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
if err := req.ParseForm(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := mapForm(obj, req.PostForm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (formMultipartBinding) Name() string {
|
||||||
|
return "multipart/form-data"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
if err := req.ParseMultipartForm(defaultMemory); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := mappingByPtr(obj, (*multipartRequest)(req), "form"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return validate(obj)
|
||||||
|
}
|
|
@ -0,0 +1,392 @@
|
||||||
|
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gin-valid/gin/internal/bytesconv"
|
||||||
|
"gin-valid/gin/internal/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errUnknownType = errors.New("unknown type")
|
||||||
|
|
||||||
|
func mapUri(ptr interface{}, m map[string][]string) error {
|
||||||
|
return mapFormByTag(ptr, m, "uri")
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapForm(ptr interface{}, form map[string][]string) error {
|
||||||
|
return mapFormByTag(ptr, form, "form")
|
||||||
|
}
|
||||||
|
|
||||||
|
var emptyField = reflect.StructField{}
|
||||||
|
|
||||||
|
func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
|
||||||
|
// Check if ptr is a map
|
||||||
|
ptrVal := reflect.ValueOf(ptr)
|
||||||
|
var pointed interface{}
|
||||||
|
if ptrVal.Kind() == reflect.Ptr {
|
||||||
|
ptrVal = ptrVal.Elem()
|
||||||
|
pointed = ptrVal.Interface()
|
||||||
|
}
|
||||||
|
if ptrVal.Kind() == reflect.Map &&
|
||||||
|
ptrVal.Type().Key().Kind() == reflect.String {
|
||||||
|
if pointed != nil {
|
||||||
|
ptr = pointed
|
||||||
|
}
|
||||||
|
return setFormMap(ptr, form)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappingByPtr(ptr, formSource(form), tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setter tries to set value on a walking by fields of a struct
|
||||||
|
type setter interface {
|
||||||
|
TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type formSource map[string][]string
|
||||||
|
|
||||||
|
var _ setter = formSource(nil)
|
||||||
|
|
||||||
|
// TrySet tries to set a value by request's form source (like map[string][]string)
|
||||||
|
func (form formSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
|
||||||
|
return setByForm(value, field, form, tagValue, opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mappingByPtr(ptr interface{}, setter setter, tag string) error {
|
||||||
|
_, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
|
||||||
|
if field.Tag.Get(tag) == "-" { // just ignoring this field
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var vKind = value.Kind()
|
||||||
|
|
||||||
|
if vKind == reflect.Ptr {
|
||||||
|
var isNew bool
|
||||||
|
vPtr := value
|
||||||
|
if value.IsNil() {
|
||||||
|
isNew = true
|
||||||
|
vPtr = reflect.New(value.Type().Elem())
|
||||||
|
}
|
||||||
|
isSetted, err := mapping(vPtr.Elem(), field, setter, tag)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if isNew && isSetted {
|
||||||
|
value.Set(vPtr)
|
||||||
|
}
|
||||||
|
return isSetted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if vKind != reflect.Struct || !field.Anonymous {
|
||||||
|
ok, err := tryToSetValue(value, field, setter, tag)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if vKind == reflect.Struct {
|
||||||
|
tValue := value.Type()
|
||||||
|
|
||||||
|
var isSetted bool
|
||||||
|
for i := 0; i < value.NumField(); i++ {
|
||||||
|
sf := tValue.Field(i)
|
||||||
|
if sf.PkgPath != "" && !sf.Anonymous { // unexported
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ok, err := mapping(value.Field(i), tValue.Field(i), setter, tag)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
isSetted = isSetted || ok
|
||||||
|
}
|
||||||
|
return isSetted, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type setOptions struct {
|
||||||
|
isDefaultExists bool
|
||||||
|
defaultValue string
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
|
||||||
|
var tagValue string
|
||||||
|
var setOpt setOptions
|
||||||
|
|
||||||
|
tagValue = field.Tag.Get(tag)
|
||||||
|
tagValue, opts := head(tagValue, ",")
|
||||||
|
|
||||||
|
if tagValue == "" { // default value is FieldName
|
||||||
|
tagValue = field.Name
|
||||||
|
}
|
||||||
|
if tagValue == "" { // when field is "emptyField" variable
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var opt string
|
||||||
|
for len(opts) > 0 {
|
||||||
|
opt, opts = head(opts, ",")
|
||||||
|
|
||||||
|
if k, v := head(opt, "="); k == "default" {
|
||||||
|
setOpt.isDefaultExists = true
|
||||||
|
setOpt.defaultValue = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return setter.TrySet(value, field, tagValue, setOpt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSetted bool, err error) {
|
||||||
|
vs, ok := form[tagValue]
|
||||||
|
if !ok && !opt.isDefaultExists {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch value.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
if !ok {
|
||||||
|
vs = []string{opt.defaultValue}
|
||||||
|
}
|
||||||
|
return true, setSlice(vs, value, field)
|
||||||
|
case reflect.Array:
|
||||||
|
if !ok {
|
||||||
|
vs = []string{opt.defaultValue}
|
||||||
|
}
|
||||||
|
if len(vs) != value.Len() {
|
||||||
|
return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String())
|
||||||
|
}
|
||||||
|
return true, setArray(vs, value, field)
|
||||||
|
default:
|
||||||
|
var val string
|
||||||
|
if !ok {
|
||||||
|
val = opt.defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(vs) > 0 {
|
||||||
|
val = vs[0]
|
||||||
|
}
|
||||||
|
return true, setWithProperType(val, value, field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setWithProperType(val string, value reflect.Value, field reflect.StructField) error {
|
||||||
|
switch value.Kind() {
|
||||||
|
case reflect.Int:
|
||||||
|
return setIntField(val, 0, value)
|
||||||
|
case reflect.Int8:
|
||||||
|
return setIntField(val, 8, value)
|
||||||
|
case reflect.Int16:
|
||||||
|
return setIntField(val, 16, value)
|
||||||
|
case reflect.Int32:
|
||||||
|
return setIntField(val, 32, value)
|
||||||
|
case reflect.Int64:
|
||||||
|
switch value.Interface().(type) {
|
||||||
|
case time.Duration:
|
||||||
|
return setTimeDuration(val, value, field)
|
||||||
|
}
|
||||||
|
return setIntField(val, 64, value)
|
||||||
|
case reflect.Uint:
|
||||||
|
return setUintField(val, 0, value)
|
||||||
|
case reflect.Uint8:
|
||||||
|
return setUintField(val, 8, value)
|
||||||
|
case reflect.Uint16:
|
||||||
|
return setUintField(val, 16, value)
|
||||||
|
case reflect.Uint32:
|
||||||
|
return setUintField(val, 32, value)
|
||||||
|
case reflect.Uint64:
|
||||||
|
return setUintField(val, 64, value)
|
||||||
|
case reflect.Bool:
|
||||||
|
return setBoolField(val, value)
|
||||||
|
case reflect.Float32:
|
||||||
|
return setFloatField(val, 32, value)
|
||||||
|
case reflect.Float64:
|
||||||
|
return setFloatField(val, 64, value)
|
||||||
|
case reflect.String:
|
||||||
|
value.SetString(val)
|
||||||
|
case reflect.Struct:
|
||||||
|
switch value.Interface().(type) {
|
||||||
|
case time.Time:
|
||||||
|
return setTimeField(val, field, value)
|
||||||
|
}
|
||||||
|
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
||||||
|
case reflect.Map:
|
||||||
|
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
||||||
|
default:
|
||||||
|
return errUnknownType
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setIntField(val string, bitSize int, field reflect.Value) error {
|
||||||
|
if val == "" {
|
||||||
|
val = "0"
|
||||||
|
}
|
||||||
|
intVal, err := strconv.ParseInt(val, 10, bitSize)
|
||||||
|
if err == nil {
|
||||||
|
field.SetInt(intVal)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUintField(val string, bitSize int, field reflect.Value) error {
|
||||||
|
if val == "" {
|
||||||
|
val = "0"
|
||||||
|
}
|
||||||
|
uintVal, err := strconv.ParseUint(val, 10, bitSize)
|
||||||
|
if err == nil {
|
||||||
|
field.SetUint(uintVal)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func setBoolField(val string, field reflect.Value) error {
|
||||||
|
if val == "" {
|
||||||
|
val = "false"
|
||||||
|
}
|
||||||
|
boolVal, err := strconv.ParseBool(val)
|
||||||
|
if err == nil {
|
||||||
|
field.SetBool(boolVal)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func setFloatField(val string, bitSize int, field reflect.Value) error {
|
||||||
|
if val == "" {
|
||||||
|
val = "0.0"
|
||||||
|
}
|
||||||
|
floatVal, err := strconv.ParseFloat(val, bitSize)
|
||||||
|
if err == nil {
|
||||||
|
field.SetFloat(floatVal)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTimeField(val string, structField reflect.StructField, value reflect.Value) error {
|
||||||
|
timeFormat := structField.Tag.Get("time_format")
|
||||||
|
if timeFormat == "" {
|
||||||
|
timeFormat = time.RFC3339
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tf := strings.ToLower(timeFormat); tf {
|
||||||
|
case "unix", "unixnano":
|
||||||
|
tv, err := strconv.ParseInt(val, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d := time.Duration(1)
|
||||||
|
if tf == "unixnano" {
|
||||||
|
d = time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
t := time.Unix(tv/int64(d), tv%int64(d))
|
||||||
|
value.Set(reflect.ValueOf(t))
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if val == "" {
|
||||||
|
value.Set(reflect.ValueOf(time.Time{}))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
l := time.Local
|
||||||
|
if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC {
|
||||||
|
l = time.UTC
|
||||||
|
}
|
||||||
|
|
||||||
|
if locTag := structField.Tag.Get("time_location"); locTag != "" {
|
||||||
|
loc, err := time.LoadLocation(locTag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
l = loc
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := time.ParseInLocation(timeFormat, val, l)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
value.Set(reflect.ValueOf(t))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setArray(vals []string, value reflect.Value, field reflect.StructField) error {
|
||||||
|
for i, s := range vals {
|
||||||
|
err := setWithProperType(s, value.Index(i), field)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSlice(vals []string, value reflect.Value, field reflect.StructField) error {
|
||||||
|
slice := reflect.MakeSlice(value.Type(), len(vals), len(vals))
|
||||||
|
err := setArray(vals, slice, field)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
value.Set(slice)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTimeDuration(val string, value reflect.Value, field reflect.StructField) error {
|
||||||
|
d, err := time.ParseDuration(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
value.Set(reflect.ValueOf(d))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func head(str, sep string) (head string, tail string) {
|
||||||
|
idx := strings.Index(str, sep)
|
||||||
|
if idx < 0 {
|
||||||
|
return str, ""
|
||||||
|
}
|
||||||
|
return str[:idx], str[idx+len(sep):]
|
||||||
|
}
|
||||||
|
|
||||||
|
func setFormMap(ptr interface{}, form map[string][]string) error {
|
||||||
|
el := reflect.TypeOf(ptr).Elem()
|
||||||
|
|
||||||
|
if el.Kind() == reflect.Slice {
|
||||||
|
ptrMap, ok := ptr.(map[string][]string)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("cannot convert to map slices of strings")
|
||||||
|
}
|
||||||
|
for k, v := range form {
|
||||||
|
ptrMap[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ptrMap, ok := ptr.(map[string]string)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("cannot convert to map of strings")
|
||||||
|
}
|
||||||
|
for k, v := range form {
|
||||||
|
ptrMap[k] = v[len(v)-1] // pick last
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
// Copyright 2019 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var form = map[string][]string{
|
||||||
|
"name": {"mike"},
|
||||||
|
"friends": {"anna", "nicole"},
|
||||||
|
"id_number": {"12345678"},
|
||||||
|
"id_date": {"2018-01-20"},
|
||||||
|
}
|
||||||
|
|
||||||
|
type structFull struct {
|
||||||
|
Name string `form:"name"`
|
||||||
|
Age int `form:"age,default=25"`
|
||||||
|
Friends []string `form:"friends"`
|
||||||
|
ID *struct {
|
||||||
|
Number string `form:"id_number"`
|
||||||
|
DateOfIssue time.Time `form:"id_date" time_format:"2006-01-02" time_utc:"true"`
|
||||||
|
}
|
||||||
|
Nationality *string `form:"nationality"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMapFormFull(b *testing.B) {
|
||||||
|
var s structFull
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err := mapForm(&s, form)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("Error on a form mapping")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.StopTimer()
|
||||||
|
|
||||||
|
t := b
|
||||||
|
assert.Equal(t, "mike", s.Name)
|
||||||
|
assert.Equal(t, 25, s.Age)
|
||||||
|
assert.Equal(t, []string{"anna", "nicole"}, s.Friends)
|
||||||
|
assert.Equal(t, "12345678", s.ID.Number)
|
||||||
|
assert.Equal(t, time.Date(2018, 1, 20, 0, 0, 0, 0, time.UTC), s.ID.DateOfIssue)
|
||||||
|
assert.Nil(t, s.Nationality)
|
||||||
|
}
|
||||||
|
|
||||||
|
type structName struct {
|
||||||
|
Name string `form:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMapFormName(b *testing.B) {
|
||||||
|
var s structName
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err := mapForm(&s, form)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("Error on a form mapping")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.StopTimer()
|
||||||
|
|
||||||
|
t := b
|
||||||
|
assert.Equal(t, "mike", s.Name)
|
||||||
|
}
|
|
@ -0,0 +1,281 @@
|
||||||
|
// Copyright 2019 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMappingBaseTypes(t *testing.T) {
|
||||||
|
intPtr := func(i int) *int {
|
||||||
|
return &i
|
||||||
|
}
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
value interface{}
|
||||||
|
form string
|
||||||
|
expect interface{}
|
||||||
|
}{
|
||||||
|
{"base type", struct{ F int }{}, "9", int(9)},
|
||||||
|
{"base type", struct{ F int8 }{}, "9", int8(9)},
|
||||||
|
{"base type", struct{ F int16 }{}, "9", int16(9)},
|
||||||
|
{"base type", struct{ F int32 }{}, "9", int32(9)},
|
||||||
|
{"base type", struct{ F int64 }{}, "9", int64(9)},
|
||||||
|
{"base type", struct{ F uint }{}, "9", uint(9)},
|
||||||
|
{"base type", struct{ F uint8 }{}, "9", uint8(9)},
|
||||||
|
{"base type", struct{ F uint16 }{}, "9", uint16(9)},
|
||||||
|
{"base type", struct{ F uint32 }{}, "9", uint32(9)},
|
||||||
|
{"base type", struct{ F uint64 }{}, "9", uint64(9)},
|
||||||
|
{"base type", struct{ F bool }{}, "True", true},
|
||||||
|
{"base type", struct{ F float32 }{}, "9.1", float32(9.1)},
|
||||||
|
{"base type", struct{ F float64 }{}, "9.1", float64(9.1)},
|
||||||
|
{"base type", struct{ F string }{}, "test", string("test")},
|
||||||
|
{"base type", struct{ F *int }{}, "9", intPtr(9)},
|
||||||
|
|
||||||
|
// zero values
|
||||||
|
{"zero value", struct{ F int }{}, "", int(0)},
|
||||||
|
{"zero value", struct{ F uint }{}, "", uint(0)},
|
||||||
|
{"zero value", struct{ F bool }{}, "", false},
|
||||||
|
{"zero value", struct{ F float32 }{}, "", float32(0)},
|
||||||
|
} {
|
||||||
|
tp := reflect.TypeOf(tt.value)
|
||||||
|
testName := tt.name + ":" + tp.Field(0).Type.String()
|
||||||
|
|
||||||
|
val := reflect.New(reflect.TypeOf(tt.value))
|
||||||
|
val.Elem().Set(reflect.ValueOf(tt.value))
|
||||||
|
|
||||||
|
field := val.Elem().Type().Field(0)
|
||||||
|
|
||||||
|
_, err := mapping(val, emptyField, formSource{field.Name: {tt.form}}, "form")
|
||||||
|
assert.NoError(t, err, testName)
|
||||||
|
|
||||||
|
actual := val.Elem().Field(0).Interface()
|
||||||
|
assert.Equal(t, tt.expect, actual, testName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingDefault(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
Int int `form:",default=9"`
|
||||||
|
Slice []int `form:",default=9"`
|
||||||
|
Array [1]int `form:",default=9"`
|
||||||
|
}
|
||||||
|
err := mappingByPtr(&s, formSource{}, "form")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 9, s.Int)
|
||||||
|
assert.Equal(t, []int{9}, s.Slice)
|
||||||
|
assert.Equal(t, [1]int{9}, s.Array)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingSkipField(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
A int
|
||||||
|
}
|
||||||
|
err := mappingByPtr(&s, formSource{}, "form")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 0, s.A)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingIgnoreField(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
A int `form:"A"`
|
||||||
|
B int `form:"-"`
|
||||||
|
}
|
||||||
|
err := mappingByPtr(&s, formSource{"A": {"9"}, "B": {"9"}}, "form")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 9, s.A)
|
||||||
|
assert.Equal(t, 0, s.B)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingUnexportedField(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
A int `form:"a"`
|
||||||
|
b int `form:"b"`
|
||||||
|
}
|
||||||
|
err := mappingByPtr(&s, formSource{"a": {"9"}, "b": {"9"}}, "form")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 9, s.A)
|
||||||
|
assert.Equal(t, 0, s.b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingPrivateField(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
f int `form:"field"`
|
||||||
|
}
|
||||||
|
err := mappingByPtr(&s, formSource{"field": {"6"}}, "form")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, int(0), s.f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingUnknownFieldType(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
U uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mappingByPtr(&s, formSource{"U": {"unknown"}}, "form")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, errUnknownType, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingURI(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
F int `uri:"field"`
|
||||||
|
}
|
||||||
|
err := mapUri(&s, map[string][]string{"field": {"6"}})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, int(6), s.F)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingForm(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
F int `form:"field"`
|
||||||
|
}
|
||||||
|
err := mapForm(&s, map[string][]string{"field": {"6"}})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, int(6), s.F)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingTime(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
Time time.Time
|
||||||
|
LocalTime time.Time `time_format:"2006-01-02"`
|
||||||
|
ZeroValue time.Time
|
||||||
|
CSTTime time.Time `time_format:"2006-01-02" time_location:"Asia/Shanghai"`
|
||||||
|
UTCTime time.Time `time_format:"2006-01-02" time_utc:"1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
time.Local, err = time.LoadLocation("Europe/Berlin")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = mapForm(&s, map[string][]string{
|
||||||
|
"Time": {"2019-01-20T16:02:58Z"},
|
||||||
|
"LocalTime": {"2019-01-20"},
|
||||||
|
"ZeroValue": {},
|
||||||
|
"CSTTime": {"2019-01-20"},
|
||||||
|
"UTCTime": {"2019-01-20"},
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, "2019-01-20 16:02:58 +0000 UTC", s.Time.String())
|
||||||
|
assert.Equal(t, "2019-01-20 00:00:00 +0100 CET", s.LocalTime.String())
|
||||||
|
assert.Equal(t, "2019-01-19 23:00:00 +0000 UTC", s.LocalTime.UTC().String())
|
||||||
|
assert.Equal(t, "0001-01-01 00:00:00 +0000 UTC", s.ZeroValue.String())
|
||||||
|
assert.Equal(t, "2019-01-20 00:00:00 +0800 CST", s.CSTTime.String())
|
||||||
|
assert.Equal(t, "2019-01-19 16:00:00 +0000 UTC", s.CSTTime.UTC().String())
|
||||||
|
assert.Equal(t, "2019-01-20 00:00:00 +0000 UTC", s.UTCTime.String())
|
||||||
|
|
||||||
|
// wrong location
|
||||||
|
var wrongLoc struct {
|
||||||
|
Time time.Time `time_location:"wrong"`
|
||||||
|
}
|
||||||
|
err = mapForm(&wrongLoc, map[string][]string{"Time": {"2019-01-20T16:02:58Z"}})
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
// wrong time value
|
||||||
|
var wrongTime struct {
|
||||||
|
Time time.Time
|
||||||
|
}
|
||||||
|
err = mapForm(&wrongTime, map[string][]string{"Time": {"wrong"}})
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingTimeDuration(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
D time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// ok
|
||||||
|
err := mappingByPtr(&s, formSource{"D": {"5s"}}, "form")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 5*time.Second, s.D)
|
||||||
|
|
||||||
|
// error
|
||||||
|
err = mappingByPtr(&s, formSource{"D": {"wrong"}}, "form")
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingSlice(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
Slice []int `form:"slice,default=9"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// default value
|
||||||
|
err := mappingByPtr(&s, formSource{}, "form")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []int{9}, s.Slice)
|
||||||
|
|
||||||
|
// ok
|
||||||
|
err = mappingByPtr(&s, formSource{"slice": {"3", "4"}}, "form")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []int{3, 4}, s.Slice)
|
||||||
|
|
||||||
|
// error
|
||||||
|
err = mappingByPtr(&s, formSource{"slice": {"wrong"}}, "form")
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingArray(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
Array [2]int `form:"array,default=9"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrong default
|
||||||
|
err := mappingByPtr(&s, formSource{}, "form")
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
// ok
|
||||||
|
err = mappingByPtr(&s, formSource{"array": {"3", "4"}}, "form")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, [2]int{3, 4}, s.Array)
|
||||||
|
|
||||||
|
// error - not enough vals
|
||||||
|
err = mappingByPtr(&s, formSource{"array": {"3"}}, "form")
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
// error - wrong value
|
||||||
|
err = mappingByPtr(&s, formSource{"array": {"wrong"}}, "form")
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingStructField(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
J struct {
|
||||||
|
I int
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mappingByPtr(&s, formSource{"J": {`{"I": 9}`}}, "form")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 9, s.J.I)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingMapField(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
M map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mappingByPtr(&s, formSource{"M": {`{"one": 1}`}}, "form")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, map[string]int{"one": 1}, s.M)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingIgnoredCircularRef(t *testing.T) {
|
||||||
|
type S struct {
|
||||||
|
S *S `form:"-"`
|
||||||
|
}
|
||||||
|
var s S
|
||||||
|
|
||||||
|
err := mappingByPtr(&s, formSource{}, "form")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/textproto"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type headerBinding struct{}
|
||||||
|
|
||||||
|
func (headerBinding) Name() string {
|
||||||
|
return "header"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (headerBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
|
||||||
|
if err := mapHeader(obj, req.Header); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapHeader(ptr interface{}, h map[string][]string) error {
|
||||||
|
return mappingByPtr(ptr, headerSource(h), "header")
|
||||||
|
}
|
||||||
|
|
||||||
|
type headerSource map[string][]string
|
||||||
|
|
||||||
|
var _ setter = headerSource(nil)
|
||||||
|
|
||||||
|
func (hs headerSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
|
||||||
|
return setByForm(value, field, hs, textproto.CanonicalMIMEHeaderKey(tagValue), opt)
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gin-valid/gin/internal/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EnableDecoderUseNumber is used to call the UseNumber method on the JSON
|
||||||
|
// Decoder instance. UseNumber causes the Decoder to unmarshal a number into an
|
||||||
|
// interface{} as a Number instead of as a float64.
|
||||||
|
var EnableDecoderUseNumber = false
|
||||||
|
|
||||||
|
// EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method
|
||||||
|
// on the JSON Decoder instance. DisallowUnknownFields causes the Decoder to
|
||||||
|
// return an error when the destination is a struct and the input contains object
|
||||||
|
// keys which do not match any non-ignored, exported fields in the destination.
|
||||||
|
var EnableDecoderDisallowUnknownFields = false
|
||||||
|
|
||||||
|
type jsonBinding struct{}
|
||||||
|
|
||||||
|
func (jsonBinding) Name() string {
|
||||||
|
return "json"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
if req == nil || req.Body == nil {
|
||||||
|
return fmt.Errorf("invalid request")
|
||||||
|
}
|
||||||
|
return decodeJSON(req.Body, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jsonBinding) BindBody(body []byte, obj interface{}) error {
|
||||||
|
return decodeJSON(bytes.NewReader(body), obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeJSON(r io.Reader, obj interface{}) error {
|
||||||
|
decoder := json.NewDecoder(r)
|
||||||
|
if EnableDecoderUseNumber {
|
||||||
|
decoder.UseNumber()
|
||||||
|
}
|
||||||
|
if EnableDecoderDisallowUnknownFields {
|
||||||
|
decoder.DisallowUnknownFields()
|
||||||
|
}
|
||||||
|
if err := decoder.Decode(obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/ Copyright 2019 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestJSONBindingBindBody(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
Foo string `json:"foo"`
|
||||||
|
}
|
||||||
|
err := jsonBinding{}.BindBody([]byte(`{"foo": "FOO"}`), &s)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "FOO", s.Foo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONBindingBindBodyMap(t *testing.T) {
|
||||||
|
s := make(map[string]string)
|
||||||
|
err := jsonBinding{}.BindBody([]byte(`{"foo": "FOO","hello":"world"}`), &s)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, s, 2)
|
||||||
|
assert.Equal(t, "FOO", s["foo"])
|
||||||
|
assert.Equal(t, "world", s["hello"])
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !nomsgpack
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type msgpackBinding struct{}
|
||||||
|
|
||||||
|
func (msgpackBinding) Name() string {
|
||||||
|
return "msgpack"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msgpackBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
return decodeMsgPack(req.Body, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msgpackBinding) BindBody(body []byte, obj interface{}) error {
|
||||||
|
return decodeMsgPack(bytes.NewReader(body), obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMsgPack(r io.Reader, obj interface{}) error {
|
||||||
|
cdc := new(codec.MsgpackHandle)
|
||||||
|
if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2019 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !nomsgpack
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMsgpackBindingBindBody(t *testing.T) {
|
||||||
|
type teststruct struct {
|
||||||
|
Foo string `msgpack:"foo"`
|
||||||
|
}
|
||||||
|
var s teststruct
|
||||||
|
err := msgpackBinding{}.BindBody(msgpackBody(t, teststruct{"FOO"}), &s)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "FOO", s.Foo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func msgpackBody(t *testing.T, obj interface{}) []byte {
|
||||||
|
var bs bytes.Buffer
|
||||||
|
h := &codec.MsgpackHandle{}
|
||||||
|
err := codec.NewEncoder(&bs, h).Encode(obj)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return bs.Bytes()
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright 2019 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type multipartRequest http.Request
|
||||||
|
|
||||||
|
var _ setter = (*multipartRequest)(nil)
|
||||||
|
|
||||||
|
// TrySet tries to set a value by the multipart request with the binding a form file
|
||||||
|
func (r *multipartRequest) TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error) {
|
||||||
|
if files := r.MultipartForm.File[key]; len(files) != 0 {
|
||||||
|
return setByMultipartFormFile(value, field, files)
|
||||||
|
}
|
||||||
|
|
||||||
|
return setByForm(value, field, r.MultipartForm.Value, key, opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setByMultipartFormFile(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) {
|
||||||
|
switch value.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
switch value.Interface().(type) {
|
||||||
|
case *multipart.FileHeader:
|
||||||
|
value.Set(reflect.ValueOf(files[0]))
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
switch value.Interface().(type) {
|
||||||
|
case multipart.FileHeader:
|
||||||
|
value.Set(reflect.ValueOf(*files[0]))
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
slice := reflect.MakeSlice(value.Type(), len(files), len(files))
|
||||||
|
isSetted, err = setArrayOfMultipartFormFiles(slice, field, files)
|
||||||
|
if err != nil || !isSetted {
|
||||||
|
return isSetted, err
|
||||||
|
}
|
||||||
|
value.Set(slice)
|
||||||
|
return true, nil
|
||||||
|
case reflect.Array:
|
||||||
|
return setArrayOfMultipartFormFiles(value, field, files)
|
||||||
|
}
|
||||||
|
return false, errors.New("unsupported field type for multipart.FileHeader")
|
||||||
|
}
|
||||||
|
|
||||||
|
func setArrayOfMultipartFormFiles(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) {
|
||||||
|
if value.Len() != len(files) {
|
||||||
|
return false, errors.New("unsupported len of array for []*multipart.FileHeader")
|
||||||
|
}
|
||||||
|
for i := range files {
|
||||||
|
setted, err := setByMultipartFormFile(value.Index(i), field, files[i:i+1])
|
||||||
|
if err != nil || !setted {
|
||||||
|
return setted, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
// Copyright 2019 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFormMultipartBindingBindOneFile(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
FileValue multipart.FileHeader `form:"file"`
|
||||||
|
FilePtr *multipart.FileHeader `form:"file"`
|
||||||
|
SliceValues []multipart.FileHeader `form:"file"`
|
||||||
|
SlicePtrs []*multipart.FileHeader `form:"file"`
|
||||||
|
ArrayValues [1]multipart.FileHeader `form:"file"`
|
||||||
|
ArrayPtrs [1]*multipart.FileHeader `form:"file"`
|
||||||
|
}
|
||||||
|
file := testFile{"file", "file1", []byte("hello")}
|
||||||
|
|
||||||
|
req := createRequestMultipartFiles(t, file)
|
||||||
|
err := FormMultipart.Bind(req, &s)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assertMultipartFileHeader(t, &s.FileValue, file)
|
||||||
|
assertMultipartFileHeader(t, s.FilePtr, file)
|
||||||
|
assert.Len(t, s.SliceValues, 1)
|
||||||
|
assertMultipartFileHeader(t, &s.SliceValues[0], file)
|
||||||
|
assert.Len(t, s.SlicePtrs, 1)
|
||||||
|
assertMultipartFileHeader(t, s.SlicePtrs[0], file)
|
||||||
|
assertMultipartFileHeader(t, &s.ArrayValues[0], file)
|
||||||
|
assertMultipartFileHeader(t, s.ArrayPtrs[0], file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormMultipartBindingBindTwoFiles(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
SliceValues []multipart.FileHeader `form:"file"`
|
||||||
|
SlicePtrs []*multipart.FileHeader `form:"file"`
|
||||||
|
ArrayValues [2]multipart.FileHeader `form:"file"`
|
||||||
|
ArrayPtrs [2]*multipart.FileHeader `form:"file"`
|
||||||
|
}
|
||||||
|
files := []testFile{
|
||||||
|
{"file", "file1", []byte("hello")},
|
||||||
|
{"file", "file2", []byte("world")},
|
||||||
|
}
|
||||||
|
|
||||||
|
req := createRequestMultipartFiles(t, files...)
|
||||||
|
err := FormMultipart.Bind(req, &s)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Len(t, s.SliceValues, len(files))
|
||||||
|
assert.Len(t, s.SlicePtrs, len(files))
|
||||||
|
assert.Len(t, s.ArrayValues, len(files))
|
||||||
|
assert.Len(t, s.ArrayPtrs, len(files))
|
||||||
|
|
||||||
|
for i, file := range files {
|
||||||
|
assertMultipartFileHeader(t, &s.SliceValues[i], file)
|
||||||
|
assertMultipartFileHeader(t, s.SlicePtrs[i], file)
|
||||||
|
assertMultipartFileHeader(t, &s.ArrayValues[i], file)
|
||||||
|
assertMultipartFileHeader(t, s.ArrayPtrs[i], file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormMultipartBindingBindError(t *testing.T) {
|
||||||
|
files := []testFile{
|
||||||
|
{"file", "file1", []byte("hello")},
|
||||||
|
{"file", "file2", []byte("world")},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
s interface{}
|
||||||
|
}{
|
||||||
|
{"wrong type", &struct {
|
||||||
|
Files int `form:"file"`
|
||||||
|
}{}},
|
||||||
|
{"wrong array size", &struct {
|
||||||
|
Files [1]*multipart.FileHeader `form:"file"`
|
||||||
|
}{}},
|
||||||
|
{"wrong slice type", &struct {
|
||||||
|
Files []int `form:"file"`
|
||||||
|
}{}},
|
||||||
|
} {
|
||||||
|
req := createRequestMultipartFiles(t, files...)
|
||||||
|
err := FormMultipart.Bind(req, tt.s)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testFile struct {
|
||||||
|
Fieldname string
|
||||||
|
Filename string
|
||||||
|
Content []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRequestMultipartFiles(t *testing.T, files ...testFile) *http.Request {
|
||||||
|
var body bytes.Buffer
|
||||||
|
|
||||||
|
mw := multipart.NewWriter(&body)
|
||||||
|
for _, file := range files {
|
||||||
|
fw, err := mw.CreateFormFile(file.Fieldname, file.Filename)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
n, err := fw.Write(file.Content)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, len(file.Content), n)
|
||||||
|
}
|
||||||
|
err := mw.Close()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", "/", &body)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+mw.Boundary())
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertMultipartFileHeader(t *testing.T, fh *multipart.FileHeader, file testFile) {
|
||||||
|
assert.Equal(t, file.Filename, fh.Filename)
|
||||||
|
// assert.Equal(t, int64(len(file.Content)), fh.Size) // fh.Size does not exist on go1.8
|
||||||
|
|
||||||
|
fl, err := fh.Open()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(fl)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, string(file.Content), string(body))
|
||||||
|
|
||||||
|
err = fl.Close()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type protobufBinding struct{}
|
||||||
|
|
||||||
|
func (protobufBinding) Name() string {
|
||||||
|
return "protobuf"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b protobufBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
buf, err := ioutil.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return b.BindBody(buf, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (protobufBinding) BindBody(body []byte, obj interface{}) error {
|
||||||
|
if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Here it's same to return validate(obj), but util now we can't add
|
||||||
|
// `binding:""` to the struct which automatically generate by gen-proto
|
||||||
|
return nil
|
||||||
|
// return validate(obj)
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
type queryBinding struct{}
|
||||||
|
|
||||||
|
func (queryBinding) Name() string {
|
||||||
|
return "query"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queryBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
values := req.URL.Query()
|
||||||
|
if err := mapForm(obj, values); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright 2018 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
type uriBinding struct{}
|
||||||
|
|
||||||
|
func (uriBinding) Name() string {
|
||||||
|
return "uri"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uriBinding) BindUri(m map[string][]string, obj interface{}) error {
|
||||||
|
if err := mapUri(obj, m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
|
@ -0,0 +1,228 @@
|
||||||
|
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go-playground/validator/v10"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testInterface interface {
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type substructNoValidation struct {
|
||||||
|
IString string
|
||||||
|
IInt int
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapNoValidationSub map[string]substructNoValidation
|
||||||
|
|
||||||
|
type structNoValidationValues struct {
|
||||||
|
substructNoValidation
|
||||||
|
|
||||||
|
Boolean bool
|
||||||
|
|
||||||
|
Uinteger uint
|
||||||
|
Integer int
|
||||||
|
Integer8 int8
|
||||||
|
Integer16 int16
|
||||||
|
Integer32 int32
|
||||||
|
Integer64 int64
|
||||||
|
Uinteger8 uint8
|
||||||
|
Uinteger16 uint16
|
||||||
|
Uinteger32 uint32
|
||||||
|
Uinteger64 uint64
|
||||||
|
|
||||||
|
Float32 float32
|
||||||
|
Float64 float64
|
||||||
|
|
||||||
|
String string
|
||||||
|
|
||||||
|
Date time.Time
|
||||||
|
|
||||||
|
Struct substructNoValidation
|
||||||
|
InlinedStruct struct {
|
||||||
|
String []string
|
||||||
|
Integer int
|
||||||
|
}
|
||||||
|
|
||||||
|
IntSlice []int
|
||||||
|
IntPointerSlice []*int
|
||||||
|
StructPointerSlice []*substructNoValidation
|
||||||
|
StructSlice []substructNoValidation
|
||||||
|
InterfaceSlice []testInterface
|
||||||
|
|
||||||
|
UniversalInterface interface{}
|
||||||
|
CustomInterface testInterface
|
||||||
|
|
||||||
|
FloatMap map[string]float32
|
||||||
|
StructMap mapNoValidationSub
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNoValidationValues() structNoValidationValues {
|
||||||
|
integer := 1
|
||||||
|
s := structNoValidationValues{
|
||||||
|
Boolean: true,
|
||||||
|
Uinteger: 1 << 29,
|
||||||
|
Integer: -10000,
|
||||||
|
Integer8: 120,
|
||||||
|
Integer16: -20000,
|
||||||
|
Integer32: 1 << 29,
|
||||||
|
Integer64: 1 << 61,
|
||||||
|
Uinteger8: 250,
|
||||||
|
Uinteger16: 50000,
|
||||||
|
Uinteger32: 1 << 31,
|
||||||
|
Uinteger64: 1 << 62,
|
||||||
|
Float32: 123.456,
|
||||||
|
Float64: 123.456789,
|
||||||
|
String: "text",
|
||||||
|
Date: time.Time{},
|
||||||
|
CustomInterface: &bytes.Buffer{},
|
||||||
|
Struct: substructNoValidation{},
|
||||||
|
IntSlice: []int{-3, -2, 1, 0, 1, 2, 3},
|
||||||
|
IntPointerSlice: []*int{&integer},
|
||||||
|
StructSlice: []substructNoValidation{},
|
||||||
|
UniversalInterface: 1.2,
|
||||||
|
FloatMap: map[string]float32{
|
||||||
|
"foo": 1.23,
|
||||||
|
"bar": 232.323,
|
||||||
|
},
|
||||||
|
StructMap: mapNoValidationSub{
|
||||||
|
"foo": substructNoValidation{},
|
||||||
|
"bar": substructNoValidation{},
|
||||||
|
},
|
||||||
|
// StructPointerSlice []noValidationSub
|
||||||
|
// InterfaceSlice []testInterface
|
||||||
|
}
|
||||||
|
s.InlinedStruct.Integer = 1000
|
||||||
|
s.InlinedStruct.String = []string{"first", "second"}
|
||||||
|
s.IString = "substring"
|
||||||
|
s.IInt = 987654
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateNoValidationValues(t *testing.T) {
|
||||||
|
origin := createNoValidationValues()
|
||||||
|
test := createNoValidationValues()
|
||||||
|
empty := structNoValidationValues{}
|
||||||
|
|
||||||
|
assert.Nil(t, validate(test))
|
||||||
|
assert.Nil(t, validate(&test))
|
||||||
|
assert.Nil(t, validate(empty))
|
||||||
|
assert.Nil(t, validate(&empty))
|
||||||
|
|
||||||
|
assert.Equal(t, origin, test)
|
||||||
|
}
|
||||||
|
|
||||||
|
type structNoValidationPointer struct {
|
||||||
|
substructNoValidation
|
||||||
|
|
||||||
|
Boolean bool
|
||||||
|
|
||||||
|
Uinteger *uint
|
||||||
|
Integer *int
|
||||||
|
Integer8 *int8
|
||||||
|
Integer16 *int16
|
||||||
|
Integer32 *int32
|
||||||
|
Integer64 *int64
|
||||||
|
Uinteger8 *uint8
|
||||||
|
Uinteger16 *uint16
|
||||||
|
Uinteger32 *uint32
|
||||||
|
Uinteger64 *uint64
|
||||||
|
|
||||||
|
Float32 *float32
|
||||||
|
Float64 *float64
|
||||||
|
|
||||||
|
String *string
|
||||||
|
|
||||||
|
Date *time.Time
|
||||||
|
|
||||||
|
Struct *substructNoValidation
|
||||||
|
|
||||||
|
IntSlice *[]int
|
||||||
|
IntPointerSlice *[]*int
|
||||||
|
StructPointerSlice *[]*substructNoValidation
|
||||||
|
StructSlice *[]substructNoValidation
|
||||||
|
InterfaceSlice *[]testInterface
|
||||||
|
|
||||||
|
FloatMap *map[string]float32
|
||||||
|
StructMap *mapNoValidationSub
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateNoValidationPointers(t *testing.T) {
|
||||||
|
//origin := createNoValidation_values()
|
||||||
|
//test := createNoValidation_values()
|
||||||
|
empty := structNoValidationPointer{}
|
||||||
|
|
||||||
|
//assert.Nil(t, validate(test))
|
||||||
|
//assert.Nil(t, validate(&test))
|
||||||
|
assert.Nil(t, validate(empty))
|
||||||
|
assert.Nil(t, validate(&empty))
|
||||||
|
|
||||||
|
//assert.Equal(t, origin, test)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Object map[string]interface{}
|
||||||
|
|
||||||
|
func TestValidatePrimitives(t *testing.T) {
|
||||||
|
obj := Object{"foo": "bar", "bar": 1}
|
||||||
|
assert.NoError(t, validate(obj))
|
||||||
|
assert.NoError(t, validate(&obj))
|
||||||
|
assert.Equal(t, Object{"foo": "bar", "bar": 1}, obj)
|
||||||
|
|
||||||
|
obj2 := []Object{{"foo": "bar", "bar": 1}, {"foo": "bar", "bar": 1}}
|
||||||
|
assert.NoError(t, validate(obj2))
|
||||||
|
assert.NoError(t, validate(&obj2))
|
||||||
|
|
||||||
|
nu := 10
|
||||||
|
assert.NoError(t, validate(nu))
|
||||||
|
assert.NoError(t, validate(&nu))
|
||||||
|
assert.Equal(t, 10, nu)
|
||||||
|
|
||||||
|
str := "value"
|
||||||
|
assert.NoError(t, validate(str))
|
||||||
|
assert.NoError(t, validate(&str))
|
||||||
|
assert.Equal(t, "value", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// structCustomValidation is a helper struct we use to check that
|
||||||
|
// custom validation can be registered on it.
|
||||||
|
// The `notone` binding directive is for custom validation and registered later.
|
||||||
|
type structCustomValidation struct {
|
||||||
|
Integer int `binding:"notone"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func notOne(f1 validator.FieldLevel) bool {
|
||||||
|
if val, ok := f1.Field().Interface().(int); ok {
|
||||||
|
return val != 1
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidatorEngine(t *testing.T) {
|
||||||
|
// This validates that the function `notOne` matches
|
||||||
|
// the expected function signature by `defaultValidator`
|
||||||
|
// and by extension the validator library.
|
||||||
|
engine, ok := Validator.Engine().(*validator.Validate)
|
||||||
|
assert.True(t, ok)
|
||||||
|
|
||||||
|
err := engine.RegisterValidation("notone", notOne)
|
||||||
|
// Check that we can register custom validation without error
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
// Create an instance which will fail validation
|
||||||
|
withOne := structCustomValidation{Integer: 1}
|
||||||
|
errs := validate(withOne)
|
||||||
|
|
||||||
|
// Check that we got back non-nil errs
|
||||||
|
assert.NotNil(t, errs)
|
||||||
|
// Check that the error matches expectation
|
||||||
|
assert.Error(t, errs, "", "", "notone")
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type xmlBinding struct{}
|
||||||
|
|
||||||
|
func (xmlBinding) Name() string {
|
||||||
|
return "xml"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xmlBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
return decodeXML(req.Body, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xmlBinding) BindBody(body []byte, obj interface{}) error {
|
||||||
|
return decodeXML(bytes.NewReader(body), obj)
|
||||||
|
}
|
||||||
|
func decodeXML(r io.Reader, obj interface{}) error {
|
||||||
|
decoder := xml.NewDecoder(r)
|
||||||
|
if err := decoder.Decode(obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2019 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestXMLBindingBindBody(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
Foo string `xml:"foo"`
|
||||||
|
}
|
||||||
|
xmlBody := `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<foo>FOO</foo>
|
||||||
|
</root>`
|
||||||
|
err := xmlBinding{}.BindBody([]byte(xmlBody), &s)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "FOO", s.Foo)
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright 2018 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type yamlBinding struct{}
|
||||||
|
|
||||||
|
func (yamlBinding) Name() string {
|
||||||
|
return "yaml"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (yamlBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
return decodeYAML(req.Body, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (yamlBinding) BindBody(body []byte, obj interface{}) error {
|
||||||
|
return decodeYAML(bytes.NewReader(body), obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeYAML(r io.Reader, obj interface{}) error {
|
||||||
|
decoder := yaml.NewDecoder(r)
|
||||||
|
if err := decoder.Decode(obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2019 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestYAMLBindingBindBody(t *testing.T) {
|
||||||
|
var s struct {
|
||||||
|
Foo string `yaml:"foo"`
|
||||||
|
}
|
||||||
|
err := yamlBinding{}.BindBody([]byte("foo: FOO"), &s)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "FOO", s.Foo)
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright 2020 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bytesconv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StringToBytes converts string to byte slice without a memory allocation.
|
||||||
|
func StringToBytes(s string) (b []byte) {
|
||||||
|
sh := *(*reflect.StringHeader)(unsafe.Pointer(&s))
|
||||||
|
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||||
|
bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesToString converts byte slice to string without a memory allocation.
|
||||||
|
func BytesToString(b []byte) string {
|
||||||
|
return *(*string)(unsafe.Pointer(&b))
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
// Copyright 2020 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bytesconv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testString = "Albert Einstein: Logic will get you from A to B. Imagination will take you everywhere."
|
||||||
|
var testBytes = []byte(testString)
|
||||||
|
|
||||||
|
func rawBytesToStr(b []byte) string {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func rawStrToBytes(s string) []byte {
|
||||||
|
return []byte(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// go test -v
|
||||||
|
|
||||||
|
func TestBytesToString(t *testing.T) {
|
||||||
|
data := make([]byte, 1024)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
rand.Read(data)
|
||||||
|
if rawBytesToStr(data) != BytesToString(data) {
|
||||||
|
t.Fatal("don't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
const (
|
||||||
|
letterIdxBits = 6 // 6 bits to represent a letter index
|
||||||
|
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
|
||||||
|
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
||||||
|
)
|
||||||
|
|
||||||
|
var src = rand.NewSource(time.Now().UnixNano())
|
||||||
|
|
||||||
|
func RandStringBytesMaskImprSrcSB(n int) string {
|
||||||
|
sb := strings.Builder{}
|
||||||
|
sb.Grow(n)
|
||||||
|
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
|
||||||
|
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
|
||||||
|
if remain == 0 {
|
||||||
|
cache, remain = src.Int63(), letterIdxMax
|
||||||
|
}
|
||||||
|
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
|
||||||
|
sb.WriteByte(letterBytes[idx])
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
cache >>= letterIdxBits
|
||||||
|
remain--
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringToBytes(t *testing.T) {
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
s := RandStringBytesMaskImprSrcSB(64)
|
||||||
|
if !bytes.Equal(rawStrToBytes(s), StringToBytes(s)) {
|
||||||
|
t.Fatal("don't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// go test -v -run=none -bench=^BenchmarkBytesConv -benchmem=true
|
||||||
|
|
||||||
|
func BenchmarkBytesConvBytesToStrRaw(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rawBytesToStr(testBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkBytesConvBytesToStr(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
BytesToString(testBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkBytesConvStrToBytesRaw(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rawStrToBytes(testString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkBytesConvStrToBytes(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
StringToBytes(testString)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright 2017 Bo-Yi Wu. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !jsoniter
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Marshal is exported by gin/json package.
|
||||||
|
Marshal = json.Marshal
|
||||||
|
// Unmarshal is exported by gin/json package.
|
||||||
|
Unmarshal = json.Unmarshal
|
||||||
|
// MarshalIndent is exported by gin/json package.
|
||||||
|
MarshalIndent = json.MarshalIndent
|
||||||
|
// NewDecoder is exported by gin/json package.
|
||||||
|
NewDecoder = json.NewDecoder
|
||||||
|
// NewEncoder is exported by gin/json package.
|
||||||
|
NewEncoder = json.NewEncoder
|
||||||
|
)
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright 2017 Bo-Yi Wu. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build jsoniter
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import jsoniter "github.com/json-iterator/go"
|
||||||
|
|
||||||
|
var (
|
||||||
|
json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
// Marshal is exported by gin/json package.
|
||||||
|
Marshal = json.Marshal
|
||||||
|
// Unmarshal is exported by gin/json package.
|
||||||
|
Unmarshal = json.Unmarshal
|
||||||
|
// MarshalIndent is exported by gin/json package.
|
||||||
|
MarshalIndent = json.MarshalIndent
|
||||||
|
// NewDecoder is exported by gin/json package.
|
||||||
|
NewDecoder = json.NewDecoder
|
||||||
|
// NewEncoder is exported by gin/json package.
|
||||||
|
NewEncoder = json.NewEncoder
|
||||||
|
)
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
|
@ -0,0 +1,26 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.13.1
|
||||||
|
- tip
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
recipients: dean.karn@gmail.com
|
||||||
|
on_success: change
|
||||||
|
on_failure: always
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go install github.com/mattn/goveralls
|
||||||
|
|
||||||
|
# Only clone the most recent commit.
|
||||||
|
git:
|
||||||
|
depth: 1
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./...
|
||||||
|
|
||||||
|
after_success: |
|
||||||
|
goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Go Playground
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,172 @@
|
||||||
|
## locales
|
||||||
|
<img align="right" src="https://raw.githubusercontent.com/go-playground/locales/master/logo.png">![Project status](https://img.shields.io/badge/version-0.13.0-green.svg)
|
||||||
|
[![Build Status](https://travis-ci.org/go-playground/locales.svg?branch=master)](https://travis-ci.org/go-playground/locales)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/go-playground/locales)](https://goreportcard.com/report/go-playground/locales)
|
||||||
|
[![GoDoc](https://godoc.org/go-playground/locales?status.svg)](https://godoc.org/go-playground/locales)
|
||||||
|
![License](https://img.shields.io/dub/l/vibe-d.svg)
|
||||||
|
[![Gitter](https://badges.gitter.im/go-playground/locales.svg)](https://gitter.im/go-playground/locales?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||||
|
|
||||||
|
Locales is a set of locales generated from the [Unicode CLDR Project](http://cldr.unicode.org/) which can be used independently or within
|
||||||
|
an i18n package; these were built for use with, but not exclusive to, [Universal Translator](https://github.com/go-playground/universal-translator).
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
- [x] Rules generated from the latest [CLDR](http://cldr.unicode.org/index/downloads) data, v31.0.1
|
||||||
|
- [x] Contains Cardinal, Ordinal and Range Plural Rules
|
||||||
|
- [x] Contains Month, Weekday and Timezone translations built in
|
||||||
|
- [x] Contains Date & Time formatting functions
|
||||||
|
- [x] Contains Number, Currency, Accounting and Percent formatting functions
|
||||||
|
- [x] Supports the "Gregorian" calendar only ( my time isn't unlimited, had to draw the line somewhere )
|
||||||
|
|
||||||
|
Full Tests
|
||||||
|
--------------------
|
||||||
|
I could sure use your help adding tests for every locale, it is a huge undertaking and I just don't have the free time to do it all at the moment;
|
||||||
|
any help would be **greatly appreciated!!!!** please see [issue](https://go-playground/locales/issues/1) for details.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Use go get
|
||||||
|
|
||||||
|
```shell
|
||||||
|
go get go-playground/locales
|
||||||
|
```
|
||||||
|
|
||||||
|
NOTES
|
||||||
|
--------
|
||||||
|
You'll notice most return types are []byte, this is because most of the time the results will be concatenated with a larger body
|
||||||
|
of text and can avoid some allocations if already appending to a byte array, otherwise just cast as string.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-------
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go-playground/locales/currency"
|
||||||
|
"go-playground/locales/en_CA"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
loc, _ := time.LoadLocation("America/Toronto")
|
||||||
|
datetime := time.Date(2016, 02, 03, 9, 0, 1, 0, loc)
|
||||||
|
|
||||||
|
l := en_CA.New()
|
||||||
|
|
||||||
|
// Dates
|
||||||
|
fmt.Println(l.FmtDateFull(datetime))
|
||||||
|
fmt.Println(l.FmtDateLong(datetime))
|
||||||
|
fmt.Println(l.FmtDateMedium(datetime))
|
||||||
|
fmt.Println(l.FmtDateShort(datetime))
|
||||||
|
|
||||||
|
// Times
|
||||||
|
fmt.Println(l.FmtTimeFull(datetime))
|
||||||
|
fmt.Println(l.FmtTimeLong(datetime))
|
||||||
|
fmt.Println(l.FmtTimeMedium(datetime))
|
||||||
|
fmt.Println(l.FmtTimeShort(datetime))
|
||||||
|
|
||||||
|
// Months Wide
|
||||||
|
fmt.Println(l.MonthWide(time.January))
|
||||||
|
fmt.Println(l.MonthWide(time.February))
|
||||||
|
fmt.Println(l.MonthWide(time.March))
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Months Abbreviated
|
||||||
|
fmt.Println(l.MonthAbbreviated(time.January))
|
||||||
|
fmt.Println(l.MonthAbbreviated(time.February))
|
||||||
|
fmt.Println(l.MonthAbbreviated(time.March))
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Months Narrow
|
||||||
|
fmt.Println(l.MonthNarrow(time.January))
|
||||||
|
fmt.Println(l.MonthNarrow(time.February))
|
||||||
|
fmt.Println(l.MonthNarrow(time.March))
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Weekdays Wide
|
||||||
|
fmt.Println(l.WeekdayWide(time.Sunday))
|
||||||
|
fmt.Println(l.WeekdayWide(time.Monday))
|
||||||
|
fmt.Println(l.WeekdayWide(time.Tuesday))
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Weekdays Abbreviated
|
||||||
|
fmt.Println(l.WeekdayAbbreviated(time.Sunday))
|
||||||
|
fmt.Println(l.WeekdayAbbreviated(time.Monday))
|
||||||
|
fmt.Println(l.WeekdayAbbreviated(time.Tuesday))
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Weekdays Short
|
||||||
|
fmt.Println(l.WeekdayShort(time.Sunday))
|
||||||
|
fmt.Println(l.WeekdayShort(time.Monday))
|
||||||
|
fmt.Println(l.WeekdayShort(time.Tuesday))
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Weekdays Narrow
|
||||||
|
fmt.Println(l.WeekdayNarrow(time.Sunday))
|
||||||
|
fmt.Println(l.WeekdayNarrow(time.Monday))
|
||||||
|
fmt.Println(l.WeekdayNarrow(time.Tuesday))
|
||||||
|
// ...
|
||||||
|
|
||||||
|
var f64 float64
|
||||||
|
|
||||||
|
f64 = -10356.4523
|
||||||
|
|
||||||
|
// Number
|
||||||
|
fmt.Println(l.FmtNumber(f64, 2))
|
||||||
|
|
||||||
|
// Currency
|
||||||
|
fmt.Println(l.FmtCurrency(f64, 2, currency.CAD))
|
||||||
|
fmt.Println(l.FmtCurrency(f64, 2, currency.USD))
|
||||||
|
|
||||||
|
// Accounting
|
||||||
|
fmt.Println(l.FmtAccounting(f64, 2, currency.CAD))
|
||||||
|
fmt.Println(l.FmtAccounting(f64, 2, currency.USD))
|
||||||
|
|
||||||
|
f64 = 78.12
|
||||||
|
|
||||||
|
// Percent
|
||||||
|
fmt.Println(l.FmtPercent(f64, 0))
|
||||||
|
|
||||||
|
// Plural Rules for locale, so you know what rules you must cover
|
||||||
|
fmt.Println(l.PluralsCardinal())
|
||||||
|
fmt.Println(l.PluralsOrdinal())
|
||||||
|
|
||||||
|
// Cardinal Plural Rules
|
||||||
|
fmt.Println(l.CardinalPluralRule(1, 0))
|
||||||
|
fmt.Println(l.CardinalPluralRule(1.0, 0))
|
||||||
|
fmt.Println(l.CardinalPluralRule(1.0, 1))
|
||||||
|
fmt.Println(l.CardinalPluralRule(3, 0))
|
||||||
|
|
||||||
|
// Ordinal Plural Rules
|
||||||
|
fmt.Println(l.OrdinalPluralRule(21, 0)) // 21st
|
||||||
|
fmt.Println(l.OrdinalPluralRule(22, 0)) // 22nd
|
||||||
|
fmt.Println(l.OrdinalPluralRule(33, 0)) // 33rd
|
||||||
|
fmt.Println(l.OrdinalPluralRule(34, 0)) // 34th
|
||||||
|
|
||||||
|
// Range Plural Rules
|
||||||
|
fmt.Println(l.RangePluralRule(1, 0, 1, 0)) // 1-1
|
||||||
|
fmt.Println(l.RangePluralRule(1, 0, 2, 0)) // 1-2
|
||||||
|
fmt.Println(l.RangePluralRule(5, 0, 8, 0)) // 5-8
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
NOTES:
|
||||||
|
-------
|
||||||
|
These rules were generated from the [Unicode CLDR Project](http://cldr.unicode.org/), if you encounter any issues
|
||||||
|
I strongly encourage contributing to the CLDR project to get the locale information corrected and the next time
|
||||||
|
these locales are regenerated the fix will come with.
|
||||||
|
|
||||||
|
I do however realize that time constraints are often important and so there are two options:
|
||||||
|
|
||||||
|
1. Create your own locale, copy, paste and modify, and ensure it complies with the `Translator` interface.
|
||||||
|
2. Add an exception in the locale generation code directly and once regenerated, fix will be in place.
|
||||||
|
|
||||||
|
Please to not make fixes inside the locale files, they WILL get overwritten when the locales are regenerated.
|
||||||
|
|
||||||
|
License
|
||||||
|
------
|
||||||
|
Distributed under MIT License, please see license file in code for more details.
|
|
@ -0,0 +1,308 @@
|
||||||
|
package currency
|
||||||
|
|
||||||
|
// Type is the currency type associated with the locales currency enum
|
||||||
|
type Type int
|
||||||
|
|
||||||
|
// locale currencies
|
||||||
|
const (
|
||||||
|
ADP Type = iota
|
||||||
|
AED
|
||||||
|
AFA
|
||||||
|
AFN
|
||||||
|
ALK
|
||||||
|
ALL
|
||||||
|
AMD
|
||||||
|
ANG
|
||||||
|
AOA
|
||||||
|
AOK
|
||||||
|
AON
|
||||||
|
AOR
|
||||||
|
ARA
|
||||||
|
ARL
|
||||||
|
ARM
|
||||||
|
ARP
|
||||||
|
ARS
|
||||||
|
ATS
|
||||||
|
AUD
|
||||||
|
AWG
|
||||||
|
AZM
|
||||||
|
AZN
|
||||||
|
BAD
|
||||||
|
BAM
|
||||||
|
BAN
|
||||||
|
BBD
|
||||||
|
BDT
|
||||||
|
BEC
|
||||||
|
BEF
|
||||||
|
BEL
|
||||||
|
BGL
|
||||||
|
BGM
|
||||||
|
BGN
|
||||||
|
BGO
|
||||||
|
BHD
|
||||||
|
BIF
|
||||||
|
BMD
|
||||||
|
BND
|
||||||
|
BOB
|
||||||
|
BOL
|
||||||
|
BOP
|
||||||
|
BOV
|
||||||
|
BRB
|
||||||
|
BRC
|
||||||
|
BRE
|
||||||
|
BRL
|
||||||
|
BRN
|
||||||
|
BRR
|
||||||
|
BRZ
|
||||||
|
BSD
|
||||||
|
BTN
|
||||||
|
BUK
|
||||||
|
BWP
|
||||||
|
BYB
|
||||||
|
BYN
|
||||||
|
BYR
|
||||||
|
BZD
|
||||||
|
CAD
|
||||||
|
CDF
|
||||||
|
CHE
|
||||||
|
CHF
|
||||||
|
CHW
|
||||||
|
CLE
|
||||||
|
CLF
|
||||||
|
CLP
|
||||||
|
CNH
|
||||||
|
CNX
|
||||||
|
CNY
|
||||||
|
COP
|
||||||
|
COU
|
||||||
|
CRC
|
||||||
|
CSD
|
||||||
|
CSK
|
||||||
|
CUC
|
||||||
|
CUP
|
||||||
|
CVE
|
||||||
|
CYP
|
||||||
|
CZK
|
||||||
|
DDM
|
||||||
|
DEM
|
||||||
|
DJF
|
||||||
|
DKK
|
||||||
|
DOP
|
||||||
|
DZD
|
||||||
|
ECS
|
||||||
|
ECV
|
||||||
|
EEK
|
||||||
|
EGP
|
||||||
|
ERN
|
||||||
|
ESA
|
||||||
|
ESB
|
||||||
|
ESP
|
||||||
|
ETB
|
||||||
|
EUR
|
||||||
|
FIM
|
||||||
|
FJD
|
||||||
|
FKP
|
||||||
|
FRF
|
||||||
|
GBP
|
||||||
|
GEK
|
||||||
|
GEL
|
||||||
|
GHC
|
||||||
|
GHS
|
||||||
|
GIP
|
||||||
|
GMD
|
||||||
|
GNF
|
||||||
|
GNS
|
||||||
|
GQE
|
||||||
|
GRD
|
||||||
|
GTQ
|
||||||
|
GWE
|
||||||
|
GWP
|
||||||
|
GYD
|
||||||
|
HKD
|
||||||
|
HNL
|
||||||
|
HRD
|
||||||
|
HRK
|
||||||
|
HTG
|
||||||
|
HUF
|
||||||
|
IDR
|
||||||
|
IEP
|
||||||
|
ILP
|
||||||
|
ILR
|
||||||
|
ILS
|
||||||
|
INR
|
||||||
|
IQD
|
||||||
|
IRR
|
||||||
|
ISJ
|
||||||
|
ISK
|
||||||
|
ITL
|
||||||
|
JMD
|
||||||
|
JOD
|
||||||
|
JPY
|
||||||
|
KES
|
||||||
|
KGS
|
||||||
|
KHR
|
||||||
|
KMF
|
||||||
|
KPW
|
||||||
|
KRH
|
||||||
|
KRO
|
||||||
|
KRW
|
||||||
|
KWD
|
||||||
|
KYD
|
||||||
|
KZT
|
||||||
|
LAK
|
||||||
|
LBP
|
||||||
|
LKR
|
||||||
|
LRD
|
||||||
|
LSL
|
||||||
|
LTL
|
||||||
|
LTT
|
||||||
|
LUC
|
||||||
|
LUF
|
||||||
|
LUL
|
||||||
|
LVL
|
||||||
|
LVR
|
||||||
|
LYD
|
||||||
|
MAD
|
||||||
|
MAF
|
||||||
|
MCF
|
||||||
|
MDC
|
||||||
|
MDL
|
||||||
|
MGA
|
||||||
|
MGF
|
||||||
|
MKD
|
||||||
|
MKN
|
||||||
|
MLF
|
||||||
|
MMK
|
||||||
|
MNT
|
||||||
|
MOP
|
||||||
|
MRO
|
||||||
|
MTL
|
||||||
|
MTP
|
||||||
|
MUR
|
||||||
|
MVP
|
||||||
|
MVR
|
||||||
|
MWK
|
||||||
|
MXN
|
||||||
|
MXP
|
||||||
|
MXV
|
||||||
|
MYR
|
||||||
|
MZE
|
||||||
|
MZM
|
||||||
|
MZN
|
||||||
|
NAD
|
||||||
|
NGN
|
||||||
|
NIC
|
||||||
|
NIO
|
||||||
|
NLG
|
||||||
|
NOK
|
||||||
|
NPR
|
||||||
|
NZD
|
||||||
|
OMR
|
||||||
|
PAB
|
||||||
|
PEI
|
||||||
|
PEN
|
||||||
|
PES
|
||||||
|
PGK
|
||||||
|
PHP
|
||||||
|
PKR
|
||||||
|
PLN
|
||||||
|
PLZ
|
||||||
|
PTE
|
||||||
|
PYG
|
||||||
|
QAR
|
||||||
|
RHD
|
||||||
|
ROL
|
||||||
|
RON
|
||||||
|
RSD
|
||||||
|
RUB
|
||||||
|
RUR
|
||||||
|
RWF
|
||||||
|
SAR
|
||||||
|
SBD
|
||||||
|
SCR
|
||||||
|
SDD
|
||||||
|
SDG
|
||||||
|
SDP
|
||||||
|
SEK
|
||||||
|
SGD
|
||||||
|
SHP
|
||||||
|
SIT
|
||||||
|
SKK
|
||||||
|
SLL
|
||||||
|
SOS
|
||||||
|
SRD
|
||||||
|
SRG
|
||||||
|
SSP
|
||||||
|
STD
|
||||||
|
STN
|
||||||
|
SUR
|
||||||
|
SVC
|
||||||
|
SYP
|
||||||
|
SZL
|
||||||
|
THB
|
||||||
|
TJR
|
||||||
|
TJS
|
||||||
|
TMM
|
||||||
|
TMT
|
||||||
|
TND
|
||||||
|
TOP
|
||||||
|
TPE
|
||||||
|
TRL
|
||||||
|
TRY
|
||||||
|
TTD
|
||||||
|
TWD
|
||||||
|
TZS
|
||||||
|
UAH
|
||||||
|
UAK
|
||||||
|
UGS
|
||||||
|
UGX
|
||||||
|
USD
|
||||||
|
USN
|
||||||
|
USS
|
||||||
|
UYI
|
||||||
|
UYP
|
||||||
|
UYU
|
||||||
|
UZS
|
||||||
|
VEB
|
||||||
|
VEF
|
||||||
|
VND
|
||||||
|
VNN
|
||||||
|
VUV
|
||||||
|
WST
|
||||||
|
XAF
|
||||||
|
XAG
|
||||||
|
XAU
|
||||||
|
XBA
|
||||||
|
XBB
|
||||||
|
XBC
|
||||||
|
XBD
|
||||||
|
XCD
|
||||||
|
XDR
|
||||||
|
XEU
|
||||||
|
XFO
|
||||||
|
XFU
|
||||||
|
XOF
|
||||||
|
XPD
|
||||||
|
XPF
|
||||||
|
XPT
|
||||||
|
XRE
|
||||||
|
XSU
|
||||||
|
XTS
|
||||||
|
XUA
|
||||||
|
XXX
|
||||||
|
YDD
|
||||||
|
YER
|
||||||
|
YUD
|
||||||
|
YUM
|
||||||
|
YUN
|
||||||
|
YUR
|
||||||
|
ZAL
|
||||||
|
ZAR
|
||||||
|
ZMK
|
||||||
|
ZMW
|
||||||
|
ZRN
|
||||||
|
ZRZ
|
||||||
|
ZWD
|
||||||
|
ZWL
|
||||||
|
ZWR
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
|
@ -0,0 +1,293 @@
|
||||||
|
package locales
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go-playground/locales/currency"
|
||||||
|
)
|
||||||
|
|
||||||
|
// // ErrBadNumberValue is returned when the number passed for
|
||||||
|
// // plural rule determination cannot be parsed
|
||||||
|
// type ErrBadNumberValue struct {
|
||||||
|
// NumberValue string
|
||||||
|
// InnerError error
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Error returns ErrBadNumberValue error string
|
||||||
|
// func (e *ErrBadNumberValue) Error() string {
|
||||||
|
// return fmt.Sprintf("Invalid Number Value '%s' %s", e.NumberValue, e.InnerError)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var _ error = new(ErrBadNumberValue)
|
||||||
|
|
||||||
|
// PluralRule denotes the type of plural rules
|
||||||
|
type PluralRule int
|
||||||
|
|
||||||
|
// PluralRule's
|
||||||
|
const (
|
||||||
|
PluralRuleUnknown PluralRule = iota
|
||||||
|
PluralRuleZero // zero
|
||||||
|
PluralRuleOne // one - singular
|
||||||
|
PluralRuleTwo // two - dual
|
||||||
|
PluralRuleFew // few - paucal
|
||||||
|
PluralRuleMany // many - also used for fractions if they have a separate class
|
||||||
|
PluralRuleOther // other - required—general plural form—also used if the language only has a single form
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
pluralsString = "UnknownZeroOneTwoFewManyOther"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Translator encapsulates an instance of a locale
|
||||||
|
// NOTE: some values are returned as a []byte just in case the caller
|
||||||
|
// wishes to add more and can help avoid allocations; otherwise just cast as string
|
||||||
|
type Translator interface {
|
||||||
|
|
||||||
|
// The following Functions are for overriding, debugging or developing
|
||||||
|
// with a Translator Locale
|
||||||
|
|
||||||
|
// Locale returns the string value of the translator
|
||||||
|
Locale() string
|
||||||
|
|
||||||
|
// returns an array of cardinal plural rules associated
|
||||||
|
// with this translator
|
||||||
|
PluralsCardinal() []PluralRule
|
||||||
|
|
||||||
|
// returns an array of ordinal plural rules associated
|
||||||
|
// with this translator
|
||||||
|
PluralsOrdinal() []PluralRule
|
||||||
|
|
||||||
|
// returns an array of range plural rules associated
|
||||||
|
// with this translator
|
||||||
|
PluralsRange() []PluralRule
|
||||||
|
|
||||||
|
// returns the cardinal PluralRule given 'num' and digits/precision of 'v' for locale
|
||||||
|
CardinalPluralRule(num float64, v uint64) PluralRule
|
||||||
|
|
||||||
|
// returns the ordinal PluralRule given 'num' and digits/precision of 'v' for locale
|
||||||
|
OrdinalPluralRule(num float64, v uint64) PluralRule
|
||||||
|
|
||||||
|
// returns the ordinal PluralRule given 'num1', 'num2' and digits/precision of 'v1' and 'v2' for locale
|
||||||
|
RangePluralRule(num1 float64, v1 uint64, num2 float64, v2 uint64) PluralRule
|
||||||
|
|
||||||
|
// returns the locales abbreviated month given the 'month' provided
|
||||||
|
MonthAbbreviated(month time.Month) string
|
||||||
|
|
||||||
|
// returns the locales abbreviated months
|
||||||
|
MonthsAbbreviated() []string
|
||||||
|
|
||||||
|
// returns the locales narrow month given the 'month' provided
|
||||||
|
MonthNarrow(month time.Month) string
|
||||||
|
|
||||||
|
// returns the locales narrow months
|
||||||
|
MonthsNarrow() []string
|
||||||
|
|
||||||
|
// returns the locales wide month given the 'month' provided
|
||||||
|
MonthWide(month time.Month) string
|
||||||
|
|
||||||
|
// returns the locales wide months
|
||||||
|
MonthsWide() []string
|
||||||
|
|
||||||
|
// returns the locales abbreviated weekday given the 'weekday' provided
|
||||||
|
WeekdayAbbreviated(weekday time.Weekday) string
|
||||||
|
|
||||||
|
// returns the locales abbreviated weekdays
|
||||||
|
WeekdaysAbbreviated() []string
|
||||||
|
|
||||||
|
// returns the locales narrow weekday given the 'weekday' provided
|
||||||
|
WeekdayNarrow(weekday time.Weekday) string
|
||||||
|
|
||||||
|
// WeekdaysNarrowreturns the locales narrow weekdays
|
||||||
|
WeekdaysNarrow() []string
|
||||||
|
|
||||||
|
// returns the locales short weekday given the 'weekday' provided
|
||||||
|
WeekdayShort(weekday time.Weekday) string
|
||||||
|
|
||||||
|
// returns the locales short weekdays
|
||||||
|
WeekdaysShort() []string
|
||||||
|
|
||||||
|
// returns the locales wide weekday given the 'weekday' provided
|
||||||
|
WeekdayWide(weekday time.Weekday) string
|
||||||
|
|
||||||
|
// returns the locales wide weekdays
|
||||||
|
WeekdaysWide() []string
|
||||||
|
|
||||||
|
// The following Functions are common Formatting functionsfor the Translator's Locale
|
||||||
|
|
||||||
|
// returns 'num' with digits/precision of 'v' for locale and handles both Whole and Real numbers based on 'v'
|
||||||
|
FmtNumber(num float64, v uint64) string
|
||||||
|
|
||||||
|
// returns 'num' with digits/precision of 'v' for locale and handles both Whole and Real numbers based on 'v'
|
||||||
|
// NOTE: 'num' passed into FmtPercent is assumed to be in percent already
|
||||||
|
FmtPercent(num float64, v uint64) string
|
||||||
|
|
||||||
|
// returns the currency representation of 'num' with digits/precision of 'v' for locale
|
||||||
|
FmtCurrency(num float64, v uint64, currency currency.Type) string
|
||||||
|
|
||||||
|
// returns the currency representation of 'num' with digits/precision of 'v' for locale
|
||||||
|
// in accounting notation.
|
||||||
|
FmtAccounting(num float64, v uint64, currency currency.Type) string
|
||||||
|
|
||||||
|
// returns the short date representation of 't' for locale
|
||||||
|
FmtDateShort(t time.Time) string
|
||||||
|
|
||||||
|
// returns the medium date representation of 't' for locale
|
||||||
|
FmtDateMedium(t time.Time) string
|
||||||
|
|
||||||
|
// returns the long date representation of 't' for locale
|
||||||
|
FmtDateLong(t time.Time) string
|
||||||
|
|
||||||
|
// returns the full date representation of 't' for locale
|
||||||
|
FmtDateFull(t time.Time) string
|
||||||
|
|
||||||
|
// returns the short time representation of 't' for locale
|
||||||
|
FmtTimeShort(t time.Time) string
|
||||||
|
|
||||||
|
// returns the medium time representation of 't' for locale
|
||||||
|
FmtTimeMedium(t time.Time) string
|
||||||
|
|
||||||
|
// returns the long time representation of 't' for locale
|
||||||
|
FmtTimeLong(t time.Time) string
|
||||||
|
|
||||||
|
// returns the full time representation of 't' for locale
|
||||||
|
FmtTimeFull(t time.Time) string
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string value of PluralRule
|
||||||
|
func (p PluralRule) String() string {
|
||||||
|
|
||||||
|
switch p {
|
||||||
|
case PluralRuleZero:
|
||||||
|
return pluralsString[7:11]
|
||||||
|
case PluralRuleOne:
|
||||||
|
return pluralsString[11:14]
|
||||||
|
case PluralRuleTwo:
|
||||||
|
return pluralsString[14:17]
|
||||||
|
case PluralRuleFew:
|
||||||
|
return pluralsString[17:20]
|
||||||
|
case PluralRuleMany:
|
||||||
|
return pluralsString[20:24]
|
||||||
|
case PluralRuleOther:
|
||||||
|
return pluralsString[24:]
|
||||||
|
default:
|
||||||
|
return pluralsString[:7]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Precision Notes:
|
||||||
|
//
|
||||||
|
// must specify a precision >= 0, and here is why https://play.golang.org/p/LyL90U0Vyh
|
||||||
|
//
|
||||||
|
// v := float64(3.141)
|
||||||
|
// i := float64(int64(v))
|
||||||
|
//
|
||||||
|
// fmt.Println(v - i)
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// s := strconv.FormatFloat(v-i, 'f', -1, 64)
|
||||||
|
// fmt.Println(s)
|
||||||
|
//
|
||||||
|
// these will not print what you'd expect: 0.14100000000000001
|
||||||
|
// and so this library requires a precision to be specified, or
|
||||||
|
// inaccurate plural rules could be applied.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// n - absolute value of the source number (integer and decimals).
|
||||||
|
// i - integer digits of n.
|
||||||
|
// v - number of visible fraction digits in n, with trailing zeros.
|
||||||
|
// w - number of visible fraction digits in n, without trailing zeros.
|
||||||
|
// f - visible fractional digits in n, with trailing zeros.
|
||||||
|
// t - visible fractional digits in n, without trailing zeros.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Func(num float64, v uint64) // v = digits/precision and prevents -1 as a special case as this can lead to very unexpected behaviour, see precision note's above.
|
||||||
|
//
|
||||||
|
// n := math.Abs(num)
|
||||||
|
// i := int64(n)
|
||||||
|
// v := v
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// w := strconv.FormatFloat(num-float64(i), 'f', int(v), 64) // then parse backwards on string until no more zero's....
|
||||||
|
// f := strconv.FormatFloat(n, 'f', int(v), 64) // then turn everything after decimal into an int64
|
||||||
|
// t := strconv.FormatFloat(n, 'f', int(v), 64) // then parse backwards on string until no more zero's....
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// General Inclusion Rules
|
||||||
|
// - v will always be available inherently
|
||||||
|
// - all require n
|
||||||
|
// - w requires i
|
||||||
|
//
|
||||||
|
|
||||||
|
// W returns the number of visible fraction digits in N, without trailing zeros.
|
||||||
|
func W(n float64, v uint64) (w int64) {
|
||||||
|
|
||||||
|
s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64)
|
||||||
|
|
||||||
|
// with either be '0' or '0.xxxx', so if 1 then w will be zero
|
||||||
|
// otherwise need to parse
|
||||||
|
if len(s) != 1 {
|
||||||
|
|
||||||
|
s = s[2:]
|
||||||
|
end := len(s) + 1
|
||||||
|
|
||||||
|
for i := end; i >= 0; i-- {
|
||||||
|
if s[i] != '0' {
|
||||||
|
end = i + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w = int64(len(s[:end]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// F returns the visible fractional digits in N, with trailing zeros.
|
||||||
|
func F(n float64, v uint64) (f int64) {
|
||||||
|
|
||||||
|
s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64)
|
||||||
|
|
||||||
|
// with either be '0' or '0.xxxx', so if 1 then f will be zero
|
||||||
|
// otherwise need to parse
|
||||||
|
if len(s) != 1 {
|
||||||
|
|
||||||
|
// ignoring error, because it can't fail as we generated
|
||||||
|
// the string internally from a real number
|
||||||
|
f, _ = strconv.ParseInt(s[2:], 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// T returns the visible fractional digits in N, without trailing zeros.
|
||||||
|
func T(n float64, v uint64) (t int64) {
|
||||||
|
|
||||||
|
s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64)
|
||||||
|
|
||||||
|
// with either be '0' or '0.xxxx', so if 1 then t will be zero
|
||||||
|
// otherwise need to parse
|
||||||
|
if len(s) != 1 {
|
||||||
|
|
||||||
|
s = s[2:]
|
||||||
|
end := len(s) + 1
|
||||||
|
|
||||||
|
for i := end; i >= 0; i-- {
|
||||||
|
if s[i] != '0' {
|
||||||
|
end = i + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignoring error, because it can't fail as we generated
|
||||||
|
// the string internally from a real number
|
||||||
|
t, _ = strconv.ParseInt(s[:end], 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,619 @@
|
||||||
|
package zh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go-playground/locales"
|
||||||
|
"go-playground/locales/currency"
|
||||||
|
)
|
||||||
|
|
||||||
|
type zh struct {
|
||||||
|
locale string
|
||||||
|
pluralsCardinal []locales.PluralRule
|
||||||
|
pluralsOrdinal []locales.PluralRule
|
||||||
|
pluralsRange []locales.PluralRule
|
||||||
|
decimal string
|
||||||
|
group string
|
||||||
|
minus string
|
||||||
|
percent string
|
||||||
|
perMille string
|
||||||
|
timeSeparator string
|
||||||
|
inifinity string
|
||||||
|
currencies []string // idx = enum of currency code
|
||||||
|
monthsAbbreviated []string
|
||||||
|
monthsNarrow []string
|
||||||
|
monthsWide []string
|
||||||
|
daysAbbreviated []string
|
||||||
|
daysNarrow []string
|
||||||
|
daysShort []string
|
||||||
|
daysWide []string
|
||||||
|
periodsAbbreviated []string
|
||||||
|
periodsNarrow []string
|
||||||
|
periodsShort []string
|
||||||
|
periodsWide []string
|
||||||
|
erasAbbreviated []string
|
||||||
|
erasNarrow []string
|
||||||
|
erasWide []string
|
||||||
|
timezones map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new instance of translator for the 'zh' locale
|
||||||
|
func New() locales.Translator {
|
||||||
|
return &zh{
|
||||||
|
locale: "zh",
|
||||||
|
pluralsCardinal: []locales.PluralRule{6},
|
||||||
|
pluralsOrdinal: []locales.PluralRule{6},
|
||||||
|
pluralsRange: []locales.PluralRule{6},
|
||||||
|
decimal: ".",
|
||||||
|
group: ",",
|
||||||
|
minus: "-",
|
||||||
|
percent: "%",
|
||||||
|
perMille: "‰",
|
||||||
|
timeSeparator: ":",
|
||||||
|
inifinity: "∞",
|
||||||
|
currencies: []string{"ADP", "AED", "AFA", "AFN", "ALK", "ALL", "AMD", "ANG", "AOA", "AOK", "AON", "AOR", "ARA", "ARL", "ARM", "ARP", "ARS", "ATS", "AU$", "AWG", "AZM", "AZN", "BAD", "BAM", "BAN", "BBD", "BDT", "BEC", "BEF", "BEL", "BGL", "BGM", "BGN", "BGO", "BHD", "BIF", "BMD", "BND", "BOB", "BOL", "BOP", "BOV", "BRB", "BRC", "BRE", "R$", "BRN", "BRR", "BRZ", "BSD", "BTN", "BUK", "BWP", "BYB", "BYN", "BYR", "BZD", "CA$", "CDF", "CHE", "CHF", "CHW", "CLE", "CLF", "CLP", "CNH", "CNX", "¥", "COP", "COU", "CRC", "CSD", "CSK", "CUC", "CUP", "CVE", "CYP", "CZK", "DDM", "DEM", "DJF", "DKK", "DOP", "DZD", "ECS", "ECV", "EEK", "EGP", "ERN", "ESA", "ESB", "ESP", "ETB", "€", "FIM", "FJD", "FKP", "FRF", "£", "GEK", "GEL", "GHC", "GHS", "GIP", "GMD", "GNF", "GNS", "GQE", "GRD", "GTQ", "GWE", "GWP", "GYD", "HK$", "HNL", "HRD", "HRK", "HTG", "HUF", "IDR", "IEP", "ILP", "ILS", "₪", "₹", "IQD", "IRR", "ISJ", "ISK", "ITL", "JMD", "JOD", "JP¥", "KES", "KGS", "KHR", "KMF", "KPW", "KRH", "KRO", "₩", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LTL", "LTT", "LUC", "LUF", "LUL", "LVL", "LVR", "LYD", "MAD", "MAF", "MCF", "MDC", "MDL", "MGA", "MGF", "MKD", "MKN", "MLF", "MMK", "MNT", "MOP", "MRO", "MTL", "MTP", "MUR", "MVP", "MVR", "MWK", "MX$", "MXP", "MXV", "MYR", "MZE", "MZM", "MZN", "NAD", "NGN", "NIC", "NIO", "NLG", "NOK", "NPR", "NZ$", "OMR", "PAB", "PEI", "PEN", "PES", "PGK", "PHP", "PKR", "PLN", "PLZ", "PTE", "PYG", "QAR", "RHD", "ROL", "RON", "RSD", "RUB", "RUR", "RWF", "SAR", "SBD", "SCR", "SDD", "SDG", "SDP", "SEK", "SGD", "SHP", "SIT", "SKK", "SLL", "SOS", "SRD", "SRG", "SSP", "STD", "STN", "SUR", "SVC", "SYP", "SZL", "THB", "TJR", "TJS", "TMM", "TMT", "TND", "TOP", "TPE", "TRL", "TRY", "TTD", "NT$", "TZS", "UAH", "UAK", "UGS", "UGX", "US$", "USN", "USS", "UYI", "UYP", "UYU", "UZS", "VEB", "VEF", "₫", "VNN", "VUV", "WST", "FCFA", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "EC$", "XDR", "XEU", "XFO", "XFU", "CFA", "XPD", "CFPF", "XPT", "XRE", "XSU", "XTS", "XUA", "XXX", "YDD", "YER", "YUD", "YUM", "YUN", "YUR", "ZAL", "ZAR", "ZMK", "ZMW", "ZRN", "ZRZ", "ZWD", "ZWL", "ZWR"},
|
||||||
|
monthsAbbreviated: []string{"", "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"},
|
||||||
|
monthsNarrow: []string{"", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"},
|
||||||
|
monthsWide: []string{"", "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"},
|
||||||
|
daysAbbreviated: []string{"周日", "周一", "周二", "周三", "周四", "周五", "周六"},
|
||||||
|
daysNarrow: []string{"日", "一", "二", "三", "四", "五", "六"},
|
||||||
|
daysShort: []string{"周日", "周一", "周二", "周三", "周四", "周五", "周六"},
|
||||||
|
daysWide: []string{"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"},
|
||||||
|
periodsAbbreviated: []string{"上午", "下午"},
|
||||||
|
periodsNarrow: []string{"上午", "下午"},
|
||||||
|
periodsWide: []string{"上午", "下午"},
|
||||||
|
erasAbbreviated: []string{"公元前", "公元"},
|
||||||
|
erasNarrow: []string{"公元前", "公元"},
|
||||||
|
erasWide: []string{"公元前", "公元"},
|
||||||
|
timezones: map[string]string{"UYST": "乌拉圭夏令时间", "HNPMX": "墨西哥太平洋标准时间", "MDT": "北美山区夏令时间", "WESZ": "西欧夏令时间", "AKST": "阿拉斯加标准时间", "ACWST": "澳大利亚中西部标准时间", "HENOMX": "墨西哥西北部夏令时间", "WIT": "印度尼西亚东部时间", "HEPMX": "墨西哥太平洋夏令时间", "JST": "日本标准时间", "SRT": "苏里南时间", "CLST": "智利夏令时间", "UYT": "乌拉圭标准时间", "AWDT": "澳大利亚西部夏令时间", "MST": "北美山区标准时间", "WAST": "西部非洲夏令时间", "NZST": "新西兰标准时间", "EAT": "东部非洲时间", "HECU": "古巴夏令时间", "BT": "不丹时间", "EDT": "北美东部夏令时间", "WARST": "阿根廷西部夏令时间", "HNPM": "圣皮埃尔和密克隆群岛标准时间", "HNCU": "古巴标准时间", "PDT": "北美太平洋夏令时间", "LHDT": "豪勋爵岛夏令时间", "CLT": "智利标准时间", "PST": "北美太平洋标准时间", "JDT": "日本夏令时间", "OEZ": "东欧标准时间", "TMT": "土库曼斯坦标准时间", "CST": "北美中部标准时间", "AWST": "澳大利亚西部标准时间", "AEST": "澳大利亚东部标准时间", "AKDT": "阿拉斯加夏令时间", "HKT": "香港标准时间", "LHST": "豪勋爵岛标准时间", "HNNOMX": "墨西哥西北部标准时间", "BOT": "玻利维亚标准时间", "HNOG": "格陵兰岛西部标准时间", "EST": "北美东部标准时间", "MESZ": "中欧夏令时间", "WITA": "印度尼西亚中部时间", "CAT": "中部非洲时间", "COST": "哥伦比亚夏令时间", "CHADT": "查坦夏令时间", "AST": "大西洋标准时间", "MYT": "马来西亚时间", "OESZ": "东欧夏令时间", "COT": "哥伦比亚标准时间", "SAST": "南非标准时间", "HEOG": "格陵兰岛西部夏令时间", "ACWDT": "澳大利亚中西部夏令时间", "MEZ": "中欧标准时间", "GYT": "圭亚那时间", "ADT": "大西洋夏令时间", "HEEG": "格陵兰岛东部夏令时间", "WART": "阿根廷西部标准时间", "VET": "委内瑞拉时间", "GMT": "格林尼治标准时间", "∅∅∅": "巴西利亚夏令时间", "SGT": "新加坡标准时间", "ACDT": "澳大利亚中部夏令时间", "HAT": "纽芬兰夏令时间", "HADT": "夏威夷-阿留申夏令时间", "CHAST": "查坦标准时间", "CDT": "北美中部夏令时间", "AEDT": "澳大利亚东部夏令时间", "WEZ": "西欧标准时间", "NZDT": "新西兰夏令时间", "ECT": "厄瓜多尔标准时间", "GFT": "法属圭亚那标准时间", "HKST": "香港夏令时间", "IST": "印度时间", "HNT": "纽芬兰标准时间", "ART": "阿根廷标准时间", "ChST": "查莫罗时间", "WAT": "西部非洲标准时间", "HNEG": "格陵兰岛东部标准时间", "HEPM": "圣皮埃尔和密克隆群岛夏令时间", "TMST": "土库曼斯坦夏令时间", "HAST": "夏威夷-阿留申标准时间", "WIB": "印度尼西亚西部时间", "ACST": "澳大利亚中部标准时间", "ARST": "阿根廷夏令时间"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locale returns the current translators string locale
|
||||||
|
func (zh *zh) Locale() string {
|
||||||
|
return zh.locale
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluralsCardinal returns the list of cardinal plural rules associated with 'zh'
|
||||||
|
func (zh *zh) PluralsCardinal() []locales.PluralRule {
|
||||||
|
return zh.pluralsCardinal
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluralsOrdinal returns the list of ordinal plural rules associated with 'zh'
|
||||||
|
func (zh *zh) PluralsOrdinal() []locales.PluralRule {
|
||||||
|
return zh.pluralsOrdinal
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluralsRange returns the list of range plural rules associated with 'zh'
|
||||||
|
func (zh *zh) PluralsRange() []locales.PluralRule {
|
||||||
|
return zh.pluralsRange
|
||||||
|
}
|
||||||
|
|
||||||
|
// CardinalPluralRule returns the cardinal PluralRule given 'num' and digits/precision of 'v' for 'zh'
|
||||||
|
func (zh *zh) CardinalPluralRule(num float64, v uint64) locales.PluralRule {
|
||||||
|
return locales.PluralRuleOther
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrdinalPluralRule returns the ordinal PluralRule given 'num' and digits/precision of 'v' for 'zh'
|
||||||
|
func (zh *zh) OrdinalPluralRule(num float64, v uint64) locales.PluralRule {
|
||||||
|
return locales.PluralRuleOther
|
||||||
|
}
|
||||||
|
|
||||||
|
// RangePluralRule returns the ordinal PluralRule given 'num1', 'num2' and digits/precision of 'v1' and 'v2' for 'zh'
|
||||||
|
func (zh *zh) RangePluralRule(num1 float64, v1 uint64, num2 float64, v2 uint64) locales.PluralRule {
|
||||||
|
return locales.PluralRuleOther
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonthAbbreviated returns the locales abbreviated month given the 'month' provided
|
||||||
|
func (zh *zh) MonthAbbreviated(month time.Month) string {
|
||||||
|
return zh.monthsAbbreviated[month]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonthsAbbreviated returns the locales abbreviated months
|
||||||
|
func (zh *zh) MonthsAbbreviated() []string {
|
||||||
|
return zh.monthsAbbreviated[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonthNarrow returns the locales narrow month given the 'month' provided
|
||||||
|
func (zh *zh) MonthNarrow(month time.Month) string {
|
||||||
|
return zh.monthsNarrow[month]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonthsNarrow returns the locales narrow months
|
||||||
|
func (zh *zh) MonthsNarrow() []string {
|
||||||
|
return zh.monthsNarrow[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonthWide returns the locales wide month given the 'month' provided
|
||||||
|
func (zh *zh) MonthWide(month time.Month) string {
|
||||||
|
return zh.monthsWide[month]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonthsWide returns the locales wide months
|
||||||
|
func (zh *zh) MonthsWide() []string {
|
||||||
|
return zh.monthsWide[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeekdayAbbreviated returns the locales abbreviated weekday given the 'weekday' provided
|
||||||
|
func (zh *zh) WeekdayAbbreviated(weekday time.Weekday) string {
|
||||||
|
return zh.daysAbbreviated[weekday]
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeekdaysAbbreviated returns the locales abbreviated weekdays
|
||||||
|
func (zh *zh) WeekdaysAbbreviated() []string {
|
||||||
|
return zh.daysAbbreviated
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeekdayNarrow returns the locales narrow weekday given the 'weekday' provided
|
||||||
|
func (zh *zh) WeekdayNarrow(weekday time.Weekday) string {
|
||||||
|
return zh.daysNarrow[weekday]
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeekdaysNarrow returns the locales narrow weekdays
|
||||||
|
func (zh *zh) WeekdaysNarrow() []string {
|
||||||
|
return zh.daysNarrow
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeekdayShort returns the locales short weekday given the 'weekday' provided
|
||||||
|
func (zh *zh) WeekdayShort(weekday time.Weekday) string {
|
||||||
|
return zh.daysShort[weekday]
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeekdaysShort returns the locales short weekdays
|
||||||
|
func (zh *zh) WeekdaysShort() []string {
|
||||||
|
return zh.daysShort
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeekdayWide returns the locales wide weekday given the 'weekday' provided
|
||||||
|
func (zh *zh) WeekdayWide(weekday time.Weekday) string {
|
||||||
|
return zh.daysWide[weekday]
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeekdaysWide returns the locales wide weekdays
|
||||||
|
func (zh *zh) WeekdaysWide() []string {
|
||||||
|
return zh.daysWide
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decimal returns the decimal point of number
|
||||||
|
func (zh *zh) Decimal() string {
|
||||||
|
return zh.decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group returns the group of number
|
||||||
|
func (zh *zh) Group() string {
|
||||||
|
return zh.group
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group returns the minus sign of number
|
||||||
|
func (zh *zh) Minus() string {
|
||||||
|
return zh.minus
|
||||||
|
}
|
||||||
|
|
||||||
|
// FmtNumber returns 'num' with digits/precision of 'v' for 'zh' and handles both Whole and Real numbers based on 'v'
|
||||||
|
func (zh *zh) FmtNumber(num float64, v uint64) string {
|
||||||
|
|
||||||
|
s := strconv.FormatFloat(math.Abs(num), 'f', int(v), 64)
|
||||||
|
l := len(s) + 2 + 1*len(s[:len(s)-int(v)-1])/3
|
||||||
|
count := 0
|
||||||
|
inWhole := v == 0
|
||||||
|
b := make([]byte, 0, l)
|
||||||
|
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
|
||||||
|
if s[i] == '.' {
|
||||||
|
b = append(b, zh.decimal[0])
|
||||||
|
inWhole = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if inWhole {
|
||||||
|
if count == 3 {
|
||||||
|
b = append(b, zh.group[0])
|
||||||
|
count = 1
|
||||||
|
} else {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, s[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if num < 0 {
|
||||||
|
b = append(b, zh.minus[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverse
|
||||||
|
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
b[i], b[j] = b[j], b[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FmtPercent returns 'num' with digits/precision of 'v' for 'zh' and handles both Whole and Real numbers based on 'v'
|
||||||
|
// NOTE: 'num' passed into FmtPercent is assumed to be in percent already
|
||||||
|
func (zh *zh) FmtPercent(num float64, v uint64) string {
|
||||||
|
s := strconv.FormatFloat(math.Abs(num), 'f', int(v), 64)
|
||||||
|
l := len(s) + 3
|
||||||
|
b := make([]byte, 0, l)
|
||||||
|
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
|
||||||
|
if s[i] == '.' {
|
||||||
|
b = append(b, zh.decimal[0])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, s[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if num < 0 {
|
||||||
|
b = append(b, zh.minus[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverse
|
||||||
|
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
b[i], b[j] = b[j], b[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, zh.percent...)
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FmtCurrency returns the currency representation of 'num' with digits/precision of 'v' for 'zh'
|
||||||
|
func (zh *zh) FmtCurrency(num float64, v uint64, currency currency.Type) string {
|
||||||
|
|
||||||
|
s := strconv.FormatFloat(math.Abs(num), 'f', int(v), 64)
|
||||||
|
symbol := zh.currencies[currency]
|
||||||
|
l := len(s) + len(symbol) + 2 + 1*len(s[:len(s)-int(v)-1])/3
|
||||||
|
count := 0
|
||||||
|
inWhole := v == 0
|
||||||
|
b := make([]byte, 0, l)
|
||||||
|
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
|
||||||
|
if s[i] == '.' {
|
||||||
|
b = append(b, zh.decimal[0])
|
||||||
|
inWhole = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if inWhole {
|
||||||
|
if count == 3 {
|
||||||
|
b = append(b, zh.group[0])
|
||||||
|
count = 1
|
||||||
|
} else {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, s[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := len(symbol) - 1; j >= 0; j-- {
|
||||||
|
b = append(b, symbol[j])
|
||||||
|
}
|
||||||
|
|
||||||
|
if num < 0 {
|
||||||
|
b = append(b, zh.minus[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverse
|
||||||
|
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
b[i], b[j] = b[j], b[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(v) < 2 {
|
||||||
|
|
||||||
|
if v == 0 {
|
||||||
|
b = append(b, zh.decimal...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 2-int(v); i++ {
|
||||||
|
b = append(b, '0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FmtAccounting returns the currency representation of 'num' with digits/precision of 'v' for 'zh'
|
||||||
|
// in accounting notation.
|
||||||
|
func (zh *zh) FmtAccounting(num float64, v uint64, currency currency.Type) string {
|
||||||
|
|
||||||
|
s := strconv.FormatFloat(math.Abs(num), 'f', int(v), 64)
|
||||||
|
symbol := zh.currencies[currency]
|
||||||
|
l := len(s) + len(symbol) + 2 + 1*len(s[:len(s)-int(v)-1])/3
|
||||||
|
count := 0
|
||||||
|
inWhole := v == 0
|
||||||
|
b := make([]byte, 0, l)
|
||||||
|
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
|
||||||
|
if s[i] == '.' {
|
||||||
|
b = append(b, zh.decimal[0])
|
||||||
|
inWhole = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if inWhole {
|
||||||
|
if count == 3 {
|
||||||
|
b = append(b, zh.group[0])
|
||||||
|
count = 1
|
||||||
|
} else {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, s[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if num < 0 {
|
||||||
|
|
||||||
|
for j := len(symbol) - 1; j >= 0; j-- {
|
||||||
|
b = append(b, symbol[j])
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, zh.minus[0])
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
for j := len(symbol) - 1; j >= 0; j-- {
|
||||||
|
b = append(b, symbol[j])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverse
|
||||||
|
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
b[i], b[j] = b[j], b[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(v) < 2 {
|
||||||
|
|
||||||
|
if v == 0 {
|
||||||
|
b = append(b, zh.decimal...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 2-int(v); i++ {
|
||||||
|
b = append(b, '0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FmtDateShort returns the short date representation of 't' for 'zh'
|
||||||
|
func (zh *zh) FmtDateShort(t time.Time) string {
|
||||||
|
|
||||||
|
b := make([]byte, 0, 32)
|
||||||
|
|
||||||
|
if t.Year() > 0 {
|
||||||
|
b = strconv.AppendInt(b, int64(t.Year()), 10)
|
||||||
|
} else {
|
||||||
|
b = strconv.AppendInt(b, int64(-t.Year()), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, []byte{0x2f}...)
|
||||||
|
b = strconv.AppendInt(b, int64(t.Month()), 10)
|
||||||
|
b = append(b, []byte{0x2f}...)
|
||||||
|
b = strconv.AppendInt(b, int64(t.Day()), 10)
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FmtDateMedium returns the medium date representation of 't' for 'zh'
|
||||||
|
func (zh *zh) FmtDateMedium(t time.Time) string {
|
||||||
|
|
||||||
|
b := make([]byte, 0, 32)
|
||||||
|
|
||||||
|
if t.Year() > 0 {
|
||||||
|
b = strconv.AppendInt(b, int64(t.Year()), 10)
|
||||||
|
} else {
|
||||||
|
b = strconv.AppendInt(b, int64(-t.Year()), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, []byte{0xe5, 0xb9, 0xb4}...)
|
||||||
|
b = strconv.AppendInt(b, int64(t.Month()), 10)
|
||||||
|
b = append(b, []byte{0xe6, 0x9c, 0x88}...)
|
||||||
|
b = strconv.AppendInt(b, int64(t.Day()), 10)
|
||||||
|
b = append(b, []byte{0xe6, 0x97, 0xa5}...)
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FmtDateLong returns the long date representation of 't' for 'zh'
|
||||||
|
func (zh *zh) FmtDateLong(t time.Time) string {
|
||||||
|
|
||||||
|
b := make([]byte, 0, 32)
|
||||||
|
|
||||||
|
if t.Year() > 0 {
|
||||||
|
b = strconv.AppendInt(b, int64(t.Year()), 10)
|
||||||
|
} else {
|
||||||
|
b = strconv.AppendInt(b, int64(-t.Year()), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, []byte{0xe5, 0xb9, 0xb4}...)
|
||||||
|
b = strconv.AppendInt(b, int64(t.Month()), 10)
|
||||||
|
b = append(b, []byte{0xe6, 0x9c, 0x88}...)
|
||||||
|
b = strconv.AppendInt(b, int64(t.Day()), 10)
|
||||||
|
b = append(b, []byte{0xe6, 0x97, 0xa5}...)
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FmtDateFull returns the full date representation of 't' for 'zh'
|
||||||
|
func (zh *zh) FmtDateFull(t time.Time) string {
|
||||||
|
|
||||||
|
b := make([]byte, 0, 32)
|
||||||
|
|
||||||
|
if t.Year() > 0 {
|
||||||
|
b = strconv.AppendInt(b, int64(t.Year()), 10)
|
||||||
|
} else {
|
||||||
|
b = strconv.AppendInt(b, int64(-t.Year()), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, []byte{0xe5, 0xb9, 0xb4}...)
|
||||||
|
b = strconv.AppendInt(b, int64(t.Month()), 10)
|
||||||
|
b = append(b, []byte{0xe6, 0x9c, 0x88}...)
|
||||||
|
b = strconv.AppendInt(b, int64(t.Day()), 10)
|
||||||
|
b = append(b, []byte{0xe6, 0x97, 0xa5}...)
|
||||||
|
b = append(b, zh.daysWide[t.Weekday()]...)
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FmtTimeShort returns the short time representation of 't' for 'zh'
|
||||||
|
func (zh *zh) FmtTimeShort(t time.Time) string {
|
||||||
|
|
||||||
|
b := make([]byte, 0, 32)
|
||||||
|
|
||||||
|
if t.Hour() < 12 {
|
||||||
|
b = append(b, zh.periodsAbbreviated[0]...)
|
||||||
|
} else {
|
||||||
|
b = append(b, zh.periodsAbbreviated[1]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
h := t.Hour()
|
||||||
|
|
||||||
|
if h > 12 {
|
||||||
|
h -= 12
|
||||||
|
}
|
||||||
|
|
||||||
|
b = strconv.AppendInt(b, int64(h), 10)
|
||||||
|
b = append(b, zh.timeSeparator...)
|
||||||
|
|
||||||
|
if t.Minute() < 10 {
|
||||||
|
b = append(b, '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
b = strconv.AppendInt(b, int64(t.Minute()), 10)
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FmtTimeMedium returns the medium time representation of 't' for 'zh'
|
||||||
|
func (zh *zh) FmtTimeMedium(t time.Time) string {
|
||||||
|
|
||||||
|
b := make([]byte, 0, 32)
|
||||||
|
|
||||||
|
if t.Hour() < 12 {
|
||||||
|
b = append(b, zh.periodsAbbreviated[0]...)
|
||||||
|
} else {
|
||||||
|
b = append(b, zh.periodsAbbreviated[1]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
h := t.Hour()
|
||||||
|
|
||||||
|
if h > 12 {
|
||||||
|
h -= 12
|
||||||
|
}
|
||||||
|
|
||||||
|
b = strconv.AppendInt(b, int64(h), 10)
|
||||||
|
b = append(b, zh.timeSeparator...)
|
||||||
|
|
||||||
|
if t.Minute() < 10 {
|
||||||
|
b = append(b, '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
b = strconv.AppendInt(b, int64(t.Minute()), 10)
|
||||||
|
b = append(b, zh.timeSeparator...)
|
||||||
|
|
||||||
|
if t.Second() < 10 {
|
||||||
|
b = append(b, '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
b = strconv.AppendInt(b, int64(t.Second()), 10)
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FmtTimeLong returns the long time representation of 't' for 'zh'
|
||||||
|
func (zh *zh) FmtTimeLong(t time.Time) string {
|
||||||
|
|
||||||
|
b := make([]byte, 0, 32)
|
||||||
|
|
||||||
|
tz, _ := t.Zone()
|
||||||
|
b = append(b, tz...)
|
||||||
|
|
||||||
|
b = append(b, []byte{0x20}...)
|
||||||
|
|
||||||
|
if t.Hour() < 12 {
|
||||||
|
b = append(b, zh.periodsAbbreviated[0]...)
|
||||||
|
} else {
|
||||||
|
b = append(b, zh.periodsAbbreviated[1]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
h := t.Hour()
|
||||||
|
|
||||||
|
if h > 12 {
|
||||||
|
h -= 12
|
||||||
|
}
|
||||||
|
|
||||||
|
b = strconv.AppendInt(b, int64(h), 10)
|
||||||
|
b = append(b, zh.timeSeparator...)
|
||||||
|
|
||||||
|
if t.Minute() < 10 {
|
||||||
|
b = append(b, '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
b = strconv.AppendInt(b, int64(t.Minute()), 10)
|
||||||
|
b = append(b, zh.timeSeparator...)
|
||||||
|
|
||||||
|
if t.Second() < 10 {
|
||||||
|
b = append(b, '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
b = strconv.AppendInt(b, int64(t.Second()), 10)
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FmtTimeFull returns the full time representation of 't' for 'zh'
|
||||||
|
func (zh *zh) FmtTimeFull(t time.Time) string {
|
||||||
|
|
||||||
|
b := make([]byte, 0, 32)
|
||||||
|
|
||||||
|
tz, _ := t.Zone()
|
||||||
|
|
||||||
|
if btz, ok := zh.timezones[tz]; ok {
|
||||||
|
b = append(b, btz...)
|
||||||
|
} else {
|
||||||
|
b = append(b, tz...)
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, []byte{0x20}...)
|
||||||
|
|
||||||
|
if t.Hour() < 12 {
|
||||||
|
b = append(b, zh.periodsAbbreviated[0]...)
|
||||||
|
} else {
|
||||||
|
b = append(b, zh.periodsAbbreviated[1]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
h := t.Hour()
|
||||||
|
|
||||||
|
if h > 12 {
|
||||||
|
h -= 12
|
||||||
|
}
|
||||||
|
|
||||||
|
b = strconv.AppendInt(b, int64(h), 10)
|
||||||
|
b = append(b, zh.timeSeparator...)
|
||||||
|
|
||||||
|
if t.Minute() < 10 {
|
||||||
|
b = append(b, '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
b = strconv.AppendInt(b, int64(t.Minute()), 10)
|
||||||
|
b = append(b, zh.timeSeparator...)
|
||||||
|
|
||||||
|
if t.Second() < 10 {
|
||||||
|
b = append(b, '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
b = strconv.AppendInt(b, int64(t.Second()), 10)
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
*.coverprofile
|
|
@ -0,0 +1,27 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.13.4
|
||||||
|
- tip
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
recipients: dean.karn@gmail.com
|
||||||
|
on_success: change
|
||||||
|
on_failure: always
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go install github.com/mattn/goveralls
|
||||||
|
|
||||||
|
# Only clone the most recent commit.
|
||||||
|
git:
|
||||||
|
depth: 1
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./...
|
||||||
|
|
||||||
|
after_success: |
|
||||||
|
[ $TRAVIS_GO_VERSION = 1.13.4 ] &&
|
||||||
|
goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Go Playground
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,89 @@
|
||||||
|
## universal-translator
|
||||||
|
<img align="right" src="https://raw.githubusercontent.com/go-playground/universal-translator/master/logo.png">![Project status](https://img.shields.io/badge/version-0.17.0-green.svg)
|
||||||
|
[![Build Status](https://travis-ci.org/go-playground/universal-translator.svg?branch=master)](https://travis-ci.org/go-playground/universal-translator)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/github/go-playground/universal-translator/badge.svg)](https://coveralls.io/github/go-playground/universal-translator)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/universal-translator)](https://goreportcard.com/report/github.com/go-playground/universal-translator)
|
||||||
|
[![GoDoc](https://godoc.org/github.com/go-playground/universal-translator?status.svg)](https://godoc.org/github.com/go-playground/universal-translator)
|
||||||
|
![License](https://img.shields.io/dub/l/vibe-d.svg)
|
||||||
|
[![Gitter](https://badges.gitter.im/go-playground/universal-translator.svg)](https://gitter.im/go-playground/universal-translator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||||
|
|
||||||
|
Universal Translator is an i18n Translator for Go/Golang using CLDR data + pluralization rules
|
||||||
|
|
||||||
|
Why another i18n library?
|
||||||
|
--------------------------
|
||||||
|
Because none of the plural rules seem to be correct out there, including the previous implementation of this package,
|
||||||
|
so I took it upon myself to create [locales](https://go-playground/locales) for everyone to use; this package
|
||||||
|
is a thin wrapper around [locales](https://go-playground/locales) in order to store and translate text for
|
||||||
|
use in your applications.
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
- [x] Rules generated from the [CLDR](http://cldr.unicode.org/index/downloads) data, v30.0.3
|
||||||
|
- [x] Contains Cardinal, Ordinal and Range Plural Rules
|
||||||
|
- [x] Contains Month, Weekday and Timezone translations built in
|
||||||
|
- [x] Contains Date & Time formatting functions
|
||||||
|
- [x] Contains Number, Currency, Accounting and Percent formatting functions
|
||||||
|
- [x] Supports the "Gregorian" calendar only ( my time isn't unlimited, had to draw the line somewhere )
|
||||||
|
- [x] Support loading translations from files
|
||||||
|
- [x] Exporting translations to file(s), mainly for getting them professionally translated
|
||||||
|
- [ ] Code Generation for translation files -> Go code.. i.e. after it has been professionally translated
|
||||||
|
- [ ] Tests for all languages, I need help with this, please see [here](https://go-playground/locales/issues/1)
|
||||||
|
|
||||||
|
Installation
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Use go get
|
||||||
|
|
||||||
|
```shell
|
||||||
|
go get github.com/go-playground/universal-translator
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage & Documentation
|
||||||
|
-------
|
||||||
|
|
||||||
|
Please see https://godoc.org/github.com/go-playground/universal-translator for usage docs
|
||||||
|
|
||||||
|
##### Examples:
|
||||||
|
|
||||||
|
- [Basic](https://github.com/go-playground/universal-translator/tree/master/_examples/basic)
|
||||||
|
- [Full - no files](https://github.com/go-playground/universal-translator/tree/master/_examples/full-no-files)
|
||||||
|
- [Full - with files](https://github.com/go-playground/universal-translator/tree/master/_examples/full-with-files)
|
||||||
|
|
||||||
|
File formatting
|
||||||
|
--------------
|
||||||
|
All types, Plain substitution, Cardinal, Ordinal and Range translations can all be contained withing the same file(s);
|
||||||
|
they are only separated for easy viewing.
|
||||||
|
|
||||||
|
##### Examples:
|
||||||
|
|
||||||
|
- [Formats](https://github.com/go-playground/universal-translator/tree/master/_examples/file-formats)
|
||||||
|
|
||||||
|
##### Basic Makeup
|
||||||
|
NOTE: not all fields are needed for all translation types, see [examples](https://github.com/go-playground/universal-translator/tree/master/_examples/file-formats)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"locale": "en",
|
||||||
|
"key": "days-left",
|
||||||
|
"trans": "You have {0} day left.",
|
||||||
|
"type": "Cardinal",
|
||||||
|
"rule": "One",
|
||||||
|
"override": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|Field|Description|
|
||||||
|
|---|---|
|
||||||
|
|locale|The locale for which the translation is for.|
|
||||||
|
|key|The translation key that will be used to store and lookup each translation; normally it is a string or integer.|
|
||||||
|
|trans|The actual translation text.|
|
||||||
|
|type|The type of translation Cardinal, Ordinal, Range or "" for a plain substitution(not required to be defined if plain used)|
|
||||||
|
|rule|The plural rule for which the translation is for eg. One, Two, Few, Many or Other.(not required to be defined if plain used)|
|
||||||
|
|override|If you wish to override an existing translation that has already been registered, set this to 'true'. 99% of the time there is no need to define it.|
|
||||||
|
|
||||||
|
Help With Tests
|
||||||
|
---------------
|
||||||
|
To anyone interesting in helping or contributing, I sure could use some help creating tests for each language.
|
||||||
|
Please see issue [here](https://go-playground/locales/issues/1) for details.
|
||||||
|
|
||||||
|
License
|
||||||
|
------
|
||||||
|
Distributed under MIT License, please see license file in code for more details.
|
|
@ -0,0 +1,148 @@
|
||||||
|
package ut
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gin-valid/go-playground/locales"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrUnknowTranslation indicates the translation could not be found
|
||||||
|
ErrUnknowTranslation = errors.New("Unknown Translation")
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ error = new(ErrConflictingTranslation)
|
||||||
|
var _ error = new(ErrRangeTranslation)
|
||||||
|
var _ error = new(ErrOrdinalTranslation)
|
||||||
|
var _ error = new(ErrCardinalTranslation)
|
||||||
|
var _ error = new(ErrMissingPluralTranslation)
|
||||||
|
var _ error = new(ErrExistingTranslator)
|
||||||
|
|
||||||
|
// ErrExistingTranslator is the error representing a conflicting translator
|
||||||
|
type ErrExistingTranslator struct {
|
||||||
|
locale string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns ErrExistingTranslator's internal error text
|
||||||
|
func (e *ErrExistingTranslator) Error() string {
|
||||||
|
return fmt.Sprintf("error: conflicting translator for locale '%s'", e.locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrConflictingTranslation is the error representing a conflicting translation
|
||||||
|
type ErrConflictingTranslation struct {
|
||||||
|
locale string
|
||||||
|
key interface{}
|
||||||
|
rule locales.PluralRule
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns ErrConflictingTranslation's internal error text
|
||||||
|
func (e *ErrConflictingTranslation) Error() string {
|
||||||
|
|
||||||
|
if _, ok := e.key.(string); !ok {
|
||||||
|
return fmt.Sprintf("error: conflicting key '%#v' rule '%s' with text '%s' for locale '%s', value being ignored", e.key, e.rule, e.text, e.locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("error: conflicting key '%s' rule '%s' with text '%s' for locale '%s', value being ignored", e.key, e.rule, e.text, e.locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrRangeTranslation is the error representing a range translation error
|
||||||
|
type ErrRangeTranslation struct {
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns ErrRangeTranslation's internal error text
|
||||||
|
func (e *ErrRangeTranslation) Error() string {
|
||||||
|
return e.text
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrOrdinalTranslation is the error representing an ordinal translation error
|
||||||
|
type ErrOrdinalTranslation struct {
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns ErrOrdinalTranslation's internal error text
|
||||||
|
func (e *ErrOrdinalTranslation) Error() string {
|
||||||
|
return e.text
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrCardinalTranslation is the error representing a cardinal translation error
|
||||||
|
type ErrCardinalTranslation struct {
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns ErrCardinalTranslation's internal error text
|
||||||
|
func (e *ErrCardinalTranslation) Error() string {
|
||||||
|
return e.text
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMissingPluralTranslation is the error signifying a missing translation given
|
||||||
|
// the locales plural rules.
|
||||||
|
type ErrMissingPluralTranslation struct {
|
||||||
|
locale string
|
||||||
|
key interface{}
|
||||||
|
rule locales.PluralRule
|
||||||
|
translationType string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns ErrMissingPluralTranslation's internal error text
|
||||||
|
func (e *ErrMissingPluralTranslation) Error() string {
|
||||||
|
|
||||||
|
if _, ok := e.key.(string); !ok {
|
||||||
|
return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%#v' and locale '%s'", e.translationType, e.rule, e.key, e.locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%s' and locale '%s'", e.translationType, e.rule, e.key, e.locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMissingBracket is the error representing a missing bracket in a translation
|
||||||
|
// eg. This is a {0 <-- missing ending '}'
|
||||||
|
type ErrMissingBracket struct {
|
||||||
|
locale string
|
||||||
|
key interface{}
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns ErrMissingBracket error message
|
||||||
|
func (e *ErrMissingBracket) Error() string {
|
||||||
|
return fmt.Sprintf("error: missing bracket '{}', in translation. locale: '%s' key: '%v' text: '%s'", e.locale, e.key, e.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrBadParamSyntax is the error representing a bad parameter definition in a translation
|
||||||
|
// eg. This is a {must-be-int}
|
||||||
|
type ErrBadParamSyntax struct {
|
||||||
|
locale string
|
||||||
|
param string
|
||||||
|
key interface{}
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns ErrBadParamSyntax error message
|
||||||
|
func (e *ErrBadParamSyntax) Error() string {
|
||||||
|
return fmt.Sprintf("error: bad parameter syntax, missing parameter '%s' in translation. locale: '%s' key: '%v' text: '%s'", e.param, e.locale, e.key, e.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// import/export errors
|
||||||
|
|
||||||
|
// ErrMissingLocale is the error representing an expected locale that could
|
||||||
|
// not be found aka locale not registered with the UniversalTranslator Instance
|
||||||
|
type ErrMissingLocale struct {
|
||||||
|
locale string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns ErrMissingLocale's internal error text
|
||||||
|
func (e *ErrMissingLocale) Error() string {
|
||||||
|
return fmt.Sprintf("error: locale '%s' not registered.", e.locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrBadPluralDefinition is the error representing an incorrect plural definition
|
||||||
|
// usually found within translations defined within files during the import process.
|
||||||
|
type ErrBadPluralDefinition struct {
|
||||||
|
tl translation
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns ErrBadPluralDefinition's internal error text
|
||||||
|
func (e *ErrBadPluralDefinition) Error() string {
|
||||||
|
return fmt.Sprintf("error: bad plural definition '%#v'", e.tl)
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||||
|
go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
@ -0,0 +1,274 @@
|
||||||
|
package ut
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"go-playground/locales"
|
||||||
|
)
|
||||||
|
|
||||||
|
type translation struct {
|
||||||
|
Locale string `json:"locale"`
|
||||||
|
Key interface{} `json:"key"` // either string or integer
|
||||||
|
Translation string `json:"trans"`
|
||||||
|
PluralType string `json:"type,omitempty"`
|
||||||
|
PluralRule string `json:"rule,omitempty"`
|
||||||
|
OverrideExisting bool `json:"override,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
cardinalType = "Cardinal"
|
||||||
|
ordinalType = "Ordinal"
|
||||||
|
rangeType = "Range"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ImportExportFormat is the format of the file import or export
|
||||||
|
type ImportExportFormat uint8
|
||||||
|
|
||||||
|
// supported Export Formats
|
||||||
|
const (
|
||||||
|
FormatJSON ImportExportFormat = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
// Export writes the translations out to a file on disk.
|
||||||
|
//
|
||||||
|
// NOTE: this currently only works with string or int translations keys.
|
||||||
|
func (t *UniversalTranslator) Export(format ImportExportFormat, dirname string) error {
|
||||||
|
|
||||||
|
_, err := os.Stat(dirname)
|
||||||
|
fmt.Println(dirname, err, os.IsNotExist(err))
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.MkdirAll(dirname, 0744); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build up translations
|
||||||
|
var trans []translation
|
||||||
|
var b []byte
|
||||||
|
var ext string
|
||||||
|
|
||||||
|
for _, locale := range t.translators {
|
||||||
|
|
||||||
|
for k, v := range locale.(*translator).translations {
|
||||||
|
trans = append(trans, translation{
|
||||||
|
Locale: locale.Locale(),
|
||||||
|
Key: k,
|
||||||
|
Translation: v.text,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, pluralTrans := range locale.(*translator).cardinalTanslations {
|
||||||
|
|
||||||
|
for i, plural := range pluralTrans {
|
||||||
|
|
||||||
|
// leave enough for all plural rules
|
||||||
|
// but not all are set for all languages.
|
||||||
|
if plural == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
trans = append(trans, translation{
|
||||||
|
Locale: locale.Locale(),
|
||||||
|
Key: k.(string),
|
||||||
|
Translation: plural.text,
|
||||||
|
PluralType: cardinalType,
|
||||||
|
PluralRule: locales.PluralRule(i).String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, pluralTrans := range locale.(*translator).ordinalTanslations {
|
||||||
|
|
||||||
|
for i, plural := range pluralTrans {
|
||||||
|
|
||||||
|
// leave enough for all plural rules
|
||||||
|
// but not all are set for all languages.
|
||||||
|
if plural == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
trans = append(trans, translation{
|
||||||
|
Locale: locale.Locale(),
|
||||||
|
Key: k.(string),
|
||||||
|
Translation: plural.text,
|
||||||
|
PluralType: ordinalType,
|
||||||
|
PluralRule: locales.PluralRule(i).String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, pluralTrans := range locale.(*translator).rangeTanslations {
|
||||||
|
|
||||||
|
for i, plural := range pluralTrans {
|
||||||
|
|
||||||
|
// leave enough for all plural rules
|
||||||
|
// but not all are set for all languages.
|
||||||
|
if plural == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
trans = append(trans, translation{
|
||||||
|
Locale: locale.Locale(),
|
||||||
|
Key: k.(string),
|
||||||
|
Translation: plural.text,
|
||||||
|
PluralType: rangeType,
|
||||||
|
PluralRule: locales.PluralRule(i).String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch format {
|
||||||
|
case FormatJSON:
|
||||||
|
b, err = json.MarshalIndent(trans, "", " ")
|
||||||
|
ext = ".json"
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(filepath.Join(dirname, fmt.Sprintf("%s%s", locale.Locale(), ext)), b, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
trans = trans[0:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import reads the translations out of a file or directory on disk.
|
||||||
|
//
|
||||||
|
// NOTE: this currently only works with string or int translations keys.
|
||||||
|
func (t *UniversalTranslator) Import(format ImportExportFormat, dirnameOrFilename string) error {
|
||||||
|
|
||||||
|
fi, err := os.Stat(dirnameOrFilename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
processFn := func(filename string) error {
|
||||||
|
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
return t.ImportByReader(format, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fi.IsDir() {
|
||||||
|
return processFn(dirnameOrFilename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// recursively go through directory
|
||||||
|
walker := func(path string, info os.FileInfo, err error) error {
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch format {
|
||||||
|
case FormatJSON:
|
||||||
|
// skip non JSON files
|
||||||
|
if filepath.Ext(info.Name()) != ".json" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return processFn(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Walk(dirnameOrFilename, walker)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportByReader imports the the translations found within the contents read from the supplied reader.
|
||||||
|
//
|
||||||
|
// NOTE: generally used when assets have been embedded into the binary and are already in memory.
|
||||||
|
func (t *UniversalTranslator) ImportByReader(format ImportExportFormat, reader io.Reader) error {
|
||||||
|
|
||||||
|
b, err := ioutil.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var trans []translation
|
||||||
|
|
||||||
|
switch format {
|
||||||
|
case FormatJSON:
|
||||||
|
err = json.Unmarshal(b, &trans)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tl := range trans {
|
||||||
|
|
||||||
|
locale, found := t.FindTranslator(tl.Locale)
|
||||||
|
if !found {
|
||||||
|
return &ErrMissingLocale{locale: tl.Locale}
|
||||||
|
}
|
||||||
|
|
||||||
|
pr := stringToPR(tl.PluralRule)
|
||||||
|
|
||||||
|
if pr == locales.PluralRuleUnknown {
|
||||||
|
|
||||||
|
err = locale.Add(tl.Key, tl.Translation, tl.OverrideExisting)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tl.PluralType {
|
||||||
|
case cardinalType:
|
||||||
|
err = locale.AddCardinal(tl.Key, tl.Translation, pr, tl.OverrideExisting)
|
||||||
|
case ordinalType:
|
||||||
|
err = locale.AddOrdinal(tl.Key, tl.Translation, pr, tl.OverrideExisting)
|
||||||
|
case rangeType:
|
||||||
|
err = locale.AddRange(tl.Key, tl.Translation, pr, tl.OverrideExisting)
|
||||||
|
default:
|
||||||
|
return &ErrBadPluralDefinition{tl: tl}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringToPR(s string) locales.PluralRule {
|
||||||
|
|
||||||
|
switch s {
|
||||||
|
case "One":
|
||||||
|
return locales.PluralRuleOne
|
||||||
|
case "Two":
|
||||||
|
return locales.PluralRuleTwo
|
||||||
|
case "Few":
|
||||||
|
return locales.PluralRuleFew
|
||||||
|
case "Many":
|
||||||
|
return locales.PluralRuleMany
|
||||||
|
case "Other":
|
||||||
|
return locales.PluralRuleOther
|
||||||
|
default:
|
||||||
|
return locales.PluralRuleUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
|
@ -0,0 +1,420 @@
|
||||||
|
package ut
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gin-valid/go-playground/locales"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
paramZero = "{0}"
|
||||||
|
paramOne = "{1}"
|
||||||
|
unknownTranslation = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
// Translator is universal translators
|
||||||
|
// translator instance which is a thin wrapper
|
||||||
|
// around locales.Translator instance providing
|
||||||
|
// some extra functionality
|
||||||
|
type Translator interface {
|
||||||
|
locales.Translator
|
||||||
|
|
||||||
|
// adds a normal translation for a particular language/locale
|
||||||
|
// {#} is the only replacement type accepted and are ad infinitum
|
||||||
|
// eg. one: '{0} day left' other: '{0} days left'
|
||||||
|
Add(key interface{}, text string, override bool) error
|
||||||
|
|
||||||
|
// adds a cardinal plural translation for a particular language/locale
|
||||||
|
// {0} is the only replacement type accepted and only one variable is accepted as
|
||||||
|
// multiple cannot be used for a plural rule determination, unless it is a range;
|
||||||
|
// see AddRange below.
|
||||||
|
// eg. in locale 'en' one: '{0} day left' other: '{0} days left'
|
||||||
|
AddCardinal(key interface{}, text string, rule locales.PluralRule, override bool) error
|
||||||
|
|
||||||
|
// adds an ordinal plural translation for a particular language/locale
|
||||||
|
// {0} is the only replacement type accepted and only one variable is accepted as
|
||||||
|
// multiple cannot be used for a plural rule determination, unless it is a range;
|
||||||
|
// see AddRange below.
|
||||||
|
// eg. in locale 'en' one: '{0}st day of spring' other: '{0}nd day of spring'
|
||||||
|
// - 1st, 2nd, 3rd...
|
||||||
|
AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error
|
||||||
|
|
||||||
|
// adds a range plural translation for a particular language/locale
|
||||||
|
// {0} and {1} are the only replacement types accepted and only these are accepted.
|
||||||
|
// eg. in locale 'nl' one: '{0}-{1} day left' other: '{0}-{1} days left'
|
||||||
|
AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error
|
||||||
|
|
||||||
|
// creates the translation for the locale given the 'key' and params passed in
|
||||||
|
T(key interface{}, params ...string) (string, error)
|
||||||
|
|
||||||
|
// creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments
|
||||||
|
// and param passed in
|
||||||
|
C(key interface{}, num float64, digits uint64, param string) (string, error)
|
||||||
|
|
||||||
|
// creates the ordinal translation for the locale given the 'key', 'num' and 'digit' arguments
|
||||||
|
// and param passed in
|
||||||
|
O(key interface{}, num float64, digits uint64, param string) (string, error)
|
||||||
|
|
||||||
|
// creates the range translation for the locale given the 'key', 'num1', 'digit1', 'num2' and
|
||||||
|
// 'digit2' arguments and 'param1' and 'param2' passed in
|
||||||
|
R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) (string, error)
|
||||||
|
|
||||||
|
// VerifyTranslations checks to ensures that no plural rules have been
|
||||||
|
// missed within the translations.
|
||||||
|
VerifyTranslations() error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Translator = new(translator)
|
||||||
|
var _ locales.Translator = new(translator)
|
||||||
|
|
||||||
|
type translator struct {
|
||||||
|
locales.Translator
|
||||||
|
translations map[interface{}]*transText
|
||||||
|
cardinalTanslations map[interface{}][]*transText // array index is mapped to locales.PluralRule index + the locales.PluralRuleUnknown
|
||||||
|
ordinalTanslations map[interface{}][]*transText
|
||||||
|
rangeTanslations map[interface{}][]*transText
|
||||||
|
}
|
||||||
|
|
||||||
|
type transText struct {
|
||||||
|
text string
|
||||||
|
indexes []int //记录每个占位符的始末位置,也就是 { 和 } 的位置
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTranslator(trans locales.Translator) Translator {
|
||||||
|
return &translator{
|
||||||
|
Translator: trans,
|
||||||
|
translations: make(map[interface{}]*transText), // translation text broken up by byte index
|
||||||
|
cardinalTanslations: make(map[interface{}][]*transText),
|
||||||
|
ordinalTanslations: make(map[interface{}][]*transText),
|
||||||
|
rangeTanslations: make(map[interface{}][]*transText),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a normal translation for a particular language/locale
|
||||||
|
// {#} is the only replacement type accepted and are ad infinitum
|
||||||
|
// eg. one: '{0} day left' other: '{0} days left'
|
||||||
|
func (t *translator) Add(key interface{}, text string, override bool) error {
|
||||||
|
|
||||||
|
if _, ok := t.translations[key]; ok && !override {
|
||||||
|
return &ErrConflictingTranslation{locale: t.Locale(), key: key, text: text}
|
||||||
|
}
|
||||||
|
|
||||||
|
lb := strings.Count(text, "{")
|
||||||
|
rb := strings.Count(text, "}")
|
||||||
|
|
||||||
|
if lb != rb {
|
||||||
|
return &ErrMissingBracket{locale: t.Locale(), key: key, text: text}
|
||||||
|
}
|
||||||
|
|
||||||
|
trans := &transText{
|
||||||
|
text: text,
|
||||||
|
}
|
||||||
|
|
||||||
|
var idx int
|
||||||
|
|
||||||
|
for i := 0; i < lb; i++ {
|
||||||
|
s := "{" + strconv.Itoa(i) + "}"
|
||||||
|
idx = strings.Index(text, s)
|
||||||
|
if idx == -1 {
|
||||||
|
return &ErrBadParamSyntax{locale: t.Locale(), param: s, key: key, text: text}
|
||||||
|
}
|
||||||
|
|
||||||
|
trans.indexes = append(trans.indexes, idx)
|
||||||
|
trans.indexes = append(trans.indexes, idx+len(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
t.translations[key] = trans
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCardinal adds a cardinal plural translation for a particular language/locale
|
||||||
|
// {0} is the only replacement type accepted and only one variable is accepted as
|
||||||
|
// multiple cannot be used for a plural rule determination, unless it is a range;
|
||||||
|
// see AddRange below.
|
||||||
|
// eg. in locale 'en' one: '{0} day left' other: '{0} days left'
|
||||||
|
func (t *translator) AddCardinal(key interface{}, text string, rule locales.PluralRule, override bool) error {
|
||||||
|
|
||||||
|
var verified bool
|
||||||
|
|
||||||
|
// verify plural rule exists for locale
|
||||||
|
for _, pr := range t.PluralsCardinal() {
|
||||||
|
if pr == rule {
|
||||||
|
verified = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !verified {
|
||||||
|
return &ErrCardinalTranslation{text: fmt.Sprintf("error: cardinal plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)}
|
||||||
|
}
|
||||||
|
|
||||||
|
tarr, ok := t.cardinalTanslations[key]
|
||||||
|
if ok {
|
||||||
|
// verify not adding a conflicting record
|
||||||
|
if len(tarr) > 0 && tarr[rule] != nil && !override {
|
||||||
|
return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
tarr = make([]*transText, 7, 7)
|
||||||
|
t.cardinalTanslations[key] = tarr
|
||||||
|
}
|
||||||
|
|
||||||
|
trans := &transText{
|
||||||
|
text: text,
|
||||||
|
indexes: make([]int, 2, 2),
|
||||||
|
}
|
||||||
|
|
||||||
|
tarr[rule] = trans
|
||||||
|
|
||||||
|
idx := strings.Index(text, paramZero)
|
||||||
|
if idx == -1 {
|
||||||
|
tarr[rule] = nil
|
||||||
|
return &ErrCardinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddCardinal'. locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)}
|
||||||
|
}
|
||||||
|
|
||||||
|
trans.indexes[0] = idx
|
||||||
|
trans.indexes[1] = idx + len(paramZero)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddOrdinal adds an ordinal plural translation for a particular language/locale
|
||||||
|
// {0} is the only replacement type accepted and only one variable is accepted as
|
||||||
|
// multiple cannot be used for a plural rule determination, unless it is a range;
|
||||||
|
// see AddRange below.
|
||||||
|
// eg. in locale 'en' one: '{0}st day of spring' other: '{0}nd day of spring' - 1st, 2nd, 3rd...
|
||||||
|
func (t *translator) AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error {
|
||||||
|
|
||||||
|
var verified bool
|
||||||
|
|
||||||
|
// verify plural rule exists for locale
|
||||||
|
for _, pr := range t.PluralsOrdinal() {
|
||||||
|
if pr == rule {
|
||||||
|
verified = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !verified {
|
||||||
|
return &ErrOrdinalTranslation{text: fmt.Sprintf("error: ordinal plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)}
|
||||||
|
}
|
||||||
|
|
||||||
|
tarr, ok := t.ordinalTanslations[key]
|
||||||
|
if ok {
|
||||||
|
// verify not adding a conflicting record
|
||||||
|
if len(tarr) > 0 && tarr[rule] != nil && !override {
|
||||||
|
return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
tarr = make([]*transText, 7, 7)
|
||||||
|
t.ordinalTanslations[key] = tarr
|
||||||
|
}
|
||||||
|
|
||||||
|
trans := &transText{
|
||||||
|
text: text,
|
||||||
|
indexes: make([]int, 2, 2),
|
||||||
|
}
|
||||||
|
|
||||||
|
tarr[rule] = trans
|
||||||
|
|
||||||
|
idx := strings.Index(text, paramZero)
|
||||||
|
if idx == -1 {
|
||||||
|
tarr[rule] = nil
|
||||||
|
return &ErrOrdinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddOrdinal'. locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)}
|
||||||
|
}
|
||||||
|
|
||||||
|
trans.indexes[0] = idx
|
||||||
|
trans.indexes[1] = idx + len(paramZero)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRange adds a range plural translation for a particular language/locale
|
||||||
|
// {0} and {1} are the only replacement types accepted and only these are accepted.
|
||||||
|
// eg. in locale 'nl' one: '{0}-{1} day left' other: '{0}-{1} days left'
|
||||||
|
func (t *translator) AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error {
|
||||||
|
|
||||||
|
var verified bool
|
||||||
|
|
||||||
|
// verify plural rule exists for locale
|
||||||
|
for _, pr := range t.PluralsRange() {
|
||||||
|
if pr == rule {
|
||||||
|
verified = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !verified {
|
||||||
|
return &ErrRangeTranslation{text: fmt.Sprintf("error: range plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)}
|
||||||
|
}
|
||||||
|
|
||||||
|
tarr, ok := t.rangeTanslations[key]
|
||||||
|
if ok {
|
||||||
|
// verify not adding a conflicting record
|
||||||
|
if len(tarr) > 0 && tarr[rule] != nil && !override {
|
||||||
|
return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
tarr = make([]*transText, 7, 7)
|
||||||
|
t.rangeTanslations[key] = tarr
|
||||||
|
}
|
||||||
|
|
||||||
|
trans := &transText{
|
||||||
|
text: text,
|
||||||
|
indexes: make([]int, 4, 4),
|
||||||
|
}
|
||||||
|
|
||||||
|
tarr[rule] = trans
|
||||||
|
|
||||||
|
idx := strings.Index(text, paramZero)
|
||||||
|
if idx == -1 {
|
||||||
|
tarr[rule] = nil
|
||||||
|
return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, are you sure you're adding a Range Translation? locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)}
|
||||||
|
}
|
||||||
|
|
||||||
|
trans.indexes[0] = idx
|
||||||
|
trans.indexes[1] = idx + len(paramZero)
|
||||||
|
|
||||||
|
idx = strings.Index(text, paramOne)
|
||||||
|
if idx == -1 {
|
||||||
|
tarr[rule] = nil
|
||||||
|
return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, a Range Translation requires two parameters. locale: '%s' key: '%v' text: '%s'", paramOne, t.Locale(), key, text)}
|
||||||
|
}
|
||||||
|
|
||||||
|
trans.indexes[2] = idx
|
||||||
|
trans.indexes[3] = idx + len(paramOne)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// T creates the translation for the locale given the 'key' and params passed in
|
||||||
|
func (t *translator) T(key interface{}, params ...string) (string, error) {
|
||||||
|
|
||||||
|
trans, ok := t.translations[key]
|
||||||
|
if !ok {
|
||||||
|
return unknownTranslation, ErrUnknowTranslation
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make([]byte, 0, 64)
|
||||||
|
|
||||||
|
var start, end, count int
|
||||||
|
|
||||||
|
for i := 0; i < len(trans.indexes); i++ {
|
||||||
|
end = trans.indexes[i]
|
||||||
|
b = append(b, trans.text[start:end]...)
|
||||||
|
b = append(b, params[count]...)
|
||||||
|
i++
|
||||||
|
start = trans.indexes[i]
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, trans.text[start:]...)
|
||||||
|
|
||||||
|
return string(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// C creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in
|
||||||
|
func (t *translator) C(key interface{}, num float64, digits uint64, param string) (string, error) {
|
||||||
|
|
||||||
|
tarr, ok := t.cardinalTanslations[key]
|
||||||
|
if !ok {
|
||||||
|
return unknownTranslation, ErrUnknowTranslation
|
||||||
|
}
|
||||||
|
|
||||||
|
rule := t.CardinalPluralRule(num, digits)
|
||||||
|
|
||||||
|
trans := tarr[rule]
|
||||||
|
|
||||||
|
b := make([]byte, 0, 64)
|
||||||
|
b = append(b, trans.text[:trans.indexes[0]]...)
|
||||||
|
b = append(b, param...)
|
||||||
|
b = append(b, trans.text[trans.indexes[1]:]...)
|
||||||
|
|
||||||
|
return string(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// O creates the ordinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in
|
||||||
|
func (t *translator) O(key interface{}, num float64, digits uint64, param string) (string, error) {
|
||||||
|
|
||||||
|
tarr, ok := t.ordinalTanslations[key]
|
||||||
|
if !ok {
|
||||||
|
return unknownTranslation, ErrUnknowTranslation
|
||||||
|
}
|
||||||
|
|
||||||
|
rule := t.OrdinalPluralRule(num, digits)
|
||||||
|
|
||||||
|
trans := tarr[rule]
|
||||||
|
|
||||||
|
b := make([]byte, 0, 64)
|
||||||
|
b = append(b, trans.text[:trans.indexes[0]]...)
|
||||||
|
b = append(b, param...)
|
||||||
|
b = append(b, trans.text[trans.indexes[1]:]...)
|
||||||
|
|
||||||
|
return string(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// R creates the range translation for the locale given the 'key', 'num1', 'digit1', 'num2' and 'digit2' arguments
|
||||||
|
// and 'param1' and 'param2' passed in
|
||||||
|
func (t *translator) R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) (string, error) {
|
||||||
|
|
||||||
|
tarr, ok := t.rangeTanslations[key]
|
||||||
|
if !ok {
|
||||||
|
return unknownTranslation, ErrUnknowTranslation
|
||||||
|
}
|
||||||
|
|
||||||
|
rule := t.RangePluralRule(num1, digits1, num2, digits2)
|
||||||
|
|
||||||
|
trans := tarr[rule]
|
||||||
|
|
||||||
|
b := make([]byte, 0, 64)
|
||||||
|
b = append(b, trans.text[:trans.indexes[0]]...)
|
||||||
|
b = append(b, param1...)
|
||||||
|
b = append(b, trans.text[trans.indexes[1]:trans.indexes[2]]...)
|
||||||
|
b = append(b, param2...)
|
||||||
|
b = append(b, trans.text[trans.indexes[3]:]...)
|
||||||
|
|
||||||
|
return string(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyTranslations checks to ensures that no plural rules have been
|
||||||
|
// missed within the translations.
|
||||||
|
func (t *translator) VerifyTranslations() error {
|
||||||
|
|
||||||
|
for k, v := range t.cardinalTanslations {
|
||||||
|
|
||||||
|
for _, rule := range t.PluralsCardinal() {
|
||||||
|
|
||||||
|
if v[rule] == nil {
|
||||||
|
return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "plural", rule: rule, key: k}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range t.ordinalTanslations {
|
||||||
|
|
||||||
|
for _, rule := range t.PluralsOrdinal() {
|
||||||
|
|
||||||
|
if v[rule] == nil {
|
||||||
|
return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "ordinal", rule: rule, key: k}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range t.rangeTanslations {
|
||||||
|
|
||||||
|
for _, rule := range t.PluralsRange() {
|
||||||
|
|
||||||
|
if v[rule] == nil {
|
||||||
|
return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "range", rule: rule, key: k}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
package ut
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go-playground/locales"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UniversalTranslator holds all locale & translation data
|
||||||
|
type UniversalTranslator struct {
|
||||||
|
translators map[string]Translator
|
||||||
|
fallback Translator
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new UniversalTranslator instance set with
|
||||||
|
// the fallback locale and locales it should support
|
||||||
|
func New(fallback locales.Translator, supportedLocales ...locales.Translator) *UniversalTranslator {
|
||||||
|
|
||||||
|
t := &UniversalTranslator{
|
||||||
|
translators: make(map[string]Translator),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range supportedLocales {
|
||||||
|
|
||||||
|
trans := newTranslator(v)
|
||||||
|
t.translators[strings.ToLower(trans.Locale())] = trans
|
||||||
|
|
||||||
|
if fallback.Locale() == v.Locale() {
|
||||||
|
t.fallback = trans
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.fallback == nil && fallback != nil {
|
||||||
|
t.fallback = newTranslator(fallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindTranslator trys to find a Translator based on an array of locales
|
||||||
|
// and returns the first one it can find, otherwise returns the
|
||||||
|
// fallback translator.
|
||||||
|
func (t *UniversalTranslator) FindTranslator(locales ...string) (trans Translator, found bool) {
|
||||||
|
|
||||||
|
for _, locale := range locales {
|
||||||
|
|
||||||
|
if trans, found = t.translators[strings.ToLower(locale)]; found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.fallback, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTranslator returns the specified translator for the given locale,
|
||||||
|
// or fallback if not found
|
||||||
|
func (t *UniversalTranslator) GetTranslator(locale string) (trans Translator, found bool) {
|
||||||
|
|
||||||
|
if trans, found = t.translators[strings.ToLower(locale)]; found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.fallback, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFallback returns the fallback locale
|
||||||
|
func (t *UniversalTranslator) GetFallback() Translator {
|
||||||
|
return t.fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTranslator adds the supplied translator, if it already exists the override param
|
||||||
|
// will be checked and if false an error will be returned, otherwise the translator will be
|
||||||
|
// overridden; if the fallback matches the supplied translator it will be overridden as well
|
||||||
|
// NOTE: this is normally only used when translator is embedded within a library
|
||||||
|
func (t *UniversalTranslator) AddTranslator(translator locales.Translator, override bool) error {
|
||||||
|
|
||||||
|
lc := strings.ToLower(translator.Locale())
|
||||||
|
_, ok := t.translators[lc]
|
||||||
|
if ok && !override {
|
||||||
|
return &ErrExistingTranslator{locale: translator.Locale()}
|
||||||
|
}
|
||||||
|
|
||||||
|
trans := newTranslator(translator)
|
||||||
|
|
||||||
|
if t.fallback.Locale() == translator.Locale() {
|
||||||
|
|
||||||
|
// because it's optional to have a fallback, I don't impose that limitation
|
||||||
|
// don't know why you wouldn't but...
|
||||||
|
if !override {
|
||||||
|
return &ErrExistingTranslator{locale: translator.Locale()}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.fallback = trans
|
||||||
|
}
|
||||||
|
|
||||||
|
t.translators[lc] = trans
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyTranslations runs through all locales and identifies any issues
|
||||||
|
// eg. missing plural rules for a locale
|
||||||
|
func (t *UniversalTranslator) VerifyTranslations() (err error) {
|
||||||
|
|
||||||
|
for _, trans := range t.translators {
|
||||||
|
err = trans.VerifyTranslations()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Contribution Guidelines
|
||||||
|
|
||||||
|
## Quality Standard
|
||||||
|
|
||||||
|
To ensure the continued stability of this package, tests are required to be written or already exist in order for a pull request to be merged.
|
||||||
|
|
||||||
|
## Reporting issues
|
||||||
|
|
||||||
|
Please open an issue or join the gitter chat [![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) for any issues, questions or possible enhancements to the package.
|
|
@ -0,0 +1,13 @@
|
||||||
|
### Package version eg. v8, v9:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Issue, Question or Enhancement:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Code sample, to showcase or reproduce:
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
```
|
|
@ -0,0 +1,13 @@
|
||||||
|
Fixes Or Enhances # .
|
||||||
|
|
||||||
|
**Make sure that you've checked the boxes below before you submit PR:**
|
||||||
|
- [ ] Tests exist or have been written that cover this particular change.
|
||||||
|
|
||||||
|
Change Details:
|
||||||
|
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-
|
||||||
|
|
||||||
|
|
||||||
|
@go-playground/admins
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
bin
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
*.test
|
||||||
|
*.out
|
||||||
|
*.txt
|
||||||
|
cover.html
|
||||||
|
README.html
|
|
@ -0,0 +1,29 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.15.2
|
||||||
|
- tip
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
recipients: dean.karn@gmail.com
|
||||||
|
on_success: change
|
||||||
|
on_failure: always
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go install github.com/mattn/goveralls
|
||||||
|
- mkdir -p $GOPATH/src/gopkg.in
|
||||||
|
- ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/validator.v9
|
||||||
|
|
||||||
|
# Only clone the most recent commit.
|
||||||
|
git:
|
||||||
|
depth: 1
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./...
|
||||||
|
|
||||||
|
after_success: |
|
||||||
|
[ $TRAVIS_GO_VERSION = 1.15.2 ] &&
|
||||||
|
goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN
|
|
@ -0,0 +1,22 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Dean Karn
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
GOCMD=GO111MODULE=on go
|
||||||
|
|
||||||
|
linters-install:
|
||||||
|
@golangci-lint --version >/dev/null 2>&1 || { \
|
||||||
|
echo "installing linting tools..."; \
|
||||||
|
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.21.0; \
|
||||||
|
}
|
||||||
|
|
||||||
|
lint: linters-install
|
||||||
|
$(PWD)/bin/golangci-lint run
|
||||||
|
|
||||||
|
test:
|
||||||
|
$(GOCMD) test -cover -race ./...
|
||||||
|
|
||||||
|
bench:
|
||||||
|
$(GOCMD) test -bench=. -benchmem ./...
|
||||||
|
|
||||||
|
.PHONY: test lint linters-install
|
|
@ -0,0 +1,299 @@
|
||||||
|
Package validator
|
||||||
|
================
|
||||||
|
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
![Project status](https://img.shields.io/badge/version-10.4.1-green.svg)
|
||||||
|
[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
|
||||||
|
[![GoDoc](https://godoc.org/github.com/go-playground/validator?status.svg)](https://pkg.go.dev/gin-valid/go-playground/validator/v10)
|
||||||
|
![License](https://img.shields.io/dub/l/vibe-d.svg)
|
||||||
|
|
||||||
|
Package validator implements value validations for structs and individual fields based on tags.
|
||||||
|
|
||||||
|
It has the following **unique** features:
|
||||||
|
|
||||||
|
- Cross Field and Cross Struct validations by using validation tags or custom validators.
|
||||||
|
- Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated.
|
||||||
|
- Ability to dive into both map keys and values for validation
|
||||||
|
- Handles type interface by determining it's underlying type prior to validation.
|
||||||
|
- Handles custom field types such as sql driver Valuer see [Valuer](https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29)
|
||||||
|
- Alias validation tags, which allows for mapping of several validations to a single tag for easier defining of validations on structs
|
||||||
|
- Extraction of custom defined Field Name e.g. can specify to extract the JSON name while validating and have it available in the resulting FieldError
|
||||||
|
- Customizable i18n aware error messages.
|
||||||
|
- Default validator for the [gin](https://github.com/gin-gonic/gin) web framework; upgrading from v8 to v9 in gin see [here](https://github.com/go-playground/validator/tree/master/_examples/gin-upgrading-overriding)
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
Use go get.
|
||||||
|
|
||||||
|
go get gin-valid/go-playground/validator/v10
|
||||||
|
|
||||||
|
Then import the validator package into your own code.
|
||||||
|
|
||||||
|
import "gin-valid/go-playground/validator/v10"
|
||||||
|
|
||||||
|
Error Return Value
|
||||||
|
-------
|
||||||
|
|
||||||
|
Validation functions return type error
|
||||||
|
|
||||||
|
They return type error to avoid the issue discussed in the following, where err is always != nil:
|
||||||
|
|
||||||
|
* http://stackoverflow.com/a/29138676/3158232
|
||||||
|
* https://github.com/go-playground/validator/issues/134
|
||||||
|
|
||||||
|
Validator only InvalidValidationError for bad validation input, nil or ValidationErrors as type error; so, in your code all you need to do is check if the error returned is not nil, and if it's not check if error is InvalidValidationError ( if necessary, most of the time it isn't ) type cast it to type ValidationErrors like so:
|
||||||
|
|
||||||
|
```go
|
||||||
|
err := validate.Struct(mystruct)
|
||||||
|
validationErrors := err.(validator.ValidationErrors)
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage and documentation
|
||||||
|
------
|
||||||
|
|
||||||
|
Please see https://godoc.org/github.com/go-playground/validator for detailed usage docs.
|
||||||
|
|
||||||
|
##### Examples:
|
||||||
|
|
||||||
|
- [Simple](https://github.com/go-playground/validator/blob/master/_examples/simple/main.go)
|
||||||
|
- [Custom Field Types](https://github.com/go-playground/validator/blob/master/_examples/custom/main.go)
|
||||||
|
- [Struct Level](https://github.com/go-playground/validator/blob/master/_examples/struct-level/main.go)
|
||||||
|
- [Translations & Custom Errors](https://github.com/go-playground/validator/blob/master/_examples/translations/main.go)
|
||||||
|
- [Gin upgrade and/or override validator](https://github.com/go-playground/validator/tree/v9/_examples/gin-upgrading-overriding)
|
||||||
|
- [wash - an example application putting it all together](https://github.com/bluesuncorp/wash)
|
||||||
|
|
||||||
|
Baked-in Validations
|
||||||
|
------
|
||||||
|
|
||||||
|
### Fields:
|
||||||
|
|
||||||
|
| Tag | Description |
|
||||||
|
| - | - |
|
||||||
|
| eqcsfield | Field Equals Another Field (relative)|
|
||||||
|
| eqfield | Field Equals Another Field |
|
||||||
|
| fieldcontains | NOT DOCUMENTED IN doc.go |
|
||||||
|
| fieldexcludes | NOT DOCUMENTED IN doc.go |
|
||||||
|
| gtcsfield | Field Greater Than Another Relative Field |
|
||||||
|
| gtecsfield | Field Greater Than or Equal To Another Relative Field |
|
||||||
|
| gtefield | Field Greater Than or Equal To Another Field |
|
||||||
|
| gtfield | Field Greater Than Another Field |
|
||||||
|
| ltcsfield | Less Than Another Relative Field |
|
||||||
|
| ltecsfield | Less Than or Equal To Another Relative Field |
|
||||||
|
| ltefield | Less Than or Equal To Another Field |
|
||||||
|
| ltfield | Less Than Another Field |
|
||||||
|
| necsfield | Field Does Not Equal Another Field (relative) |
|
||||||
|
| nefield | Field Does Not Equal Another Field |
|
||||||
|
|
||||||
|
### Network:
|
||||||
|
|
||||||
|
| Tag | Description |
|
||||||
|
| - | - |
|
||||||
|
| cidr | Classless Inter-Domain Routing CIDR |
|
||||||
|
| cidrv4 | Classless Inter-Domain Routing CIDRv4 |
|
||||||
|
| cidrv6 | Classless Inter-Domain Routing CIDRv6 |
|
||||||
|
| datauri | Data URL |
|
||||||
|
| fqdn | Full Qualified Domain Name (FQDN) |
|
||||||
|
| hostname | Hostname RFC 952 |
|
||||||
|
| hostname_port | HostPort |
|
||||||
|
| hostname_rfc1123 | Hostname RFC 1123 |
|
||||||
|
| ip | Internet Protocol Address IP |
|
||||||
|
| ip4_addr | Internet Protocol Address IPv4 |
|
||||||
|
| ip6_addr |Internet Protocol Address IPv6 |
|
||||||
|
| ip_addr | Internet Protocol Address IP |
|
||||||
|
| ipv4 | Internet Protocol Address IPv4 |
|
||||||
|
| ipv6 | Internet Protocol Address IPv6 |
|
||||||
|
| mac | Media Access Control Address MAC |
|
||||||
|
| tcp4_addr | Transmission Control Protocol Address TCPv4 |
|
||||||
|
| tcp6_addr | Transmission Control Protocol Address TCPv6 |
|
||||||
|
| tcp_addr | Transmission Control Protocol Address TCP |
|
||||||
|
| udp4_addr | User Datagram Protocol Address UDPv4 |
|
||||||
|
| udp6_addr | User Datagram Protocol Address UDPv6 |
|
||||||
|
| udp_addr | User Datagram Protocol Address UDP |
|
||||||
|
| unix_addr | Unix domain socket end point Address |
|
||||||
|
| uri | URI String |
|
||||||
|
| url | URL String |
|
||||||
|
| url_encoded | URL Encoded |
|
||||||
|
| urn_rfc2141 | Urn RFC 2141 String |
|
||||||
|
|
||||||
|
### Strings:
|
||||||
|
|
||||||
|
| Tag | Description |
|
||||||
|
| - | - |
|
||||||
|
| alpha | Alpha Only |
|
||||||
|
| alphanum | Alphanumeric |
|
||||||
|
| alphanumunicode | Alphanumeric Unicode |
|
||||||
|
| alphaunicode | Alpha Unicode |
|
||||||
|
| ascii | ASCII |
|
||||||
|
| contains | Contains |
|
||||||
|
| containsany | Contains Any |
|
||||||
|
| containsrune | Contains Rune |
|
||||||
|
| endswith | Ends With |
|
||||||
|
| lowercase | Lowercase |
|
||||||
|
| multibyte | Multi-Byte Characters |
|
||||||
|
| number | NOT DOCUMENTED IN doc.go |
|
||||||
|
| numeric | Numeric |
|
||||||
|
| printascii | Printable ASCII |
|
||||||
|
| startswith | Starts With |
|
||||||
|
| uppercase | Uppercase |
|
||||||
|
|
||||||
|
### Format:
|
||||||
|
| Tag | Description |
|
||||||
|
| - | - |
|
||||||
|
| base64 | Base64 String |
|
||||||
|
| base64url | Base64URL String |
|
||||||
|
| btc_addr | Bitcoin Address |
|
||||||
|
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
|
||||||
|
| datetime | Datetime |
|
||||||
|
| e164 | e164 formatted phone number |
|
||||||
|
| email | E-mail String
|
||||||
|
| eth_addr | Ethereum Address |
|
||||||
|
| hexadecimal | Hexadecimal String |
|
||||||
|
| hexcolor | Hexcolor String |
|
||||||
|
| hsl | HSL String |
|
||||||
|
| hsla | HSLA String |
|
||||||
|
| html | HTML Tags |
|
||||||
|
| html_encoded | HTML Encoded |
|
||||||
|
| isbn | International Standard Book Number |
|
||||||
|
| isbn10 | International Standard Book Number 10 |
|
||||||
|
| isbn13 | International Standard Book Number 13 |
|
||||||
|
| json | JSON |
|
||||||
|
| latitude | Latitude |
|
||||||
|
| longitude | Longitude |
|
||||||
|
| rgb | RGB String |
|
||||||
|
| rgba | RGBA String |
|
||||||
|
| ssn | Social Security Number SSN |
|
||||||
|
| uuid | Universally Unique Identifier UUID |
|
||||||
|
| uuid3 | Universally Unique Identifier UUID v3 |
|
||||||
|
| uuid3_rfc4122 | Universally Unique Identifier UUID v3 RFC4122 |
|
||||||
|
| uuid4 | Universally Unique Identifier UUID v4 |
|
||||||
|
| uuid4_rfc4122 | Universally Unique Identifier UUID v4 RFC4122 |
|
||||||
|
| uuid5 | Universally Unique Identifier UUID v5 |
|
||||||
|
| uuid5_rfc4122 | Universally Unique Identifier UUID v5 RFC4122 |
|
||||||
|
| uuid_rfc4122 | Universally Unique Identifier UUID RFC4122 |
|
||||||
|
|
||||||
|
### Comparisons:
|
||||||
|
| Tag | Description |
|
||||||
|
| - | - |
|
||||||
|
| eq | Equals |
|
||||||
|
| gt | Greater than|
|
||||||
|
| gte |Greater than or equal |
|
||||||
|
| lt | Less Than |
|
||||||
|
| lte | Less Than or Equal |
|
||||||
|
| ne | Not Equal |
|
||||||
|
|
||||||
|
### Other:
|
||||||
|
| Tag | Description |
|
||||||
|
| - | - |
|
||||||
|
| dir | Directory |
|
||||||
|
| endswith | Ends With |
|
||||||
|
| excludes | Excludes |
|
||||||
|
| excludesall | Excludes All |
|
||||||
|
| excludesrune | Excludes Rune |
|
||||||
|
| file | File path |
|
||||||
|
| isdefault | Is Default |
|
||||||
|
| len | Length |
|
||||||
|
| max | Maximum |
|
||||||
|
| min | Minimum |
|
||||||
|
| oneof | One Of |
|
||||||
|
| required | Required |
|
||||||
|
| required_if | Required If |
|
||||||
|
| required_unless | Required Unless |
|
||||||
|
| required_with | Required With |
|
||||||
|
| required_with_all | Required With All |
|
||||||
|
| required_without | Required Without |
|
||||||
|
| required_without_all | Required Without All |
|
||||||
|
| excluded_with | Excluded With |
|
||||||
|
| excluded_with_all | Excluded With All |
|
||||||
|
| excluded_without | Excluded Without |
|
||||||
|
| excluded_without_all | Excluded Without All |
|
||||||
|
| unique | Unique |
|
||||||
|
|
||||||
|
Benchmarks
|
||||||
|
------
|
||||||
|
###### Run on MacBook Pro (15-inch, 2017) go version go1.10.2 darwin/amd64
|
||||||
|
```go
|
||||||
|
goos: darwin
|
||||||
|
goarch: amd64
|
||||||
|
pkg: github.com/go-playground/validator
|
||||||
|
BenchmarkFieldSuccess-8 20000000 83.6 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkFieldSuccessParallel-8 50000000 26.8 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkFieldFailure-8 5000000 291 ns/op 208 B/op 4 allocs/op
|
||||||
|
BenchmarkFieldFailureParallel-8 20000000 107 ns/op 208 B/op 4 allocs/op
|
||||||
|
BenchmarkFieldArrayDiveSuccess-8 2000000 623 ns/op 201 B/op 11 allocs/op
|
||||||
|
BenchmarkFieldArrayDiveSuccessParallel-8 10000000 237 ns/op 201 B/op 11 allocs/op
|
||||||
|
BenchmarkFieldArrayDiveFailure-8 2000000 859 ns/op 412 B/op 16 allocs/op
|
||||||
|
BenchmarkFieldArrayDiveFailureParallel-8 5000000 335 ns/op 413 B/op 16 allocs/op
|
||||||
|
BenchmarkFieldMapDiveSuccess-8 1000000 1292 ns/op 432 B/op 18 allocs/op
|
||||||
|
BenchmarkFieldMapDiveSuccessParallel-8 3000000 467 ns/op 432 B/op 18 allocs/op
|
||||||
|
BenchmarkFieldMapDiveFailure-8 1000000 1082 ns/op 512 B/op 16 allocs/op
|
||||||
|
BenchmarkFieldMapDiveFailureParallel-8 5000000 425 ns/op 512 B/op 16 allocs/op
|
||||||
|
BenchmarkFieldMapDiveWithKeysSuccess-8 1000000 1539 ns/op 480 B/op 21 allocs/op
|
||||||
|
BenchmarkFieldMapDiveWithKeysSuccessParallel-8 3000000 613 ns/op 480 B/op 21 allocs/op
|
||||||
|
BenchmarkFieldMapDiveWithKeysFailure-8 1000000 1413 ns/op 721 B/op 21 allocs/op
|
||||||
|
BenchmarkFieldMapDiveWithKeysFailureParallel-8 3000000 575 ns/op 721 B/op 21 allocs/op
|
||||||
|
BenchmarkFieldCustomTypeSuccess-8 10000000 216 ns/op 32 B/op 2 allocs/op
|
||||||
|
BenchmarkFieldCustomTypeSuccessParallel-8 20000000 82.2 ns/op 32 B/op 2 allocs/op
|
||||||
|
BenchmarkFieldCustomTypeFailure-8 5000000 274 ns/op 208 B/op 4 allocs/op
|
||||||
|
BenchmarkFieldCustomTypeFailureParallel-8 20000000 116 ns/op 208 B/op 4 allocs/op
|
||||||
|
BenchmarkFieldOrTagSuccess-8 2000000 740 ns/op 16 B/op 1 allocs/op
|
||||||
|
BenchmarkFieldOrTagSuccessParallel-8 3000000 474 ns/op 16 B/op 1 allocs/op
|
||||||
|
BenchmarkFieldOrTagFailure-8 3000000 471 ns/op 224 B/op 5 allocs/op
|
||||||
|
BenchmarkFieldOrTagFailureParallel-8 3000000 414 ns/op 224 B/op 5 allocs/op
|
||||||
|
BenchmarkStructLevelValidationSuccess-8 10000000 213 ns/op 32 B/op 2 allocs/op
|
||||||
|
BenchmarkStructLevelValidationSuccessParallel-8 20000000 91.8 ns/op 32 B/op 2 allocs/op
|
||||||
|
BenchmarkStructLevelValidationFailure-8 3000000 473 ns/op 304 B/op 8 allocs/op
|
||||||
|
BenchmarkStructLevelValidationFailureParallel-8 10000000 234 ns/op 304 B/op 8 allocs/op
|
||||||
|
BenchmarkStructSimpleCustomTypeSuccess-8 5000000 385 ns/op 32 B/op 2 allocs/op
|
||||||
|
BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 161 ns/op 32 B/op 2 allocs/op
|
||||||
|
BenchmarkStructSimpleCustomTypeFailure-8 2000000 640 ns/op 424 B/op 9 allocs/op
|
||||||
|
BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 318 ns/op 440 B/op 10 allocs/op
|
||||||
|
BenchmarkStructFilteredSuccess-8 2000000 597 ns/op 288 B/op 9 allocs/op
|
||||||
|
BenchmarkStructFilteredSuccessParallel-8 10000000 266 ns/op 288 B/op 9 allocs/op
|
||||||
|
BenchmarkStructFilteredFailure-8 3000000 454 ns/op 256 B/op 7 allocs/op
|
||||||
|
BenchmarkStructFilteredFailureParallel-8 10000000 214 ns/op 256 B/op 7 allocs/op
|
||||||
|
BenchmarkStructPartialSuccess-8 3000000 502 ns/op 256 B/op 6 allocs/op
|
||||||
|
BenchmarkStructPartialSuccessParallel-8 10000000 225 ns/op 256 B/op 6 allocs/op
|
||||||
|
BenchmarkStructPartialFailure-8 2000000 702 ns/op 480 B/op 11 allocs/op
|
||||||
|
BenchmarkStructPartialFailureParallel-8 5000000 329 ns/op 480 B/op 11 allocs/op
|
||||||
|
BenchmarkStructExceptSuccess-8 2000000 793 ns/op 496 B/op 12 allocs/op
|
||||||
|
BenchmarkStructExceptSuccessParallel-8 10000000 193 ns/op 240 B/op 5 allocs/op
|
||||||
|
BenchmarkStructExceptFailure-8 2000000 639 ns/op 464 B/op 10 allocs/op
|
||||||
|
BenchmarkStructExceptFailureParallel-8 5000000 300 ns/op 464 B/op 10 allocs/op
|
||||||
|
BenchmarkStructSimpleCrossFieldSuccess-8 3000000 417 ns/op 72 B/op 3 allocs/op
|
||||||
|
BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 163 ns/op 72 B/op 3 allocs/op
|
||||||
|
BenchmarkStructSimpleCrossFieldFailure-8 2000000 645 ns/op 304 B/op 8 allocs/op
|
||||||
|
BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 285 ns/op 304 B/op 8 allocs/op
|
||||||
|
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 3000000 588 ns/op 80 B/op 4 allocs/op
|
||||||
|
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 221 ns/op 80 B/op 4 allocs/op
|
||||||
|
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 868 ns/op 320 B/op 9 allocs/op
|
||||||
|
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 337 ns/op 320 B/op 9 allocs/op
|
||||||
|
BenchmarkStructSimpleSuccess-8 5000000 260 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkStructSimpleSuccessParallel-8 20000000 90.6 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkStructSimpleFailure-8 2000000 619 ns/op 424 B/op 9 allocs/op
|
||||||
|
BenchmarkStructSimpleFailureParallel-8 5000000 296 ns/op 424 B/op 9 allocs/op
|
||||||
|
BenchmarkStructComplexSuccess-8 1000000 1454 ns/op 128 B/op 8 allocs/op
|
||||||
|
BenchmarkStructComplexSuccessParallel-8 3000000 579 ns/op 128 B/op 8 allocs/op
|
||||||
|
BenchmarkStructComplexFailure-8 300000 4140 ns/op 3041 B/op 53 allocs/op
|
||||||
|
BenchmarkStructComplexFailureParallel-8 1000000 2127 ns/op 3041 B/op 53 allocs/op
|
||||||
|
BenchmarkOneof-8 10000000 140 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkOneofParallel-8 20000000 70.1 ns/op 0 B/op 0 allocs/op
|
||||||
|
```
|
||||||
|
|
||||||
|
Complementary Software
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Here is a list of software that complements using this library either pre or post validation.
|
||||||
|
|
||||||
|
* [form](https://github.com/go-playground/form) - Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values. Dual Array and Full map support.
|
||||||
|
* [mold](https://github.com/go-playground/mold) - A general library to help modify or set data within data structures and other objects
|
||||||
|
|
||||||
|
How to Contribute
|
||||||
|
------
|
||||||
|
|
||||||
|
Make a pull request...
|
||||||
|
|
||||||
|
License
|
||||||
|
------
|
||||||
|
Distributed under MIT License, please see license file within the code for more details.
|
|
@ -0,0 +1,39 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MyStruct ..
|
||||||
|
type MyStruct struct {
|
||||||
|
String string `validate:"is-awesome"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// use a single instance of Validate, it caches struct info
|
||||||
|
var validate *validator.Validate
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
validate = validator.New()
|
||||||
|
validate.RegisterValidation("is-awesome", ValidateMyVal)
|
||||||
|
|
||||||
|
s := MyStruct{String: "awesome"}
|
||||||
|
|
||||||
|
err := validate.Struct(s)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Err(s):\n%+v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.String = "not awesome"
|
||||||
|
err = validate.Struct(s)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Err(s):\n%+v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateMyVal implements validator.Func
|
||||||
|
func ValidateMyVal(fl validator.FieldLevel) bool {
|
||||||
|
return fl.Field().String() == "awesome"
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DbBackedUser User struct
|
||||||
|
type DbBackedUser struct {
|
||||||
|
Name sql.NullString `validate:"required"`
|
||||||
|
Age sql.NullInt64 `validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// use a single instance of Validate, it caches struct info
|
||||||
|
var validate *validator.Validate
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
validate = validator.New()
|
||||||
|
|
||||||
|
// register all sql.Null* types to use the ValidateValuer CustomTypeFunc
|
||||||
|
validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{})
|
||||||
|
|
||||||
|
// build object for validation
|
||||||
|
x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}}
|
||||||
|
|
||||||
|
err := validate.Struct(x)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Err(s):\n%+v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateValuer implements validator.CustomTypeFunc
|
||||||
|
func ValidateValuer(field reflect.Value) interface{} {
|
||||||
|
|
||||||
|
if valuer, ok := field.Interface().(driver.Valuer); ok {
|
||||||
|
|
||||||
|
val, err := valuer.Value()
|
||||||
|
if err == nil {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
// handle the error how you want
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Test ...
|
||||||
|
type Test struct {
|
||||||
|
Array []string `validate:"required,gt=0,dive,required"`
|
||||||
|
Map map[string]string `validate:"required,gt=0,dive,keys,keymax,endkeys,required,max=1000"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// use a single instance of Validate, it caches struct info
|
||||||
|
var validate *validator.Validate
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
validate = validator.New()
|
||||||
|
|
||||||
|
// registering alias so we can see the differences between
|
||||||
|
// map key, value validation errors
|
||||||
|
validate.RegisterAlias("keymax", "max=10")
|
||||||
|
|
||||||
|
var test Test
|
||||||
|
|
||||||
|
val(test)
|
||||||
|
|
||||||
|
test.Array = []string{""}
|
||||||
|
test.Map = map[string]string{"test > than 10": ""}
|
||||||
|
val(test)
|
||||||
|
}
|
||||||
|
|
||||||
|
func val(test Test) {
|
||||||
|
fmt.Println("testing")
|
||||||
|
err := validate.Struct(test)
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "gin-valid/gin/binding"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
binding.Validator = new(defaultValidator)
|
||||||
|
|
||||||
|
// regular gin logic
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"gin-valid/gin/binding"
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
type defaultValidator struct {
|
||||||
|
once sync.Once
|
||||||
|
validate *validator.Validate
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ binding.StructValidator = &defaultValidator{}
|
||||||
|
|
||||||
|
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
||||||
|
|
||||||
|
if kindOfData(obj) == reflect.Struct {
|
||||||
|
|
||||||
|
v.lazyinit()
|
||||||
|
|
||||||
|
if err := v.validate.Struct(obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *defaultValidator) Engine() interface{} {
|
||||||
|
v.lazyinit()
|
||||||
|
return v.validate
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *defaultValidator) lazyinit() {
|
||||||
|
v.once.Do(func() {
|
||||||
|
v.validate = validator.New()
|
||||||
|
v.validate.SetTagName("binding")
|
||||||
|
|
||||||
|
// add any custom validations etc. here
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func kindOfData(data interface{}) reflect.Kind {
|
||||||
|
|
||||||
|
value := reflect.ValueOf(data)
|
||||||
|
valueType := value.Kind()
|
||||||
|
|
||||||
|
if valueType == reflect.Ptr {
|
||||||
|
valueType = value.Elem().Kind()
|
||||||
|
}
|
||||||
|
return valueType
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
// User contains user information
|
||||||
|
type User struct {
|
||||||
|
FirstName string `validate:"required"`
|
||||||
|
LastName string `validate:"required"`
|
||||||
|
Age uint8 `validate:"gte=0,lte=130"`
|
||||||
|
Email string `validate:"required,email"`
|
||||||
|
FavouriteColor string `validate:"iscolor"` // alias for 'hexcolor|rgb|rgba|hsl|hsla'
|
||||||
|
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address houses a users address information
|
||||||
|
type Address struct {
|
||||||
|
Street string `validate:"required"`
|
||||||
|
City string `validate:"required"`
|
||||||
|
Planet string `validate:"required"`
|
||||||
|
Phone string `validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// use a single instance of Validate, it caches struct info
|
||||||
|
var validate *validator.Validate
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
validate = validator.New()
|
||||||
|
|
||||||
|
validateStruct()
|
||||||
|
validateVariable()
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateStruct() {
|
||||||
|
|
||||||
|
address := &Address{
|
||||||
|
Street: "Eavesdown Docks",
|
||||||
|
Planet: "Persphone",
|
||||||
|
Phone: "none",
|
||||||
|
}
|
||||||
|
|
||||||
|
user := &User{
|
||||||
|
FirstName: "Badger",
|
||||||
|
LastName: "Smith",
|
||||||
|
Age: 135,
|
||||||
|
Email: "Badger.Smith@gmail.com",
|
||||||
|
FavouriteColor: "#000-",
|
||||||
|
Addresses: []*Address{address},
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns nil or ValidationErrors ( []FieldError )
|
||||||
|
err := validate.Struct(user)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
// this check is only needed when your code could produce
|
||||||
|
// an invalid value for validation such as interface with nil
|
||||||
|
// value most including myself do not usually have code like this.
|
||||||
|
if _, ok := err.(*validator.InvalidValidationError); ok {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, err := range err.(validator.ValidationErrors) {
|
||||||
|
|
||||||
|
fmt.Println(err.Namespace())
|
||||||
|
fmt.Println(err.Field())
|
||||||
|
fmt.Println(err.StructNamespace())
|
||||||
|
fmt.Println(err.StructField())
|
||||||
|
fmt.Println(err.Tag())
|
||||||
|
fmt.Println(err.ActualTag())
|
||||||
|
fmt.Println(err.Kind())
|
||||||
|
fmt.Println(err.Type())
|
||||||
|
fmt.Println(err.Value())
|
||||||
|
fmt.Println(err.Param())
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// from here you can create your own error messages in whatever language you wish
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// save user to database
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateVariable() {
|
||||||
|
|
||||||
|
myEmail := "joeybloggs.gmail.com"
|
||||||
|
|
||||||
|
errs := validate.Var(myEmail, "required,email")
|
||||||
|
|
||||||
|
if errs != nil {
|
||||||
|
fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// email ok, move on
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
// User contains user information
|
||||||
|
type User struct {
|
||||||
|
FirstName string `json:"fname"`
|
||||||
|
LastName string `json:"lname"`
|
||||||
|
Age uint8 `validate:"gte=0,lte=130"`
|
||||||
|
Email string `json:"e-mail" validate:"required,email"`
|
||||||
|
FavouriteColor string `validate:"hexcolor|rgb|rgba"`
|
||||||
|
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address houses a users address information
|
||||||
|
type Address struct {
|
||||||
|
Street string `validate:"required"`
|
||||||
|
City string `validate:"required"`
|
||||||
|
Planet string `validate:"required"`
|
||||||
|
Phone string `validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// use a single instance of Validate, it caches struct info
|
||||||
|
var validate *validator.Validate
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
validate = validator.New()
|
||||||
|
|
||||||
|
// register function to get tag name from json tags.
|
||||||
|
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
|
||||||
|
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
|
||||||
|
if name == "-" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
})
|
||||||
|
|
||||||
|
// register validation for 'User'
|
||||||
|
// NOTE: only have to register a non-pointer type for 'User', validator
|
||||||
|
// interanlly dereferences during it's type checks.
|
||||||
|
validate.RegisterStructValidation(UserStructLevelValidation, User{})
|
||||||
|
|
||||||
|
// build 'User' info, normally posted data etc...
|
||||||
|
address := &Address{
|
||||||
|
Street: "Eavesdown Docks",
|
||||||
|
Planet: "Persphone",
|
||||||
|
Phone: "none",
|
||||||
|
City: "Unknown",
|
||||||
|
}
|
||||||
|
|
||||||
|
user := &User{
|
||||||
|
FirstName: "",
|
||||||
|
LastName: "",
|
||||||
|
Age: 45,
|
||||||
|
Email: "Badger.Smith@gmail",
|
||||||
|
FavouriteColor: "#000",
|
||||||
|
Addresses: []*Address{address},
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns InvalidValidationError for bad validation input, nil or ValidationErrors ( []FieldError )
|
||||||
|
err := validate.Struct(user)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
// this check is only needed when your code could produce
|
||||||
|
// an invalid value for validation such as interface with nil
|
||||||
|
// value most including myself do not usually have code like this.
|
||||||
|
if _, ok := err.(*validator.InvalidValidationError); ok {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, err := range err.(validator.ValidationErrors) {
|
||||||
|
|
||||||
|
fmt.Println(err.Namespace()) // can differ when a custom TagNameFunc is registered or
|
||||||
|
fmt.Println(err.Field()) // by passing alt name to ReportError like below
|
||||||
|
fmt.Println(err.StructNamespace())
|
||||||
|
fmt.Println(err.StructField())
|
||||||
|
fmt.Println(err.Tag())
|
||||||
|
fmt.Println(err.ActualTag())
|
||||||
|
fmt.Println(err.Kind())
|
||||||
|
fmt.Println(err.Type())
|
||||||
|
fmt.Println(err.Value())
|
||||||
|
fmt.Println(err.Param())
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// from here you can create your own error messages in whatever language you wish
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// save user to database
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserStructLevelValidation contains custom struct level validations that don't always
|
||||||
|
// make sense at the field validation level. For Example this function validates that either
|
||||||
|
// FirstName or LastName exist; could have done that with a custom field validation but then
|
||||||
|
// would have had to add it to both fields duplicating the logic + overhead, this way it's
|
||||||
|
// only validated once.
|
||||||
|
//
|
||||||
|
// NOTE: you may ask why wouldn't I just do this outside of validator, because doing this way
|
||||||
|
// hooks right into validator and you can combine with validation tags and still have a
|
||||||
|
// common error output format.
|
||||||
|
func UserStructLevelValidation(sl validator.StructLevel) {
|
||||||
|
|
||||||
|
user := sl.Current().Interface().(User)
|
||||||
|
|
||||||
|
if len(user.FirstName) == 0 && len(user.LastName) == 0 {
|
||||||
|
sl.ReportError(user.FirstName, "fname", "FirstName", "fnameorlname", "")
|
||||||
|
sl.ReportError(user.LastName, "lname", "LastName", "fnameorlname", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// plus can do more, even with different tag than "fnameorlname"
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gin-valid/go-playground/locales/en"
|
||||||
|
ut "gin-valid/go-playground/universal-translator"
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
en_translations "gin-valid/go-playground/validator/v10/translations/en"
|
||||||
|
)
|
||||||
|
|
||||||
|
// User contains user information
|
||||||
|
type User struct {
|
||||||
|
FirstName string `validate:"required"`
|
||||||
|
LastName string `validate:"required"`
|
||||||
|
Age uint8 `validate:"gte=0,lte=130"`
|
||||||
|
Email string `validate:"required,email"`
|
||||||
|
FavouriteColor string `validate:"iscolor"` // alias for 'hexcolor|rgb|rgba|hsl|hsla'
|
||||||
|
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address houses a users address information
|
||||||
|
type Address struct {
|
||||||
|
Street string `validate:"required"`
|
||||||
|
City string `validate:"required"`
|
||||||
|
Planet string `validate:"required"`
|
||||||
|
Phone string `validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// use a single instance , it caches struct info
|
||||||
|
var (
|
||||||
|
uni *ut.UniversalTranslator
|
||||||
|
validate *validator.Validate
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// NOTE: ommitting allot of error checking for brevity
|
||||||
|
|
||||||
|
en := en.New()
|
||||||
|
uni = ut.New(en, en)
|
||||||
|
|
||||||
|
// this is usually know or extracted from http 'Accept-Language' header
|
||||||
|
// also see uni.FindTranslator(...)
|
||||||
|
trans, _ := uni.GetTranslator("en")
|
||||||
|
|
||||||
|
validate = validator.New()
|
||||||
|
en_translations.RegisterDefaultTranslations(validate, trans)
|
||||||
|
|
||||||
|
translateAll(trans)
|
||||||
|
translateIndividual(trans)
|
||||||
|
translateOverride(trans) // yep you can specify your own in whatever locale you want!
|
||||||
|
}
|
||||||
|
|
||||||
|
func translateAll(trans ut.Translator) {
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Username string `validate:"required"`
|
||||||
|
Tagline string `validate:"required,lt=10"`
|
||||||
|
Tagline2 string `validate:"required,gt=1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
user := User{
|
||||||
|
Username: "Joeybloggs",
|
||||||
|
Tagline: "This tagline is way too long.",
|
||||||
|
Tagline2: "1",
|
||||||
|
}
|
||||||
|
|
||||||
|
err := validate.Struct(user)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
// translate all error at once
|
||||||
|
errs := err.(validator.ValidationErrors)
|
||||||
|
|
||||||
|
// returns a map with key = namespace & value = translated error
|
||||||
|
// NOTICE: 2 errors are returned and you'll see something surprising
|
||||||
|
// translations are i18n aware!!!!
|
||||||
|
// eg. '10 characters' vs '1 character'
|
||||||
|
fmt.Println(errs.Translate(trans))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func translateIndividual(trans ut.Translator) {
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Username string `validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var user User
|
||||||
|
|
||||||
|
err := validate.Struct(user)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
errs := err.(validator.ValidationErrors)
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
// can translate each error one at a time.
|
||||||
|
fmt.Println(e.Translate(trans))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func translateOverride(trans ut.Translator) {
|
||||||
|
|
||||||
|
validate.RegisterTranslation("required", trans, func(ut ut.Translator) error {
|
||||||
|
return ut.Add("required", "{0} must have a value!", true) // see universal-translator for details
|
||||||
|
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||||
|
t, _ := ut.T("required", fe.Field())
|
||||||
|
|
||||||
|
return t
|
||||||
|
})
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Username string `validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var user User
|
||||||
|
|
||||||
|
err := validate.Struct(user)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
errs := err.(validator.ValidationErrors)
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
// can translate each error one at a time.
|
||||||
|
fmt.Println(e.Translate(trans))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,322 @@
|
||||||
|
package validator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tagType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
typeDefault tagType = iota
|
||||||
|
typeOmitEmpty
|
||||||
|
typeIsDefault
|
||||||
|
typeNoStructLevel
|
||||||
|
typeStructOnly
|
||||||
|
typeDive
|
||||||
|
typeOr
|
||||||
|
typeKeys
|
||||||
|
typeEndKeys
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
invalidValidation = "Invalid validation tag on field '%s'"
|
||||||
|
undefinedValidation = "Undefined validation function '%s' on field '%s'"
|
||||||
|
keysTagNotDefined = "'" + endKeysTag + "' tag encountered without a corresponding '" + keysTag + "' tag"
|
||||||
|
)
|
||||||
|
|
||||||
|
type structCache struct {
|
||||||
|
lock sync.Mutex
|
||||||
|
m atomic.Value // map[reflect.Type]*cStruct
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *structCache) Get(key reflect.Type) (c *cStruct, found bool) {
|
||||||
|
c, found = sc.m.Load().(map[reflect.Type]*cStruct)[key] //有一个断言的封装
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *structCache) Set(key reflect.Type, value *cStruct) {
|
||||||
|
m := sc.m.Load().(map[reflect.Type]*cStruct)
|
||||||
|
nm := make(map[reflect.Type]*cStruct, len(m)+1)
|
||||||
|
for k, v := range m {
|
||||||
|
nm[k] = v
|
||||||
|
}
|
||||||
|
nm[key] = value
|
||||||
|
sc.m.Store(nm)
|
||||||
|
}
|
||||||
|
|
||||||
|
type tagCache struct {
|
||||||
|
lock sync.Mutex
|
||||||
|
m atomic.Value // map[string]*cTag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *tagCache) Get(key string) (c *cTag, found bool) {
|
||||||
|
c, found = tc.m.Load().(map[string]*cTag)[key]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *tagCache) Set(key string, value *cTag) {
|
||||||
|
m := tc.m.Load().(map[string]*cTag)
|
||||||
|
nm := make(map[string]*cTag, len(m)+1)
|
||||||
|
for k, v := range m {
|
||||||
|
nm[k] = v
|
||||||
|
}
|
||||||
|
nm[key] = value
|
||||||
|
tc.m.Store(nm)
|
||||||
|
}
|
||||||
|
|
||||||
|
type cStruct struct {
|
||||||
|
name string
|
||||||
|
fields []*cField
|
||||||
|
fn StructLevelFuncCtx
|
||||||
|
}
|
||||||
|
|
||||||
|
type cField struct {
|
||||||
|
idx int
|
||||||
|
name string // field.name
|
||||||
|
altName string // 如果没有自定义 tagNameFunc,就是field.name, 如果有,比如可以按 json_tag中的, 报错的时候就会按照这个 报错
|
||||||
|
namesEqual bool // field.name 和 altName是否相同
|
||||||
|
cTags *cTag //以链表的形式存在
|
||||||
|
}
|
||||||
|
|
||||||
|
type cTag struct {
|
||||||
|
tag string // (gte=10) 实际是 gte,不带参数
|
||||||
|
aliasTag string // 应该是别名, 不过在没有别名的时候等同于 tag
|
||||||
|
actualAliasTag string // 实际名字,而不是在func *Validate.parseFieldTagsRecursive 解析后的 别名
|
||||||
|
param string
|
||||||
|
keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation
|
||||||
|
next *cTag
|
||||||
|
fn FuncCtx
|
||||||
|
typeof tagType
|
||||||
|
hasTag bool
|
||||||
|
hasAlias bool
|
||||||
|
hasParam bool // true if parameter used eg. eq= where the equal sign has been set
|
||||||
|
isBlockEnd bool // indicates the current tag represents the last validation in the block , 表示当前field的最后一个,比如 `require,gte=10`,那么到gte就是true
|
||||||
|
runValidationWhenNil bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct {
|
||||||
|
v.structCache.lock.Lock()
|
||||||
|
defer v.structCache.lock.Unlock() // leave as defer! because if inner panics, it will never get unlocked otherwise!
|
||||||
|
|
||||||
|
typ := current.Type()
|
||||||
|
|
||||||
|
// could have been multiple trying to access, but once first is done this ensures struct
|
||||||
|
// isn't parsed again.
|
||||||
|
cs, ok := v.structCache.Get(typ)
|
||||||
|
if ok {
|
||||||
|
return cs
|
||||||
|
}
|
||||||
|
|
||||||
|
cs = &cStruct{name: sName, fields: make([]*cField, 0), fn: v.structLevelFuncs[typ]}
|
||||||
|
|
||||||
|
numFields := current.NumField()
|
||||||
|
|
||||||
|
var ctag *cTag
|
||||||
|
var fld reflect.StructField
|
||||||
|
var tag string
|
||||||
|
var customName string
|
||||||
|
|
||||||
|
for i := 0; i < numFields; i++ {
|
||||||
|
|
||||||
|
fld = typ.Field(i)
|
||||||
|
|
||||||
|
if !fld.Anonymous && len(fld.PkgPath) > 0 { // 如果不是 嵌套field 且是 未导出字段, (大写的字段 pakPath为空)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tag = fld.Tag.Get(v.tagName)
|
||||||
|
|
||||||
|
if tag == skipValidationTag {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
customName = fld.Name
|
||||||
|
|
||||||
|
if v.hasTagNameFunc { // 自定义的获取 名字的func
|
||||||
|
name := v.tagNameFunc(fld)
|
||||||
|
if len(name) > 0 {
|
||||||
|
customName = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: cannot use shared tag cache, because tags may be equal, but things like alias may be different
|
||||||
|
// and so only struct level caching can be used instead of combined with Field tag caching
|
||||||
|
|
||||||
|
if len(tag) > 0 {
|
||||||
|
ctag, _ = v.parseFieldTagsRecursive(tag, fld.Name, "", false) //返回链头
|
||||||
|
} else {
|
||||||
|
// even if field doesn't have validations need cTag for traversing to potential inner/nested
|
||||||
|
// elements of the field.
|
||||||
|
ctag = new(cTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
cs.fields = append(cs.fields, &cField{
|
||||||
|
idx: i,
|
||||||
|
name: fld.Name,
|
||||||
|
altName: customName,
|
||||||
|
cTags: ctag,
|
||||||
|
namesEqual: fld.Name == customName,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
v.structCache.Set(typ, cs)
|
||||||
|
return cs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) {
|
||||||
|
var t string
|
||||||
|
noAlias := len(alias) == 0
|
||||||
|
tags := strings.Split(tag, tagSeparator)
|
||||||
|
|
||||||
|
for i := 0; i < len(tags); i++ {
|
||||||
|
t = tags[i]
|
||||||
|
if noAlias {
|
||||||
|
alias = t
|
||||||
|
}
|
||||||
|
|
||||||
|
// check map for alias and process new tags, otherwise process as usual
|
||||||
|
if tagsVal, found := v.aliases[t]; found {
|
||||||
|
if i == 0 {
|
||||||
|
firstCtag, current = v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
|
||||||
|
} else {
|
||||||
|
next, curr := v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
|
||||||
|
current.next, current = next, curr
|
||||||
|
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var prevTag tagType
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true, typeof: typeDefault}
|
||||||
|
firstCtag = current
|
||||||
|
} else {
|
||||||
|
prevTag = current.typeof // 此刻之前current 还是 上一个的状态
|
||||||
|
current.next = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true}
|
||||||
|
current = current.next
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case diveTag:
|
||||||
|
current.typeof = typeDive
|
||||||
|
continue
|
||||||
|
|
||||||
|
case keysTag:
|
||||||
|
current.typeof = typeKeys
|
||||||
|
|
||||||
|
if i == 0 || prevTag != typeDive {
|
||||||
|
panic(fmt.Sprintf("'%s' tag must be immediately preceded by the '%s' tag", keysTag, diveTag))
|
||||||
|
}
|
||||||
|
|
||||||
|
current.typeof = typeKeys
|
||||||
|
|
||||||
|
// need to pass along only keys tag
|
||||||
|
// need to increment i to skip over the keys tags
|
||||||
|
b := make([]byte, 0, 64)
|
||||||
|
|
||||||
|
i++
|
||||||
|
|
||||||
|
for ; i < len(tags); i++ {
|
||||||
|
|
||||||
|
b = append(b, tags[i]...)
|
||||||
|
b = append(b, ',')
|
||||||
|
|
||||||
|
if tags[i] == endKeysTag {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current.keys, _ = v.parseFieldTagsRecursive(string(b[:len(b)-1]), fieldName, "", false)
|
||||||
|
continue
|
||||||
|
|
||||||
|
case endKeysTag:
|
||||||
|
current.typeof = typeEndKeys
|
||||||
|
|
||||||
|
// if there are more in tags then there was no keysTag defined
|
||||||
|
// and an error should be thrown
|
||||||
|
if i != len(tags)-1 {
|
||||||
|
panic(keysTagNotDefined)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case omitempty:
|
||||||
|
current.typeof = typeOmitEmpty
|
||||||
|
continue
|
||||||
|
|
||||||
|
case structOnlyTag:
|
||||||
|
current.typeof = typeStructOnly
|
||||||
|
continue
|
||||||
|
|
||||||
|
case noStructLevelTag:
|
||||||
|
current.typeof = typeNoStructLevel
|
||||||
|
continue
|
||||||
|
|
||||||
|
default:
|
||||||
|
if t == isdefault {
|
||||||
|
current.typeof = typeIsDefault
|
||||||
|
}
|
||||||
|
// if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C"
|
||||||
|
orVals := strings.Split(t, orSeparator)
|
||||||
|
|
||||||
|
for j := 0; j < len(orVals); j++ {
|
||||||
|
vals := strings.SplitN(orVals[j], tagKeySeparator, 2) // binding:"gte=10" 的 =
|
||||||
|
if noAlias {
|
||||||
|
alias = vals[0] //如果没有别名就 gte , 前面的是 gte=10
|
||||||
|
current.aliasTag = alias
|
||||||
|
} else {
|
||||||
|
current.actualAliasTag = t // 原始名字 gte=10
|
||||||
|
}
|
||||||
|
|
||||||
|
if j > 0 { //这里的current.next 不应该是 .pre 前一个么,不过前一个也不对啊,后面的赋值还是给 current赋值啊
|
||||||
|
current.next = &cTag{aliasTag: alias, actualAliasTag: current.actualAliasTag, hasAlias: hasAlias, hasTag: true}
|
||||||
|
current = current.next
|
||||||
|
}
|
||||||
|
current.hasParam = len(vals) > 1
|
||||||
|
|
||||||
|
current.tag = vals[0]
|
||||||
|
if len(current.tag) == 0 {
|
||||||
|
panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if wrapper, ok := v.validations[current.tag]; ok {
|
||||||
|
current.fn = wrapper.fn
|
||||||
|
current.runValidationWhenNil = wrapper.runValidatinOnNil
|
||||||
|
} else {
|
||||||
|
panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, current.tag, fieldName)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(orVals) > 1 {
|
||||||
|
current.typeof = typeOr
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(vals) > 1 {
|
||||||
|
current.param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current.isBlockEnd = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validate) fetchCacheTag(tag string) *cTag {
|
||||||
|
// find cached tag
|
||||||
|
ctag, found := v.tagCache.Get(tag)
|
||||||
|
if !found {
|
||||||
|
v.tagCache.lock.Lock()
|
||||||
|
defer v.tagCache.lock.Unlock()
|
||||||
|
|
||||||
|
// could have been multiple trying to access, but once first is done this ensures tag
|
||||||
|
// isn't parsed again.
|
||||||
|
ctag, found = v.tagCache.Get(tag)
|
||||||
|
if !found {
|
||||||
|
ctag, _ = v.parseFieldTagsRecursive(tag, "", "", false)
|
||||||
|
v.tagCache.Set(tag, ctag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctag
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
package validator
|
||||||
|
|
||||||
|
var iso3166_1_alpha2 = map[string]bool{
|
||||||
|
// see: https://www.iso.org/iso-3166-country-codes.html
|
||||||
|
"AF": true, "AX": true, "AL": true, "DZ": true, "AS": true,
|
||||||
|
"AD": true, "AO": true, "AI": true, "AQ": true, "AG": true,
|
||||||
|
"AR": true, "AM": true, "AW": true, "AU": true, "AT": true,
|
||||||
|
"AZ": true, "BS": true, "BH": true, "BD": true, "BB": true,
|
||||||
|
"BY": true, "BE": true, "BZ": true, "BJ": true, "BM": true,
|
||||||
|
"BT": true, "BO": true, "BQ": true, "BA": true, "BW": true,
|
||||||
|
"BV": true, "BR": true, "IO": true, "BN": true, "BG": true,
|
||||||
|
"BF": true, "BI": true, "KH": true, "CM": true, "CA": true,
|
||||||
|
"CV": true, "KY": true, "CF": true, "TD": true, "CL": true,
|
||||||
|
"CN": true, "CX": true, "CC": true, "CO": true, "KM": true,
|
||||||
|
"CG": true, "CD": true, "CK": true, "CR": true, "CI": true,
|
||||||
|
"HR": true, "CU": true, "CW": true, "CY": true, "CZ": true,
|
||||||
|
"DK": true, "DJ": true, "DM": true, "DO": true, "EC": true,
|
||||||
|
"EG": true, "SV": true, "GQ": true, "ER": true, "EE": true,
|
||||||
|
"ET": true, "FK": true, "FO": true, "FJ": true, "FI": true,
|
||||||
|
"FR": true, "GF": true, "PF": true, "TF": true, "GA": true,
|
||||||
|
"GM": true, "GE": true, "DE": true, "GH": true, "GI": true,
|
||||||
|
"GR": true, "GL": true, "GD": true, "GP": true, "GU": true,
|
||||||
|
"GT": true, "GG": true, "GN": true, "GW": true, "GY": true,
|
||||||
|
"HT": true, "HM": true, "VA": true, "HN": true, "HK": true,
|
||||||
|
"HU": true, "IS": true, "IN": true, "ID": true, "IR": true,
|
||||||
|
"IQ": true, "IE": true, "IM": true, "IL": true, "IT": true,
|
||||||
|
"JM": true, "JP": true, "JE": true, "JO": true, "KZ": true,
|
||||||
|
"KE": true, "KI": true, "KP": true, "KR": true, "KW": true,
|
||||||
|
"KG": true, "LA": true, "LV": true, "LB": true, "LS": true,
|
||||||
|
"LR": true, "LY": true, "LI": true, "LT": true, "LU": true,
|
||||||
|
"MO": true, "MK": true, "MG": true, "MW": true, "MY": true,
|
||||||
|
"MV": true, "ML": true, "MT": true, "MH": true, "MQ": true,
|
||||||
|
"MR": true, "MU": true, "YT": true, "MX": true, "FM": true,
|
||||||
|
"MD": true, "MC": true, "MN": true, "ME": true, "MS": true,
|
||||||
|
"MA": true, "MZ": true, "MM": true, "NA": true, "NR": true,
|
||||||
|
"NP": true, "NL": true, "NC": true, "NZ": true, "NI": true,
|
||||||
|
"NE": true, "NG": true, "NU": true, "NF": true, "MP": true,
|
||||||
|
"NO": true, "OM": true, "PK": true, "PW": true, "PS": true,
|
||||||
|
"PA": true, "PG": true, "PY": true, "PE": true, "PH": true,
|
||||||
|
"PN": true, "PL": true, "PT": true, "PR": true, "QA": true,
|
||||||
|
"RE": true, "RO": true, "RU": true, "RW": true, "BL": true,
|
||||||
|
"SH": true, "KN": true, "LC": true, "MF": true, "PM": true,
|
||||||
|
"VC": true, "WS": true, "SM": true, "ST": true, "SA": true,
|
||||||
|
"SN": true, "RS": true, "SC": true, "SL": true, "SG": true,
|
||||||
|
"SX": true, "SK": true, "SI": true, "SB": true, "SO": true,
|
||||||
|
"ZA": true, "GS": true, "SS": true, "ES": true, "LK": true,
|
||||||
|
"SD": true, "SR": true, "SJ": true, "SZ": true, "SE": true,
|
||||||
|
"CH": true, "SY": true, "TW": true, "TJ": true, "TZ": true,
|
||||||
|
"TH": true, "TL": true, "TG": true, "TK": true, "TO": true,
|
||||||
|
"TT": true, "TN": true, "TR": true, "TM": true, "TC": true,
|
||||||
|
"TV": true, "UG": true, "UA": true, "AE": true, "GB": true,
|
||||||
|
"US": true, "UM": true, "UY": true, "UZ": true, "VU": true,
|
||||||
|
"VE": true, "VN": true, "VG": true, "VI": true, "WF": true,
|
||||||
|
"EH": true, "YE": true, "ZM": true, "ZW": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var iso3166_1_alpha3 = map[string]bool{
|
||||||
|
// see: https://www.iso.org/iso-3166-country-codes.html
|
||||||
|
"AFG": true, "ALB": true, "DZA": true, "ASM": true, "AND": true,
|
||||||
|
"AGO": true, "AIA": true, "ATA": true, "ATG": true, "ARG": true,
|
||||||
|
"ARM": true, "ABW": true, "AUS": true, "AUT": true, "AZE": true,
|
||||||
|
"BHS": true, "BHR": true, "BGD": true, "BRB": true, "BLR": true,
|
||||||
|
"BEL": true, "BLZ": true, "BEN": true, "BMU": true, "BTN": true,
|
||||||
|
"BOL": true, "BES": true, "BIH": true, "BWA": true, "BVT": true,
|
||||||
|
"BRA": true, "IOT": true, "BRN": true, "BGR": true, "BFA": true,
|
||||||
|
"BDI": true, "CPV": true, "KHM": true, "CMR": true, "CAN": true,
|
||||||
|
"CYM": true, "CAF": true, "TCD": true, "CHL": true, "CHN": true,
|
||||||
|
"CXR": true, "CCK": true, "COL": true, "COM": true, "COD": true,
|
||||||
|
"COG": true, "COK": true, "CRI": true, "HRV": true, "CUB": true,
|
||||||
|
"CUW": true, "CYP": true, "CZE": true, "CIV": true, "DNK": true,
|
||||||
|
"DJI": true, "DMA": true, "DOM": true, "ECU": true, "EGY": true,
|
||||||
|
"SLV": true, "GNQ": true, "ERI": true, "EST": true, "SWZ": true,
|
||||||
|
"ETH": true, "FLK": true, "FRO": true, "FJI": true, "FIN": true,
|
||||||
|
"FRA": true, "GUF": true, "PYF": true, "ATF": true, "GAB": true,
|
||||||
|
"GMB": true, "GEO": true, "DEU": true, "GHA": true, "GIB": true,
|
||||||
|
"GRC": true, "GRL": true, "GRD": true, "GLP": true, "GUM": true,
|
||||||
|
"GTM": true, "GGY": true, "GIN": true, "GNB": true, "GUY": true,
|
||||||
|
"HTI": true, "HMD": true, "VAT": true, "HND": true, "HKG": true,
|
||||||
|
"HUN": true, "ISL": true, "IND": true, "IDN": true, "IRN": true,
|
||||||
|
"IRQ": true, "IRL": true, "IMN": true, "ISR": true, "ITA": true,
|
||||||
|
"JAM": true, "JPN": true, "JEY": true, "JOR": true, "KAZ": true,
|
||||||
|
"KEN": true, "KIR": true, "PRK": true, "KOR": true, "KWT": true,
|
||||||
|
"KGZ": true, "LAO": true, "LVA": true, "LBN": true, "LSO": true,
|
||||||
|
"LBR": true, "LBY": true, "LIE": true, "LTU": true, "LUX": true,
|
||||||
|
"MAC": true, "MDG": true, "MWI": true, "MYS": true, "MDV": true,
|
||||||
|
"MLI": true, "MLT": true, "MHL": true, "MTQ": true, "MRT": true,
|
||||||
|
"MUS": true, "MYT": true, "MEX": true, "FSM": true, "MDA": true,
|
||||||
|
"MCO": true, "MNG": true, "MNE": true, "MSR": true, "MAR": true,
|
||||||
|
"MOZ": true, "MMR": true, "NAM": true, "NRU": true, "NPL": true,
|
||||||
|
"NLD": true, "NCL": true, "NZL": true, "NIC": true, "NER": true,
|
||||||
|
"NGA": true, "NIU": true, "NFK": true, "MKD": true, "MNP": true,
|
||||||
|
"NOR": true, "OMN": true, "PAK": true, "PLW": true, "PSE": true,
|
||||||
|
"PAN": true, "PNG": true, "PRY": true, "PER": true, "PHL": true,
|
||||||
|
"PCN": true, "POL": true, "PRT": true, "PRI": true, "QAT": true,
|
||||||
|
"ROU": true, "RUS": true, "RWA": true, "REU": true, "BLM": true,
|
||||||
|
"SHN": true, "KNA": true, "LCA": true, "MAF": true, "SPM": true,
|
||||||
|
"VCT": true, "WSM": true, "SMR": true, "STP": true, "SAU": true,
|
||||||
|
"SEN": true, "SRB": true, "SYC": true, "SLE": true, "SGP": true,
|
||||||
|
"SXM": true, "SVK": true, "SVN": true, "SLB": true, "SOM": true,
|
||||||
|
"ZAF": true, "SGS": true, "SSD": true, "ESP": true, "LKA": true,
|
||||||
|
"SDN": true, "SUR": true, "SJM": true, "SWE": true, "CHE": true,
|
||||||
|
"SYR": true, "TWN": true, "TJK": true, "TZA": true, "THA": true,
|
||||||
|
"TLS": true, "TGO": true, "TKL": true, "TON": true, "TTO": true,
|
||||||
|
"TUN": true, "TUR": true, "TKM": true, "TCA": true, "TUV": true,
|
||||||
|
"UGA": true, "UKR": true, "ARE": true, "GBR": true, "UMI": true,
|
||||||
|
"USA": true, "URY": true, "UZB": true, "VUT": true, "VEN": true,
|
||||||
|
"VNM": true, "VGB": true, "VIR": true, "WLF": true, "ESH": true,
|
||||||
|
"YEM": true, "ZMB": true, "ZWE": true, "ALA": true,
|
||||||
|
}
|
||||||
|
var iso3166_1_alpha_numeric = map[int]bool{
|
||||||
|
// see: https://www.iso.org/iso-3166-country-codes.html
|
||||||
|
4: true, 8: true, 12: true, 16: true, 20: true,
|
||||||
|
24: true, 660: true, 10: true, 28: true, 32: true,
|
||||||
|
51: true, 533: true, 36: true, 40: true, 31: true,
|
||||||
|
44: true, 48: true, 50: true, 52: true, 112: true,
|
||||||
|
56: true, 84: true, 204: true, 60: true, 64: true,
|
||||||
|
68: true, 535: true, 70: true, 72: true, 74: true,
|
||||||
|
76: true, 86: true, 96: true, 100: true, 854: true,
|
||||||
|
108: true, 132: true, 116: true, 120: true, 124: true,
|
||||||
|
136: true, 140: true, 148: true, 152: true, 156: true,
|
||||||
|
162: true, 166: true, 170: true, 174: true, 180: true,
|
||||||
|
178: true, 184: true, 188: true, 191: true, 192: true,
|
||||||
|
531: true, 196: true, 203: true, 384: true, 208: true,
|
||||||
|
262: true, 212: true, 214: true, 218: true, 818: true,
|
||||||
|
222: true, 226: true, 232: true, 233: true, 748: true,
|
||||||
|
231: true, 238: true, 234: true, 242: true, 246: true,
|
||||||
|
250: true, 254: true, 258: true, 260: true, 266: true,
|
||||||
|
270: true, 268: true, 276: true, 288: true, 292: true,
|
||||||
|
300: true, 304: true, 308: true, 312: true, 316: true,
|
||||||
|
320: true, 831: true, 324: true, 624: true, 328: true,
|
||||||
|
332: true, 334: true, 336: true, 340: true, 344: true,
|
||||||
|
348: true, 352: true, 356: true, 360: true, 364: true,
|
||||||
|
368: true, 372: true, 833: true, 376: true, 380: true,
|
||||||
|
388: true, 392: true, 832: true, 400: true, 398: true,
|
||||||
|
404: true, 296: true, 408: true, 410: true, 414: true,
|
||||||
|
417: true, 418: true, 428: true, 422: true, 426: true,
|
||||||
|
430: true, 434: true, 438: true, 440: true, 442: true,
|
||||||
|
446: true, 450: true, 454: true, 458: true, 462: true,
|
||||||
|
466: true, 470: true, 584: true, 474: true, 478: true,
|
||||||
|
480: true, 175: true, 484: true, 583: true, 498: true,
|
||||||
|
492: true, 496: true, 499: true, 500: true, 504: true,
|
||||||
|
508: true, 104: true, 516: true, 520: true, 524: true,
|
||||||
|
528: true, 540: true, 554: true, 558: true, 562: true,
|
||||||
|
566: true, 570: true, 574: true, 807: true, 580: true,
|
||||||
|
578: true, 512: true, 586: true, 585: true, 275: true,
|
||||||
|
591: true, 598: true, 600: true, 604: true, 608: true,
|
||||||
|
612: true, 616: true, 620: true, 630: true, 634: true,
|
||||||
|
642: true, 643: true, 646: true, 638: true, 652: true,
|
||||||
|
654: true, 659: true, 662: true, 663: true, 666: true,
|
||||||
|
670: true, 882: true, 674: true, 678: true, 682: true,
|
||||||
|
686: true, 688: true, 690: true, 694: true, 702: true,
|
||||||
|
534: true, 703: true, 705: true, 90: true, 706: true,
|
||||||
|
710: true, 239: true, 728: true, 724: true, 144: true,
|
||||||
|
729: true, 740: true, 744: true, 752: true, 756: true,
|
||||||
|
760: true, 158: true, 762: true, 834: true, 764: true,
|
||||||
|
626: true, 768: true, 772: true, 776: true, 780: true,
|
||||||
|
788: true, 792: true, 795: true, 796: true, 798: true,
|
||||||
|
800: true, 804: true, 784: true, 826: true, 581: true,
|
||||||
|
840: true, 858: true, 860: true, 548: true, 862: true,
|
||||||
|
704: true, 92: true, 850: true, 876: true, 732: true,
|
||||||
|
887: true, 894: true, 716: true, 248: true,
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,295 @@
|
||||||
|
package validator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
ut "gin-valid/go-playground/universal-translator"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
fieldErrMsg = "Key: '%s' Error:Field validation for '%s' failed on the '%s' tag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidationErrorsTranslations is the translation return type
|
||||||
|
type ValidationErrorsTranslations map[string]string
|
||||||
|
|
||||||
|
// InvalidValidationError describes an invalid argument passed to
|
||||||
|
// `Struct`, `StructExcept`, StructPartial` or `Field`
|
||||||
|
type InvalidValidationError struct {
|
||||||
|
Type reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns InvalidValidationError message
|
||||||
|
func (e *InvalidValidationError) Error() string {
|
||||||
|
|
||||||
|
if e.Type == nil {
|
||||||
|
return "validator: (nil)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "validator: (nil " + e.Type.String() + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidationErrors is an array of FieldError's
|
||||||
|
// for use in custom error messages post validation.
|
||||||
|
type ValidationErrors []FieldError
|
||||||
|
|
||||||
|
// Error is intended for use in development + debugging and not intended to be a production error message.
|
||||||
|
// It allows ValidationErrors to subscribe to the Error interface.
|
||||||
|
// All information to create an error message specific to your application is contained within
|
||||||
|
// the FieldError found within the ValidationErrors array
|
||||||
|
func (ve ValidationErrors) Error() string {
|
||||||
|
|
||||||
|
buff := bytes.NewBufferString("")
|
||||||
|
|
||||||
|
var fe *fieldError
|
||||||
|
|
||||||
|
for i := 0; i < len(ve); i++ {
|
||||||
|
|
||||||
|
fe = ve[i].(*fieldError)
|
||||||
|
buff.WriteString(fe.Error())
|
||||||
|
buff.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(buff.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// yang 修改
|
||||||
|
// Translate translates all of the ValidationErrors
|
||||||
|
//func (ve ValidationErrors) Translate(ut ut.Translator) ValidationErrorsTranslations {
|
||||||
|
//
|
||||||
|
// trans := make(ValidationErrorsTranslations)
|
||||||
|
//
|
||||||
|
// var fe *fieldError
|
||||||
|
//
|
||||||
|
// for i := 0; i < len(ve); i++ {
|
||||||
|
// fe = ve[i].(*fieldError)
|
||||||
|
//
|
||||||
|
// // // in case an Anonymous struct was used, ensure that the key
|
||||||
|
// // // would be 'Username' instead of ".Username"
|
||||||
|
// // if len(fe.ns) > 0 && fe.ns[:1] == "." {
|
||||||
|
// // trans[fe.ns[1:]] = fe.Translate(ut)
|
||||||
|
// // continue
|
||||||
|
// // }
|
||||||
|
//
|
||||||
|
// trans[fe.ns] = fe.Translate(ut)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return trans
|
||||||
|
//}
|
||||||
|
type TransValidError struct {
|
||||||
|
ErrorString string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TransValidError) Error() string {
|
||||||
|
return e.ErrorString
|
||||||
|
}
|
||||||
|
func (ve ValidationErrors) Translate(ut ut.Translator) TransValidError {
|
||||||
|
var result TransValidError
|
||||||
|
var fe *fieldError
|
||||||
|
if len(ve) == 0 {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
fe = ve[0].(*fieldError)
|
||||||
|
result.ErrorString = fe.Translate(ut)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// yang 修改结束
|
||||||
|
|
||||||
|
// FieldError contains all functions to get error details
|
||||||
|
type FieldError interface {
|
||||||
|
|
||||||
|
// returns the validation tag that failed. if the
|
||||||
|
// validation was an alias, this will return the
|
||||||
|
// alias name and not the underlying tag that failed.
|
||||||
|
//
|
||||||
|
// eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla"
|
||||||
|
// will return "iscolor"
|
||||||
|
Tag() string
|
||||||
|
|
||||||
|
// returns the validation tag that failed, even if an
|
||||||
|
// alias the actual tag within the alias will be returned.
|
||||||
|
// If an 'or' validation fails the entire or will be returned.
|
||||||
|
//
|
||||||
|
// eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla"
|
||||||
|
// will return "hexcolor|rgb|rgba|hsl|hsla"
|
||||||
|
ActualTag() string
|
||||||
|
|
||||||
|
// returns the namespace for the field error, with the tag
|
||||||
|
// name taking precedence over the field's actual name.
|
||||||
|
//
|
||||||
|
// eg. JSON name "User.fname"
|
||||||
|
//
|
||||||
|
// See StructNamespace() for a version that returns actual names.
|
||||||
|
//
|
||||||
|
// NOTE: this field can be blank when validating a single primitive field
|
||||||
|
// using validate.Field(...) as there is no way to extract it's name
|
||||||
|
Namespace() string
|
||||||
|
|
||||||
|
// returns the namespace for the field error, with the field's
|
||||||
|
// actual name.
|
||||||
|
//
|
||||||
|
// eq. "User.FirstName" see Namespace for comparison
|
||||||
|
//
|
||||||
|
// NOTE: this field can be blank when validating a single primitive field
|
||||||
|
// using validate.Field(...) as there is no way to extract its name
|
||||||
|
StructNamespace() string
|
||||||
|
|
||||||
|
// returns the fields name with the tag name taking precedence over the
|
||||||
|
// field's actual name.
|
||||||
|
//
|
||||||
|
// eq. JSON name "fname"
|
||||||
|
// see StructField for comparison
|
||||||
|
Field() string
|
||||||
|
|
||||||
|
// returns the field's actual name from the struct, when able to determine.
|
||||||
|
//
|
||||||
|
// eq. "FirstName"
|
||||||
|
// see Field for comparison
|
||||||
|
StructField() string
|
||||||
|
|
||||||
|
// returns the actual field's value in case needed for creating the error
|
||||||
|
// message
|
||||||
|
Value() interface{}
|
||||||
|
|
||||||
|
// returns the param value, in string form for comparison; this will also
|
||||||
|
// help with generating an error message
|
||||||
|
Param() string
|
||||||
|
|
||||||
|
// Kind returns the Field's reflect Kind
|
||||||
|
//
|
||||||
|
// eg. time.Time's kind is a struct
|
||||||
|
Kind() reflect.Kind
|
||||||
|
|
||||||
|
// Type returns the Field's reflect Type
|
||||||
|
//
|
||||||
|
// // eg. time.Time's type is time.Time
|
||||||
|
Type() reflect.Type
|
||||||
|
|
||||||
|
// returns the FieldError's translated error
|
||||||
|
// from the provided 'ut.Translator' and registered 'TranslationFunc'
|
||||||
|
//
|
||||||
|
// NOTE: if no registered translator can be found it returns the same as
|
||||||
|
// calling fe.Error()
|
||||||
|
Translate(ut ut.Translator) string
|
||||||
|
|
||||||
|
// Error returns the FieldError's message
|
||||||
|
Error() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile time interface checks
|
||||||
|
var _ FieldError = new(fieldError)
|
||||||
|
var _ error = new(fieldError)
|
||||||
|
|
||||||
|
// fieldError contains a single field's validation error along
|
||||||
|
// with other properties that may be needed for error message creation
|
||||||
|
// it complies with the FieldError interface
|
||||||
|
type fieldError struct {
|
||||||
|
v *Validate
|
||||||
|
tag string
|
||||||
|
actualTag string
|
||||||
|
ns string
|
||||||
|
structNs string
|
||||||
|
fieldLen uint8
|
||||||
|
structfieldLen uint8
|
||||||
|
value interface{}
|
||||||
|
param string
|
||||||
|
kind reflect.Kind
|
||||||
|
typ reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag returns the validation tag that failed.
|
||||||
|
func (fe *fieldError) Tag() string {
|
||||||
|
return fe.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActualTag returns the validation tag that failed, even if an
|
||||||
|
// alias the actual tag within the alias will be returned.
|
||||||
|
func (fe *fieldError) ActualTag() string {
|
||||||
|
return fe.actualTag
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace returns the namespace for the field error, with the tag
|
||||||
|
// name taking precedence over the field's actual name.
|
||||||
|
func (fe *fieldError) Namespace() string {
|
||||||
|
return fe.ns
|
||||||
|
}
|
||||||
|
|
||||||
|
// StructNamespace returns the namespace for the field error, with the field's
|
||||||
|
// actual name.
|
||||||
|
func (fe *fieldError) StructNamespace() string {
|
||||||
|
return fe.structNs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field returns the field's name with the tag name taking precedence over the
|
||||||
|
// field's actual name.
|
||||||
|
func (fe *fieldError) Field() string {
|
||||||
|
|
||||||
|
return fe.ns[len(fe.ns)-int(fe.fieldLen):]
|
||||||
|
// // return fe.field
|
||||||
|
// fld := fe.ns[len(fe.ns)-int(fe.fieldLen):]
|
||||||
|
|
||||||
|
// log.Println("FLD:", fld)
|
||||||
|
|
||||||
|
// if len(fld) > 0 && fld[:1] == "." {
|
||||||
|
// return fld[1:]
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return fld
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the field's actual name from the struct, when able to determine.
|
||||||
|
func (fe *fieldError) StructField() string {
|
||||||
|
// return fe.structField
|
||||||
|
return fe.structNs[len(fe.structNs)-int(fe.structfieldLen):]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the actual field's value in case needed for creating the error
|
||||||
|
// message
|
||||||
|
func (fe *fieldError) Value() interface{} {
|
||||||
|
return fe.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Param returns the param value, in string form for comparison; this will
|
||||||
|
// also help with generating an error message
|
||||||
|
func (fe *fieldError) Param() string {
|
||||||
|
return fe.param
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kind returns the Field's reflect Kind
|
||||||
|
func (fe *fieldError) Kind() reflect.Kind {
|
||||||
|
return fe.kind
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the Field's reflect Type
|
||||||
|
func (fe *fieldError) Type() reflect.Type {
|
||||||
|
return fe.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the fieldError's error message
|
||||||
|
func (fe *fieldError) Error() string {
|
||||||
|
return fmt.Sprintf(fieldErrMsg, fe.ns, fe.Field(), fe.tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate returns the FieldError's translated error
|
||||||
|
// from the provided 'ut.Translator' and registered 'TranslationFunc'
|
||||||
|
//
|
||||||
|
// NOTE: if no registered translation can be found, it returns the original
|
||||||
|
// untranslated error message.
|
||||||
|
func (fe *fieldError) Translate(ut ut.Translator) string {
|
||||||
|
|
||||||
|
m, ok := fe.v.transTagFunc[ut]
|
||||||
|
if !ok {
|
||||||
|
return fe.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn, ok := m[fe.tag]
|
||||||
|
if !ok {
|
||||||
|
return fe.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn(ut, fe)
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
package validator
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
// FieldLevel contains all the information and helper functions
|
||||||
|
// to validate a field
|
||||||
|
type FieldLevel interface {
|
||||||
|
// returns the top level struct, if any
|
||||||
|
Top() reflect.Value
|
||||||
|
|
||||||
|
// returns the current fields parent struct, if any or
|
||||||
|
// the comparison value if called 'VarWithValue'
|
||||||
|
Parent() reflect.Value
|
||||||
|
|
||||||
|
// returns current field for validation
|
||||||
|
Field() reflect.Value
|
||||||
|
|
||||||
|
// returns the field's name with the tag
|
||||||
|
// name taking precedence over the fields actual name.
|
||||||
|
FieldName() string
|
||||||
|
|
||||||
|
// returns the struct field's name
|
||||||
|
StructFieldName() string
|
||||||
|
|
||||||
|
// returns param for validation against current field
|
||||||
|
Param() string
|
||||||
|
|
||||||
|
// GetTag returns the current validations tag name
|
||||||
|
GetTag() string
|
||||||
|
|
||||||
|
// ExtractType gets the actual underlying type of field value.
|
||||||
|
// It will dive into pointers, customTypes and return you the
|
||||||
|
// underlying value and it's kind.
|
||||||
|
ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool)
|
||||||
|
|
||||||
|
// traverses the parent struct to retrieve a specific field denoted by the provided namespace
|
||||||
|
// in the param and returns the field, field kind and whether is was successful in retrieving
|
||||||
|
// the field at all.
|
||||||
|
//
|
||||||
|
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
|
||||||
|
// could not be retrieved because it didn't exist.
|
||||||
|
//
|
||||||
|
// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable.
|
||||||
|
GetStructFieldOK() (reflect.Value, reflect.Kind, bool)
|
||||||
|
|
||||||
|
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
|
||||||
|
// the field and namespace allowing more extensibility for validators.
|
||||||
|
//
|
||||||
|
// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable.
|
||||||
|
GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool)
|
||||||
|
|
||||||
|
// traverses the parent struct to retrieve a specific field denoted by the provided namespace
|
||||||
|
// in the param and returns the field, field kind, if it's a nullable type and whether is was successful in retrieving
|
||||||
|
// the field at all.
|
||||||
|
//
|
||||||
|
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
|
||||||
|
// could not be retrieved because it didn't exist.
|
||||||
|
GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool)
|
||||||
|
|
||||||
|
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
|
||||||
|
// the field and namespace allowing more extensibility for validators.
|
||||||
|
GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ FieldLevel = new(validate)
|
||||||
|
|
||||||
|
// Field returns current field for validation
|
||||||
|
func (v *validate) Field() reflect.Value {
|
||||||
|
return v.flField
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldName returns the field's name with the tag
|
||||||
|
// name taking precedence over the fields actual name.
|
||||||
|
func (v *validate) FieldName() string {
|
||||||
|
return v.cf.altName
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTag returns the current validations tag name
|
||||||
|
func (v *validate) GetTag() string {
|
||||||
|
return v.ct.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// StructFieldName returns the struct field's name
|
||||||
|
func (v *validate) StructFieldName() string {
|
||||||
|
return v.cf.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Param returns param for validation against current field
|
||||||
|
func (v *validate) Param() string {
|
||||||
|
return v.ct.param
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStructFieldOK returns Param returns param for validation against current field
|
||||||
|
//
|
||||||
|
// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable.
|
||||||
|
func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, bool) {
|
||||||
|
current, kind, _, found := v.getStructFieldOKInternal(v.slflParent, v.ct.param)
|
||||||
|
return current, kind, found
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
|
||||||
|
// the field and namespace allowing more extensibility for validators.
|
||||||
|
//
|
||||||
|
// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable.
|
||||||
|
func (v *validate) GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) {
|
||||||
|
current, kind, _, found := v.GetStructFieldOKAdvanced2(val, namespace)
|
||||||
|
return current, kind, found
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStructFieldOK returns Param returns param for validation against current field
|
||||||
|
func (v *validate) GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool) {
|
||||||
|
return v.getStructFieldOKInternal(v.slflParent, v.ct.param)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
|
||||||
|
// the field and namespace allowing more extensibility for validators.
|
||||||
|
func (v *validate) GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool) {
|
||||||
|
return v.getStructFieldOKInternal(val, namespace)
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||||
|
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
|
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||||
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
gin-valid/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||||
|
gin-valid/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
|
gin-valid/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||||
|
gin-valid/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
|
gin-valid/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||||
|
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||||
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||||
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
|
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||||
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
|
@ -0,0 +1,25 @@
|
||||||
|
package validators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NotBlank is the validation function for validating if the current field
|
||||||
|
// has a value or length greater than zero, or is not a space only string.
|
||||||
|
func NotBlank(fl validator.FieldLevel) bool {
|
||||||
|
field := fl.Field()
|
||||||
|
|
||||||
|
switch field.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
return len(strings.TrimSpace(field.String())) > 0
|
||||||
|
case reflect.Chan, reflect.Map, reflect.Slice, reflect.Array:
|
||||||
|
return field.Len() > 0
|
||||||
|
case reflect.Ptr, reflect.Interface, reflect.Func:
|
||||||
|
return !field.IsNil()
|
||||||
|
default:
|
||||||
|
return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package validators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
"github.com/go-playground/assert/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
String string `validate:"notblank"`
|
||||||
|
Array []int `validate:"notblank"`
|
||||||
|
Pointer *int `validate:"notblank"`
|
||||||
|
Number int `validate:"notblank"`
|
||||||
|
Interface interface{} `validate:"notblank"`
|
||||||
|
Func func() `validate:"notblank"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNotBlank(t *testing.T) {
|
||||||
|
v := validator.New()
|
||||||
|
err := v.RegisterValidation("notblank", NotBlank)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
var x *int
|
||||||
|
invalid := test{
|
||||||
|
String: " ",
|
||||||
|
Array: []int{},
|
||||||
|
Pointer: x,
|
||||||
|
Number: 0,
|
||||||
|
Interface: nil,
|
||||||
|
Func: nil,
|
||||||
|
}
|
||||||
|
fieldsWithError := []string{
|
||||||
|
"String",
|
||||||
|
"Array",
|
||||||
|
"Pointer",
|
||||||
|
"Number",
|
||||||
|
"Interface",
|
||||||
|
"Func",
|
||||||
|
}
|
||||||
|
|
||||||
|
errors := v.Struct(invalid).(validator.ValidationErrors)
|
||||||
|
var fields []string
|
||||||
|
for _, err := range errors {
|
||||||
|
fields = append(fields, err.Field())
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fieldsWithError, fields)
|
||||||
|
|
||||||
|
// No errors
|
||||||
|
y := 1
|
||||||
|
x = &y
|
||||||
|
valid := test{
|
||||||
|
String: "str",
|
||||||
|
Array: []int{1},
|
||||||
|
Pointer: x,
|
||||||
|
Number: 1,
|
||||||
|
Interface: "value",
|
||||||
|
Func: func() {},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = v.Struct(valid)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
package validator
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
const (
|
||||||
|
alphaRegexString = "^[a-zA-Z]+$"
|
||||||
|
alphaNumericRegexString = "^[a-zA-Z0-9]+$"
|
||||||
|
alphaUnicodeRegexString = "^[\\p{L}]+$"
|
||||||
|
alphaUnicodeNumericRegexString = "^[\\p{L}\\p{N}]+$"
|
||||||
|
numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$"
|
||||||
|
numberRegexString = "^[0-9]+$"
|
||||||
|
hexadecimalRegexString = "^(0[xX])?[0-9a-fA-F]+$"
|
||||||
|
hexcolorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
|
||||||
|
rgbRegexString = "^rgb\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*\\)$"
|
||||||
|
rgbaRegexString = "^rgba\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
|
||||||
|
hslRegexString = "^hsl\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*\\)$"
|
||||||
|
hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
|
||||||
|
emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
|
||||||
|
e164RegexString = "^\\+[1-9]?[0-9]{7,14}$"
|
||||||
|
base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
|
||||||
|
base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$"
|
||||||
|
iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$"
|
||||||
|
iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$"
|
||||||
|
uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||||
|
uUID4RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||||
|
uUID5RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||||
|
uUIDRegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||||
|
uUID3RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-3[0-9a-fA-F]{3}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
|
||||||
|
uUID4RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
|
||||||
|
uUID5RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-5[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
|
||||||
|
uUIDRFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
|
||||||
|
aSCIIRegexString = "^[\x00-\x7F]*$"
|
||||||
|
printableASCIIRegexString = "^[\x20-\x7E]*$"
|
||||||
|
multibyteRegexString = "[^\x00-\x7F]"
|
||||||
|
dataURIRegexString = `^data:((?:\w+\/(?:([^;]|;[^;]).)+)?)`
|
||||||
|
latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
|
||||||
|
longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
|
||||||
|
sSNRegexString = `^[0-9]{3}[ -]?(0[1-9]|[1-9][0-9])[ -]?([1-9][0-9]{3}|[0-9][1-9][0-9]{2}|[0-9]{2}[1-9][0-9]|[0-9]{3}[1-9])$`
|
||||||
|
hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952
|
||||||
|
hostnameRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*?$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123
|
||||||
|
fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,62})(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9]{0,62})\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.')
|
||||||
|
btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address
|
||||||
|
btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
|
||||||
|
btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
|
||||||
|
ethAddressRegexString = `^0x[0-9a-fA-F]{40}$`
|
||||||
|
ethAddressUpperRegexString = `^0x[0-9A-F]{40}$`
|
||||||
|
ethAddressLowerRegexString = `^0x[0-9a-f]{40}$`
|
||||||
|
uRLEncodedRegexString = `(%[A-Fa-f0-9]{2})`
|
||||||
|
hTMLEncodedRegexString = `&#[x]?([0-9a-fA-F]{2})|(>)|(<)|(")|(&)+[;]?`
|
||||||
|
hTMLRegexString = `<[/]?([a-zA-Z]+).*?>`
|
||||||
|
splitParamsRegexString = `'[^']*'|\S+`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
alphaRegex = regexp.MustCompile(alphaRegexString)
|
||||||
|
alphaNumericRegex = regexp.MustCompile(alphaNumericRegexString)
|
||||||
|
alphaUnicodeRegex = regexp.MustCompile(alphaUnicodeRegexString)
|
||||||
|
alphaUnicodeNumericRegex = regexp.MustCompile(alphaUnicodeNumericRegexString)
|
||||||
|
numericRegex = regexp.MustCompile(numericRegexString)
|
||||||
|
numberRegex = regexp.MustCompile(numberRegexString)
|
||||||
|
hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString)
|
||||||
|
hexcolorRegex = regexp.MustCompile(hexcolorRegexString)
|
||||||
|
rgbRegex = regexp.MustCompile(rgbRegexString)
|
||||||
|
rgbaRegex = regexp.MustCompile(rgbaRegexString)
|
||||||
|
hslRegex = regexp.MustCompile(hslRegexString)
|
||||||
|
hslaRegex = regexp.MustCompile(hslaRegexString)
|
||||||
|
e164Regex = regexp.MustCompile(e164RegexString)
|
||||||
|
emailRegex = regexp.MustCompile(emailRegexString)
|
||||||
|
base64Regex = regexp.MustCompile(base64RegexString)
|
||||||
|
base64URLRegex = regexp.MustCompile(base64URLRegexString)
|
||||||
|
iSBN10Regex = regexp.MustCompile(iSBN10RegexString)
|
||||||
|
iSBN13Regex = regexp.MustCompile(iSBN13RegexString)
|
||||||
|
uUID3Regex = regexp.MustCompile(uUID3RegexString)
|
||||||
|
uUID4Regex = regexp.MustCompile(uUID4RegexString)
|
||||||
|
uUID5Regex = regexp.MustCompile(uUID5RegexString)
|
||||||
|
uUIDRegex = regexp.MustCompile(uUIDRegexString)
|
||||||
|
uUID3RFC4122Regex = regexp.MustCompile(uUID3RFC4122RegexString)
|
||||||
|
uUID4RFC4122Regex = regexp.MustCompile(uUID4RFC4122RegexString)
|
||||||
|
uUID5RFC4122Regex = regexp.MustCompile(uUID5RFC4122RegexString)
|
||||||
|
uUIDRFC4122Regex = regexp.MustCompile(uUIDRFC4122RegexString)
|
||||||
|
aSCIIRegex = regexp.MustCompile(aSCIIRegexString)
|
||||||
|
printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString)
|
||||||
|
multibyteRegex = regexp.MustCompile(multibyteRegexString)
|
||||||
|
dataURIRegex = regexp.MustCompile(dataURIRegexString)
|
||||||
|
latitudeRegex = regexp.MustCompile(latitudeRegexString)
|
||||||
|
longitudeRegex = regexp.MustCompile(longitudeRegexString)
|
||||||
|
sSNRegex = regexp.MustCompile(sSNRegexString)
|
||||||
|
hostnameRegexRFC952 = regexp.MustCompile(hostnameRegexStringRFC952)
|
||||||
|
hostnameRegexRFC1123 = regexp.MustCompile(hostnameRegexStringRFC1123)
|
||||||
|
fqdnRegexRFC1123 = regexp.MustCompile(fqdnRegexStringRFC1123)
|
||||||
|
btcAddressRegex = regexp.MustCompile(btcAddressRegexString)
|
||||||
|
btcUpperAddressRegexBech32 = regexp.MustCompile(btcAddressUpperRegexStringBech32)
|
||||||
|
btcLowerAddressRegexBech32 = regexp.MustCompile(btcAddressLowerRegexStringBech32)
|
||||||
|
ethAddressRegex = regexp.MustCompile(ethAddressRegexString)
|
||||||
|
ethaddressRegexUpper = regexp.MustCompile(ethAddressUpperRegexString)
|
||||||
|
ethAddressRegexLower = regexp.MustCompile(ethAddressLowerRegexString)
|
||||||
|
uRLEncodedRegex = regexp.MustCompile(uRLEncodedRegexString)
|
||||||
|
hTMLEncodedRegex = regexp.MustCompile(hTMLEncodedRegexString)
|
||||||
|
hTMLRegex = regexp.MustCompile(hTMLRegexString)
|
||||||
|
splitParamsRegex = regexp.MustCompile(splitParamsRegexString)
|
||||||
|
)
|
|
@ -0,0 +1,175 @@
|
||||||
|
package validator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StructLevelFunc accepts all values needed for struct level validation
|
||||||
|
type StructLevelFunc func(sl StructLevel)
|
||||||
|
|
||||||
|
// StructLevelFuncCtx accepts all values needed for struct level validation
|
||||||
|
// but also allows passing of contextual validation information via context.Context.
|
||||||
|
type StructLevelFuncCtx func(ctx context.Context, sl StructLevel)
|
||||||
|
|
||||||
|
// wrapStructLevelFunc wraps normal StructLevelFunc makes it compatible with StructLevelFuncCtx
|
||||||
|
func wrapStructLevelFunc(fn StructLevelFunc) StructLevelFuncCtx {
|
||||||
|
return func(ctx context.Context, sl StructLevel) {
|
||||||
|
fn(sl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StructLevel contains all the information and helper functions
|
||||||
|
// to validate a struct
|
||||||
|
type StructLevel interface {
|
||||||
|
|
||||||
|
// returns the main validation object, in case one wants to call validations internally.
|
||||||
|
// this is so you don't have to use anonymous functions to get access to the validate
|
||||||
|
// instance.
|
||||||
|
Validator() *Validate
|
||||||
|
|
||||||
|
// returns the top level struct, if any
|
||||||
|
Top() reflect.Value
|
||||||
|
|
||||||
|
// returns the current fields parent struct, if any
|
||||||
|
Parent() reflect.Value
|
||||||
|
|
||||||
|
// returns the current struct.
|
||||||
|
Current() reflect.Value
|
||||||
|
|
||||||
|
// ExtractType gets the actual underlying type of field value.
|
||||||
|
// It will dive into pointers, customTypes and return you the
|
||||||
|
// underlying value and its kind.
|
||||||
|
ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool)
|
||||||
|
|
||||||
|
// reports an error just by passing the field and tag information
|
||||||
|
//
|
||||||
|
// NOTES:
|
||||||
|
//
|
||||||
|
// fieldName and altName get appended to the existing namespace that
|
||||||
|
// validator is on. e.g. pass 'FirstName' or 'Names[0]' depending
|
||||||
|
// on the nesting
|
||||||
|
//
|
||||||
|
// tag can be an existing validation tag or just something you make up
|
||||||
|
// and process on the flip side it's up to you.
|
||||||
|
ReportError(field interface{}, fieldName, structFieldName string, tag, param string)
|
||||||
|
|
||||||
|
// reports an error just by passing ValidationErrors
|
||||||
|
//
|
||||||
|
// NOTES:
|
||||||
|
//
|
||||||
|
// relativeNamespace and relativeActualNamespace get appended to the
|
||||||
|
// existing namespace that validator is on.
|
||||||
|
// e.g. pass 'User.FirstName' or 'Users[0].FirstName' depending
|
||||||
|
// on the nesting. most of the time they will be blank, unless you validate
|
||||||
|
// at a level lower the the current field depth
|
||||||
|
ReportValidationErrors(relativeNamespace, relativeActualNamespace string, errs ValidationErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ StructLevel = new(validate)
|
||||||
|
|
||||||
|
// Top returns the top level struct
|
||||||
|
//
|
||||||
|
// NOTE: this can be the same as the current struct being validated
|
||||||
|
// if not is a nested struct.
|
||||||
|
//
|
||||||
|
// this is only called when within Struct and Field Level validation and
|
||||||
|
// should not be relied upon for an acurate value otherwise.
|
||||||
|
func (v *validate) Top() reflect.Value {
|
||||||
|
return v.top
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parent returns the current structs parent
|
||||||
|
//
|
||||||
|
// NOTE: this can be the same as the current struct being validated
|
||||||
|
// if not is a nested struct.
|
||||||
|
//
|
||||||
|
// this is only called when within Struct and Field Level validation and
|
||||||
|
// should not be relied upon for an acurate value otherwise.
|
||||||
|
func (v *validate) Parent() reflect.Value {
|
||||||
|
return v.slflParent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current returns the current struct.
|
||||||
|
func (v *validate) Current() reflect.Value {
|
||||||
|
return v.slCurrent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validator returns the main validation object, in case one want to call validations internally.
|
||||||
|
func (v *validate) Validator() *Validate {
|
||||||
|
return v.v
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractType gets the actual underlying type of field value.
|
||||||
|
func (v *validate) ExtractType(field reflect.Value) (reflect.Value, reflect.Kind, bool) {
|
||||||
|
return v.extractTypeInternal(field, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReportError reports an error just by passing the field and tag information
|
||||||
|
func (v *validate) ReportError(field interface{}, fieldName, structFieldName, tag, param string) {
|
||||||
|
|
||||||
|
fv, kind, _ := v.extractTypeInternal(reflect.ValueOf(field), false)
|
||||||
|
|
||||||
|
if len(structFieldName) == 0 {
|
||||||
|
structFieldName = fieldName
|
||||||
|
}
|
||||||
|
|
||||||
|
v.str1 = string(append(v.ns, fieldName...))
|
||||||
|
|
||||||
|
if v.v.hasTagNameFunc || fieldName != structFieldName {
|
||||||
|
v.str2 = string(append(v.actualNs, structFieldName...))
|
||||||
|
} else {
|
||||||
|
v.str2 = v.str1
|
||||||
|
}
|
||||||
|
|
||||||
|
if kind == reflect.Invalid {
|
||||||
|
|
||||||
|
v.errs = append(v.errs,
|
||||||
|
&fieldError{
|
||||||
|
v: v.v,
|
||||||
|
tag: tag,
|
||||||
|
actualTag: tag,
|
||||||
|
ns: v.str1,
|
||||||
|
structNs: v.str2,
|
||||||
|
fieldLen: uint8(len(fieldName)),
|
||||||
|
structfieldLen: uint8(len(structFieldName)),
|
||||||
|
param: param,
|
||||||
|
kind: kind,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v.errs = append(v.errs,
|
||||||
|
&fieldError{
|
||||||
|
v: v.v,
|
||||||
|
tag: tag,
|
||||||
|
actualTag: tag,
|
||||||
|
ns: v.str1,
|
||||||
|
structNs: v.str2,
|
||||||
|
fieldLen: uint8(len(fieldName)),
|
||||||
|
structfieldLen: uint8(len(structFieldName)),
|
||||||
|
value: fv.Interface(),
|
||||||
|
param: param,
|
||||||
|
kind: kind,
|
||||||
|
typ: fv.Type(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReportValidationErrors reports ValidationErrors obtained from running validations within the Struct Level validation.
|
||||||
|
//
|
||||||
|
// NOTE: this function prepends the current namespace to the relative ones.
|
||||||
|
func (v *validate) ReportValidationErrors(relativeNamespace, relativeStructNamespace string, errs ValidationErrors) {
|
||||||
|
|
||||||
|
var err *fieldError
|
||||||
|
|
||||||
|
for i := 0; i < len(errs); i++ {
|
||||||
|
|
||||||
|
err = errs[i].(*fieldError)
|
||||||
|
err.ns = string(append(append(v.ns, relativeNamespace...), err.ns...))
|
||||||
|
err.structNs = string(append(append(v.actualNs, relativeStructNamespace...), err.structNs...))
|
||||||
|
|
||||||
|
v.errs = append(v.errs, err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
package testdata
|
|
@ -0,0 +1,11 @@
|
||||||
|
package validator
|
||||||
|
|
||||||
|
import ut "gin-valid/go-playground/universal-translator"
|
||||||
|
|
||||||
|
// TranslationFunc is the function type used to register or override
|
||||||
|
// custom translations
|
||||||
|
type TranslationFunc func(ut ut.Translator, fe FieldError) string
|
||||||
|
|
||||||
|
// RegisterTranslationsFunc allows for registering of translations
|
||||||
|
// for a 'ut.Translator' for use within the 'TranslationFunc'
|
||||||
|
type RegisterTranslationsFunc func(ut ut.Translator) error
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,676 @@
|
||||||
|
package en
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
english "gin-valid/go-playground/locales/en"
|
||||||
|
ut "gin-valid/go-playground/universal-translator"
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
. "github.com/go-playground/assert/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTranslations(t *testing.T) {
|
||||||
|
|
||||||
|
eng := english.New()
|
||||||
|
uni := ut.New(eng, eng)
|
||||||
|
trans, _ := uni.GetTranslator("en")
|
||||||
|
|
||||||
|
validate := validator.New()
|
||||||
|
|
||||||
|
err := RegisterDefaultTranslations(validate, trans)
|
||||||
|
Equal(t, err, nil)
|
||||||
|
|
||||||
|
type Inner struct {
|
||||||
|
EqCSFieldString string
|
||||||
|
NeCSFieldString string
|
||||||
|
GtCSFieldString string
|
||||||
|
GteCSFieldString string
|
||||||
|
LtCSFieldString string
|
||||||
|
LteCSFieldString string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Inner Inner
|
||||||
|
RequiredString string `validate:"required"`
|
||||||
|
RequiredNumber int `validate:"required"`
|
||||||
|
RequiredMultiple []string `validate:"required"`
|
||||||
|
LenString string `validate:"len=1"`
|
||||||
|
LenNumber float64 `validate:"len=1113.00"`
|
||||||
|
LenMultiple []string `validate:"len=7"`
|
||||||
|
MinString string `validate:"min=1"`
|
||||||
|
MinNumber float64 `validate:"min=1113.00"`
|
||||||
|
MinMultiple []string `validate:"min=7"`
|
||||||
|
MaxString string `validate:"max=3"`
|
||||||
|
MaxNumber float64 `validate:"max=1113.00"`
|
||||||
|
MaxMultiple []string `validate:"max=7"`
|
||||||
|
EqString string `validate:"eq=3"`
|
||||||
|
EqNumber float64 `validate:"eq=2.33"`
|
||||||
|
EqMultiple []string `validate:"eq=7"`
|
||||||
|
NeString string `validate:"ne="`
|
||||||
|
NeNumber float64 `validate:"ne=0.00"`
|
||||||
|
NeMultiple []string `validate:"ne=0"`
|
||||||
|
LtString string `validate:"lt=3"`
|
||||||
|
LtNumber float64 `validate:"lt=5.56"`
|
||||||
|
LtMultiple []string `validate:"lt=2"`
|
||||||
|
LtTime time.Time `validate:"lt"`
|
||||||
|
LteString string `validate:"lte=3"`
|
||||||
|
LteNumber float64 `validate:"lte=5.56"`
|
||||||
|
LteMultiple []string `validate:"lte=2"`
|
||||||
|
LteTime time.Time `validate:"lte"`
|
||||||
|
GtString string `validate:"gt=3"`
|
||||||
|
GtNumber float64 `validate:"gt=5.56"`
|
||||||
|
GtMultiple []string `validate:"gt=2"`
|
||||||
|
GtTime time.Time `validate:"gt"`
|
||||||
|
GteString string `validate:"gte=3"`
|
||||||
|
GteNumber float64 `validate:"gte=5.56"`
|
||||||
|
GteMultiple []string `validate:"gte=2"`
|
||||||
|
GteTime time.Time `validate:"gte"`
|
||||||
|
EqFieldString string `validate:"eqfield=MaxString"`
|
||||||
|
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
|
||||||
|
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
|
||||||
|
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
|
||||||
|
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
|
||||||
|
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
|
||||||
|
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
|
||||||
|
NeFieldString string `validate:"nefield=EqFieldString"`
|
||||||
|
GtFieldString string `validate:"gtfield=MaxString"`
|
||||||
|
GteFieldString string `validate:"gtefield=MaxString"`
|
||||||
|
LtFieldString string `validate:"ltfield=MaxString"`
|
||||||
|
LteFieldString string `validate:"ltefield=MaxString"`
|
||||||
|
AlphaString string `validate:"alpha"`
|
||||||
|
AlphanumString string `validate:"alphanum"`
|
||||||
|
NumericString string `validate:"numeric"`
|
||||||
|
NumberString string `validate:"number"`
|
||||||
|
HexadecimalString string `validate:"hexadecimal"`
|
||||||
|
HexColorString string `validate:"hexcolor"`
|
||||||
|
RGBColorString string `validate:"rgb"`
|
||||||
|
RGBAColorString string `validate:"rgba"`
|
||||||
|
HSLColorString string `validate:"hsl"`
|
||||||
|
HSLAColorString string `validate:"hsla"`
|
||||||
|
Email string `validate:"email"`
|
||||||
|
URL string `validate:"url"`
|
||||||
|
URI string `validate:"uri"`
|
||||||
|
Base64 string `validate:"base64"`
|
||||||
|
Contains string `validate:"contains=purpose"`
|
||||||
|
ContainsAny string `validate:"containsany=!@#$"`
|
||||||
|
Excludes string `validate:"excludes=text"`
|
||||||
|
ExcludesAll string `validate:"excludesall=!@#$"`
|
||||||
|
ExcludesRune string `validate:"excludesrune=☻"`
|
||||||
|
ISBN string `validate:"isbn"`
|
||||||
|
ISBN10 string `validate:"isbn10"`
|
||||||
|
ISBN13 string `validate:"isbn13"`
|
||||||
|
UUID string `validate:"uuid"`
|
||||||
|
UUID3 string `validate:"uuid3"`
|
||||||
|
UUID4 string `validate:"uuid4"`
|
||||||
|
UUID5 string `validate:"uuid5"`
|
||||||
|
ASCII string `validate:"ascii"`
|
||||||
|
PrintableASCII string `validate:"printascii"`
|
||||||
|
MultiByte string `validate:"multibyte"`
|
||||||
|
DataURI string `validate:"datauri"`
|
||||||
|
Latitude string `validate:"latitude"`
|
||||||
|
Longitude string `validate:"longitude"`
|
||||||
|
SSN string `validate:"ssn"`
|
||||||
|
IP string `validate:"ip"`
|
||||||
|
IPv4 string `validate:"ipv4"`
|
||||||
|
IPv6 string `validate:"ipv6"`
|
||||||
|
CIDR string `validate:"cidr"`
|
||||||
|
CIDRv4 string `validate:"cidrv4"`
|
||||||
|
CIDRv6 string `validate:"cidrv6"`
|
||||||
|
TCPAddr string `validate:"tcp_addr"`
|
||||||
|
TCPAddrv4 string `validate:"tcp4_addr"`
|
||||||
|
TCPAddrv6 string `validate:"tcp6_addr"`
|
||||||
|
UDPAddr string `validate:"udp_addr"`
|
||||||
|
UDPAddrv4 string `validate:"udp4_addr"`
|
||||||
|
UDPAddrv6 string `validate:"udp6_addr"`
|
||||||
|
IPAddr string `validate:"ip_addr"`
|
||||||
|
IPAddrv4 string `validate:"ip4_addr"`
|
||||||
|
IPAddrv6 string `validate:"ip6_addr"`
|
||||||
|
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
|
||||||
|
MAC string `validate:"mac"`
|
||||||
|
IsColor string `validate:"iscolor"`
|
||||||
|
StrPtrMinLen *string `validate:"min=10"`
|
||||||
|
StrPtrMaxLen *string `validate:"max=1"`
|
||||||
|
StrPtrLen *string `validate:"len=2"`
|
||||||
|
StrPtrLt *string `validate:"lt=1"`
|
||||||
|
StrPtrLte *string `validate:"lte=1"`
|
||||||
|
StrPtrGt *string `validate:"gt=10"`
|
||||||
|
StrPtrGte *string `validate:"gte=10"`
|
||||||
|
OneOfString string `validate:"oneof=red green"`
|
||||||
|
OneOfInt int `validate:"oneof=5 63"`
|
||||||
|
UniqueSlice []string `validate:"unique"`
|
||||||
|
UniqueArray [3]string `validate:"unique"`
|
||||||
|
UniqueMap map[string]string `validate:"unique"`
|
||||||
|
JSONString string `validate:"json"`
|
||||||
|
LowercaseString string `validate:"lowercase"`
|
||||||
|
UppercaseString string `validate:"uppercase"`
|
||||||
|
Datetime string `validate:"datetime=2006-01-02"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var test Test
|
||||||
|
|
||||||
|
test.Inner.EqCSFieldString = "1234"
|
||||||
|
test.Inner.GtCSFieldString = "1234"
|
||||||
|
test.Inner.GteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.MaxString = "1234"
|
||||||
|
test.MaxNumber = 2000
|
||||||
|
test.MaxMultiple = make([]string, 9)
|
||||||
|
|
||||||
|
test.LtString = "1234"
|
||||||
|
test.LtNumber = 6
|
||||||
|
test.LtMultiple = make([]string, 3)
|
||||||
|
test.LtTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LteString = "1234"
|
||||||
|
test.LteNumber = 6
|
||||||
|
test.LteMultiple = make([]string, 3)
|
||||||
|
test.LteTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LtFieldString = "12345"
|
||||||
|
test.LteFieldString = "12345"
|
||||||
|
|
||||||
|
test.LtCSFieldString = "1234"
|
||||||
|
test.LteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.AlphaString = "abc3"
|
||||||
|
test.AlphanumString = "abc3!"
|
||||||
|
test.NumericString = "12E.00"
|
||||||
|
test.NumberString = "12E"
|
||||||
|
|
||||||
|
test.Excludes = "this is some test text"
|
||||||
|
test.ExcludesAll = "This is Great!"
|
||||||
|
test.ExcludesRune = "Love it ☻"
|
||||||
|
|
||||||
|
test.ASCII = "カタカナ"
|
||||||
|
test.PrintableASCII = "カタカナ"
|
||||||
|
|
||||||
|
test.MultiByte = "1234feerf"
|
||||||
|
|
||||||
|
test.LowercaseString = "ABCDEFG"
|
||||||
|
test.UppercaseString = "abcdefg"
|
||||||
|
|
||||||
|
s := "toolong"
|
||||||
|
test.StrPtrMaxLen = &s
|
||||||
|
test.StrPtrLen = &s
|
||||||
|
|
||||||
|
test.UniqueSlice = []string{"1234", "1234"}
|
||||||
|
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
|
||||||
|
test.Datetime = "2008-Feb-01"
|
||||||
|
|
||||||
|
err = validate.Struct(test)
|
||||||
|
NotEqual(t, err, nil)
|
||||||
|
|
||||||
|
errs, ok := err.(validator.ValidationErrors)
|
||||||
|
Equal(t, ok, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
ns string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ns: "Test.IsColor",
|
||||||
|
expected: "IsColor must be a valid color",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MAC",
|
||||||
|
expected: "MAC must contain a valid MAC address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddr",
|
||||||
|
expected: "IPAddr must be a resolvable IP address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv4",
|
||||||
|
expected: "IPAddrv4 must be a resolvable IPv4 address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv6",
|
||||||
|
expected: "IPAddrv6 must be a resolvable IPv6 address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddr",
|
||||||
|
expected: "UDPAddr must be a valid UDP address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv4",
|
||||||
|
expected: "UDPAddrv4 must be a valid IPv4 UDP address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv6",
|
||||||
|
expected: "UDPAddrv6 must be a valid IPv6 UDP address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddr",
|
||||||
|
expected: "TCPAddr must be a valid TCP address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv4",
|
||||||
|
expected: "TCPAddrv4 must be a valid IPv4 TCP address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv6",
|
||||||
|
expected: "TCPAddrv6 must be a valid IPv6 TCP address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDR",
|
||||||
|
expected: "CIDR must contain a valid CIDR notation",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv4",
|
||||||
|
expected: "CIDRv4 must contain a valid CIDR notation for an IPv4 address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv6",
|
||||||
|
expected: "CIDRv6 must contain a valid CIDR notation for an IPv6 address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.SSN",
|
||||||
|
expected: "SSN must be a valid SSN number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IP",
|
||||||
|
expected: "IP must be a valid IP address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv4",
|
||||||
|
expected: "IPv4 must be a valid IPv4 address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv6",
|
||||||
|
expected: "IPv6 must be a valid IPv6 address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.DataURI",
|
||||||
|
expected: "DataURI must contain a valid Data URI",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Latitude",
|
||||||
|
expected: "Latitude must contain valid latitude coordinates",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Longitude",
|
||||||
|
expected: "Longitude must contain a valid longitude coordinates",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MultiByte",
|
||||||
|
expected: "MultiByte must contain multibyte characters",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ASCII",
|
||||||
|
expected: "ASCII must contain only ascii characters",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.PrintableASCII",
|
||||||
|
expected: "PrintableASCII must contain only printable ascii characters",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID",
|
||||||
|
expected: "UUID must be a valid UUID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID3",
|
||||||
|
expected: "UUID3 must be a valid version 3 UUID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID4",
|
||||||
|
expected: "UUID4 must be a valid version 4 UUID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID5",
|
||||||
|
expected: "UUID5 must be a valid version 5 UUID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN",
|
||||||
|
expected: "ISBN must be a valid ISBN number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN10",
|
||||||
|
expected: "ISBN10 must be a valid ISBN-10 number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN13",
|
||||||
|
expected: "ISBN13 must be a valid ISBN-13 number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Excludes",
|
||||||
|
expected: "Excludes cannot contain the text 'text'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesAll",
|
||||||
|
expected: "ExcludesAll cannot contain any of the following characters '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesRune",
|
||||||
|
expected: "ExcludesRune cannot contain the following '☻'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ContainsAny",
|
||||||
|
expected: "ContainsAny must contain at least one of the following characters '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Contains",
|
||||||
|
expected: "Contains must contain the text 'purpose'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Base64",
|
||||||
|
expected: "Base64 must be a valid Base64 string",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Email",
|
||||||
|
expected: "Email must be a valid email address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URL",
|
||||||
|
expected: "URL must be a valid URL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URI",
|
||||||
|
expected: "URI must be a valid URI",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBColorString",
|
||||||
|
expected: "RGBColorString must be a valid RGB color",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBAColorString",
|
||||||
|
expected: "RGBAColorString must be a valid RGBA color",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLColorString",
|
||||||
|
expected: "HSLColorString must be a valid HSL color",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLAColorString",
|
||||||
|
expected: "HSLAColorString must be a valid HSLA color",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexadecimalString",
|
||||||
|
expected: "HexadecimalString must be a valid hexadecimal",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexColorString",
|
||||||
|
expected: "HexColorString must be a valid HEX color",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumberString",
|
||||||
|
expected: "NumberString must be a valid number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumericString",
|
||||||
|
expected: "NumericString must be a valid numeric value",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphanumString",
|
||||||
|
expected: "AlphanumString can only contain alphanumeric characters",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphaString",
|
||||||
|
expected: "AlphaString can only contain alphabetic characters",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtFieldString",
|
||||||
|
expected: "LtFieldString must be less than MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteFieldString",
|
||||||
|
expected: "LteFieldString must be less than or equal to MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtFieldString",
|
||||||
|
expected: "GtFieldString must be greater than MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteFieldString",
|
||||||
|
expected: "GteFieldString must be greater than or equal to MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeFieldString",
|
||||||
|
expected: "NeFieldString cannot be equal to EqFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtCSFieldString",
|
||||||
|
expected: "LtCSFieldString must be less than Inner.LtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteCSFieldString",
|
||||||
|
expected: "LteCSFieldString must be less than or equal to Inner.LteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtCSFieldString",
|
||||||
|
expected: "GtCSFieldString must be greater than Inner.GtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteCSFieldString",
|
||||||
|
expected: "GteCSFieldString must be greater than or equal to Inner.GteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeCSFieldString",
|
||||||
|
expected: "NeCSFieldString cannot be equal to Inner.NeCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqCSFieldString",
|
||||||
|
expected: "EqCSFieldString must be equal to Inner.EqCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqFieldString",
|
||||||
|
expected: "EqFieldString must be equal to MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteString",
|
||||||
|
expected: "GteString must be at least 3 characters in length",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteNumber",
|
||||||
|
expected: "GteNumber must be 5.56 or greater",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteMultiple",
|
||||||
|
expected: "GteMultiple must contain at least 2 items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteTime",
|
||||||
|
expected: "GteTime must be greater than or equal to the current Date & Time",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtString",
|
||||||
|
expected: "GtString must be greater than 3 characters in length",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtNumber",
|
||||||
|
expected: "GtNumber must be greater than 5.56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtMultiple",
|
||||||
|
expected: "GtMultiple must contain more than 2 items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtTime",
|
||||||
|
expected: "GtTime must be greater than the current Date & Time",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteString",
|
||||||
|
expected: "LteString must be at maximum 3 characters in length",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteNumber",
|
||||||
|
expected: "LteNumber must be 5.56 or less",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteMultiple",
|
||||||
|
expected: "LteMultiple must contain at maximum 2 items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteTime",
|
||||||
|
expected: "LteTime must be less than or equal to the current Date & Time",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtString",
|
||||||
|
expected: "LtString must be less than 3 characters in length",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtNumber",
|
||||||
|
expected: "LtNumber must be less than 5.56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtMultiple",
|
||||||
|
expected: "LtMultiple must contain less than 2 items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtTime",
|
||||||
|
expected: "LtTime must be less than the current Date & Time",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeString",
|
||||||
|
expected: "NeString should not be equal to ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeNumber",
|
||||||
|
expected: "NeNumber should not be equal to 0.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeMultiple",
|
||||||
|
expected: "NeMultiple should not be equal to 0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqString",
|
||||||
|
expected: "EqString is not equal to 3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqNumber",
|
||||||
|
expected: "EqNumber is not equal to 2.33",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqMultiple",
|
||||||
|
expected: "EqMultiple is not equal to 7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxString",
|
||||||
|
expected: "MaxString must be a maximum of 3 characters in length",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxNumber",
|
||||||
|
expected: "MaxNumber must be 1,113.00 or less",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxMultiple",
|
||||||
|
expected: "MaxMultiple must contain at maximum 7 items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinString",
|
||||||
|
expected: "MinString must be at least 1 character in length",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinNumber",
|
||||||
|
expected: "MinNumber must be 1,113.00 or greater",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinMultiple",
|
||||||
|
expected: "MinMultiple must contain at least 7 items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenString",
|
||||||
|
expected: "LenString must be 1 character in length",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenNumber",
|
||||||
|
expected: "LenNumber must be equal to 1,113.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenMultiple",
|
||||||
|
expected: "LenMultiple must contain 7 items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredString",
|
||||||
|
expected: "RequiredString is a required field",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredNumber",
|
||||||
|
expected: "RequiredNumber is a required field",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredMultiple",
|
||||||
|
expected: "RequiredMultiple is a required field",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMinLen",
|
||||||
|
expected: "StrPtrMinLen must be at least 10 characters in length",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMaxLen",
|
||||||
|
expected: "StrPtrMaxLen must be a maximum of 1 character in length",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLen",
|
||||||
|
expected: "StrPtrLen must be 2 characters in length",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLt",
|
||||||
|
expected: "StrPtrLt must be less than 1 character in length",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLte",
|
||||||
|
expected: "StrPtrLte must be at maximum 1 character in length",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGt",
|
||||||
|
expected: "StrPtrGt must be greater than 10 characters in length",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGte",
|
||||||
|
expected: "StrPtrGte must be at least 10 characters in length",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfString",
|
||||||
|
expected: "OneOfString must be one of [red green]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfInt",
|
||||||
|
expected: "OneOfInt must be one of [5 63]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueSlice",
|
||||||
|
expected: "UniqueSlice must contain unique values",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueArray",
|
||||||
|
expected: "UniqueArray must contain unique values",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueMap",
|
||||||
|
expected: "UniqueMap must contain unique values",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.JSONString",
|
||||||
|
expected: "JSONString must be a valid json string",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LowercaseString",
|
||||||
|
expected: "LowercaseString must be a lowercase string",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UppercaseString",
|
||||||
|
expected: "UppercaseString must be an uppercase string",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Datetime",
|
||||||
|
expected: "Datetime does not match the 2006-01-02 format",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
|
||||||
|
var fe validator.FieldError
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
if tt.ns == e.Namespace() {
|
||||||
|
fe = e
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotEqual(t, fe, nil)
|
||||||
|
Equal(t, tt.expected, fe.Translate(trans))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,652 @@
|
||||||
|
package es
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
spanish "gin-valid/go-playground/locales/es"
|
||||||
|
ut "gin-valid/go-playground/universal-translator"
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
. "github.com/go-playground/assert/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTranslations(t *testing.T) {
|
||||||
|
|
||||||
|
spa := spanish.New()
|
||||||
|
uni := ut.New(spa, spa)
|
||||||
|
trans, _ := uni.GetTranslator("es")
|
||||||
|
|
||||||
|
validate := validator.New()
|
||||||
|
|
||||||
|
err := RegisterDefaultTranslations(validate, trans)
|
||||||
|
Equal(t, err, nil)
|
||||||
|
|
||||||
|
type Inner struct {
|
||||||
|
EqCSFieldString string
|
||||||
|
NeCSFieldString string
|
||||||
|
GtCSFieldString string
|
||||||
|
GteCSFieldString string
|
||||||
|
LtCSFieldString string
|
||||||
|
LteCSFieldString string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Inner Inner
|
||||||
|
RequiredString string `validate:"required"`
|
||||||
|
RequiredNumber int `validate:"required"`
|
||||||
|
RequiredMultiple []string `validate:"required"`
|
||||||
|
LenString string `validate:"len=1"`
|
||||||
|
LenNumber float64 `validate:"len=1113.00"`
|
||||||
|
LenMultiple []string `validate:"len=7"`
|
||||||
|
MinString string `validate:"min=1"`
|
||||||
|
MinNumber float64 `validate:"min=1113.00"`
|
||||||
|
MinMultiple []string `validate:"min=7"`
|
||||||
|
MaxString string `validate:"max=3"`
|
||||||
|
MaxNumber float64 `validate:"max=1113.00"`
|
||||||
|
MaxMultiple []string `validate:"max=7"`
|
||||||
|
EqString string `validate:"eq=3"`
|
||||||
|
EqNumber float64 `validate:"eq=2.33"`
|
||||||
|
EqMultiple []string `validate:"eq=7"`
|
||||||
|
NeString string `validate:"ne="`
|
||||||
|
NeNumber float64 `validate:"ne=0.00"`
|
||||||
|
NeMultiple []string `validate:"ne=0"`
|
||||||
|
LtString string `validate:"lt=3"`
|
||||||
|
LtNumber float64 `validate:"lt=5.56"`
|
||||||
|
LtMultiple []string `validate:"lt=2"`
|
||||||
|
LtTime time.Time `validate:"lt"`
|
||||||
|
LteString string `validate:"lte=3"`
|
||||||
|
LteNumber float64 `validate:"lte=5.56"`
|
||||||
|
LteMultiple []string `validate:"lte=2"`
|
||||||
|
LteTime time.Time `validate:"lte"`
|
||||||
|
GtString string `validate:"gt=3"`
|
||||||
|
GtNumber float64 `validate:"gt=5.56"`
|
||||||
|
GtMultiple []string `validate:"gt=2"`
|
||||||
|
GtTime time.Time `validate:"gt"`
|
||||||
|
GteString string `validate:"gte=3"`
|
||||||
|
GteNumber float64 `validate:"gte=5.56"`
|
||||||
|
GteMultiple []string `validate:"gte=2"`
|
||||||
|
GteTime time.Time `validate:"gte"`
|
||||||
|
EqFieldString string `validate:"eqfield=MaxString"`
|
||||||
|
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
|
||||||
|
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
|
||||||
|
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
|
||||||
|
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
|
||||||
|
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
|
||||||
|
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
|
||||||
|
NeFieldString string `validate:"nefield=EqFieldString"`
|
||||||
|
GtFieldString string `validate:"gtfield=MaxString"`
|
||||||
|
GteFieldString string `validate:"gtefield=MaxString"`
|
||||||
|
LtFieldString string `validate:"ltfield=MaxString"`
|
||||||
|
LteFieldString string `validate:"ltefield=MaxString"`
|
||||||
|
AlphaString string `validate:"alpha"`
|
||||||
|
AlphanumString string `validate:"alphanum"`
|
||||||
|
NumericString string `validate:"numeric"`
|
||||||
|
NumberString string `validate:"number"`
|
||||||
|
HexadecimalString string `validate:"hexadecimal"`
|
||||||
|
HexColorString string `validate:"hexcolor"`
|
||||||
|
RGBColorString string `validate:"rgb"`
|
||||||
|
RGBAColorString string `validate:"rgba"`
|
||||||
|
HSLColorString string `validate:"hsl"`
|
||||||
|
HSLAColorString string `validate:"hsla"`
|
||||||
|
Email string `validate:"email"`
|
||||||
|
URL string `validate:"url"`
|
||||||
|
URI string `validate:"uri"`
|
||||||
|
Base64 string `validate:"base64"`
|
||||||
|
Contains string `validate:"contains=purpose"`
|
||||||
|
ContainsAny string `validate:"containsany=!@#$"`
|
||||||
|
Excludes string `validate:"excludes=text"`
|
||||||
|
ExcludesAll string `validate:"excludesall=!@#$"`
|
||||||
|
ExcludesRune string `validate:"excludesrune=☻"`
|
||||||
|
ISBN string `validate:"isbn"`
|
||||||
|
ISBN10 string `validate:"isbn10"`
|
||||||
|
ISBN13 string `validate:"isbn13"`
|
||||||
|
UUID string `validate:"uuid"`
|
||||||
|
UUID3 string `validate:"uuid3"`
|
||||||
|
UUID4 string `validate:"uuid4"`
|
||||||
|
UUID5 string `validate:"uuid5"`
|
||||||
|
ASCII string `validate:"ascii"`
|
||||||
|
PrintableASCII string `validate:"printascii"`
|
||||||
|
MultiByte string `validate:"multibyte"`
|
||||||
|
DataURI string `validate:"datauri"`
|
||||||
|
Latitude string `validate:"latitude"`
|
||||||
|
Longitude string `validate:"longitude"`
|
||||||
|
SSN string `validate:"ssn"`
|
||||||
|
IP string `validate:"ip"`
|
||||||
|
IPv4 string `validate:"ipv4"`
|
||||||
|
IPv6 string `validate:"ipv6"`
|
||||||
|
CIDR string `validate:"cidr"`
|
||||||
|
CIDRv4 string `validate:"cidrv4"`
|
||||||
|
CIDRv6 string `validate:"cidrv6"`
|
||||||
|
TCPAddr string `validate:"tcp_addr"`
|
||||||
|
TCPAddrv4 string `validate:"tcp4_addr"`
|
||||||
|
TCPAddrv6 string `validate:"tcp6_addr"`
|
||||||
|
UDPAddr string `validate:"udp_addr"`
|
||||||
|
UDPAddrv4 string `validate:"udp4_addr"`
|
||||||
|
UDPAddrv6 string `validate:"udp6_addr"`
|
||||||
|
IPAddr string `validate:"ip_addr"`
|
||||||
|
IPAddrv4 string `validate:"ip4_addr"`
|
||||||
|
IPAddrv6 string `validate:"ip6_addr"`
|
||||||
|
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
|
||||||
|
MAC string `validate:"mac"`
|
||||||
|
IsColor string `validate:"iscolor"`
|
||||||
|
StrPtrMinLen *string `validate:"min=10"`
|
||||||
|
StrPtrMaxLen *string `validate:"max=1"`
|
||||||
|
StrPtrLen *string `validate:"len=2"`
|
||||||
|
StrPtrLt *string `validate:"lt=1"`
|
||||||
|
StrPtrLte *string `validate:"lte=1"`
|
||||||
|
StrPtrGt *string `validate:"gt=10"`
|
||||||
|
StrPtrGte *string `validate:"gte=10"`
|
||||||
|
OneOfString string `validate:"oneof=red green"`
|
||||||
|
OneOfInt int `validate:"oneof=5 63"`
|
||||||
|
UniqueSlice []string `validate:"unique"`
|
||||||
|
UniqueArray [3]string `validate:"unique"`
|
||||||
|
UniqueMap map[string]string `validate:"unique"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var test Test
|
||||||
|
|
||||||
|
test.Inner.EqCSFieldString = "1234"
|
||||||
|
test.Inner.GtCSFieldString = "1234"
|
||||||
|
test.Inner.GteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.MaxString = "1234"
|
||||||
|
test.MaxNumber = 2000
|
||||||
|
test.MaxMultiple = make([]string, 9)
|
||||||
|
|
||||||
|
test.LtString = "1234"
|
||||||
|
test.LtNumber = 6
|
||||||
|
test.LtMultiple = make([]string, 3)
|
||||||
|
test.LtTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LteString = "1234"
|
||||||
|
test.LteNumber = 6
|
||||||
|
test.LteMultiple = make([]string, 3)
|
||||||
|
test.LteTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LtFieldString = "12345"
|
||||||
|
test.LteFieldString = "12345"
|
||||||
|
|
||||||
|
test.LtCSFieldString = "1234"
|
||||||
|
test.LteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.AlphaString = "abc3"
|
||||||
|
test.AlphanumString = "abc3!"
|
||||||
|
test.NumericString = "12E.00"
|
||||||
|
test.NumberString = "12E"
|
||||||
|
|
||||||
|
test.Excludes = "this is some test text"
|
||||||
|
test.ExcludesAll = "This is Great!"
|
||||||
|
test.ExcludesRune = "Love it ☻"
|
||||||
|
|
||||||
|
test.ASCII = "カタカナ"
|
||||||
|
test.PrintableASCII = "カタカナ"
|
||||||
|
|
||||||
|
test.MultiByte = "1234feerf"
|
||||||
|
|
||||||
|
s := "toolong"
|
||||||
|
test.StrPtrMaxLen = &s
|
||||||
|
test.StrPtrLen = &s
|
||||||
|
|
||||||
|
test.UniqueSlice = []string{"1234", "1234"}
|
||||||
|
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
|
||||||
|
|
||||||
|
err = validate.Struct(test)
|
||||||
|
NotEqual(t, err, nil)
|
||||||
|
|
||||||
|
errs, ok := err.(validator.ValidationErrors)
|
||||||
|
Equal(t, ok, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
ns string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ns: "Test.IsColor",
|
||||||
|
expected: "IsColor debe ser un color válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MAC",
|
||||||
|
expected: "MAC debe contener una dirección MAC válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddr",
|
||||||
|
expected: "IPAddr debe ser una dirección IP resoluble",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv4",
|
||||||
|
expected: "IPAddrv4 debe ser una dirección IPv4 resoluble",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv6",
|
||||||
|
expected: "IPAddrv6 debe ser una dirección IPv6 resoluble",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddr",
|
||||||
|
expected: "UDPAddr debe ser una dirección UDP válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv4",
|
||||||
|
expected: "UDPAddrv4 debe ser una dirección IPv4 UDP válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv6",
|
||||||
|
expected: "UDPAddrv6 debe ser una dirección IPv6 UDP válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddr",
|
||||||
|
expected: "TCPAddr debe ser una dirección TCP válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv4",
|
||||||
|
expected: "TCPAddrv4 debe ser una dirección IPv4 TCP válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv6",
|
||||||
|
expected: "TCPAddrv6 debe ser una dirección IPv6 TCP válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDR",
|
||||||
|
expected: "CIDR debe contener una anotación válida del CIDR",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv4",
|
||||||
|
expected: "CIDRv4 debe contener una notación CIDR válida para una dirección IPv4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv6",
|
||||||
|
expected: "CIDRv6 debe contener una notación CIDR válida para una dirección IPv6",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.SSN",
|
||||||
|
expected: "SSN debe ser un número válido de SSN",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IP",
|
||||||
|
expected: "IP debe ser una dirección IP válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv4",
|
||||||
|
expected: "IPv4 debe ser una dirección IPv4 válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv6",
|
||||||
|
expected: "IPv6 debe ser una dirección IPv6 válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.DataURI",
|
||||||
|
expected: "DataURI debe contener un URI de datos válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Latitude",
|
||||||
|
expected: "Latitude debe contener coordenadas de latitud válidas",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Longitude",
|
||||||
|
expected: "Longitude debe contener unas coordenadas de longitud válidas",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MultiByte",
|
||||||
|
expected: "MultiByte debe contener caracteres multibyte",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ASCII",
|
||||||
|
expected: "ASCII debe contener sólo caracteres ascii",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.PrintableASCII",
|
||||||
|
expected: "PrintableASCII debe contener sólo caracteres ASCII imprimibles",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID",
|
||||||
|
expected: "UUID debe ser un UUID válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID3",
|
||||||
|
expected: "UUID3 debe ser una versión válida 3 UUID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID4",
|
||||||
|
expected: "UUID4 debe ser una versión válida 4 UUID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID5",
|
||||||
|
expected: "UUID5 debe ser una versión válida 5 UUID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN",
|
||||||
|
expected: "ISBN debe ser un número ISBN válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN10",
|
||||||
|
expected: "ISBN10 debe ser un número ISBN-10 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN13",
|
||||||
|
expected: "ISBN13 debe ser un número ISBN-13 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Excludes",
|
||||||
|
expected: "Excludes no puede contener el texto 'text'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesAll",
|
||||||
|
expected: "ExcludesAll no puede contener ninguno de los siguientes caracteres '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesRune",
|
||||||
|
expected: "ExcludesRune no puede contener lo siguiente '☻'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ContainsAny",
|
||||||
|
expected: "ContainsAny debe contener al menos uno de los siguientes caracteres '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Contains",
|
||||||
|
expected: "Contains debe contener el texto 'purpose'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Base64",
|
||||||
|
expected: "Base64 debe ser una cadena de Base64 válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Email",
|
||||||
|
expected: "Email debe ser una dirección de correo electrónico válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URL",
|
||||||
|
expected: "URL debe ser un URL válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URI",
|
||||||
|
expected: "URI debe ser una URI válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBColorString",
|
||||||
|
expected: "RGBColorString debe ser un color RGB válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBAColorString",
|
||||||
|
expected: "RGBAColorString debe ser un color RGBA válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLColorString",
|
||||||
|
expected: "HSLColorString debe ser un color HSL válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLAColorString",
|
||||||
|
expected: "HSLAColorString debe ser un color HSL válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexadecimalString",
|
||||||
|
expected: "HexadecimalString debe ser un hexadecimal válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexColorString",
|
||||||
|
expected: "HexColorString debe ser un color HEX válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumberString",
|
||||||
|
expected: "NumberString debe ser un número válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumericString",
|
||||||
|
expected: "NumericString debe ser un valor numérico válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphanumString",
|
||||||
|
expected: "AlphanumString sólo puede contener caracteres alfanuméricos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphaString",
|
||||||
|
expected: "AlphaString sólo puede contener caracteres alfabéticos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtFieldString",
|
||||||
|
expected: "LtFieldString debe ser menor que MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteFieldString",
|
||||||
|
expected: "LteFieldString debe ser menor o igual a MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtFieldString",
|
||||||
|
expected: "GtFieldString debe ser mayor que MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteFieldString",
|
||||||
|
expected: "GteFieldString debe ser mayor o igual a MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeFieldString",
|
||||||
|
expected: "NeFieldString no puede ser igual a EqFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtCSFieldString",
|
||||||
|
expected: "LtCSFieldString debe ser menor que Inner.LtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteCSFieldString",
|
||||||
|
expected: "LteCSFieldString debe ser menor o igual a Inner.LteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtCSFieldString",
|
||||||
|
expected: "GtCSFieldString debe ser mayor que Inner.GtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteCSFieldString",
|
||||||
|
expected: "GteCSFieldString debe ser mayor o igual a Inner.GteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeCSFieldString",
|
||||||
|
expected: "NeCSFieldString no puede ser igual a Inner.NeCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqCSFieldString",
|
||||||
|
expected: "EqCSFieldString debe ser igual a Inner.EqCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqFieldString",
|
||||||
|
expected: "EqFieldString debe ser igual a MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteString",
|
||||||
|
expected: "GteString debe tener al menos 3 caracteres de longitud",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteNumber",
|
||||||
|
expected: "GteNumber debe ser 5,56 o mayor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteMultiple",
|
||||||
|
expected: "GteMultiple debe contener al menos 2 elementos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteTime",
|
||||||
|
expected: "GteTime debe ser después o durante la fecha y hora actuales",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtString",
|
||||||
|
expected: "GtString debe ser mayor que 3 caracteres en longitud",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtNumber",
|
||||||
|
expected: "GtNumber debe ser mayor que 5,56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtMultiple",
|
||||||
|
expected: "GtMultiple debe contener más de 2 elementos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtTime",
|
||||||
|
expected: "GtTime debe ser después de la fecha y hora actual",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteString",
|
||||||
|
expected: "LteString debe tener un máximo de 3 caracteres de longitud",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteNumber",
|
||||||
|
expected: "LteNumber debe ser 5,56 o menos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteMultiple",
|
||||||
|
expected: "LteMultiple debe contener como máximo 2 elementos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteTime",
|
||||||
|
expected: "LteTime debe ser antes o durante la fecha y hora actual",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtString",
|
||||||
|
expected: "LtString debe tener menos de 3 caracteres de longitud",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtNumber",
|
||||||
|
expected: "LtNumber debe ser menos de 5,56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtMultiple",
|
||||||
|
expected: "LtMultiple debe contener menos de 2 elementos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtTime",
|
||||||
|
expected: "LtTime debe ser antes de la fecha y hora actual",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeString",
|
||||||
|
expected: "NeString no debería ser igual a ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeNumber",
|
||||||
|
expected: "NeNumber no debería ser igual a 0.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeMultiple",
|
||||||
|
expected: "NeMultiple no debería ser igual a 0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqString",
|
||||||
|
expected: "EqString no es igual a 3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqNumber",
|
||||||
|
expected: "EqNumber no es igual a 2.33",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqMultiple",
|
||||||
|
expected: "EqMultiple no es igual a 7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxString",
|
||||||
|
expected: "MaxString debe tener un máximo de 3 caracteres de longitud",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxNumber",
|
||||||
|
expected: "MaxNumber debe ser 1.113,00 o menos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxMultiple",
|
||||||
|
expected: "MaxMultiple debe contener como máximo 7 elementos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinString",
|
||||||
|
expected: "MinString debe tener al menos 1 carácter de longitud",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinNumber",
|
||||||
|
expected: "MinNumber debe ser 1.113,00 o más",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinMultiple",
|
||||||
|
expected: "MinMultiple debe contener al menos 7 elementos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenString",
|
||||||
|
expected: "LenString debe tener 1 carácter de longitud",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenNumber",
|
||||||
|
expected: "LenNumber debe ser igual a 1.113,00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenMultiple",
|
||||||
|
expected: "LenMultiple debe contener 7 elementos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredString",
|
||||||
|
expected: "RequiredString es un campo requerido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredNumber",
|
||||||
|
expected: "RequiredNumber es un campo requerido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredMultiple",
|
||||||
|
expected: "RequiredMultiple es un campo requerido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMinLen",
|
||||||
|
expected: "StrPtrMinLen debe tener al menos 10 caracteres de longitud",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMaxLen",
|
||||||
|
expected: "StrPtrMaxLen debe tener un máximo de 1 carácter de longitud",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLen",
|
||||||
|
expected: "StrPtrLen debe tener 2 caracteres de longitud",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLt",
|
||||||
|
expected: "StrPtrLt debe tener menos de 1 carácter de longitud",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLte",
|
||||||
|
expected: "StrPtrLte debe tener un máximo de 1 carácter de longitud",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGt",
|
||||||
|
expected: "StrPtrGt debe ser mayor que 10 caracteres en longitud",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGte",
|
||||||
|
expected: "StrPtrGte debe tener al menos 10 caracteres de longitud",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfString",
|
||||||
|
expected: "OneOfString debe ser uno de [red green]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfInt",
|
||||||
|
expected: "OneOfInt debe ser uno de [5 63]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueSlice",
|
||||||
|
expected: "UniqueSlice debe contener valores únicos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueArray",
|
||||||
|
expected: "UniqueArray debe contener valores únicos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueMap",
|
||||||
|
expected: "UniqueMap debe contener valores únicos",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
|
||||||
|
var fe validator.FieldError
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
if tt.ns == e.Namespace() {
|
||||||
|
fe = e
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotEqual(t, fe, nil)
|
||||||
|
Equal(t, tt.expected, fe.Translate(trans))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,634 @@
|
||||||
|
package fr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
french "gin-valid/go-playground/locales/fr"
|
||||||
|
ut "gin-valid/go-playground/universal-translator"
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
. "github.com/go-playground/assert/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTranslations(t *testing.T) {
|
||||||
|
|
||||||
|
fre := french.New()
|
||||||
|
uni := ut.New(fre, fre)
|
||||||
|
trans, _ := uni.GetTranslator("fr")
|
||||||
|
|
||||||
|
validate := validator.New()
|
||||||
|
|
||||||
|
err := RegisterDefaultTranslations(validate, trans)
|
||||||
|
Equal(t, err, nil)
|
||||||
|
|
||||||
|
type Inner struct {
|
||||||
|
EqCSFieldString string
|
||||||
|
NeCSFieldString string
|
||||||
|
GtCSFieldString string
|
||||||
|
GteCSFieldString string
|
||||||
|
LtCSFieldString string
|
||||||
|
LteCSFieldString string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Inner Inner
|
||||||
|
RequiredString string `validate:"required"`
|
||||||
|
RequiredNumber int `validate:"required"`
|
||||||
|
RequiredMultiple []string `validate:"required"`
|
||||||
|
LenString string `validate:"len=1"`
|
||||||
|
LenNumber float64 `validate:"len=1113.00"`
|
||||||
|
LenMultiple []string `validate:"len=7"`
|
||||||
|
MinString string `validate:"min=1"`
|
||||||
|
MinNumber float64 `validate:"min=1113.00"`
|
||||||
|
MinMultiple []string `validate:"min=7"`
|
||||||
|
MaxString string `validate:"max=3"`
|
||||||
|
MaxNumber float64 `validate:"max=1113.00"`
|
||||||
|
MaxMultiple []string `validate:"max=7"`
|
||||||
|
EqString string `validate:"eq=3"`
|
||||||
|
EqNumber float64 `validate:"eq=2.33"`
|
||||||
|
EqMultiple []string `validate:"eq=7"`
|
||||||
|
NeString string `validate:"ne="`
|
||||||
|
NeNumber float64 `validate:"ne=0.00"`
|
||||||
|
NeMultiple []string `validate:"ne=0"`
|
||||||
|
LtString string `validate:"lt=3"`
|
||||||
|
LtNumber float64 `validate:"lt=5.56"`
|
||||||
|
LtMultiple []string `validate:"lt=2"`
|
||||||
|
LtTime time.Time `validate:"lt"`
|
||||||
|
LteString string `validate:"lte=3"`
|
||||||
|
LteNumber float64 `validate:"lte=5.56"`
|
||||||
|
LteMultiple []string `validate:"lte=2"`
|
||||||
|
LteTime time.Time `validate:"lte"`
|
||||||
|
GtString string `validate:"gt=3"`
|
||||||
|
GtNumber float64 `validate:"gt=5.56"`
|
||||||
|
GtMultiple []string `validate:"gt=2"`
|
||||||
|
GtTime time.Time `validate:"gt"`
|
||||||
|
GteString string `validate:"gte=3"`
|
||||||
|
GteNumber float64 `validate:"gte=5.56"`
|
||||||
|
GteMultiple []string `validate:"gte=2"`
|
||||||
|
GteTime time.Time `validate:"gte"`
|
||||||
|
EqFieldString string `validate:"eqfield=MaxString"`
|
||||||
|
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
|
||||||
|
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
|
||||||
|
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
|
||||||
|
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
|
||||||
|
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
|
||||||
|
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
|
||||||
|
NeFieldString string `validate:"nefield=EqFieldString"`
|
||||||
|
GtFieldString string `validate:"gtfield=MaxString"`
|
||||||
|
GteFieldString string `validate:"gtefield=MaxString"`
|
||||||
|
LtFieldString string `validate:"ltfield=MaxString"`
|
||||||
|
LteFieldString string `validate:"ltefield=MaxString"`
|
||||||
|
AlphaString string `validate:"alpha"`
|
||||||
|
AlphanumString string `validate:"alphanum"`
|
||||||
|
NumericString string `validate:"numeric"`
|
||||||
|
NumberString string `validate:"number"`
|
||||||
|
HexadecimalString string `validate:"hexadecimal"`
|
||||||
|
HexColorString string `validate:"hexcolor"`
|
||||||
|
RGBColorString string `validate:"rgb"`
|
||||||
|
RGBAColorString string `validate:"rgba"`
|
||||||
|
HSLColorString string `validate:"hsl"`
|
||||||
|
HSLAColorString string `validate:"hsla"`
|
||||||
|
Email string `validate:"email"`
|
||||||
|
URL string `validate:"url"`
|
||||||
|
URI string `validate:"uri"`
|
||||||
|
Base64 string `validate:"base64"`
|
||||||
|
Contains string `validate:"contains=purpose"`
|
||||||
|
ContainsAny string `validate:"containsany=!@#$"`
|
||||||
|
Excludes string `validate:"excludes=text"`
|
||||||
|
ExcludesAll string `validate:"excludesall=!@#$"`
|
||||||
|
ExcludesRune string `validate:"excludesrune=☻"`
|
||||||
|
ISBN string `validate:"isbn"`
|
||||||
|
ISBN10 string `validate:"isbn10"`
|
||||||
|
ISBN13 string `validate:"isbn13"`
|
||||||
|
UUID string `validate:"uuid"`
|
||||||
|
UUID3 string `validate:"uuid3"`
|
||||||
|
UUID4 string `validate:"uuid4"`
|
||||||
|
UUID5 string `validate:"uuid5"`
|
||||||
|
ASCII string `validate:"ascii"`
|
||||||
|
PrintableASCII string `validate:"printascii"`
|
||||||
|
MultiByte string `validate:"multibyte"`
|
||||||
|
DataURI string `validate:"datauri"`
|
||||||
|
Latitude string `validate:"latitude"`
|
||||||
|
Longitude string `validate:"longitude"`
|
||||||
|
SSN string `validate:"ssn"`
|
||||||
|
IP string `validate:"ip"`
|
||||||
|
IPv4 string `validate:"ipv4"`
|
||||||
|
IPv6 string `validate:"ipv6"`
|
||||||
|
CIDR string `validate:"cidr"`
|
||||||
|
CIDRv4 string `validate:"cidrv4"`
|
||||||
|
CIDRv6 string `validate:"cidrv6"`
|
||||||
|
TCPAddr string `validate:"tcp_addr"`
|
||||||
|
TCPAddrv4 string `validate:"tcp4_addr"`
|
||||||
|
TCPAddrv6 string `validate:"tcp6_addr"`
|
||||||
|
UDPAddr string `validate:"udp_addr"`
|
||||||
|
UDPAddrv4 string `validate:"udp4_addr"`
|
||||||
|
UDPAddrv6 string `validate:"udp6_addr"`
|
||||||
|
IPAddr string `validate:"ip_addr"`
|
||||||
|
IPAddrv4 string `validate:"ip4_addr"`
|
||||||
|
IPAddrv6 string `validate:"ip6_addr"`
|
||||||
|
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
|
||||||
|
MAC string `validate:"mac"`
|
||||||
|
IsColor string `validate:"iscolor"`
|
||||||
|
StrPtrMinLen *string `validate:"min=10"`
|
||||||
|
StrPtrMaxLen *string `validate:"max=1"`
|
||||||
|
StrPtrLen *string `validate:"len=2"`
|
||||||
|
StrPtrLt *string `validate:"lt=1"`
|
||||||
|
StrPtrLte *string `validate:"lte=1"`
|
||||||
|
StrPtrGt *string `validate:"gt=10"`
|
||||||
|
StrPtrGte *string `validate:"gte=10"`
|
||||||
|
OneOfString string `validate:"oneof=red green"`
|
||||||
|
OneOfInt int `validate:"oneof=5 63"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var test Test
|
||||||
|
|
||||||
|
test.Inner.EqCSFieldString = "1234"
|
||||||
|
test.Inner.GtCSFieldString = "1234"
|
||||||
|
test.Inner.GteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.MaxString = "1234"
|
||||||
|
test.MaxNumber = 2000
|
||||||
|
test.MaxMultiple = make([]string, 9)
|
||||||
|
|
||||||
|
test.LtString = "1234"
|
||||||
|
test.LtNumber = 6
|
||||||
|
test.LtMultiple = make([]string, 3)
|
||||||
|
test.LtTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LteString = "1234"
|
||||||
|
test.LteNumber = 6
|
||||||
|
test.LteMultiple = make([]string, 3)
|
||||||
|
test.LteTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LtFieldString = "12345"
|
||||||
|
test.LteFieldString = "12345"
|
||||||
|
|
||||||
|
test.LtCSFieldString = "1234"
|
||||||
|
test.LteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.AlphaString = "abc3"
|
||||||
|
test.AlphanumString = "abc3!"
|
||||||
|
test.NumericString = "12E.00"
|
||||||
|
test.NumberString = "12E"
|
||||||
|
|
||||||
|
test.Excludes = "this is some test text"
|
||||||
|
test.ExcludesAll = "This is Great!"
|
||||||
|
test.ExcludesRune = "Love it ☻"
|
||||||
|
|
||||||
|
test.ASCII = "カタカナ"
|
||||||
|
test.PrintableASCII = "カタカナ"
|
||||||
|
|
||||||
|
test.MultiByte = "1234feerf"
|
||||||
|
|
||||||
|
s := "toolong"
|
||||||
|
test.StrPtrMaxLen = &s
|
||||||
|
test.StrPtrLen = &s
|
||||||
|
|
||||||
|
err = validate.Struct(test)
|
||||||
|
NotEqual(t, err, nil)
|
||||||
|
|
||||||
|
errs, ok := err.(validator.ValidationErrors)
|
||||||
|
Equal(t, ok, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
ns string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ns: "Test.IsColor",
|
||||||
|
expected: "IsColor doit être une couleur valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MAC",
|
||||||
|
expected: "MAC doit contenir une adresse MAC valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddr",
|
||||||
|
expected: "IPAddr doit être une adresse IP résolvable",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv4",
|
||||||
|
expected: "IPAddrv4 doit être une adresse IPv4 résolvable",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv6",
|
||||||
|
expected: "IPAddrv6 doit être une adresse IPv6 résolvable",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddr",
|
||||||
|
expected: "UDPAddr doit être une adressse UDP valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv4",
|
||||||
|
expected: "UDPAddrv4 doit être une adressse IPv4 UDP valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv6",
|
||||||
|
expected: "UDPAddrv6 doit être une adressse IPv6 UDP valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddr",
|
||||||
|
expected: "TCPAddr doit être une adressse TCP valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv4",
|
||||||
|
expected: "TCPAddrv4 doit être une adressse IPv4 TCP valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv6",
|
||||||
|
expected: "TCPAddrv6 doit être une adressse IPv6 TCP valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDR",
|
||||||
|
expected: "CIDR doit contenir une notation CIDR valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv4",
|
||||||
|
expected: "CIDRv4 doit contenir une notation CIDR valide pour une adresse IPv4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv6",
|
||||||
|
expected: "CIDRv6 doit contenir une notation CIDR valide pour une adresse IPv6",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.SSN",
|
||||||
|
expected: "SSN doit être un numéro SSN valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IP",
|
||||||
|
expected: "IP doit être une adressse IP valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv4",
|
||||||
|
expected: "IPv4 doit être une adressse IPv4 valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv6",
|
||||||
|
expected: "IPv6 doit être une adressse IPv6 valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.DataURI",
|
||||||
|
expected: "DataURI doit contenir une URI data valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Latitude",
|
||||||
|
expected: "Latitude doit contenir des coordonnées latitude valides",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Longitude",
|
||||||
|
expected: "Longitude doit contenir des coordonnées longitudes valides",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MultiByte",
|
||||||
|
expected: "MultiByte doit contenir des caractères multioctets",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ASCII",
|
||||||
|
expected: "ASCII ne doit contenir que des caractères ascii",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.PrintableASCII",
|
||||||
|
expected: "PrintableASCII ne doit contenir que des caractères ascii affichables",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID",
|
||||||
|
expected: "UUID doit être un UUID valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID3",
|
||||||
|
expected: "UUID3 doit être un UUID version 3 valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID4",
|
||||||
|
expected: "UUID4 doit être un UUID version 4 valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID5",
|
||||||
|
expected: "UUID5 doit être un UUID version 5 valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN",
|
||||||
|
expected: "ISBN doit être un numéro ISBN valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN10",
|
||||||
|
expected: "ISBN10 doit être un numéro ISBN-10 valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN13",
|
||||||
|
expected: "ISBN13 doit être un numéro ISBN-13 valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Excludes",
|
||||||
|
expected: "Excludes ne doit pas contenir le texte 'text'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesAll",
|
||||||
|
expected: "ExcludesAll ne doit pas contenir l'un des caractères suivants '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesRune",
|
||||||
|
expected: "ExcludesRune ne doit pas contenir ce qui suit '☻'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ContainsAny",
|
||||||
|
expected: "ContainsAny doit contenir au moins l' un des caractères suivants '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Contains",
|
||||||
|
expected: "Contains doit contenir le texte 'purpose'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Base64",
|
||||||
|
expected: "Base64 doit être une chaîne de caractères au format Base64 valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Email",
|
||||||
|
expected: "Email doit être une adresse email valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URL",
|
||||||
|
expected: "URL doit être une URL valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URI",
|
||||||
|
expected: "URI doit être une URI valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBColorString",
|
||||||
|
expected: "RGBColorString doit être une couleur au format RGB valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBAColorString",
|
||||||
|
expected: "RGBAColorString doit être une couleur au format RGBA valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLColorString",
|
||||||
|
expected: "HSLColorString doit être une couleur au format HSL valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLAColorString",
|
||||||
|
expected: "HSLAColorString doit être une couleur au format HSLA valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexadecimalString",
|
||||||
|
expected: "HexadecimalString doit être une chaîne de caractères au format hexadécimal valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexColorString",
|
||||||
|
expected: "HexColorString doit être une couleur au format HEX valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumberString",
|
||||||
|
expected: "NumberString doit être un nombre valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumericString",
|
||||||
|
expected: "NumericString doit être une valeur numérique valide",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphanumString",
|
||||||
|
expected: "AlphanumString ne doit contenir que des caractères alphanumériques",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphaString",
|
||||||
|
expected: "AlphaString ne doit contenir que des caractères alphabétiques",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtFieldString",
|
||||||
|
expected: "LtFieldString doit être inférieur à MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteFieldString",
|
||||||
|
expected: "LteFieldString doit être inférieur ou égal à MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtFieldString",
|
||||||
|
expected: "GtFieldString doit être supérieur à MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteFieldString",
|
||||||
|
expected: "GteFieldString doit être supérieur ou égal à MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeFieldString",
|
||||||
|
expected: "NeFieldString ne doit pas être égal à EqFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtCSFieldString",
|
||||||
|
expected: "LtCSFieldString doit être inférieur à Inner.LtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteCSFieldString",
|
||||||
|
expected: "LteCSFieldString doit être inférieur ou égal à Inner.LteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtCSFieldString",
|
||||||
|
expected: "GtCSFieldString doit être supérieur à Inner.GtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteCSFieldString",
|
||||||
|
expected: "GteCSFieldString doit être supérieur ou égal à Inner.GteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeCSFieldString",
|
||||||
|
expected: "NeCSFieldString ne doit pas être égal à Inner.NeCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqCSFieldString",
|
||||||
|
expected: "EqCSFieldString doit être égal à Inner.EqCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqFieldString",
|
||||||
|
expected: "EqFieldString doit être égal à MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteString",
|
||||||
|
expected: "GteString doit faire une taille d'au moins 3 caractères",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteNumber",
|
||||||
|
expected: "GteNumber doit être 5,56 ou plus",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteMultiple",
|
||||||
|
expected: "GteMultiple doit contenir au moins 2 elements",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteTime",
|
||||||
|
expected: "GteTime doit être après ou pendant la date et l'heure actuelle",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtString",
|
||||||
|
expected: "GtString doit avoir une taille supérieur à 3 caractères",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtNumber",
|
||||||
|
expected: "GtNumber doit être supérieur à 5,56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtMultiple",
|
||||||
|
expected: "GtMultiple doit contenir plus de 2 elements",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtTime",
|
||||||
|
expected: "GtTime doit être après la date et l'heure actuelle",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteString",
|
||||||
|
expected: "LteString doit faire une taille maximum de 3 caractères",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteNumber",
|
||||||
|
expected: "LteNumber doit faire 5,56 ou moins",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteMultiple",
|
||||||
|
expected: "LteMultiple doit contenir un maximum de 2 elements",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteTime",
|
||||||
|
expected: "LteTime doit être avant ou pendant la date et l'heure actuelle",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtString",
|
||||||
|
expected: "LtString doit avoir une taille inférieure à 3 caractères",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtNumber",
|
||||||
|
expected: "LtNumber doit être inférieur à 5,56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtMultiple",
|
||||||
|
expected: "LtMultiple doit contenir mois de 2 elements",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtTime",
|
||||||
|
expected: "LtTime doit être avant la date et l'heure actuelle",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeString",
|
||||||
|
expected: "NeString ne doit pas être égal à ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeNumber",
|
||||||
|
expected: "NeNumber ne doit pas être égal à 0.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeMultiple",
|
||||||
|
expected: "NeMultiple ne doit pas être égal à 0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqString",
|
||||||
|
expected: "EqString n'est pas égal à 3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqNumber",
|
||||||
|
expected: "EqNumber n'est pas égal à 2.33",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqMultiple",
|
||||||
|
expected: "EqMultiple n'est pas égal à 7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxString",
|
||||||
|
expected: "MaxString doit faire une taille maximum de 3 caractères",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxNumber",
|
||||||
|
expected: "MaxNumber doit être égal à 1 113,00 ou moins",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxMultiple",
|
||||||
|
expected: "MaxMultiple doit contenir au maximum 7 elements",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinString",
|
||||||
|
expected: "MinString doit faire une taille minimum de 1 caractère",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinNumber",
|
||||||
|
expected: "MinNumber doit être égal à 1 113,00 ou plus",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinMultiple",
|
||||||
|
expected: "MinMultiple doit contenir au moins 7 elements",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenString",
|
||||||
|
expected: "LenString doit faire une taille de 1 caractère",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenNumber",
|
||||||
|
expected: "LenNumber doit être égal à 1 113,00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenMultiple",
|
||||||
|
expected: "LenMultiple doit contenir 7 elements",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredString",
|
||||||
|
expected: "RequiredString est un champ obligatoire",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredNumber",
|
||||||
|
expected: "RequiredNumber est un champ obligatoire",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredMultiple",
|
||||||
|
expected: "RequiredMultiple est un champ obligatoire",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMinLen",
|
||||||
|
expected: "StrPtrMinLen doit faire une taille minimum de 10 caractères",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMaxLen",
|
||||||
|
expected: "StrPtrMaxLen doit faire une taille maximum de 1 caractère",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLen",
|
||||||
|
expected: "StrPtrLen doit faire une taille de 2 caractères",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLt",
|
||||||
|
expected: "StrPtrLt doit avoir une taille inférieure à 1 caractère",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLte",
|
||||||
|
expected: "StrPtrLte doit faire une taille maximum de 1 caractère",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGt",
|
||||||
|
expected: "StrPtrGt doit avoir une taille supérieur à 10 caractères",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGte",
|
||||||
|
expected: "StrPtrGte doit faire une taille d'au moins 10 caractères",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfString",
|
||||||
|
expected: "OneOfString doit être l'un des choix suivants [red green]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfInt",
|
||||||
|
expected: "OneOfInt doit être l'un des choix suivants [5 63]",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
|
||||||
|
var fe validator.FieldError
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
if tt.ns == e.Namespace() {
|
||||||
|
fe = e
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotEqual(t, fe, nil)
|
||||||
|
Equal(t, tt.expected, fe.Translate(trans))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,634 @@
|
||||||
|
package id
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
indonesia "gin-valid/go-playground/locales/id"
|
||||||
|
ut "gin-valid/go-playground/universal-translator"
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
. "github.com/go-playground/assert/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTranslations(t *testing.T) {
|
||||||
|
|
||||||
|
idn := indonesia.New()
|
||||||
|
uni := ut.New(idn, idn)
|
||||||
|
trans, _ := uni.GetTranslator("id")
|
||||||
|
|
||||||
|
validate := validator.New()
|
||||||
|
|
||||||
|
err := RegisterDefaultTranslations(validate, trans)
|
||||||
|
Equal(t, err, nil)
|
||||||
|
|
||||||
|
type Inner struct {
|
||||||
|
EqCSFieldString string
|
||||||
|
NeCSFieldString string
|
||||||
|
GtCSFieldString string
|
||||||
|
GteCSFieldString string
|
||||||
|
LtCSFieldString string
|
||||||
|
LteCSFieldString string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Inner Inner
|
||||||
|
RequiredString string `validate:"required"`
|
||||||
|
RequiredNumber int `validate:"required"`
|
||||||
|
RequiredMultiple []string `validate:"required"`
|
||||||
|
LenString string `validate:"len=1"`
|
||||||
|
LenNumber float64 `validate:"len=1113.00"`
|
||||||
|
LenMultiple []string `validate:"len=7"`
|
||||||
|
MinString string `validate:"min=1"`
|
||||||
|
MinNumber float64 `validate:"min=1113.00"`
|
||||||
|
MinMultiple []string `validate:"min=7"`
|
||||||
|
MaxString string `validate:"max=3"`
|
||||||
|
MaxNumber float64 `validate:"max=1113.00"`
|
||||||
|
MaxMultiple []string `validate:"max=7"`
|
||||||
|
EqString string `validate:"eq=3"`
|
||||||
|
EqNumber float64 `validate:"eq=2.33"`
|
||||||
|
EqMultiple []string `validate:"eq=7"`
|
||||||
|
NeString string `validate:"ne="`
|
||||||
|
NeNumber float64 `validate:"ne=0.00"`
|
||||||
|
NeMultiple []string `validate:"ne=0"`
|
||||||
|
LtString string `validate:"lt=3"`
|
||||||
|
LtNumber float64 `validate:"lt=5.56"`
|
||||||
|
LtMultiple []string `validate:"lt=2"`
|
||||||
|
LtTime time.Time `validate:"lt"`
|
||||||
|
LteString string `validate:"lte=3"`
|
||||||
|
LteNumber float64 `validate:"lte=5.56"`
|
||||||
|
LteMultiple []string `validate:"lte=2"`
|
||||||
|
LteTime time.Time `validate:"lte"`
|
||||||
|
GtString string `validate:"gt=3"`
|
||||||
|
GtNumber float64 `validate:"gt=5.56"`
|
||||||
|
GtMultiple []string `validate:"gt=2"`
|
||||||
|
GtTime time.Time `validate:"gt"`
|
||||||
|
GteString string `validate:"gte=3"`
|
||||||
|
GteNumber float64 `validate:"gte=5.56"`
|
||||||
|
GteMultiple []string `validate:"gte=2"`
|
||||||
|
GteTime time.Time `validate:"gte"`
|
||||||
|
EqFieldString string `validate:"eqfield=MaxString"`
|
||||||
|
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
|
||||||
|
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
|
||||||
|
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
|
||||||
|
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
|
||||||
|
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
|
||||||
|
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
|
||||||
|
NeFieldString string `validate:"nefield=EqFieldString"`
|
||||||
|
GtFieldString string `validate:"gtfield=MaxString"`
|
||||||
|
GteFieldString string `validate:"gtefield=MaxString"`
|
||||||
|
LtFieldString string `validate:"ltfield=MaxString"`
|
||||||
|
LteFieldString string `validate:"ltefield=MaxString"`
|
||||||
|
AlphaString string `validate:"alpha"`
|
||||||
|
AlphanumString string `validate:"alphanum"`
|
||||||
|
NumericString string `validate:"numeric"`
|
||||||
|
NumberString string `validate:"number"`
|
||||||
|
HexadecimalString string `validate:"hexadecimal"`
|
||||||
|
HexColorString string `validate:"hexcolor"`
|
||||||
|
RGBColorString string `validate:"rgb"`
|
||||||
|
RGBAColorString string `validate:"rgba"`
|
||||||
|
HSLColorString string `validate:"hsl"`
|
||||||
|
HSLAColorString string `validate:"hsla"`
|
||||||
|
Email string `validate:"email"`
|
||||||
|
URL string `validate:"url"`
|
||||||
|
URI string `validate:"uri"`
|
||||||
|
Base64 string `validate:"base64"`
|
||||||
|
Contains string `validate:"contains=tujuan"`
|
||||||
|
ContainsAny string `validate:"containsany=!@#$"`
|
||||||
|
Excludes string `validate:"excludes=text"`
|
||||||
|
ExcludesAll string `validate:"excludesall=!@#$"`
|
||||||
|
ExcludesRune string `validate:"excludesrune=☻"`
|
||||||
|
ISBN string `validate:"isbn"`
|
||||||
|
ISBN10 string `validate:"isbn10"`
|
||||||
|
ISBN13 string `validate:"isbn13"`
|
||||||
|
UUID string `validate:"uuid"`
|
||||||
|
UUID3 string `validate:"uuid3"`
|
||||||
|
UUID4 string `validate:"uuid4"`
|
||||||
|
UUID5 string `validate:"uuid5"`
|
||||||
|
ASCII string `validate:"ascii"`
|
||||||
|
PrintableASCII string `validate:"printascii"`
|
||||||
|
MultiByte string `validate:"multibyte"`
|
||||||
|
DataURI string `validate:"datauri"`
|
||||||
|
Latitude string `validate:"latitude"`
|
||||||
|
Longitude string `validate:"longitude"`
|
||||||
|
SSN string `validate:"ssn"`
|
||||||
|
IP string `validate:"ip"`
|
||||||
|
IPv4 string `validate:"ipv4"`
|
||||||
|
IPv6 string `validate:"ipv6"`
|
||||||
|
CIDR string `validate:"cidr"`
|
||||||
|
CIDRv4 string `validate:"cidrv4"`
|
||||||
|
CIDRv6 string `validate:"cidrv6"`
|
||||||
|
TCPAddr string `validate:"tcp_addr"`
|
||||||
|
TCPAddrv4 string `validate:"tcp4_addr"`
|
||||||
|
TCPAddrv6 string `validate:"tcp6_addr"`
|
||||||
|
UDPAddr string `validate:"udp_addr"`
|
||||||
|
UDPAddrv4 string `validate:"udp4_addr"`
|
||||||
|
UDPAddrv6 string `validate:"udp6_addr"`
|
||||||
|
IPAddr string `validate:"ip_addr"`
|
||||||
|
IPAddrv4 string `validate:"ip4_addr"`
|
||||||
|
IPAddrv6 string `validate:"ip6_addr"`
|
||||||
|
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
|
||||||
|
MAC string `validate:"mac"`
|
||||||
|
IsColor string `validate:"iscolor"`
|
||||||
|
StrPtrMinLen *string `validate:"min=10"`
|
||||||
|
StrPtrMaxLen *string `validate:"max=1"`
|
||||||
|
StrPtrLen *string `validate:"len=2"`
|
||||||
|
StrPtrLt *string `validate:"lt=1"`
|
||||||
|
StrPtrLte *string `validate:"lte=1"`
|
||||||
|
StrPtrGt *string `validate:"gt=10"`
|
||||||
|
StrPtrGte *string `validate:"gte=10"`
|
||||||
|
OneOfString string `validate:"oneof=merah hijau"`
|
||||||
|
OneOfInt int `validate:"oneof=5 63"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var test Test
|
||||||
|
|
||||||
|
test.Inner.EqCSFieldString = "1234"
|
||||||
|
test.Inner.GtCSFieldString = "1234"
|
||||||
|
test.Inner.GteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.MaxString = "1234"
|
||||||
|
test.MaxNumber = 2000
|
||||||
|
test.MaxMultiple = make([]string, 9)
|
||||||
|
|
||||||
|
test.LtString = "1234"
|
||||||
|
test.LtNumber = 6
|
||||||
|
test.LtMultiple = make([]string, 3)
|
||||||
|
test.LtTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LteString = "1234"
|
||||||
|
test.LteNumber = 6
|
||||||
|
test.LteMultiple = make([]string, 3)
|
||||||
|
test.LteTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LtFieldString = "12345"
|
||||||
|
test.LteFieldString = "12345"
|
||||||
|
|
||||||
|
test.LtCSFieldString = "1234"
|
||||||
|
test.LteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.AlphaString = "abc3"
|
||||||
|
test.AlphanumString = "abc3!"
|
||||||
|
test.NumericString = "12E.00"
|
||||||
|
test.NumberString = "12E"
|
||||||
|
|
||||||
|
test.Excludes = "this is some test text"
|
||||||
|
test.ExcludesAll = "This is Great!"
|
||||||
|
test.ExcludesRune = "Love it ☻"
|
||||||
|
|
||||||
|
test.ASCII = "カタカナ"
|
||||||
|
test.PrintableASCII = "カタカナ"
|
||||||
|
|
||||||
|
test.MultiByte = "1234feerf"
|
||||||
|
|
||||||
|
s := "toolong"
|
||||||
|
test.StrPtrMaxLen = &s
|
||||||
|
test.StrPtrLen = &s
|
||||||
|
|
||||||
|
err = validate.Struct(test)
|
||||||
|
NotEqual(t, err, nil)
|
||||||
|
|
||||||
|
errs, ok := err.(validator.ValidationErrors)
|
||||||
|
Equal(t, ok, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
ns string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ns: "Test.IsColor",
|
||||||
|
expected: "IsColor harus berupa warna yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MAC",
|
||||||
|
expected: "MAC harus berisi alamat MAC yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddr",
|
||||||
|
expected: "IPAddr harus berupa alamat IP yang dapat dipecahkan",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv4",
|
||||||
|
expected: "IPAddrv4 harus berupa alamat IPv4 yang dapat diatasi",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv6",
|
||||||
|
expected: "IPAddrv6 harus berupa alamat IPv6 yang dapat diatasi",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddr",
|
||||||
|
expected: "UDPAddr harus berupa alamat UDP yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv4",
|
||||||
|
expected: "UDPAddrv4 harus berupa alamat IPv4 UDP yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv6",
|
||||||
|
expected: "UDPAddrv6 harus berupa alamat IPv6 UDP yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddr",
|
||||||
|
expected: "TCPAddr harus berupa alamat TCP yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv4",
|
||||||
|
expected: "TCPAddrv4 harus berupa alamat TCP IPv4 yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv6",
|
||||||
|
expected: "TCPAddrv6 harus berupa alamat TCP IPv6 yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDR",
|
||||||
|
expected: "CIDR harus berisi notasi CIDR yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv4",
|
||||||
|
expected: "CIDRv4 harus berisi notasi CIDR yang valid untuk alamat IPv4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv6",
|
||||||
|
expected: "CIDRv6 harus berisi notasi CIDR yang valid untuk alamat IPv6",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.SSN",
|
||||||
|
expected: "SSN harus berupa nomor SSN yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IP",
|
||||||
|
expected: "IP harus berupa alamat IP yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv4",
|
||||||
|
expected: "IPv4 harus berupa alamat IPv4 yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv6",
|
||||||
|
expected: "IPv6 harus berupa alamat IPv6 yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.DataURI",
|
||||||
|
expected: "DataURI harus berisi URI Data yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Latitude",
|
||||||
|
expected: "Latitude harus berisi koordinat lintang yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Longitude",
|
||||||
|
expected: "Longitude harus berisi koordinat bujur yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MultiByte",
|
||||||
|
expected: "MultiByte harus berisi karakter multibyte",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ASCII",
|
||||||
|
expected: "ASCII hanya boleh berisi karakter ascii",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.PrintableASCII",
|
||||||
|
expected: "PrintableASCII hanya boleh berisi karakter ascii yang dapat dicetak",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID",
|
||||||
|
expected: "UUID harus berupa UUID yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID3",
|
||||||
|
expected: "UUID3 harus berupa UUID versi 3 yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID4",
|
||||||
|
expected: "UUID4 harus berupa UUID versi 4 yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID5",
|
||||||
|
expected: "UUID5 harus berupa UUID versi 5 yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN",
|
||||||
|
expected: "ISBN harus berupa nomor ISBN yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN10",
|
||||||
|
expected: "ISBN10 harus berupa nomor ISBN-10 yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN13",
|
||||||
|
expected: "ISBN13 harus berupa nomor ISBN-13 yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Excludes",
|
||||||
|
expected: "Excludes tidak boleh berisi teks 'text'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesAll",
|
||||||
|
expected: "ExcludesAll tidak boleh berisi salah satu karakter berikut '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesRune",
|
||||||
|
expected: "ExcludesRune tidak boleh berisi '☻'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ContainsAny",
|
||||||
|
expected: "ContainsAny harus berisi setidaknya salah satu karakter berikut '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Contains",
|
||||||
|
expected: "Contains harus berisi teks 'tujuan'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Base64",
|
||||||
|
expected: "Base64 harus berupa string Base64 yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Email",
|
||||||
|
expected: "Email harus berupa alamat email yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URL",
|
||||||
|
expected: "URL harus berupa URL yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URI",
|
||||||
|
expected: "URI harus berupa URI yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBColorString",
|
||||||
|
expected: "RGBColorString harus berupa warna RGB yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBAColorString",
|
||||||
|
expected: "RGBAColorString harus berupa warna RGBA yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLColorString",
|
||||||
|
expected: "HSLColorString harus berupa warna HSL yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLAColorString",
|
||||||
|
expected: "HSLAColorString harus berupa warna HSLA yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexadecimalString",
|
||||||
|
expected: "HexadecimalString harus berupa heksadesimal yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexColorString",
|
||||||
|
expected: "HexColorString harus berupa warna HEX yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumberString",
|
||||||
|
expected: "NumberString harus berupa angka yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumericString",
|
||||||
|
expected: "NumericString harus berupa nilai numerik yang valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphanumString",
|
||||||
|
expected: "AlphanumString hanya dapat berisi karakter alfanumerik",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphaString",
|
||||||
|
expected: "AlphaString hanya dapat berisi karakter abjad",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtFieldString",
|
||||||
|
expected: "LtFieldString harus kurang dari MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteFieldString",
|
||||||
|
expected: "LteFieldString harus kurang dari atau sama dengan MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtFieldString",
|
||||||
|
expected: "GtFieldString harus lebih besar dari MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteFieldString",
|
||||||
|
expected: "GteFieldString harus lebih besar dari atau sama dengan MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeFieldString",
|
||||||
|
expected: "NeFieldString tidak sama dengan EqFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtCSFieldString",
|
||||||
|
expected: "LtCSFieldString harus kurang dari Inner.LtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteCSFieldString",
|
||||||
|
expected: "LteCSFieldString harus kurang dari atau sama dengan Inner.LteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtCSFieldString",
|
||||||
|
expected: "GtCSFieldString harus lebih besar dari Inner.GtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteCSFieldString",
|
||||||
|
expected: "GteCSFieldString harus lebih besar dari atau sama dengan Inner.GteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeCSFieldString",
|
||||||
|
expected: "NeCSFieldString tidak sama dengan Inner.NeCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqCSFieldString",
|
||||||
|
expected: "EqCSFieldString harus sama dengan Inner.EqCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqFieldString",
|
||||||
|
expected: "EqFieldString harus sama dengan MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteString",
|
||||||
|
expected: "panjang minimal GteString adalah 3 karakter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteNumber",
|
||||||
|
expected: "GteNumber harus 5,56 atau lebih besar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteMultiple",
|
||||||
|
expected: "GteMultiple harus berisi setidaknya 2 item",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteTime",
|
||||||
|
expected: "GteTime harus lebih besar dari atau sama dengan tanggal & waktu saat ini",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtString",
|
||||||
|
expected: "panjang GtString harus lebih dari 3 karakter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtNumber",
|
||||||
|
expected: "GtNumber harus lebih besar dari 5,56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtMultiple",
|
||||||
|
expected: "GtMultiple harus berisi lebih dari 2 item",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtTime",
|
||||||
|
expected: "GtTime harus lebih besar dari tanggal & waktu saat ini",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteString",
|
||||||
|
expected: "panjang maksimal LteString adalah 3 karakter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteNumber",
|
||||||
|
expected: "LteNumber harus 5,56 atau kurang",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteMultiple",
|
||||||
|
expected: "LteMultiple harus berisi maksimal 2 item",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteTime",
|
||||||
|
expected: "LteTime harus kurang dari atau sama dengan tanggal & waktu saat ini",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtString",
|
||||||
|
expected: "panjang LtString harus kurang dari 3 karakter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtNumber",
|
||||||
|
expected: "LtNumber harus kurang dari 5,56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtMultiple",
|
||||||
|
expected: "LtMultiple harus berisi kurang dari 2 item",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtTime",
|
||||||
|
expected: "LtTime harus kurang dari tanggal & waktu saat ini",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeString",
|
||||||
|
expected: "NeString tidak sama dengan ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeNumber",
|
||||||
|
expected: "NeNumber tidak sama dengan 0.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeMultiple",
|
||||||
|
expected: "NeMultiple tidak sama dengan 0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqString",
|
||||||
|
expected: "EqString tidak sama dengan 3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqNumber",
|
||||||
|
expected: "EqNumber tidak sama dengan 2.33",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqMultiple",
|
||||||
|
expected: "EqMultiple tidak sama dengan 7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxString",
|
||||||
|
expected: "panjang maksimal MaxString adalah 3 karakter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxNumber",
|
||||||
|
expected: "MaxNumber harus 1.113,00 atau kurang",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxMultiple",
|
||||||
|
expected: "MaxMultiple harus berisi maksimal 7 item",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinString",
|
||||||
|
expected: "panjang minimal MinString adalah 1 karakter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinNumber",
|
||||||
|
expected: "MinNumber harus 1.113,00 atau lebih besar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinMultiple",
|
||||||
|
expected: "panjang minimal MinMultiple adalah 7 item",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenString",
|
||||||
|
expected: "panjang LenString harus 1 karakter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenNumber",
|
||||||
|
expected: "LenNumber harus sama dengan 1.113,00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenMultiple",
|
||||||
|
expected: "LenMultiple harus berisi 7 item",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredString",
|
||||||
|
expected: "RequiredString wajib diisi",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredNumber",
|
||||||
|
expected: "RequiredNumber wajib diisi",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredMultiple",
|
||||||
|
expected: "RequiredMultiple wajib diisi",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMinLen",
|
||||||
|
expected: "panjang minimal StrPtrMinLen adalah 10 karakter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMaxLen",
|
||||||
|
expected: "panjang maksimal StrPtrMaxLen adalah 1 karakter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLen",
|
||||||
|
expected: "panjang StrPtrLen harus 2 karakter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLt",
|
||||||
|
expected: "panjang StrPtrLt harus kurang dari 1 karakter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLte",
|
||||||
|
expected: "panjang maksimal StrPtrLte adalah 1 karakter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGt",
|
||||||
|
expected: "panjang StrPtrGt harus lebih dari 10 karakter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGte",
|
||||||
|
expected: "panjang minimal StrPtrGte adalah 10 karakter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfString",
|
||||||
|
expected: "OneOfString harus berupa salah satu dari [merah hijau]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfInt",
|
||||||
|
expected: "OneOfInt harus berupa salah satu dari [5 63]",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
|
||||||
|
var fe validator.FieldError
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
if tt.ns == e.Namespace() {
|
||||||
|
fe = e
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotEqual(t, fe, nil)
|
||||||
|
Equal(t, tt.expected, fe.Translate(trans))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,634 @@
|
||||||
|
package ja
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
ja_locale "gin-valid/go-playground/locales/ja"
|
||||||
|
ut "gin-valid/go-playground/universal-translator"
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
. "github.com/go-playground/assert/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTranslations(t *testing.T) {
|
||||||
|
|
||||||
|
japanese := ja_locale.New()
|
||||||
|
uni := ut.New(japanese, japanese)
|
||||||
|
trans, _ := uni.GetTranslator("ja")
|
||||||
|
|
||||||
|
validate := validator.New()
|
||||||
|
|
||||||
|
err := RegisterDefaultTranslations(validate, trans)
|
||||||
|
Equal(t, err, nil)
|
||||||
|
|
||||||
|
type Inner struct {
|
||||||
|
EqCSFieldString string
|
||||||
|
NeCSFieldString string
|
||||||
|
GtCSFieldString string
|
||||||
|
GteCSFieldString string
|
||||||
|
LtCSFieldString string
|
||||||
|
LteCSFieldString string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Inner Inner
|
||||||
|
RequiredString string `validate:"required"`
|
||||||
|
RequiredNumber int `validate:"required"`
|
||||||
|
RequiredMultiple []string `validate:"required"`
|
||||||
|
LenString string `validate:"len=1"`
|
||||||
|
LenNumber float64 `validate:"len=1113.00"`
|
||||||
|
LenMultiple []string `validate:"len=7"`
|
||||||
|
MinString string `validate:"min=1"`
|
||||||
|
MinNumber float64 `validate:"min=1113.00"`
|
||||||
|
MinMultiple []string `validate:"min=7"`
|
||||||
|
MaxString string `validate:"max=3"`
|
||||||
|
MaxNumber float64 `validate:"max=1113.00"`
|
||||||
|
MaxMultiple []string `validate:"max=7"`
|
||||||
|
EqString string `validate:"eq=3"`
|
||||||
|
EqNumber float64 `validate:"eq=2.33"`
|
||||||
|
EqMultiple []string `validate:"eq=7"`
|
||||||
|
NeString string `validate:"ne="`
|
||||||
|
NeNumber float64 `validate:"ne=0.00"`
|
||||||
|
NeMultiple []string `validate:"ne=0"`
|
||||||
|
LtString string `validate:"lt=3"`
|
||||||
|
LtNumber float64 `validate:"lt=5.56"`
|
||||||
|
LtMultiple []string `validate:"lt=2"`
|
||||||
|
LtTime time.Time `validate:"lt"`
|
||||||
|
LteString string `validate:"lte=3"`
|
||||||
|
LteNumber float64 `validate:"lte=5.56"`
|
||||||
|
LteMultiple []string `validate:"lte=2"`
|
||||||
|
LteTime time.Time `validate:"lte"`
|
||||||
|
GtString string `validate:"gt=3"`
|
||||||
|
GtNumber float64 `validate:"gt=5.56"`
|
||||||
|
GtMultiple []string `validate:"gt=2"`
|
||||||
|
GtTime time.Time `validate:"gt"`
|
||||||
|
GteString string `validate:"gte=3"`
|
||||||
|
GteNumber float64 `validate:"gte=5.56"`
|
||||||
|
GteMultiple []string `validate:"gte=2"`
|
||||||
|
GteTime time.Time `validate:"gte"`
|
||||||
|
EqFieldString string `validate:"eqfield=MaxString"`
|
||||||
|
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
|
||||||
|
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
|
||||||
|
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
|
||||||
|
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
|
||||||
|
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
|
||||||
|
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
|
||||||
|
NeFieldString string `validate:"nefield=EqFieldString"`
|
||||||
|
GtFieldString string `validate:"gtfield=MaxString"`
|
||||||
|
GteFieldString string `validate:"gtefield=MaxString"`
|
||||||
|
LtFieldString string `validate:"ltfield=MaxString"`
|
||||||
|
LteFieldString string `validate:"ltefield=MaxString"`
|
||||||
|
AlphaString string `validate:"alpha"`
|
||||||
|
AlphanumString string `validate:"alphanum"`
|
||||||
|
NumericString string `validate:"numeric"`
|
||||||
|
NumberString string `validate:"number"`
|
||||||
|
HexadecimalString string `validate:"hexadecimal"`
|
||||||
|
HexColorString string `validate:"hexcolor"`
|
||||||
|
RGBColorString string `validate:"rgb"`
|
||||||
|
RGBAColorString string `validate:"rgba"`
|
||||||
|
HSLColorString string `validate:"hsl"`
|
||||||
|
HSLAColorString string `validate:"hsla"`
|
||||||
|
Email string `validate:"email"`
|
||||||
|
URL string `validate:"url"`
|
||||||
|
URI string `validate:"uri"`
|
||||||
|
Base64 string `validate:"base64"`
|
||||||
|
Contains string `validate:"contains=purpose"`
|
||||||
|
ContainsAny string `validate:"containsany=!@#$"`
|
||||||
|
Excludes string `validate:"excludes=text"`
|
||||||
|
ExcludesAll string `validate:"excludesall=!@#$"`
|
||||||
|
ExcludesRune string `validate:"excludesrune=☻"`
|
||||||
|
ISBN string `validate:"isbn"`
|
||||||
|
ISBN10 string `validate:"isbn10"`
|
||||||
|
ISBN13 string `validate:"isbn13"`
|
||||||
|
UUID string `validate:"uuid"`
|
||||||
|
UUID3 string `validate:"uuid3"`
|
||||||
|
UUID4 string `validate:"uuid4"`
|
||||||
|
UUID5 string `validate:"uuid5"`
|
||||||
|
ASCII string `validate:"ascii"`
|
||||||
|
PrintableASCII string `validate:"printascii"`
|
||||||
|
MultiByte string `validate:"multibyte"`
|
||||||
|
DataURI string `validate:"datauri"`
|
||||||
|
Latitude string `validate:"latitude"`
|
||||||
|
Longitude string `validate:"longitude"`
|
||||||
|
SSN string `validate:"ssn"`
|
||||||
|
IP string `validate:"ip"`
|
||||||
|
IPv4 string `validate:"ipv4"`
|
||||||
|
IPv6 string `validate:"ipv6"`
|
||||||
|
CIDR string `validate:"cidr"`
|
||||||
|
CIDRv4 string `validate:"cidrv4"`
|
||||||
|
CIDRv6 string `validate:"cidrv6"`
|
||||||
|
TCPAddr string `validate:"tcp_addr"`
|
||||||
|
TCPAddrv4 string `validate:"tcp4_addr"`
|
||||||
|
TCPAddrv6 string `validate:"tcp6_addr"`
|
||||||
|
UDPAddr string `validate:"udp_addr"`
|
||||||
|
UDPAddrv4 string `validate:"udp4_addr"`
|
||||||
|
UDPAddrv6 string `validate:"udp6_addr"`
|
||||||
|
IPAddr string `validate:"ip_addr"`
|
||||||
|
IPAddrv4 string `validate:"ip4_addr"`
|
||||||
|
IPAddrv6 string `validate:"ip6_addr"`
|
||||||
|
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
|
||||||
|
MAC string `validate:"mac"`
|
||||||
|
IsColor string `validate:"iscolor"`
|
||||||
|
StrPtrMinLen *string `validate:"min=10"`
|
||||||
|
StrPtrMaxLen *string `validate:"max=1"`
|
||||||
|
StrPtrLen *string `validate:"len=2"`
|
||||||
|
StrPtrLt *string `validate:"lt=1"`
|
||||||
|
StrPtrLte *string `validate:"lte=1"`
|
||||||
|
StrPtrGt *string `validate:"gt=10"`
|
||||||
|
StrPtrGte *string `validate:"gte=10"`
|
||||||
|
OneOfString string `validate:"oneof=red green"`
|
||||||
|
OneOfInt int `validate:"oneof=5 63"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var test Test
|
||||||
|
|
||||||
|
test.Inner.EqCSFieldString = "1234"
|
||||||
|
test.Inner.GtCSFieldString = "1234"
|
||||||
|
test.Inner.GteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.MaxString = "1234"
|
||||||
|
test.MaxNumber = 2000
|
||||||
|
test.MaxMultiple = make([]string, 9)
|
||||||
|
|
||||||
|
test.LtString = "1234"
|
||||||
|
test.LtNumber = 6
|
||||||
|
test.LtMultiple = make([]string, 3)
|
||||||
|
test.LtTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LteString = "1234"
|
||||||
|
test.LteNumber = 6
|
||||||
|
test.LteMultiple = make([]string, 3)
|
||||||
|
test.LteTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LtFieldString = "12345"
|
||||||
|
test.LteFieldString = "12345"
|
||||||
|
|
||||||
|
test.LtCSFieldString = "1234"
|
||||||
|
test.LteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.AlphaString = "abc3"
|
||||||
|
test.AlphanumString = "abc3!"
|
||||||
|
test.NumericString = "12E.00"
|
||||||
|
test.NumberString = "12E"
|
||||||
|
|
||||||
|
test.Excludes = "this is some test text"
|
||||||
|
test.ExcludesAll = "This is Great!"
|
||||||
|
test.ExcludesRune = "Love it ☻"
|
||||||
|
|
||||||
|
test.ASCII = "カタカナ"
|
||||||
|
test.PrintableASCII = "カタカナ"
|
||||||
|
|
||||||
|
test.MultiByte = "1234feerf"
|
||||||
|
|
||||||
|
s := "toolong"
|
||||||
|
test.StrPtrMaxLen = &s
|
||||||
|
test.StrPtrLen = &s
|
||||||
|
|
||||||
|
err = validate.Struct(test)
|
||||||
|
NotEqual(t, err, nil)
|
||||||
|
|
||||||
|
errs, ok := err.(validator.ValidationErrors)
|
||||||
|
Equal(t, ok, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
ns string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ns: "Test.IsColor",
|
||||||
|
expected: "IsColorは正しい色でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MAC",
|
||||||
|
expected: "MACは正しいMACアドレスを含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddr",
|
||||||
|
expected: "IPAddrは解決可能なIPアドレスでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv4",
|
||||||
|
expected: "IPAddrv4は解決可能なIPv4アドレスでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv6",
|
||||||
|
expected: "IPAddrv6は解決可能なIPv6アドレスでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddr",
|
||||||
|
expected: "UDPAddrは正しいUDPアドレスでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv4",
|
||||||
|
expected: "UDPAddrv4は正しいIPv4のUDPアドレスでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv6",
|
||||||
|
expected: "UDPAddrv6は正しいIPv6のUDPアドレスでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddr",
|
||||||
|
expected: "TCPAddrは正しいTCPアドレスでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv4",
|
||||||
|
expected: "TCPAddrv4は正しいIPv4のTCPアドレスでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv6",
|
||||||
|
expected: "TCPAddrv6は正しいIPv6のTCPアドレスでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDR",
|
||||||
|
expected: "CIDRは正しいCIDR表記を含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv4",
|
||||||
|
expected: "CIDRv4はIPv4アドレスの正しいCIDR表記を含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv6",
|
||||||
|
expected: "CIDRv6はIPv6アドレスの正しいCIDR表記を含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.SSN",
|
||||||
|
expected: "SSNは正しい社会保障番号でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IP",
|
||||||
|
expected: "IPは正しいIPアドレスでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv4",
|
||||||
|
expected: "IPv4は正しいIPv4アドレスでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv6",
|
||||||
|
expected: "IPv6は正しいIPv6アドレスでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.DataURI",
|
||||||
|
expected: "DataURIは正しいデータURIを含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Latitude",
|
||||||
|
expected: "Latitudeは正しい緯度の座標を含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Longitude",
|
||||||
|
expected: "Longitudeは正しい経度の座標を含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MultiByte",
|
||||||
|
expected: "MultiByteはマルチバイト文字を含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ASCII",
|
||||||
|
expected: "ASCIIはASCII文字のみを含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.PrintableASCII",
|
||||||
|
expected: "PrintableASCIIは印刷可能なASCII文字のみを含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID",
|
||||||
|
expected: "UUIDは正しいUUIDでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID3",
|
||||||
|
expected: "UUID3はバージョンが3の正しいUUIDでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID4",
|
||||||
|
expected: "UUID4はバージョンが4の正しいUUIDでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID5",
|
||||||
|
expected: "UUID5はバージョンが4の正しいUUIDでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN",
|
||||||
|
expected: "ISBNは正しいISBN番号でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN10",
|
||||||
|
expected: "ISBN10は正しいISBN-10番号でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN13",
|
||||||
|
expected: "ISBN13は正しいISBN-13番号でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Excludes",
|
||||||
|
expected: "Excludesには'text'というテキストを含むことはできません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesAll",
|
||||||
|
expected: "ExcludesAllには'!@#$'のどれも含めることはできません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesRune",
|
||||||
|
expected: "ExcludesRuneには'☻'を含めることはできません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ContainsAny",
|
||||||
|
expected: "ContainsAnyは'!@#$'の少なくとも1つを含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Contains",
|
||||||
|
expected: "Containsは'purpose'を含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Base64",
|
||||||
|
expected: "Base64は正しいBase64文字列でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Email",
|
||||||
|
expected: "Emailは正しいメールアドレスでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URL",
|
||||||
|
expected: "URLは正しいURLでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URI",
|
||||||
|
expected: "URIは正しいURIでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBColorString",
|
||||||
|
expected: "RGBColorStringは正しいRGBカラーコードでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBAColorString",
|
||||||
|
expected: "RGBAColorStringは正しいRGBAカラーコードでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLColorString",
|
||||||
|
expected: "HSLColorStringは正しいHSLカラーコードでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLAColorString",
|
||||||
|
expected: "HSLAColorStringは正しいHSLAカラーコードでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexadecimalString",
|
||||||
|
expected: "HexadecimalStringは正しい16進表記でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexColorString",
|
||||||
|
expected: "HexColorStringは正しいHEXカラーコードでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumberString",
|
||||||
|
expected: "NumberStringは正しい数でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumericString",
|
||||||
|
expected: "NumericStringは正しい数字でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphanumString",
|
||||||
|
expected: "AlphanumStringはアルファベットと数字のみを含むことができます",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphaString",
|
||||||
|
expected: "AlphaStringはアルファベットのみを含むことができます",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtFieldString",
|
||||||
|
expected: "LtFieldStringはMaxStringよりも小さくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteFieldString",
|
||||||
|
expected: "LteFieldStringはMaxString以下でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtFieldString",
|
||||||
|
expected: "GtFieldStringはMaxStringよりも大きくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteFieldString",
|
||||||
|
expected: "GteFieldStringはMaxString以上でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeFieldString",
|
||||||
|
expected: "NeFieldStringはEqFieldStringとは異ならなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtCSFieldString",
|
||||||
|
expected: "LtCSFieldStringはInner.LtCSFieldStringよりも小さくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteCSFieldString",
|
||||||
|
expected: "LteCSFieldStringはInner.LteCSFieldString以下でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtCSFieldString",
|
||||||
|
expected: "GtCSFieldStringはInner.GtCSFieldStringよりも大きくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteCSFieldString",
|
||||||
|
expected: "GteCSFieldStringはInner.GteCSFieldString以上でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeCSFieldString",
|
||||||
|
expected: "NeCSFieldStringはInner.NeCSFieldStringとは異ならなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqCSFieldString",
|
||||||
|
expected: "EqCSFieldStringはInner.EqCSFieldStringと等しくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqFieldString",
|
||||||
|
expected: "EqFieldStringはMaxStringと等しくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteString",
|
||||||
|
expected: "GteStringの長さは少なくとも3文字以上はなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteNumber",
|
||||||
|
expected: "GteNumberは5.56かより大きくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteMultiple",
|
||||||
|
expected: "GteMultipleは少なくとも2つの項目を含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteTime",
|
||||||
|
expected: "GteTimeは現時刻以降でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtString",
|
||||||
|
expected: "GtStringの長さは3文字よりも多くなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtNumber",
|
||||||
|
expected: "GtNumberは5.56よりも大きくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtMultiple",
|
||||||
|
expected: "GtMultipleは2つの項目よりも多い項目を含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtTime",
|
||||||
|
expected: "GtTimeは現時刻よりも後でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteString",
|
||||||
|
expected: "LteStringの長さは最大でも3文字でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteNumber",
|
||||||
|
expected: "LteNumberは5.56かより小さくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteMultiple",
|
||||||
|
expected: "LteMultipleは最大でも2つの項目を含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteTime",
|
||||||
|
expected: "LteTimeは現時刻以前でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtString",
|
||||||
|
expected: "LtStringの長さは3文字よりも少なくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtNumber",
|
||||||
|
expected: "LtNumberは5.56よりも小さくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtMultiple",
|
||||||
|
expected: "LtMultipleは2つの項目よりも少ない項目を含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtTime",
|
||||||
|
expected: "LtTimeは現時刻よりも前でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeString",
|
||||||
|
expected: "NeStringはと異ならなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeNumber",
|
||||||
|
expected: "NeNumberは0.00と異ならなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeMultiple",
|
||||||
|
expected: "NeMultipleの項目の数は0個と異ならなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqString",
|
||||||
|
expected: "EqStringは3と等しくありません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqNumber",
|
||||||
|
expected: "EqNumberは2.33と等しくありません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqMultiple",
|
||||||
|
expected: "EqMultipleは7と等しくありません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxString",
|
||||||
|
expected: "MaxStringの長さは最大でも3文字でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxNumber",
|
||||||
|
expected: "MaxNumberは1,113.00かより小さくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxMultiple",
|
||||||
|
expected: "MaxMultipleは最大でも7つの項目を含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinString",
|
||||||
|
expected: "MinStringの長さは少なくとも1文字はなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinNumber",
|
||||||
|
expected: "MinNumberは1,113.00かより大きくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinMultiple",
|
||||||
|
expected: "MinMultipleは少なくとも7つの項目を含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenString",
|
||||||
|
expected: "LenStringの長さは1文字でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenNumber",
|
||||||
|
expected: "LenNumberは1,113.00と等しくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenMultiple",
|
||||||
|
expected: "LenMultipleは7つの項目を含まなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredString",
|
||||||
|
expected: "RequiredStringは必須フィールドです",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredNumber",
|
||||||
|
expected: "RequiredNumberは必須フィールドです",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredMultiple",
|
||||||
|
expected: "RequiredMultipleは必須フィールドです",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMinLen",
|
||||||
|
expected: "StrPtrMinLenの長さは少なくとも10文字はなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMaxLen",
|
||||||
|
expected: "StrPtrMaxLenの長さは最大でも1文字でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLen",
|
||||||
|
expected: "StrPtrLenの長さは2文字でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLt",
|
||||||
|
expected: "StrPtrLtの長さは1文字よりも少なくなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLte",
|
||||||
|
expected: "StrPtrLteの長さは最大でも1文字でなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGt",
|
||||||
|
expected: "StrPtrGtの長さは10文字よりも多くなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGte",
|
||||||
|
expected: "StrPtrGteの長さは少なくとも10文字以上はなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfString",
|
||||||
|
expected: "OneOfStringは[red green]のうちのいずれかでなければなりません",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfInt",
|
||||||
|
expected: "OneOfIntは[5 63]のうちのいずれかでなければなりません",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
|
||||||
|
var fe validator.FieldError
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
if tt.ns == e.Namespace() {
|
||||||
|
fe = e
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotEqual(t, fe, nil)
|
||||||
|
Equal(t, tt.expected, fe.Translate(trans))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,634 @@
|
||||||
|
package nl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
english "gin-valid/go-playground/locales/en"
|
||||||
|
ut "gin-valid/go-playground/universal-translator"
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
. "github.com/go-playground/assert/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTranslations(t *testing.T) {
|
||||||
|
|
||||||
|
eng := english.New()
|
||||||
|
uni := ut.New(eng, eng)
|
||||||
|
trans, _ := uni.GetTranslator("en")
|
||||||
|
|
||||||
|
validate := validator.New()
|
||||||
|
|
||||||
|
err := RegisterDefaultTranslations(validate, trans)
|
||||||
|
Equal(t, err, nil)
|
||||||
|
|
||||||
|
type Inner struct {
|
||||||
|
EqCSFieldString string
|
||||||
|
NeCSFieldString string
|
||||||
|
GtCSFieldString string
|
||||||
|
GteCSFieldString string
|
||||||
|
LtCSFieldString string
|
||||||
|
LteCSFieldString string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Inner Inner
|
||||||
|
RequiredString string `validate:"required"`
|
||||||
|
RequiredNumber int `validate:"required"`
|
||||||
|
RequiredMultiple []string `validate:"required"`
|
||||||
|
LenString string `validate:"len=1"`
|
||||||
|
LenNumber float64 `validate:"len=1113.00"`
|
||||||
|
LenMultiple []string `validate:"len=7"`
|
||||||
|
MinString string `validate:"min=1"`
|
||||||
|
MinNumber float64 `validate:"min=1113.00"`
|
||||||
|
MinMultiple []string `validate:"min=7"`
|
||||||
|
MaxString string `validate:"max=3"`
|
||||||
|
MaxNumber float64 `validate:"max=1113.00"`
|
||||||
|
MaxMultiple []string `validate:"max=7"`
|
||||||
|
EqString string `validate:"eq=3"`
|
||||||
|
EqNumber float64 `validate:"eq=2.33"`
|
||||||
|
EqMultiple []string `validate:"eq=7"`
|
||||||
|
NeString string `validate:"ne="`
|
||||||
|
NeNumber float64 `validate:"ne=0.00"`
|
||||||
|
NeMultiple []string `validate:"ne=0"`
|
||||||
|
LtString string `validate:"lt=3"`
|
||||||
|
LtNumber float64 `validate:"lt=5.56"`
|
||||||
|
LtMultiple []string `validate:"lt=2"`
|
||||||
|
LtTime time.Time `validate:"lt"`
|
||||||
|
LteString string `validate:"lte=3"`
|
||||||
|
LteNumber float64 `validate:"lte=5.56"`
|
||||||
|
LteMultiple []string `validate:"lte=2"`
|
||||||
|
LteTime time.Time `validate:"lte"`
|
||||||
|
GtString string `validate:"gt=3"`
|
||||||
|
GtNumber float64 `validate:"gt=5.56"`
|
||||||
|
GtMultiple []string `validate:"gt=2"`
|
||||||
|
GtTime time.Time `validate:"gt"`
|
||||||
|
GteString string `validate:"gte=3"`
|
||||||
|
GteNumber float64 `validate:"gte=5.56"`
|
||||||
|
GteMultiple []string `validate:"gte=2"`
|
||||||
|
GteTime time.Time `validate:"gte"`
|
||||||
|
EqFieldString string `validate:"eqfield=MaxString"`
|
||||||
|
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
|
||||||
|
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
|
||||||
|
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
|
||||||
|
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
|
||||||
|
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
|
||||||
|
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
|
||||||
|
NeFieldString string `validate:"nefield=EqFieldString"`
|
||||||
|
GtFieldString string `validate:"gtfield=MaxString"`
|
||||||
|
GteFieldString string `validate:"gtefield=MaxString"`
|
||||||
|
LtFieldString string `validate:"ltfield=MaxString"`
|
||||||
|
LteFieldString string `validate:"ltefield=MaxString"`
|
||||||
|
AlphaString string `validate:"alpha"`
|
||||||
|
AlphanumString string `validate:"alphanum"`
|
||||||
|
NumericString string `validate:"numeric"`
|
||||||
|
NumberString string `validate:"number"`
|
||||||
|
HexadecimalString string `validate:"hexadecimal"`
|
||||||
|
HexColorString string `validate:"hexcolor"`
|
||||||
|
RGBColorString string `validate:"rgb"`
|
||||||
|
RGBAColorString string `validate:"rgba"`
|
||||||
|
HSLColorString string `validate:"hsl"`
|
||||||
|
HSLAColorString string `validate:"hsla"`
|
||||||
|
Email string `validate:"email"`
|
||||||
|
URL string `validate:"url"`
|
||||||
|
URI string `validate:"uri"`
|
||||||
|
Base64 string `validate:"base64"`
|
||||||
|
Contains string `validate:"contains=purpose"`
|
||||||
|
ContainsAny string `validate:"containsany=!@#$"`
|
||||||
|
Excludes string `validate:"excludes=text"`
|
||||||
|
ExcludesAll string `validate:"excludesall=!@#$"`
|
||||||
|
ExcludesRune string `validate:"excludesrune=☻"`
|
||||||
|
ISBN string `validate:"isbn"`
|
||||||
|
ISBN10 string `validate:"isbn10"`
|
||||||
|
ISBN13 string `validate:"isbn13"`
|
||||||
|
UUID string `validate:"uuid"`
|
||||||
|
UUID3 string `validate:"uuid3"`
|
||||||
|
UUID4 string `validate:"uuid4"`
|
||||||
|
UUID5 string `validate:"uuid5"`
|
||||||
|
ASCII string `validate:"ascii"`
|
||||||
|
PrintableASCII string `validate:"printascii"`
|
||||||
|
MultiByte string `validate:"multibyte"`
|
||||||
|
DataURI string `validate:"datauri"`
|
||||||
|
Latitude string `validate:"latitude"`
|
||||||
|
Longitude string `validate:"longitude"`
|
||||||
|
SSN string `validate:"ssn"`
|
||||||
|
IP string `validate:"ip"`
|
||||||
|
IPv4 string `validate:"ipv4"`
|
||||||
|
IPv6 string `validate:"ipv6"`
|
||||||
|
CIDR string `validate:"cidr"`
|
||||||
|
CIDRv4 string `validate:"cidrv4"`
|
||||||
|
CIDRv6 string `validate:"cidrv6"`
|
||||||
|
TCPAddr string `validate:"tcp_addr"`
|
||||||
|
TCPAddrv4 string `validate:"tcp4_addr"`
|
||||||
|
TCPAddrv6 string `validate:"tcp6_addr"`
|
||||||
|
UDPAddr string `validate:"udp_addr"`
|
||||||
|
UDPAddrv4 string `validate:"udp4_addr"`
|
||||||
|
UDPAddrv6 string `validate:"udp6_addr"`
|
||||||
|
IPAddr string `validate:"ip_addr"`
|
||||||
|
IPAddrv4 string `validate:"ip4_addr"`
|
||||||
|
IPAddrv6 string `validate:"ip6_addr"`
|
||||||
|
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
|
||||||
|
MAC string `validate:"mac"`
|
||||||
|
IsColor string `validate:"iscolor"`
|
||||||
|
StrPtrMinLen *string `validate:"min=10"`
|
||||||
|
StrPtrMaxLen *string `validate:"max=1"`
|
||||||
|
StrPtrLen *string `validate:"len=2"`
|
||||||
|
StrPtrLt *string `validate:"lt=1"`
|
||||||
|
StrPtrLte *string `validate:"lte=1"`
|
||||||
|
StrPtrGt *string `validate:"gt=10"`
|
||||||
|
StrPtrGte *string `validate:"gte=10"`
|
||||||
|
OneOfString string `validate:"oneof=red green"`
|
||||||
|
OneOfInt int `validate:"oneof=5 63"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var test Test
|
||||||
|
|
||||||
|
test.Inner.EqCSFieldString = "1234"
|
||||||
|
test.Inner.GtCSFieldString = "1234"
|
||||||
|
test.Inner.GteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.MaxString = "1234"
|
||||||
|
test.MaxNumber = 2000
|
||||||
|
test.MaxMultiple = make([]string, 9)
|
||||||
|
|
||||||
|
test.LtString = "1234"
|
||||||
|
test.LtNumber = 6
|
||||||
|
test.LtMultiple = make([]string, 3)
|
||||||
|
test.LtTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LteString = "1234"
|
||||||
|
test.LteNumber = 6
|
||||||
|
test.LteMultiple = make([]string, 3)
|
||||||
|
test.LteTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LtFieldString = "12345"
|
||||||
|
test.LteFieldString = "12345"
|
||||||
|
|
||||||
|
test.LtCSFieldString = "1234"
|
||||||
|
test.LteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.AlphaString = "abc3"
|
||||||
|
test.AlphanumString = "abc3!"
|
||||||
|
test.NumericString = "12E.00"
|
||||||
|
test.NumberString = "12E"
|
||||||
|
|
||||||
|
test.Excludes = "this is some test text"
|
||||||
|
test.ExcludesAll = "This is Great!"
|
||||||
|
test.ExcludesRune = "Love it ☻"
|
||||||
|
|
||||||
|
test.ASCII = "カタカナ"
|
||||||
|
test.PrintableASCII = "カタカナ"
|
||||||
|
|
||||||
|
test.MultiByte = "1234feerf"
|
||||||
|
|
||||||
|
s := "toolong"
|
||||||
|
test.StrPtrMaxLen = &s
|
||||||
|
test.StrPtrLen = &s
|
||||||
|
|
||||||
|
err = validate.Struct(test)
|
||||||
|
NotEqual(t, err, nil)
|
||||||
|
|
||||||
|
errs, ok := err.(validator.ValidationErrors)
|
||||||
|
Equal(t, ok, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
ns string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ns: "Test.IsColor",
|
||||||
|
expected: "IsColor moet een geldige kleur zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MAC",
|
||||||
|
expected: "MAC moet een geldig MAC adres bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddr",
|
||||||
|
expected: "IPAddr moet een oplosbaar IP adres zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv4",
|
||||||
|
expected: "IPAddrv4 moet een oplosbaar IPv4 adres zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv6",
|
||||||
|
expected: "IPAddrv6 moet een oplosbaar IPv6 adres zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddr",
|
||||||
|
expected: "UDPAddr moet een geldig UDP adres zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv4",
|
||||||
|
expected: "UDPAddrv4 moet een geldig IPv4 UDP adres zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv6",
|
||||||
|
expected: "UDPAddrv6 moet een geldig IPv6 UDP adres zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddr",
|
||||||
|
expected: "TCPAddr moet een geldig TCP adres zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv4",
|
||||||
|
expected: "TCPAddrv4 moet een geldig IPv4 TCP adres zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv6",
|
||||||
|
expected: "TCPAddrv6 moet een geldig IPv6 TCP adres zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDR",
|
||||||
|
expected: "CIDR moet een geldige CIDR notatie bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv4",
|
||||||
|
expected: "CIDRv4 moet een geldige CIDR notatie voor een IPv4 adres bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv6",
|
||||||
|
expected: "CIDRv6 moet een geldige CIDR notatie voor een IPv6 adres bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.SSN",
|
||||||
|
expected: "SSN moet een geldig SSN nummer zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IP",
|
||||||
|
expected: "IP moet een geldig IP adres zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv4",
|
||||||
|
expected: "IPv4 moet een geldig IPv4 adres zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv6",
|
||||||
|
expected: "IPv6 moet een geldig IPv6 adres zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.DataURI",
|
||||||
|
expected: "DataURI moet een geldige Data URI bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Latitude",
|
||||||
|
expected: "Latitude moet geldige breedtegraadcoördinaten bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Longitude",
|
||||||
|
expected: "Longitude moet geldige lengtegraadcoördinaten bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MultiByte",
|
||||||
|
expected: "MultiByte moet multibyte karakters bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ASCII",
|
||||||
|
expected: "ASCII mag alleen ascii karakters bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.PrintableASCII",
|
||||||
|
expected: "PrintableASCII mag alleen afdrukbare ascii karakters bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID",
|
||||||
|
expected: "UUID moet een geldige UUID zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID3",
|
||||||
|
expected: "UUID3 moet een geldige versie 3 UUID zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID4",
|
||||||
|
expected: "UUID4 moet een geldige versie 4 UUID zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID5",
|
||||||
|
expected: "UUID5 moet een geldige versie 5 UUID zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN",
|
||||||
|
expected: "ISBN moet een geldig ISBN nummer zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN10",
|
||||||
|
expected: "ISBN10 moet een geldig ISBN-10 nummer zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN13",
|
||||||
|
expected: "ISBN13 moet een geldig ISBN-13 nummer zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Excludes",
|
||||||
|
expected: "Excludes mag niet de tekst 'text' bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesAll",
|
||||||
|
expected: "ExcludesAll mag niet een van de volgende karakters bevatten '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesRune",
|
||||||
|
expected: "ExcludesRune mag niet het volgende bevatten '☻'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ContainsAny",
|
||||||
|
expected: "ContainsAny moet tenminste een van de volgende karakters bevatten '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Contains",
|
||||||
|
expected: "Contains moet de tekst 'purpose' bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Base64",
|
||||||
|
expected: "Base64 moet een geldige Base64 string zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Email",
|
||||||
|
expected: "Email moet een geldig email adres zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URL",
|
||||||
|
expected: "URL moet een geldige URL zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URI",
|
||||||
|
expected: "URI moet een geldige URI zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBColorString",
|
||||||
|
expected: "RGBColorString moet een geldige RGB kleur zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBAColorString",
|
||||||
|
expected: "RGBAColorString moet een geldige RGBA kleur zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLColorString",
|
||||||
|
expected: "HSLColorString moet een geldige HSL kleur zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLAColorString",
|
||||||
|
expected: "HSLAColorString moet een geldige HSLA kleur zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexadecimalString",
|
||||||
|
expected: "HexadecimalString moet een geldig hexadecimaal getal zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexColorString",
|
||||||
|
expected: "HexColorString moet een geldige HEX kleur zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumberString",
|
||||||
|
expected: "NumberString moet een geldig getal zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumericString",
|
||||||
|
expected: "NumericString moet een geldige numerieke waarde zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphanumString",
|
||||||
|
expected: "AlphanumString mag alleen alfanumerieke karakters bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphaString",
|
||||||
|
expected: "AlphaString mag alleen alfabetische karakters bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtFieldString",
|
||||||
|
expected: "LtFieldString moet kleiner zijn dan MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteFieldString",
|
||||||
|
expected: "LteFieldString moet kleiner dan of gelijk aan MaxString zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtFieldString",
|
||||||
|
expected: "GtFieldString moet groter zijn dan MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteFieldString",
|
||||||
|
expected: "GteFieldString moet groter dan of gelijk aan MaxString zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeFieldString",
|
||||||
|
expected: "NeFieldString mag niet gelijk zijn aan EqFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtCSFieldString",
|
||||||
|
expected: "LtCSFieldString moet kleiner zijn dan Inner.LtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteCSFieldString",
|
||||||
|
expected: "LteCSFieldString moet kleiner dan of gelijk aan Inner.LteCSFieldString zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtCSFieldString",
|
||||||
|
expected: "GtCSFieldString moet groter zijn dan Inner.GtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteCSFieldString",
|
||||||
|
expected: "GteCSFieldString moet groter dan of gelijk aan Inner.GteCSFieldString zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeCSFieldString",
|
||||||
|
expected: "NeCSFieldString mag niet gelijk zijn aan Inner.NeCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqCSFieldString",
|
||||||
|
expected: "EqCSFieldString moet gelijk zijn aan Inner.EqCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqFieldString",
|
||||||
|
expected: "EqFieldString moet gelijk zijn aan MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteString",
|
||||||
|
expected: "GteString moet tenminste 3 karakters lang zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteNumber",
|
||||||
|
expected: "GteNumber moet 5.56 of groter zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteMultiple",
|
||||||
|
expected: "GteMultiple moet tenminste 2 items bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteTime",
|
||||||
|
expected: "GteTime moet groter dan of gelijk zijn aan de huidige datum & tijd",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtString",
|
||||||
|
expected: "GtString moet langer dan 3 karakters zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtNumber",
|
||||||
|
expected: "GtNumber moet groter zijn dan 5.56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtMultiple",
|
||||||
|
expected: "GtMultiple moet meer dan 2 items bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtTime",
|
||||||
|
expected: "GtTime moet groter zijn dan de huidige datum & tijd",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteString",
|
||||||
|
expected: "LteString mag maximaal 3 karakters lang zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteNumber",
|
||||||
|
expected: "LteNumber moet 5.56 of minder zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteMultiple",
|
||||||
|
expected: "LteMultiple mag maximaal 2 items bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteTime",
|
||||||
|
expected: "LteTime moet kleiner dan of gelijk aan de huidige datum & tijd zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtString",
|
||||||
|
expected: "LtString moet minder dan 3 karakters lang zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtNumber",
|
||||||
|
expected: "LtNumber moet kleiner zijn dan 5.56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtMultiple",
|
||||||
|
expected: "LtMultiple moet minder dan 2 items bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtTime",
|
||||||
|
expected: "LtTime moet kleiner zijn dan de huidige datum & tijd",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeString",
|
||||||
|
expected: "NeString mag niet gelijk zijn aan ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeNumber",
|
||||||
|
expected: "NeNumber mag niet gelijk zijn aan 0.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeMultiple",
|
||||||
|
expected: "NeMultiple mag niet gelijk zijn aan 0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqString",
|
||||||
|
expected: "EqString is niet gelijk aan 3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqNumber",
|
||||||
|
expected: "EqNumber is niet gelijk aan 2.33",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqMultiple",
|
||||||
|
expected: "EqMultiple is niet gelijk aan 7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxString",
|
||||||
|
expected: "MaxString mag maximaal 3 karakters lang zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxNumber",
|
||||||
|
expected: "MaxNumber moet 1,113.00 of kleiner zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxMultiple",
|
||||||
|
expected: "MaxMultiple mag maximaal 7 items bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinString",
|
||||||
|
expected: "MinString moet tenminste 1 karakter lang zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinNumber",
|
||||||
|
expected: "MinNumber moet 1,113.00 of groter zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinMultiple",
|
||||||
|
expected: "MinMultiple moet tenminste 7 items bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenString",
|
||||||
|
expected: "LenString moet 1 karakter lang zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenNumber",
|
||||||
|
expected: "LenNumber moet gelijk zijn aan 1,113.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenMultiple",
|
||||||
|
expected: "LenMultiple moet 7 items bevatten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredString",
|
||||||
|
expected: "RequiredString is een verplicht veld",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredNumber",
|
||||||
|
expected: "RequiredNumber is een verplicht veld",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredMultiple",
|
||||||
|
expected: "RequiredMultiple is een verplicht veld",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMinLen",
|
||||||
|
expected: "StrPtrMinLen moet tenminste 10 karakters lang zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMaxLen",
|
||||||
|
expected: "StrPtrMaxLen mag maximaal 1 karakter lang zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLen",
|
||||||
|
expected: "StrPtrLen moet 2 karakters lang zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLt",
|
||||||
|
expected: "StrPtrLt moet minder dan 1 karakter lang zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLte",
|
||||||
|
expected: "StrPtrLte mag maximaal 1 karakter lang zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGt",
|
||||||
|
expected: "StrPtrGt moet langer dan 10 karakters zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGte",
|
||||||
|
expected: "StrPtrGte moet tenminste 10 karakters lang zijn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfString",
|
||||||
|
expected: "OneOfString moet een van de volgende zijn [red green]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfInt",
|
||||||
|
expected: "OneOfInt moet een van de volgende zijn [5 63]",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
|
||||||
|
var fe validator.FieldError
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
if tt.ns == e.Namespace() {
|
||||||
|
fe = e
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotEqual(t, fe, nil)
|
||||||
|
Equal(t, tt.expected, fe.Translate(trans))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,677 @@
|
||||||
|
package pt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gin-valid/go-playground/locales/pt"
|
||||||
|
ut "gin-valid/go-playground/universal-translator"
|
||||||
|
. "github.com/go-playground/assert/v2"
|
||||||
|
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTranslations(t *testing.T) {
|
||||||
|
|
||||||
|
pt := pt.New()
|
||||||
|
uni := ut.New(pt, pt)
|
||||||
|
trans, _ := uni.GetTranslator("pt")
|
||||||
|
|
||||||
|
validate := validator.New()
|
||||||
|
|
||||||
|
err := RegisterDefaultTranslations(validate, trans)
|
||||||
|
Equal(t, err, nil)
|
||||||
|
|
||||||
|
type Inner struct {
|
||||||
|
EqCSFieldString string
|
||||||
|
NeCSFieldString string
|
||||||
|
GtCSFieldString string
|
||||||
|
GteCSFieldString string
|
||||||
|
LtCSFieldString string
|
||||||
|
LteCSFieldString string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Inner Inner
|
||||||
|
RequiredString string `validate:"required"`
|
||||||
|
RequiredNumber int `validate:"required"`
|
||||||
|
RequiredMultiple []string `validate:"required"`
|
||||||
|
LenString string `validate:"len=1"`
|
||||||
|
LenNumber float64 `validate:"len=1113.00"`
|
||||||
|
LenMultiple []string `validate:"len=7"`
|
||||||
|
MinString string `validate:"min=1"`
|
||||||
|
MinNumber float64 `validate:"min=1113.00"`
|
||||||
|
MinMultiple []string `validate:"min=7"`
|
||||||
|
MaxString string `validate:"max=3"`
|
||||||
|
MaxNumber float64 `validate:"max=1113.00"`
|
||||||
|
MaxMultiple []string `validate:"max=7"`
|
||||||
|
EqString string `validate:"eq=3"`
|
||||||
|
EqNumber float64 `validate:"eq=2.33"`
|
||||||
|
EqMultiple []string `validate:"eq=7"`
|
||||||
|
NeString string `validate:"ne="`
|
||||||
|
NeNumber float64 `validate:"ne=0.00"`
|
||||||
|
NeMultiple []string `validate:"ne=0"`
|
||||||
|
LtString string `validate:"lt=3"`
|
||||||
|
LtNumber float64 `validate:"lt=5.56"`
|
||||||
|
LtMultiple []string `validate:"lt=2"`
|
||||||
|
LtTime time.Time `validate:"lt"`
|
||||||
|
LteString string `validate:"lte=3"`
|
||||||
|
LteNumber float64 `validate:"lte=5.56"`
|
||||||
|
LteMultiple []string `validate:"lte=2"`
|
||||||
|
LteTime time.Time `validate:"lte"`
|
||||||
|
GtString string `validate:"gt=3"`
|
||||||
|
GtNumber float64 `validate:"gt=5.56"`
|
||||||
|
GtMultiple []string `validate:"gt=2"`
|
||||||
|
GtTime time.Time `validate:"gt"`
|
||||||
|
GteString string `validate:"gte=3"`
|
||||||
|
GteNumber float64 `validate:"gte=5.56"`
|
||||||
|
GteMultiple []string `validate:"gte=2"`
|
||||||
|
GteTime time.Time `validate:"gte"`
|
||||||
|
EqFieldString string `validate:"eqfield=MaxString"`
|
||||||
|
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
|
||||||
|
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
|
||||||
|
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
|
||||||
|
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
|
||||||
|
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
|
||||||
|
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
|
||||||
|
NeFieldString string `validate:"nefield=EqFieldString"`
|
||||||
|
GtFieldString string `validate:"gtfield=MaxString"`
|
||||||
|
GteFieldString string `validate:"gtefield=MaxString"`
|
||||||
|
LtFieldString string `validate:"ltfield=MaxString"`
|
||||||
|
LteFieldString string `validate:"ltefield=MaxString"`
|
||||||
|
AlphaString string `validate:"alpha"`
|
||||||
|
AlphanumString string `validate:"alphanum"`
|
||||||
|
NumericString string `validate:"numeric"`
|
||||||
|
NumberString string `validate:"number"`
|
||||||
|
HexadecimalString string `validate:"hexadecimal"`
|
||||||
|
HexColorString string `validate:"hexcolor"`
|
||||||
|
RGBColorString string `validate:"rgb"`
|
||||||
|
RGBAColorString string `validate:"rgba"`
|
||||||
|
HSLColorString string `validate:"hsl"`
|
||||||
|
HSLAColorString string `validate:"hsla"`
|
||||||
|
Email string `validate:"email"`
|
||||||
|
URL string `validate:"url"`
|
||||||
|
URI string `validate:"uri"`
|
||||||
|
Base64 string `validate:"base64"`
|
||||||
|
Contains string `validate:"contains=purpose"`
|
||||||
|
ContainsAny string `validate:"containsany=!@#$"`
|
||||||
|
Excludes string `validate:"excludes=text"`
|
||||||
|
ExcludesAll string `validate:"excludesall=!@#$"`
|
||||||
|
ExcludesRune string `validate:"excludesrune=☻"`
|
||||||
|
ISBN string `validate:"isbn"`
|
||||||
|
ISBN10 string `validate:"isbn10"`
|
||||||
|
ISBN13 string `validate:"isbn13"`
|
||||||
|
UUID string `validate:"uuid"`
|
||||||
|
UUID3 string `validate:"uuid3"`
|
||||||
|
UUID4 string `validate:"uuid4"`
|
||||||
|
UUID5 string `validate:"uuid5"`
|
||||||
|
ASCII string `validate:"ascii"`
|
||||||
|
PrintableASCII string `validate:"printascii"`
|
||||||
|
MultiByte string `validate:"multibyte"`
|
||||||
|
DataURI string `validate:"datauri"`
|
||||||
|
Latitude string `validate:"latitude"`
|
||||||
|
Longitude string `validate:"longitude"`
|
||||||
|
SSN string `validate:"ssn"`
|
||||||
|
IP string `validate:"ip"`
|
||||||
|
IPv4 string `validate:"ipv4"`
|
||||||
|
IPv6 string `validate:"ipv6"`
|
||||||
|
CIDR string `validate:"cidr"`
|
||||||
|
CIDRv4 string `validate:"cidrv4"`
|
||||||
|
CIDRv6 string `validate:"cidrv6"`
|
||||||
|
TCPAddr string `validate:"tcp_addr"`
|
||||||
|
TCPAddrv4 string `validate:"tcp4_addr"`
|
||||||
|
TCPAddrv6 string `validate:"tcp6_addr"`
|
||||||
|
UDPAddr string `validate:"udp_addr"`
|
||||||
|
UDPAddrv4 string `validate:"udp4_addr"`
|
||||||
|
UDPAddrv6 string `validate:"udp6_addr"`
|
||||||
|
IPAddr string `validate:"ip_addr"`
|
||||||
|
IPAddrv4 string `validate:"ip4_addr"`
|
||||||
|
IPAddrv6 string `validate:"ip6_addr"`
|
||||||
|
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
|
||||||
|
MAC string `validate:"mac"`
|
||||||
|
IsColor string `validate:"iscolor"`
|
||||||
|
StrPtrMinLen *string `validate:"min=10"`
|
||||||
|
StrPtrMaxLen *string `validate:"max=1"`
|
||||||
|
StrPtrLen *string `validate:"len=2"`
|
||||||
|
StrPtrLt *string `validate:"lt=1"`
|
||||||
|
StrPtrLte *string `validate:"lte=1"`
|
||||||
|
StrPtrGt *string `validate:"gt=10"`
|
||||||
|
StrPtrGte *string `validate:"gte=10"`
|
||||||
|
OneOfString string `validate:"oneof=red green"`
|
||||||
|
OneOfInt int `validate:"oneof=5 63"`
|
||||||
|
UniqueSlice []string `validate:"unique"`
|
||||||
|
UniqueArray [3]string `validate:"unique"`
|
||||||
|
UniqueMap map[string]string `validate:"unique"`
|
||||||
|
JSONString string `validate:"json"`
|
||||||
|
LowercaseString string `validate:"lowercase"`
|
||||||
|
UppercaseString string `validate:"uppercase"`
|
||||||
|
Datetime string `validate:"datetime=2006-01-02"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var test Test
|
||||||
|
|
||||||
|
test.Inner.EqCSFieldString = "1234"
|
||||||
|
test.Inner.GtCSFieldString = "1234"
|
||||||
|
test.Inner.GteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.MaxString = "1234"
|
||||||
|
test.MaxNumber = 2000
|
||||||
|
test.MaxMultiple = make([]string, 9)
|
||||||
|
|
||||||
|
test.LtString = "1234"
|
||||||
|
test.LtNumber = 6
|
||||||
|
test.LtMultiple = make([]string, 3)
|
||||||
|
test.LtTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LteString = "1234"
|
||||||
|
test.LteNumber = 6
|
||||||
|
test.LteMultiple = make([]string, 3)
|
||||||
|
test.LteTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LtFieldString = "12345"
|
||||||
|
test.LteFieldString = "12345"
|
||||||
|
|
||||||
|
test.LtCSFieldString = "1234"
|
||||||
|
test.LteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.AlphaString = "abc3"
|
||||||
|
test.AlphanumString = "abc3!"
|
||||||
|
test.NumericString = "12E.00"
|
||||||
|
test.NumberString = "12E"
|
||||||
|
|
||||||
|
test.Excludes = "this is some test text"
|
||||||
|
test.ExcludesAll = "This is Great!"
|
||||||
|
test.ExcludesRune = "Love it ☻"
|
||||||
|
|
||||||
|
test.ASCII = "カタカナ"
|
||||||
|
test.PrintableASCII = "カタカナ"
|
||||||
|
|
||||||
|
test.MultiByte = "1234feerf"
|
||||||
|
|
||||||
|
test.LowercaseString = "ABCDEFG"
|
||||||
|
test.UppercaseString = "abcdefg"
|
||||||
|
|
||||||
|
s := "toolong"
|
||||||
|
test.StrPtrMaxLen = &s
|
||||||
|
test.StrPtrLen = &s
|
||||||
|
|
||||||
|
test.UniqueSlice = []string{"1234", "1234"}
|
||||||
|
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
|
||||||
|
test.Datetime = "2008-Feb-01"
|
||||||
|
|
||||||
|
err = validate.Struct(test)
|
||||||
|
NotEqual(t, err, nil)
|
||||||
|
|
||||||
|
errs, ok := err.(validator.ValidationErrors)
|
||||||
|
Equal(t, ok, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
ns string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ns: "Test.IsColor",
|
||||||
|
expected: "IsColor deve ser uma cor válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MAC",
|
||||||
|
expected: "MAC deve conter um endereço MAC válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddr",
|
||||||
|
expected: "IPAddr deve ser um endereço IP resolvível",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv4",
|
||||||
|
expected: "IPAddrv4 deve ser um endereço IPv4 resolvível",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv6",
|
||||||
|
expected: "IPAddrv6 deve ser um endereço IPv6 resolvível",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddr",
|
||||||
|
expected: "UDPAddr deve ser um endereço UDP válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv4",
|
||||||
|
expected: "UDPAddrv4 deve ser um endereço UDP IPv4 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv6",
|
||||||
|
expected: "UDPAddrv6 deve ser um endereço UDP IPv6 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddr",
|
||||||
|
expected: "TCPAddr deve ser um endereço TCP válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv4",
|
||||||
|
expected: "TCPAddrv4 deve ser um endereço TCP IPv4 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv6",
|
||||||
|
expected: "TCPAddrv6 deve ser um endereço TCP IPv6 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDR",
|
||||||
|
expected: "CIDR não respeita a notação CIDR",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv4",
|
||||||
|
expected: "CIDRv4 não respeita a notação CIDR para um endereço IPv4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv6",
|
||||||
|
expected: "CIDRv6 não respeita a notação CIDR para um endereço IPv6",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.SSN",
|
||||||
|
expected: "SSN deve ser um número SSN válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IP",
|
||||||
|
expected: "IP deve ser um endereço IP válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv4",
|
||||||
|
expected: "IPv4 deve ser um endereço IPv4 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv6",
|
||||||
|
expected: "IPv6 deve ser um endereço IPv6 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.DataURI",
|
||||||
|
expected: "DataURI deve conter um Data URI válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Latitude",
|
||||||
|
expected: "Latitude deve conter uma coordenada de latitude válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Longitude",
|
||||||
|
expected: "Longitude deve conter uma coordenada de longitude válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MultiByte",
|
||||||
|
expected: "MultiByte deve conter caracteres multibyte",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ASCII",
|
||||||
|
expected: "ASCII deve conter apenas caracteres ascii",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.PrintableASCII",
|
||||||
|
expected: "PrintableASCII deve conter apenas caracteres ascii imprimíveis",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID",
|
||||||
|
expected: "UUID deve ser um UUID válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID3",
|
||||||
|
expected: "UUID3 deve ser um UUID versão 3 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID4",
|
||||||
|
expected: "UUID4 deve ser um UUID versão 4 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID5",
|
||||||
|
expected: "UUID5 deve ser um UUID versão 5 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN",
|
||||||
|
expected: "ISBN deve ser um número de ISBN válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN10",
|
||||||
|
expected: "ISBN10 deve ser um número ISBN-10 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN13",
|
||||||
|
expected: "ISBN13 deve ser um número ISBN-13 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Excludes",
|
||||||
|
expected: "Excludes não deve conter o texto 'text'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesAll",
|
||||||
|
expected: "ExcludesAll não deve conter os seguintes caracteres '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesRune",
|
||||||
|
expected: "ExcludesRune não pode conter o seguinte '☻'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ContainsAny",
|
||||||
|
expected: "ContainsAny deve conter pelo menos um dos seguintes caracteres '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Contains",
|
||||||
|
expected: "Contains deve conter o texto 'purpose'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Base64",
|
||||||
|
expected: "Base64 deve ser uma string Base64 válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Email",
|
||||||
|
expected: "Email deve ser um endereço de e-mail válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URL",
|
||||||
|
expected: "URL deve ser um URL válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URI",
|
||||||
|
expected: "URI deve ser um URI válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBColorString",
|
||||||
|
expected: "RGBColorString deve ser uma cor RGB válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBAColorString",
|
||||||
|
expected: "RGBAColorString deve ser uma cor RGBA válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLColorString",
|
||||||
|
expected: "HSLColorString deve ser uma cor HSL válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLAColorString",
|
||||||
|
expected: "HSLAColorString deve ser uma cor HSLA válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexadecimalString",
|
||||||
|
expected: "HexadecimalString deve ser um hexadecimal válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexColorString",
|
||||||
|
expected: "HexColorString deve ser uma cor HEX válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumberString",
|
||||||
|
expected: "NumberString deve ser um número válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumericString",
|
||||||
|
expected: "NumericString deve ser um valor numérico válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphanumString",
|
||||||
|
expected: "AlphanumString deve conter apenas caracteres alfanuméricos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphaString",
|
||||||
|
expected: "AlphaString deve conter apenas caracteres alfabéticos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtFieldString",
|
||||||
|
expected: "LtFieldString deve ser menor que MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteFieldString",
|
||||||
|
expected: "LteFieldString deve ser menor ou igual que MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtFieldString",
|
||||||
|
expected: "GtFieldString deve ser maior que MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteFieldString",
|
||||||
|
expected: "GteFieldString deve ser maior ou igual que MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeFieldString",
|
||||||
|
expected: "NeFieldString não deve ser igual a EqFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtCSFieldString",
|
||||||
|
expected: "LtCSFieldString deve ser menor que Inner.LtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteCSFieldString",
|
||||||
|
expected: "LteCSFieldString deve ser menor ou igual que Inner.LteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtCSFieldString",
|
||||||
|
expected: "GtCSFieldString deve ser maior que Inner.GtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteCSFieldString",
|
||||||
|
expected: "GteCSFieldString deve ser maior ou igual que Inner.GteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeCSFieldString",
|
||||||
|
expected: "NeCSFieldString não deve ser igual a Inner.NeCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqCSFieldString",
|
||||||
|
expected: "EqCSFieldString deve ser igual a Inner.EqCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqFieldString",
|
||||||
|
expected: "EqFieldString deve ser igual a MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteString",
|
||||||
|
expected: "GteString deve ter pelo menos 3 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteNumber",
|
||||||
|
expected: "GteNumber deve ser maior ou igual a 5,56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteMultiple",
|
||||||
|
expected: "GteMultiple deve conter pelo menos 2 items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteTime",
|
||||||
|
expected: "GteTime deve ser posterior ou igual à data/hora atual",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtString",
|
||||||
|
expected: "GtString deve conter mais de 3 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtNumber",
|
||||||
|
expected: "GtNumber deve ser maior que 5,56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtMultiple",
|
||||||
|
expected: "GtMultiple deve conter mais de 2 items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtTime",
|
||||||
|
expected: "GtTime deve ser posterior à data/hora atual",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteString",
|
||||||
|
expected: "LteString deve ter no máximo 3 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteNumber",
|
||||||
|
expected: "LteNumber deve ser menor ou igual a 5,56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteMultiple",
|
||||||
|
expected: "LteMultiple deve conter no máximo 2 items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteTime",
|
||||||
|
expected: "LteTime deve ser anterior ou igual à data/hora atual",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtString",
|
||||||
|
expected: "LtString deve ter menos de 3 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtNumber",
|
||||||
|
expected: "LtNumber deve ser menor que 5,56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtMultiple",
|
||||||
|
expected: "LtMultiple deve conter menos de 2 items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtTime",
|
||||||
|
expected: "LtTime deve ser anterior à data / hora atual",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeString",
|
||||||
|
expected: "NeString não deve ser igual a ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeNumber",
|
||||||
|
expected: "NeNumber não deve ser igual a 0.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeMultiple",
|
||||||
|
expected: "NeMultiple não deve ser igual a 0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqString",
|
||||||
|
expected: "EqString não é igual a 3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqNumber",
|
||||||
|
expected: "EqNumber não é igual a 2.33",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqMultiple",
|
||||||
|
expected: "EqMultiple não é igual a 7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxString",
|
||||||
|
expected: "MaxString deve ter no máximo 3 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxNumber",
|
||||||
|
expected: "MaxNumber deve ser 1.113,00 ou menos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxMultiple",
|
||||||
|
expected: "MaxMultiple deve conter no máximo 7 items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinString",
|
||||||
|
expected: "MinString deve ter pelo menos 1 caractere",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinNumber",
|
||||||
|
expected: "MinNumber deve ser 1.113,00 ou superior",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinMultiple",
|
||||||
|
expected: "MinMultiple deve conter pelo menos 7 items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenString",
|
||||||
|
expected: "LenString deve ter 1 caractere",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenNumber",
|
||||||
|
expected: "LenNumber deve ser igual a 1.113,00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenMultiple",
|
||||||
|
expected: "LenMultiple deve conter 7 items",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredString",
|
||||||
|
expected: "RequiredString é obrigatório",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredNumber",
|
||||||
|
expected: "RequiredNumber é obrigatório",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredMultiple",
|
||||||
|
expected: "RequiredMultiple é obrigatório",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMinLen",
|
||||||
|
expected: "StrPtrMinLen deve ter pelo menos 10 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMaxLen",
|
||||||
|
expected: "StrPtrMaxLen deve ter no máximo 1 caractere",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLen",
|
||||||
|
expected: "StrPtrLen deve ter 2 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLt",
|
||||||
|
expected: "StrPtrLt deve ter menos de 1 caractere",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLte",
|
||||||
|
expected: "StrPtrLte deve ter no máximo 1 caractere",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGt",
|
||||||
|
expected: "StrPtrGt deve conter mais de 10 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGte",
|
||||||
|
expected: "StrPtrGte deve ter pelo menos 10 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfString",
|
||||||
|
expected: "OneOfString deve ser um de [red green]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfInt",
|
||||||
|
expected: "OneOfInt deve ser um de [5 63]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueSlice",
|
||||||
|
expected: "UniqueSlice deve conter valores únicos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueArray",
|
||||||
|
expected: "UniqueArray deve conter valores únicos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueMap",
|
||||||
|
expected: "UniqueMap deve conter valores únicos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.JSONString",
|
||||||
|
expected: "JSONString deve ser uma string json válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LowercaseString",
|
||||||
|
expected: "LowercaseString deve estar em minuscúlas",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UppercaseString",
|
||||||
|
expected: "UppercaseString deve estar em maiúsculas",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Datetime",
|
||||||
|
expected: "Datetime não está no formato 2006-01-02",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
|
||||||
|
var fe validator.FieldError
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
if tt.ns == e.Namespace() {
|
||||||
|
fe = e
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotEqual(t, fe, nil)
|
||||||
|
Equal(t, tt.expected, fe.Translate(trans))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,634 @@
|
||||||
|
package pt_BR
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
brazilian_portuguese "gin-valid/go-playground/locales/pt_BR"
|
||||||
|
ut "gin-valid/go-playground/universal-translator"
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
. "github.com/go-playground/assert/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTranslations(t *testing.T) {
|
||||||
|
|
||||||
|
ptbr := brazilian_portuguese.New()
|
||||||
|
uni := ut.New(ptbr, ptbr)
|
||||||
|
trans, _ := uni.GetTranslator("pt_BR")
|
||||||
|
|
||||||
|
validate := validator.New()
|
||||||
|
|
||||||
|
err := RegisterDefaultTranslations(validate, trans)
|
||||||
|
Equal(t, err, nil)
|
||||||
|
|
||||||
|
type Inner struct {
|
||||||
|
EqCSFieldString string
|
||||||
|
NeCSFieldString string
|
||||||
|
GtCSFieldString string
|
||||||
|
GteCSFieldString string
|
||||||
|
LtCSFieldString string
|
||||||
|
LteCSFieldString string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Inner Inner
|
||||||
|
RequiredString string `validate:"required"`
|
||||||
|
RequiredNumber int `validate:"required"`
|
||||||
|
RequiredMultiple []string `validate:"required"`
|
||||||
|
LenString string `validate:"len=1"`
|
||||||
|
LenNumber float64 `validate:"len=1113.00"`
|
||||||
|
LenMultiple []string `validate:"len=7"`
|
||||||
|
MinString string `validate:"min=1"`
|
||||||
|
MinNumber float64 `validate:"min=1113.00"`
|
||||||
|
MinMultiple []string `validate:"min=7"`
|
||||||
|
MaxString string `validate:"max=3"`
|
||||||
|
MaxNumber float64 `validate:"max=1113.00"`
|
||||||
|
MaxMultiple []string `validate:"max=7"`
|
||||||
|
EqString string `validate:"eq=3"`
|
||||||
|
EqNumber float64 `validate:"eq=2.33"`
|
||||||
|
EqMultiple []string `validate:"eq=7"`
|
||||||
|
NeString string `validate:"ne="`
|
||||||
|
NeNumber float64 `validate:"ne=0.00"`
|
||||||
|
NeMultiple []string `validate:"ne=0"`
|
||||||
|
LtString string `validate:"lt=3"`
|
||||||
|
LtNumber float64 `validate:"lt=5.56"`
|
||||||
|
LtMultiple []string `validate:"lt=2"`
|
||||||
|
LtTime time.Time `validate:"lt"`
|
||||||
|
LteString string `validate:"lte=3"`
|
||||||
|
LteNumber float64 `validate:"lte=5.56"`
|
||||||
|
LteMultiple []string `validate:"lte=2"`
|
||||||
|
LteTime time.Time `validate:"lte"`
|
||||||
|
GtString string `validate:"gt=3"`
|
||||||
|
GtNumber float64 `validate:"gt=5.56"`
|
||||||
|
GtMultiple []string `validate:"gt=2"`
|
||||||
|
GtTime time.Time `validate:"gt"`
|
||||||
|
GteString string `validate:"gte=3"`
|
||||||
|
GteNumber float64 `validate:"gte=5.56"`
|
||||||
|
GteMultiple []string `validate:"gte=2"`
|
||||||
|
GteTime time.Time `validate:"gte"`
|
||||||
|
EqFieldString string `validate:"eqfield=MaxString"`
|
||||||
|
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
|
||||||
|
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
|
||||||
|
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
|
||||||
|
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
|
||||||
|
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
|
||||||
|
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
|
||||||
|
NeFieldString string `validate:"nefield=EqFieldString"`
|
||||||
|
GtFieldString string `validate:"gtfield=MaxString"`
|
||||||
|
GteFieldString string `validate:"gtefield=MaxString"`
|
||||||
|
LtFieldString string `validate:"ltfield=MaxString"`
|
||||||
|
LteFieldString string `validate:"ltefield=MaxString"`
|
||||||
|
AlphaString string `validate:"alpha"`
|
||||||
|
AlphanumString string `validate:"alphanum"`
|
||||||
|
NumericString string `validate:"numeric"`
|
||||||
|
NumberString string `validate:"number"`
|
||||||
|
HexadecimalString string `validate:"hexadecimal"`
|
||||||
|
HexColorString string `validate:"hexcolor"`
|
||||||
|
RGBColorString string `validate:"rgb"`
|
||||||
|
RGBAColorString string `validate:"rgba"`
|
||||||
|
HSLColorString string `validate:"hsl"`
|
||||||
|
HSLAColorString string `validate:"hsla"`
|
||||||
|
Email string `validate:"email"`
|
||||||
|
URL string `validate:"url"`
|
||||||
|
URI string `validate:"uri"`
|
||||||
|
Base64 string `validate:"base64"`
|
||||||
|
Contains string `validate:"contains=purpose"`
|
||||||
|
ContainsAny string `validate:"containsany=!@#$"`
|
||||||
|
Excludes string `validate:"excludes=text"`
|
||||||
|
ExcludesAll string `validate:"excludesall=!@#$"`
|
||||||
|
ExcludesRune string `validate:"excludesrune=☻"`
|
||||||
|
ISBN string `validate:"isbn"`
|
||||||
|
ISBN10 string `validate:"isbn10"`
|
||||||
|
ISBN13 string `validate:"isbn13"`
|
||||||
|
UUID string `validate:"uuid"`
|
||||||
|
UUID3 string `validate:"uuid3"`
|
||||||
|
UUID4 string `validate:"uuid4"`
|
||||||
|
UUID5 string `validate:"uuid5"`
|
||||||
|
ASCII string `validate:"ascii"`
|
||||||
|
PrintableASCII string `validate:"printascii"`
|
||||||
|
MultiByte string `validate:"multibyte"`
|
||||||
|
DataURI string `validate:"datauri"`
|
||||||
|
Latitude string `validate:"latitude"`
|
||||||
|
Longitude string `validate:"longitude"`
|
||||||
|
SSN string `validate:"ssn"`
|
||||||
|
IP string `validate:"ip"`
|
||||||
|
IPv4 string `validate:"ipv4"`
|
||||||
|
IPv6 string `validate:"ipv6"`
|
||||||
|
CIDR string `validate:"cidr"`
|
||||||
|
CIDRv4 string `validate:"cidrv4"`
|
||||||
|
CIDRv6 string `validate:"cidrv6"`
|
||||||
|
TCPAddr string `validate:"tcp_addr"`
|
||||||
|
TCPAddrv4 string `validate:"tcp4_addr"`
|
||||||
|
TCPAddrv6 string `validate:"tcp6_addr"`
|
||||||
|
UDPAddr string `validate:"udp_addr"`
|
||||||
|
UDPAddrv4 string `validate:"udp4_addr"`
|
||||||
|
UDPAddrv6 string `validate:"udp6_addr"`
|
||||||
|
IPAddr string `validate:"ip_addr"`
|
||||||
|
IPAddrv4 string `validate:"ip4_addr"`
|
||||||
|
IPAddrv6 string `validate:"ip6_addr"`
|
||||||
|
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
|
||||||
|
MAC string `validate:"mac"`
|
||||||
|
IsColor string `validate:"iscolor"`
|
||||||
|
StrPtrMinLen *string `validate:"min=10"`
|
||||||
|
StrPtrMaxLen *string `validate:"max=1"`
|
||||||
|
StrPtrLen *string `validate:"len=2"`
|
||||||
|
StrPtrLt *string `validate:"lt=1"`
|
||||||
|
StrPtrLte *string `validate:"lte=1"`
|
||||||
|
StrPtrGt *string `validate:"gt=10"`
|
||||||
|
StrPtrGte *string `validate:"gte=10"`
|
||||||
|
OneOfString string `validate:"oneof=red green"`
|
||||||
|
OneOfInt int `validate:"oneof=5 63"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var test Test
|
||||||
|
|
||||||
|
test.Inner.EqCSFieldString = "1234"
|
||||||
|
test.Inner.GtCSFieldString = "1234"
|
||||||
|
test.Inner.GteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.MaxString = "1234"
|
||||||
|
test.MaxNumber = 2000
|
||||||
|
test.MaxMultiple = make([]string, 9)
|
||||||
|
|
||||||
|
test.LtString = "1234"
|
||||||
|
test.LtNumber = 6
|
||||||
|
test.LtMultiple = make([]string, 3)
|
||||||
|
test.LtTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LteString = "1234"
|
||||||
|
test.LteNumber = 6
|
||||||
|
test.LteMultiple = make([]string, 3)
|
||||||
|
test.LteTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LtFieldString = "12345"
|
||||||
|
test.LteFieldString = "12345"
|
||||||
|
|
||||||
|
test.LtCSFieldString = "1234"
|
||||||
|
test.LteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.AlphaString = "abc3"
|
||||||
|
test.AlphanumString = "abc3!"
|
||||||
|
test.NumericString = "12E.00"
|
||||||
|
test.NumberString = "12E"
|
||||||
|
|
||||||
|
test.Excludes = "este é um texto de teste"
|
||||||
|
test.ExcludesAll = "Isso é Ótimo!"
|
||||||
|
test.ExcludesRune = "Amo isso ☻"
|
||||||
|
|
||||||
|
test.ASCII = "カタカナ"
|
||||||
|
test.PrintableASCII = "カタカナ"
|
||||||
|
|
||||||
|
test.MultiByte = "1234feerf"
|
||||||
|
|
||||||
|
s := "toolong"
|
||||||
|
test.StrPtrMaxLen = &s
|
||||||
|
test.StrPtrLen = &s
|
||||||
|
|
||||||
|
err = validate.Struct(test)
|
||||||
|
NotEqual(t, err, nil)
|
||||||
|
|
||||||
|
errs, ok := err.(validator.ValidationErrors)
|
||||||
|
Equal(t, ok, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
ns string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ns: "Test.IsColor",
|
||||||
|
expected: "IsColor deve ser uma cor válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MAC",
|
||||||
|
expected: "MAC deve conter um endereço MAC válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddr",
|
||||||
|
expected: "IPAddr deve ser um endereço IP resolvível",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv4",
|
||||||
|
expected: "IPAddrv4 deve ser um endereço IPv4 resolvível",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv6",
|
||||||
|
expected: "IPAddrv6 deve ser um endereço IPv6 resolvível",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddr",
|
||||||
|
expected: "UDPAddr deve ser um endereço UDP válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv4",
|
||||||
|
expected: "UDPAddrv4 deve ser um endereço IPv4 UDP válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv6",
|
||||||
|
expected: "UDPAddrv6 deve ser um endereço IPv6 UDP válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddr",
|
||||||
|
expected: "TCPAddr deve ser um endereço TCP válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv4",
|
||||||
|
expected: "TCPAddrv4 deve ser um endereço IPv4 TCP válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv6",
|
||||||
|
expected: "TCPAddrv6 deve ser um endereço IPv6 TCP válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDR",
|
||||||
|
expected: "CIDR deve conter uma notação CIDR válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv4",
|
||||||
|
expected: "CIDRv4 deve conter uma notação CIDR válida para um endereço IPv4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv6",
|
||||||
|
expected: "CIDRv6 deve conter uma notação CIDR válida para um endereço IPv6",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.SSN",
|
||||||
|
expected: "SSN deve ser um número SSN válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IP",
|
||||||
|
expected: "IP deve ser um endereço de IP válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv4",
|
||||||
|
expected: "IPv4 deve ser um endereço IPv4 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv6",
|
||||||
|
expected: "IPv6 deve ser um endereço IPv6 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.DataURI",
|
||||||
|
expected: "DataURI deve conter um URI data válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Latitude",
|
||||||
|
expected: "Latitude deve conter uma coordenada de latitude válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Longitude",
|
||||||
|
expected: "Longitude deve conter uma coordenada de longitude válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MultiByte",
|
||||||
|
expected: "MultiByte deve conter caracteres multibyte",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ASCII",
|
||||||
|
expected: "ASCII deve conter apenas caracteres ascii",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.PrintableASCII",
|
||||||
|
expected: "PrintableASCII deve conter apenas caracteres ascii imprimíveis",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID",
|
||||||
|
expected: "UUID deve ser um UUID válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID3",
|
||||||
|
expected: "UUID3 deve ser um UUID versão 3 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID4",
|
||||||
|
expected: "UUID4 deve ser um UUID versão 4 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID5",
|
||||||
|
expected: "UUID5 deve ser um UUID versão 5 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN",
|
||||||
|
expected: "ISBN deve ser um número ISBN válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN10",
|
||||||
|
expected: "ISBN10 deve ser um número ISBN-10 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN13",
|
||||||
|
expected: "ISBN13 deve ser um número ISBN-13 válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Excludes",
|
||||||
|
expected: "Excludes não deve conter o texto 'text'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesAll",
|
||||||
|
expected: "ExcludesAll não deve conter nenhum dos caracteres '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesRune",
|
||||||
|
expected: "ExcludesRune não deve conter '☻'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ContainsAny",
|
||||||
|
expected: "ContainsAny deve conter pelo menos um dos caracteres '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Contains",
|
||||||
|
expected: "Contains deve conter o texto 'purpose'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Base64",
|
||||||
|
expected: "Base64 deve ser uma string Base64 válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Email",
|
||||||
|
expected: "Email deve ser um endereço de e-mail válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URL",
|
||||||
|
expected: "URL deve ser uma URL válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URI",
|
||||||
|
expected: "URI deve ser uma URI válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBColorString",
|
||||||
|
expected: "RGBColorString deve ser uma cor RGB válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBAColorString",
|
||||||
|
expected: "RGBAColorString deve ser uma cor RGBA válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLColorString",
|
||||||
|
expected: "HSLColorString deve ser uma cor HSL válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLAColorString",
|
||||||
|
expected: "HSLAColorString deve ser uma cor HSLA válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexadecimalString",
|
||||||
|
expected: "HexadecimalString deve ser um hexadecimal válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexColorString",
|
||||||
|
expected: "HexColorString deve ser uma cor HEX válida",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumberString",
|
||||||
|
expected: "NumberString deve ser um número válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumericString",
|
||||||
|
expected: "NumericString deve ser um valor numérico válido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphanumString",
|
||||||
|
expected: "AlphanumString deve conter caracteres alfanuméricos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphaString",
|
||||||
|
expected: "AlphaString deve conter caracteres alfabéticos",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtFieldString",
|
||||||
|
expected: "LtFieldString deve ser menor que MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteFieldString",
|
||||||
|
expected: "LteFieldString deve ser menor ou igual a MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtFieldString",
|
||||||
|
expected: "GtFieldString deve ser maior do que MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteFieldString",
|
||||||
|
expected: "GteFieldString deve ser maior ou igual a MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeFieldString",
|
||||||
|
expected: "NeFieldString não deve ser igual a EqFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtCSFieldString",
|
||||||
|
expected: "LtCSFieldString deve ser menor que Inner.LtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteCSFieldString",
|
||||||
|
expected: "LteCSFieldString deve ser menor ou igual a Inner.LteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtCSFieldString",
|
||||||
|
expected: "GtCSFieldString deve ser maior do que Inner.GtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteCSFieldString",
|
||||||
|
expected: "GteCSFieldString deve ser maior ou igual a Inner.GteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeCSFieldString",
|
||||||
|
expected: "NeCSFieldString não deve ser igual a Inner.NeCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqCSFieldString",
|
||||||
|
expected: "EqCSFieldString deve ser igual a Inner.EqCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqFieldString",
|
||||||
|
expected: "EqFieldString deve ser igual a MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteString",
|
||||||
|
expected: "GteString deve ter pelo menos 3 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteNumber",
|
||||||
|
expected: "GteNumber deve ser 5,56 ou superior",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteMultiple",
|
||||||
|
expected: "GteMultiple deve conter pelo menos 2 itens",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteTime",
|
||||||
|
expected: "GteTime deve ser maior ou igual à Data e Hora atual",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtString",
|
||||||
|
expected: "GtString deve ter mais de 3 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtNumber",
|
||||||
|
expected: "GtNumber deve ser maior do que 5,56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtMultiple",
|
||||||
|
expected: "GtMultiple deve conter mais de 2 itens",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtTime",
|
||||||
|
expected: "GtTime deve ser maior que a Data e Hora atual",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteString",
|
||||||
|
expected: "LteString deve ter no máximo 3 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteNumber",
|
||||||
|
expected: "LteNumber deve ser 5,56 ou menor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteMultiple",
|
||||||
|
expected: "LteMultiple deve conter no máximo 2 itens",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteTime",
|
||||||
|
expected: "LteTime deve ser menor ou igual à Data e Hora atual",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtString",
|
||||||
|
expected: "LtString deve ter menos de 3 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtNumber",
|
||||||
|
expected: "LtNumber deve ser menor que 5,56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtMultiple",
|
||||||
|
expected: "LtMultiple deve conter menos de 2 itens",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtTime",
|
||||||
|
expected: "LtTime deve ser inferior à Data e Hora atual",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeString",
|
||||||
|
expected: "NeString não deve ser igual a ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeNumber",
|
||||||
|
expected: "NeNumber não deve ser igual a 0.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeMultiple",
|
||||||
|
expected: "NeMultiple não deve ser igual a 0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqString",
|
||||||
|
expected: "EqString não é igual a 3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqNumber",
|
||||||
|
expected: "EqNumber não é igual a 2.33",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqMultiple",
|
||||||
|
expected: "EqMultiple não é igual a 7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxString",
|
||||||
|
expected: "MaxString deve ter no máximo 3 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxNumber",
|
||||||
|
expected: "MaxNumber deve ser 1.113,00 ou menor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxMultiple",
|
||||||
|
expected: "MaxMultiple deve conter no máximo 7 itens",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinString",
|
||||||
|
expected: "MinString deve ter pelo menos 1 caractere",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinNumber",
|
||||||
|
expected: "MinNumber deve ser 1.113,00 ou superior",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinMultiple",
|
||||||
|
expected: "MinMultiple deve conter pelo menos 7 itens",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenString",
|
||||||
|
expected: "LenString deve ter 1 caractere",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenNumber",
|
||||||
|
expected: "LenNumber deve ser igual a 1.113,00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenMultiple",
|
||||||
|
expected: "LenMultiple deve conter 7 itens",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredString",
|
||||||
|
expected: "RequiredString é um campo requerido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredNumber",
|
||||||
|
expected: "RequiredNumber é um campo requerido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredMultiple",
|
||||||
|
expected: "RequiredMultiple é um campo requerido",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMinLen",
|
||||||
|
expected: "StrPtrMinLen deve ter pelo menos 10 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMaxLen",
|
||||||
|
expected: "StrPtrMaxLen deve ter no máximo 1 caractere",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLen",
|
||||||
|
expected: "StrPtrLen deve ter 2 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLt",
|
||||||
|
expected: "StrPtrLt deve ter menos de 1 caractere",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLte",
|
||||||
|
expected: "StrPtrLte deve ter no máximo 1 caractere",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGt",
|
||||||
|
expected: "StrPtrGt deve ter mais de 10 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGte",
|
||||||
|
expected: "StrPtrGte deve ter pelo menos 10 caracteres",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfString",
|
||||||
|
expected: "OneOfString deve ser um de [red green]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfInt",
|
||||||
|
expected: "OneOfInt deve ser um de [5 63]",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
|
||||||
|
var fe validator.FieldError
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
if tt.ns == e.Namespace() {
|
||||||
|
fe = e
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotEqual(t, fe, nil)
|
||||||
|
Equal(t, tt.expected, fe.Translate(trans))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,656 @@
|
||||||
|
package ru
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
//"github.com/rustery/validator"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
russian "gin-valid/go-playground/locales/en"
|
||||||
|
ut "gin-valid/go-playground/universal-translator"
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
. "github.com/go-playground/assert/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTranslations(t *testing.T) {
|
||||||
|
|
||||||
|
ru := russian.New()
|
||||||
|
uni := ut.New(ru, ru)
|
||||||
|
trans, _ := uni.GetTranslator("ru")
|
||||||
|
|
||||||
|
validate := validator.New()
|
||||||
|
|
||||||
|
err := RegisterDefaultTranslations(validate, trans)
|
||||||
|
Equal(t, err, nil)
|
||||||
|
|
||||||
|
type Inner struct {
|
||||||
|
EqCSFieldString string
|
||||||
|
NeCSFieldString string
|
||||||
|
GtCSFieldString string
|
||||||
|
GteCSFieldString string
|
||||||
|
LtCSFieldString string
|
||||||
|
LteCSFieldString string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Inner Inner
|
||||||
|
RequiredString string `validate:"required"`
|
||||||
|
RequiredNumber int `validate:"required"`
|
||||||
|
RequiredMultiple []string `validate:"required"`
|
||||||
|
LenString string `validate:"len=1"`
|
||||||
|
LenNumber float64 `validate:"len=1113.00"`
|
||||||
|
LenMultiple []string `validate:"len=7"`
|
||||||
|
MinString string `validate:"min=1"`
|
||||||
|
MinNumber float64 `validate:"min=1113.00"`
|
||||||
|
MinMultiple []string `validate:"min=7"`
|
||||||
|
MaxString string `validate:"max=3"`
|
||||||
|
MaxNumber float64 `validate:"max=1113.00"`
|
||||||
|
MaxMultiple []string `validate:"max=7"`
|
||||||
|
EqString string `validate:"eq=3"`
|
||||||
|
EqNumber float64 `validate:"eq=2.33"`
|
||||||
|
EqMultiple []string `validate:"eq=7"`
|
||||||
|
NeString string `validate:"ne="`
|
||||||
|
NeNumber float64 `validate:"ne=0.00"`
|
||||||
|
NeMultiple []string `validate:"ne=0"`
|
||||||
|
LtString string `validate:"lt=3"`
|
||||||
|
LtNumber float64 `validate:"lt=5.56"`
|
||||||
|
LtMultiple []string `validate:"lt=2"`
|
||||||
|
LtTime time.Time `validate:"lt"`
|
||||||
|
LteString string `validate:"lte=3"`
|
||||||
|
LteNumber float64 `validate:"lte=5.56"`
|
||||||
|
LteMultiple []string `validate:"lte=2"`
|
||||||
|
LteTime time.Time `validate:"lte"`
|
||||||
|
GtString string `validate:"gt=3"`
|
||||||
|
GtNumber float64 `validate:"gt=5.56"`
|
||||||
|
GtMultiple []string `validate:"gt=2"`
|
||||||
|
GtTime time.Time `validate:"gt"`
|
||||||
|
GteString string `validate:"gte=3"`
|
||||||
|
GteNumber float64 `validate:"gte=5.56"`
|
||||||
|
GteMultiple []string `validate:"gte=2"`
|
||||||
|
GteTime time.Time `validate:"gte"`
|
||||||
|
EqFieldString string `validate:"eqfield=MaxString"`
|
||||||
|
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
|
||||||
|
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
|
||||||
|
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
|
||||||
|
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
|
||||||
|
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
|
||||||
|
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
|
||||||
|
NeFieldString string `validate:"nefield=EqFieldString"`
|
||||||
|
GtFieldString string `validate:"gtfield=MaxString"`
|
||||||
|
GteFieldString string `validate:"gtefield=MaxString"`
|
||||||
|
LtFieldString string `validate:"ltfield=MaxString"`
|
||||||
|
LteFieldString string `validate:"ltefield=MaxString"`
|
||||||
|
AlphaString string `validate:"alpha"`
|
||||||
|
AlphanumString string `validate:"alphanum"`
|
||||||
|
NumericString string `validate:"numeric"`
|
||||||
|
NumberString string `validate:"number"`
|
||||||
|
HexadecimalString string `validate:"hexadecimal"`
|
||||||
|
HexColorString string `validate:"hexcolor"`
|
||||||
|
RGBColorString string `validate:"rgb"`
|
||||||
|
RGBAColorString string `validate:"rgba"`
|
||||||
|
HSLColorString string `validate:"hsl"`
|
||||||
|
HSLAColorString string `validate:"hsla"`
|
||||||
|
Email string `validate:"email"`
|
||||||
|
URL string `validate:"url"`
|
||||||
|
URI string `validate:"uri"`
|
||||||
|
Base64 string `validate:"base64"`
|
||||||
|
Contains string `validate:"contains=purpose"`
|
||||||
|
ContainsAny string `validate:"containsany=!@#$"`
|
||||||
|
Excludes string `validate:"excludes=text"`
|
||||||
|
ExcludesAll string `validate:"excludesall=!@#$"`
|
||||||
|
ExcludesRune string `validate:"excludesrune=☻"`
|
||||||
|
ISBN string `validate:"isbn"`
|
||||||
|
ISBN10 string `validate:"isbn10"`
|
||||||
|
ISBN13 string `validate:"isbn13"`
|
||||||
|
UUID string `validate:"uuid"`
|
||||||
|
UUID3 string `validate:"uuid3"`
|
||||||
|
UUID4 string `validate:"uuid4"`
|
||||||
|
UUID5 string `validate:"uuid5"`
|
||||||
|
ASCII string `validate:"ascii"`
|
||||||
|
PrintableASCII string `validate:"printascii"`
|
||||||
|
MultiByte string `validate:"multibyte"`
|
||||||
|
DataURI string `validate:"datauri"`
|
||||||
|
Latitude string `validate:"latitude"`
|
||||||
|
Longitude string `validate:"longitude"`
|
||||||
|
SSN string `validate:"ssn"`
|
||||||
|
IP string `validate:"ip"`
|
||||||
|
IPv4 string `validate:"ipv4"`
|
||||||
|
IPv6 string `validate:"ipv6"`
|
||||||
|
CIDR string `validate:"cidr"`
|
||||||
|
CIDRv4 string `validate:"cidrv4"`
|
||||||
|
CIDRv6 string `validate:"cidrv6"`
|
||||||
|
TCPAddr string `validate:"tcp_addr"`
|
||||||
|
TCPAddrv4 string `validate:"tcp4_addr"`
|
||||||
|
TCPAddrv6 string `validate:"tcp6_addr"`
|
||||||
|
UDPAddr string `validate:"udp_addr"`
|
||||||
|
UDPAddrv4 string `validate:"udp4_addr"`
|
||||||
|
UDPAddrv6 string `validate:"udp6_addr"`
|
||||||
|
IPAddr string `validate:"ip_addr"`
|
||||||
|
IPAddrv4 string `validate:"ip4_addr"`
|
||||||
|
IPAddrv6 string `validate:"ip6_addr"`
|
||||||
|
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
|
||||||
|
MAC string `validate:"mac"`
|
||||||
|
IsColor string `validate:"iscolor"`
|
||||||
|
StrPtrMinLen *string `validate:"min=10"`
|
||||||
|
StrPtrMaxLen *string `validate:"max=1"`
|
||||||
|
StrPtrLen *string `validate:"len=2"`
|
||||||
|
StrPtrLt *string `validate:"lt=1"`
|
||||||
|
StrPtrLte *string `validate:"lte=1"`
|
||||||
|
StrPtrGt *string `validate:"gt=10"`
|
||||||
|
StrPtrGte *string `validate:"gte=10"`
|
||||||
|
OneOfString string `validate:"oneof=red green"`
|
||||||
|
OneOfInt int `validate:"oneof=5 63"`
|
||||||
|
UniqueSlice []string `validate:"unique"`
|
||||||
|
UniqueArray [3]string `validate:"unique"`
|
||||||
|
UniqueMap map[string]string `validate:"unique"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var test Test
|
||||||
|
|
||||||
|
test.Inner.EqCSFieldString = "1234"
|
||||||
|
test.Inner.GtCSFieldString = "1234"
|
||||||
|
test.Inner.GteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.MaxString = "1234"
|
||||||
|
test.MaxNumber = 2000
|
||||||
|
test.MaxMultiple = make([]string, 9)
|
||||||
|
|
||||||
|
test.LtString = "1234"
|
||||||
|
test.LtNumber = 6
|
||||||
|
test.LtMultiple = make([]string, 3)
|
||||||
|
test.LtTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LteString = "1234"
|
||||||
|
test.LteNumber = 6
|
||||||
|
test.LteMultiple = make([]string, 3)
|
||||||
|
test.LteTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LtFieldString = "12345"
|
||||||
|
test.LteFieldString = "12345"
|
||||||
|
|
||||||
|
test.LtCSFieldString = "1234"
|
||||||
|
test.LteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.AlphaString = "abc3"
|
||||||
|
test.AlphanumString = "abc3!"
|
||||||
|
test.NumericString = "12E.00"
|
||||||
|
test.NumberString = "12E"
|
||||||
|
|
||||||
|
test.Excludes = "this is some test text"
|
||||||
|
test.ExcludesAll = "This is Great!"
|
||||||
|
test.ExcludesRune = "Love it ☻"
|
||||||
|
|
||||||
|
test.ASCII = "カタカナ"
|
||||||
|
test.PrintableASCII = "カタカナ"
|
||||||
|
|
||||||
|
test.MultiByte = "1234feerf"
|
||||||
|
|
||||||
|
s := "toolong"
|
||||||
|
test.StrPtrMaxLen = &s
|
||||||
|
test.StrPtrLen = &s
|
||||||
|
|
||||||
|
test.UniqueSlice = []string{"1234", "1234"}
|
||||||
|
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
|
||||||
|
|
||||||
|
err = validate.Struct(test)
|
||||||
|
NotEqual(t, err, nil)
|
||||||
|
|
||||||
|
errs, ok := err.(validator.ValidationErrors)
|
||||||
|
Equal(t, ok, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
ns string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ns: "Test.IsColor",
|
||||||
|
expected: "IsColor должен быть цветом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MAC",
|
||||||
|
expected: "MAC должен содержать MAC адрес",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddr",
|
||||||
|
expected: "IPAddr должен быть распознаваемым IP адресом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv4",
|
||||||
|
expected: "IPAddrv4 должен быть распознаваемым IPv4 адресом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv6",
|
||||||
|
expected: "IPAddrv6 должен быть распознаваемым IPv6 адресом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddr",
|
||||||
|
expected: "UDPAddr должен быть UDP адресом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv4",
|
||||||
|
expected: "UDPAddrv4 должен быть IPv4 UDP адресом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv6",
|
||||||
|
expected: "UDPAddrv6 должен быть IPv6 UDP адресом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddr",
|
||||||
|
expected: "TCPAddr должен быть TCP адресом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv4",
|
||||||
|
expected: "TCPAddrv4 должен быть IPv4 TCP адресом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv6",
|
||||||
|
expected: "TCPAddrv6 должен быть IPv6 TCP адресом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDR",
|
||||||
|
expected: "CIDR должен содержать CIDR обозначения",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv4",
|
||||||
|
expected: "CIDRv4 должен содержать CIDR обозначения для IPv4 адреса",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv6",
|
||||||
|
expected: "CIDRv6 должен содержать CIDR обозначения для IPv6 адреса",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.SSN",
|
||||||
|
expected: "SSN должен быть SSN номером",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IP",
|
||||||
|
expected: "IP должен быть IP адресом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv4",
|
||||||
|
expected: "IPv4 должен быть IPv4 адресом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv6",
|
||||||
|
expected: "IPv6 должен быть IPv6 адресом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.DataURI",
|
||||||
|
expected: "DataURI должен содержать Data URI",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Latitude",
|
||||||
|
expected: "Latitude должен содержать координаты широты",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Longitude",
|
||||||
|
expected: "Longitude должен содержать координаты долготы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MultiByte",
|
||||||
|
expected: "MultiByte должен содержать мультибайтные символы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ASCII",
|
||||||
|
expected: "ASCII должен содержать только ascii символы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.PrintableASCII",
|
||||||
|
expected: "PrintableASCII должен содержать только доступные для печати ascii символы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID",
|
||||||
|
expected: "UUID должен быть UUID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID3",
|
||||||
|
expected: "UUID3 должен быть UUID 3 версии",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID4",
|
||||||
|
expected: "UUID4 должен быть UUID 4 версии",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID5",
|
||||||
|
expected: "UUID5 должен быть UUID 5 версии",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN",
|
||||||
|
expected: "ISBN должен быть ISBN номером",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN10",
|
||||||
|
expected: "ISBN10 должен быть ISBN-10 номером",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN13",
|
||||||
|
expected: "ISBN13 должен быть ISBN-13 номером",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Excludes",
|
||||||
|
expected: "Excludes не должен содержать текст 'text'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesAll",
|
||||||
|
expected: "ExcludesAll не должен содержать символы '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesRune",
|
||||||
|
expected: "ExcludesRune не должен содержать '☻'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ContainsAny",
|
||||||
|
expected: "ContainsAny должен содержать минимум один из символов '!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Contains",
|
||||||
|
expected: "Contains должен содержать текст 'purpose'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Base64",
|
||||||
|
expected: "Base64 должен быть Base64 строкой",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Email",
|
||||||
|
expected: "Email должен быть email адресом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URL",
|
||||||
|
expected: "URL должен быть URL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URI",
|
||||||
|
expected: "URI должен быть URI",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBColorString",
|
||||||
|
expected: "RGBColorString должен быть RGB цветом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBAColorString",
|
||||||
|
expected: "RGBAColorString должен быть RGBA цветом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLColorString",
|
||||||
|
expected: "HSLColorString должен быть HSL цветом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLAColorString",
|
||||||
|
expected: "HSLAColorString должен быть HSLA цветом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexadecimalString",
|
||||||
|
expected: "HexadecimalString должен быть шестнадцатеричной строкой",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexColorString",
|
||||||
|
expected: "HexColorString должен быть HEX цветом",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumberString",
|
||||||
|
expected: "NumberString должен быть цифрой",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumericString",
|
||||||
|
expected: "NumericString должен быть цифровым значением",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphanumString",
|
||||||
|
expected: "AlphanumString должен содержать только буквы и цифры",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphaString",
|
||||||
|
expected: "AlphaString должен содержать только буквы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtFieldString",
|
||||||
|
expected: "LtFieldString должен быть менее MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteFieldString",
|
||||||
|
expected: "LteFieldString должен быть менее или равен MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtFieldString",
|
||||||
|
expected: "GtFieldString должен быть больше MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteFieldString",
|
||||||
|
expected: "GteFieldString должен быть больше или равен MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeFieldString",
|
||||||
|
expected: "NeFieldString не должен быть равен EqFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtCSFieldString",
|
||||||
|
expected: "LtCSFieldString должен быть менее Inner.LtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteCSFieldString",
|
||||||
|
expected: "LteCSFieldString должен быть менее или равен Inner.LteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtCSFieldString",
|
||||||
|
expected: "GtCSFieldString должен быть больше Inner.GtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteCSFieldString",
|
||||||
|
expected: "GteCSFieldString должен быть больше или равен Inner.GteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeCSFieldString",
|
||||||
|
expected: "NeCSFieldString не должен быть равен Inner.NeCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqCSFieldString",
|
||||||
|
expected: "EqCSFieldString должен быть равен Inner.EqCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqFieldString",
|
||||||
|
expected: "EqFieldString должен быть равен MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteString",
|
||||||
|
expected: "GteString должен содержать минимум 3 символы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteNumber",
|
||||||
|
expected: "GteNumber должен быть больше или равно 5.56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteMultiple",
|
||||||
|
expected: "GteMultiple должен содержать минимум 2 элементы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteTime",
|
||||||
|
expected: "GteTime должна быть позже или равна текущему моменту",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtString",
|
||||||
|
expected: "GtString должен быть длиннее 3 символы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtNumber",
|
||||||
|
expected: "GtNumber должен быть больше 5.56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtMultiple",
|
||||||
|
expected: "GtMultiple должен содержать более 2 элементы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtTime",
|
||||||
|
expected: "GtTime должна быть позже текущего момента",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteString",
|
||||||
|
expected: "LteString должен содержать максимум 3 символы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteNumber",
|
||||||
|
expected: "LteNumber должен быть менее или равен 5.56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteMultiple",
|
||||||
|
expected: "LteMultiple должен содержать максимум 2 элементы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteTime",
|
||||||
|
expected: "LteTime must be less than or equal to the current Date & Time",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtString",
|
||||||
|
expected: "LtString должен иметь менее 3 символы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtNumber",
|
||||||
|
expected: "LtNumber должен быть менее 5.56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtMultiple",
|
||||||
|
expected: "LtMultiple должен содержать менее 2 элементы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtTime",
|
||||||
|
expected: "LtTime must be less than the current Date & Time",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeString",
|
||||||
|
expected: "NeString должен быть не равен ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeNumber",
|
||||||
|
expected: "NeNumber должен быть не равен 0.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeMultiple",
|
||||||
|
expected: "NeMultiple должен быть не равен 0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqString",
|
||||||
|
expected: "EqString не равен 3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqNumber",
|
||||||
|
expected: "EqNumber не равен 2.33",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqMultiple",
|
||||||
|
expected: "EqMultiple не равен 7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxString",
|
||||||
|
expected: "MaxString должен содержать максимум 3 символы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxNumber",
|
||||||
|
expected: "MaxNumber должен быть меньше или равно 1,113.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxMultiple",
|
||||||
|
expected: "MaxMultiple должен содержать максимум 7 элементы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinString",
|
||||||
|
expected: "MinString должен содержать минимум 1 символ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinNumber",
|
||||||
|
expected: "MinNumber должен быть больше или равно 1,113.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinMultiple",
|
||||||
|
expected: "MinMultiple должен содержать минимум 7 элементы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenString",
|
||||||
|
expected: "LenString должен быть длиной в 1 символ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenNumber",
|
||||||
|
expected: "LenNumber должен быть равен 1,113.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenMultiple",
|
||||||
|
expected: "LenMultiple должен содержать 7 элементы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredString",
|
||||||
|
expected: "RequiredString обязательное поле",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredNumber",
|
||||||
|
expected: "RequiredNumber обязательное поле",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredMultiple",
|
||||||
|
expected: "RequiredMultiple обязательное поле",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMinLen",
|
||||||
|
expected: "StrPtrMinLen должен содержать минимум 10 символы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMaxLen",
|
||||||
|
expected: "StrPtrMaxLen должен содержать максимум 1 символ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLen",
|
||||||
|
expected: "StrPtrLen должен быть длиной в 2 символы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLt",
|
||||||
|
expected: "StrPtrLt должен иметь менее 1 символ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLte",
|
||||||
|
expected: "StrPtrLte должен содержать максимум 1 символ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGt",
|
||||||
|
expected: "StrPtrGt должен быть длиннее 10 символы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGte",
|
||||||
|
expected: "StrPtrGte должен содержать минимум 10 символы",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfString",
|
||||||
|
expected: "OneOfString должен быть одним из [red green]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfInt",
|
||||||
|
expected: "OneOfInt должен быть одним из [5 63]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueSlice",
|
||||||
|
expected: "UniqueSlice должен содержать уникальные значения",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueArray",
|
||||||
|
expected: "UniqueArray должен содержать уникальные значения",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueMap",
|
||||||
|
expected: "UniqueMap должен содержать уникальные значения",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
|
||||||
|
var fe validator.FieldError
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
if tt.ns == e.Namespace() {
|
||||||
|
fe = e
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println(fe)
|
||||||
|
|
||||||
|
NotEqual(t, fe, nil)
|
||||||
|
Equal(t, tt.expected, fe.Translate(trans))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,652 @@
|
||||||
|
package tr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
turkish "gin-valid/go-playground/locales/tr"
|
||||||
|
ut "gin-valid/go-playground/universal-translator"
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
. "github.com/go-playground/assert/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTranslations(t *testing.T) {
|
||||||
|
|
||||||
|
tr := turkish.New()
|
||||||
|
uni := ut.New(tr, tr)
|
||||||
|
trans, _ := uni.GetTranslator("tr")
|
||||||
|
|
||||||
|
validate := validator.New()
|
||||||
|
|
||||||
|
err := RegisterDefaultTranslations(validate, trans)
|
||||||
|
Equal(t, err, nil)
|
||||||
|
|
||||||
|
type Inner struct {
|
||||||
|
EqCSFieldString string
|
||||||
|
NeCSFieldString string
|
||||||
|
GtCSFieldString string
|
||||||
|
GteCSFieldString string
|
||||||
|
LtCSFieldString string
|
||||||
|
LteCSFieldString string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Inner Inner
|
||||||
|
RequiredString string `validate:"required"`
|
||||||
|
RequiredNumber int `validate:"required"`
|
||||||
|
RequiredMultiple []string `validate:"required"`
|
||||||
|
LenString string `validate:"len=1"`
|
||||||
|
LenNumber float64 `validate:"len=1113.00"`
|
||||||
|
LenMultiple []string `validate:"len=7"`
|
||||||
|
MinString string `validate:"min=1"`
|
||||||
|
MinNumber float64 `validate:"min=1113.00"`
|
||||||
|
MinMultiple []string `validate:"min=7"`
|
||||||
|
MaxString string `validate:"max=3"`
|
||||||
|
MaxNumber float64 `validate:"max=1113.00"`
|
||||||
|
MaxMultiple []string `validate:"max=7"`
|
||||||
|
EqString string `validate:"eq=3"`
|
||||||
|
EqNumber float64 `validate:"eq=2.33"`
|
||||||
|
EqMultiple []string `validate:"eq=7"`
|
||||||
|
NeString string `validate:"ne="`
|
||||||
|
NeNumber float64 `validate:"ne=0.00"`
|
||||||
|
NeMultiple []string `validate:"ne=0"`
|
||||||
|
LtString string `validate:"lt=3"`
|
||||||
|
LtNumber float64 `validate:"lt=5.56"`
|
||||||
|
LtMultiple []string `validate:"lt=2"`
|
||||||
|
LtTime time.Time `validate:"lt"`
|
||||||
|
LteString string `validate:"lte=3"`
|
||||||
|
LteNumber float64 `validate:"lte=5.56"`
|
||||||
|
LteMultiple []string `validate:"lte=2"`
|
||||||
|
LteTime time.Time `validate:"lte"`
|
||||||
|
GtString string `validate:"gt=3"`
|
||||||
|
GtNumber float64 `validate:"gt=5.56"`
|
||||||
|
GtMultiple []string `validate:"gt=2"`
|
||||||
|
GtTime time.Time `validate:"gt"`
|
||||||
|
GteString string `validate:"gte=3"`
|
||||||
|
GteNumber float64 `validate:"gte=5.56"`
|
||||||
|
GteMultiple []string `validate:"gte=2"`
|
||||||
|
GteTime time.Time `validate:"gte"`
|
||||||
|
EqFieldString string `validate:"eqfield=MaxString"`
|
||||||
|
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
|
||||||
|
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
|
||||||
|
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
|
||||||
|
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
|
||||||
|
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
|
||||||
|
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
|
||||||
|
NeFieldString string `validate:"nefield=EqFieldString"`
|
||||||
|
GtFieldString string `validate:"gtfield=MaxString"`
|
||||||
|
GteFieldString string `validate:"gtefield=MaxString"`
|
||||||
|
LtFieldString string `validate:"ltfield=MaxString"`
|
||||||
|
LteFieldString string `validate:"ltefield=MaxString"`
|
||||||
|
AlphaString string `validate:"alpha"`
|
||||||
|
AlphanumString string `validate:"alphanum"`
|
||||||
|
NumericString string `validate:"numeric"`
|
||||||
|
NumberString string `validate:"number"`
|
||||||
|
HexadecimalString string `validate:"hexadecimal"`
|
||||||
|
HexColorString string `validate:"hexcolor"`
|
||||||
|
RGBColorString string `validate:"rgb"`
|
||||||
|
RGBAColorString string `validate:"rgba"`
|
||||||
|
HSLColorString string `validate:"hsl"`
|
||||||
|
HSLAColorString string `validate:"hsla"`
|
||||||
|
Email string `validate:"email"`
|
||||||
|
URL string `validate:"url"`
|
||||||
|
URI string `validate:"uri"`
|
||||||
|
Base64 string `validate:"base64"`
|
||||||
|
Contains string `validate:"contains=purpose"`
|
||||||
|
ContainsAny string `validate:"containsany=!@#$"`
|
||||||
|
Excludes string `validate:"excludes=text"`
|
||||||
|
ExcludesAll string `validate:"excludesall=!@#$"`
|
||||||
|
ExcludesRune string `validate:"excludesrune=☻"`
|
||||||
|
ISBN string `validate:"isbn"`
|
||||||
|
ISBN10 string `validate:"isbn10"`
|
||||||
|
ISBN13 string `validate:"isbn13"`
|
||||||
|
UUID string `validate:"uuid"`
|
||||||
|
UUID3 string `validate:"uuid3"`
|
||||||
|
UUID4 string `validate:"uuid4"`
|
||||||
|
UUID5 string `validate:"uuid5"`
|
||||||
|
ASCII string `validate:"ascii"`
|
||||||
|
PrintableASCII string `validate:"printascii"`
|
||||||
|
MultiByte string `validate:"multibyte"`
|
||||||
|
DataURI string `validate:"datauri"`
|
||||||
|
Latitude string `validate:"latitude"`
|
||||||
|
Longitude string `validate:"longitude"`
|
||||||
|
SSN string `validate:"ssn"`
|
||||||
|
IP string `validate:"ip"`
|
||||||
|
IPv4 string `validate:"ipv4"`
|
||||||
|
IPv6 string `validate:"ipv6"`
|
||||||
|
CIDR string `validate:"cidr"`
|
||||||
|
CIDRv4 string `validate:"cidrv4"`
|
||||||
|
CIDRv6 string `validate:"cidrv6"`
|
||||||
|
TCPAddr string `validate:"tcp_addr"`
|
||||||
|
TCPAddrv4 string `validate:"tcp4_addr"`
|
||||||
|
TCPAddrv6 string `validate:"tcp6_addr"`
|
||||||
|
UDPAddr string `validate:"udp_addr"`
|
||||||
|
UDPAddrv4 string `validate:"udp4_addr"`
|
||||||
|
UDPAddrv6 string `validate:"udp6_addr"`
|
||||||
|
IPAddr string `validate:"ip_addr"`
|
||||||
|
IPAddrv4 string `validate:"ip4_addr"`
|
||||||
|
IPAddrv6 string `validate:"ip6_addr"`
|
||||||
|
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
|
||||||
|
MAC string `validate:"mac"`
|
||||||
|
IsColor string `validate:"iscolor"`
|
||||||
|
StrPtrMinLen *string `validate:"min=10"`
|
||||||
|
StrPtrMaxLen *string `validate:"max=1"`
|
||||||
|
StrPtrLen *string `validate:"len=2"`
|
||||||
|
StrPtrLt *string `validate:"lt=1"`
|
||||||
|
StrPtrLte *string `validate:"lte=1"`
|
||||||
|
StrPtrGt *string `validate:"gt=10"`
|
||||||
|
StrPtrGte *string `validate:"gte=10"`
|
||||||
|
OneOfString string `validate:"oneof=red green"`
|
||||||
|
OneOfInt int `validate:"oneof=5 63"`
|
||||||
|
UniqueSlice []string `validate:"unique"`
|
||||||
|
UniqueArray [3]string `validate:"unique"`
|
||||||
|
UniqueMap map[string]string `validate:"unique"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var test Test
|
||||||
|
|
||||||
|
test.Inner.EqCSFieldString = "1234"
|
||||||
|
test.Inner.GtCSFieldString = "1234"
|
||||||
|
test.Inner.GteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.MaxString = "1234"
|
||||||
|
test.MaxNumber = 2000
|
||||||
|
test.MaxMultiple = make([]string, 9)
|
||||||
|
|
||||||
|
test.LtString = "1234"
|
||||||
|
test.LtNumber = 6
|
||||||
|
test.LtMultiple = make([]string, 3)
|
||||||
|
test.LtTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LteString = "1234"
|
||||||
|
test.LteNumber = 6
|
||||||
|
test.LteMultiple = make([]string, 3)
|
||||||
|
test.LteTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LtFieldString = "12345"
|
||||||
|
test.LteFieldString = "12345"
|
||||||
|
|
||||||
|
test.LtCSFieldString = "1234"
|
||||||
|
test.LteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.AlphaString = "abc3"
|
||||||
|
test.AlphanumString = "abc3!"
|
||||||
|
test.NumericString = "12E.00"
|
||||||
|
test.NumberString = "12E"
|
||||||
|
|
||||||
|
test.Excludes = "this is some test text"
|
||||||
|
test.ExcludesAll = "This is Great!"
|
||||||
|
test.ExcludesRune = "Love it ☻"
|
||||||
|
|
||||||
|
test.ASCII = "カタカナ"
|
||||||
|
test.PrintableASCII = "カタカナ"
|
||||||
|
|
||||||
|
test.MultiByte = "1234feerf"
|
||||||
|
|
||||||
|
s := "toolong"
|
||||||
|
test.StrPtrMaxLen = &s
|
||||||
|
test.StrPtrLen = &s
|
||||||
|
|
||||||
|
test.UniqueSlice = []string{"1234", "1234"}
|
||||||
|
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
|
||||||
|
|
||||||
|
err = validate.Struct(test)
|
||||||
|
NotEqual(t, err, nil)
|
||||||
|
|
||||||
|
errs, ok := err.(validator.ValidationErrors)
|
||||||
|
Equal(t, ok, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
ns string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ns: "Test.IsColor",
|
||||||
|
expected: "IsColor geçerli bir renk olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MAC",
|
||||||
|
expected: "MAC geçerli bir MAC adresi içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddr",
|
||||||
|
expected: "IPAddr çözülebilir bir IP adresi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv4",
|
||||||
|
expected: "IPAddrv4 çözülebilir bir IPv4 adresi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv6",
|
||||||
|
expected: "IPAddrv6 çözülebilir bir IPv6 adresi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddr",
|
||||||
|
expected: "UDPAddr geçerli bir UDP adresi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv4",
|
||||||
|
expected: "UDPAddrv4 geçerli bir IPv4 UDP adresi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv6",
|
||||||
|
expected: "UDPAddrv6 geçerli bir IPv6 UDP adresi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddr",
|
||||||
|
expected: "TCPAddr geçerli bir TCP adresi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv4",
|
||||||
|
expected: "TCPAddrv4 geçerli bir IPv4 TCP adresi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv6",
|
||||||
|
expected: "TCPAddrv6 geçerli bir IPv6 TCP adresi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDR",
|
||||||
|
expected: "CIDR geçerli bir CIDR gösterimi içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv4",
|
||||||
|
expected: "CIDRv4 bir IPv4 adresi için geçerli bir CIDR gösterimi içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv6",
|
||||||
|
expected: "CIDRv6 bir IPv6 adresi için geçerli bir CIDR gösterimi içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.SSN",
|
||||||
|
expected: "SSN geçerli bir SSN numarası olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IP",
|
||||||
|
expected: "IP geçerli bir IP adresi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv4",
|
||||||
|
expected: "IPv4 geçerli bir IPv4 adresi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv6",
|
||||||
|
expected: "IPv6 geçerli bir IPv6 adresi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.DataURI",
|
||||||
|
expected: "DataURI geçerli bir Veri URI içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Latitude",
|
||||||
|
expected: "Latitude geçerli bir enlem koordinatı içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Longitude",
|
||||||
|
expected: "Longitude geçerli bir boylam koordinatı içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MultiByte",
|
||||||
|
expected: "MultiByte çok baytlı karakterler içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ASCII",
|
||||||
|
expected: "ASCII yalnızca ascii karakterler içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.PrintableASCII",
|
||||||
|
expected: "PrintableASCII yalnızca yazdırılabilir ascii karakterleri içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID",
|
||||||
|
expected: "UUID geçerli bir UUID olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID3",
|
||||||
|
expected: "UUID3 geçerli bir sürüm 3 UUID olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID4",
|
||||||
|
expected: "UUID4 geçerli bir sürüm 4 UUID olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID5",
|
||||||
|
expected: "UUID5 geçerli bir sürüm 5 UUID olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN",
|
||||||
|
expected: "ISBN geçerli bir ISBN numarası olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN10",
|
||||||
|
expected: "ISBN10 geçerli bir ISBN-10 numarası olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN13",
|
||||||
|
expected: "ISBN13 geçerli bir ISBN-13 numarası olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Excludes",
|
||||||
|
expected: "Excludes, 'text' metnini içeremez",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesAll",
|
||||||
|
expected: "ExcludesAll, '!@#$' karakterlerinden hiçbirini içeremez",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesRune",
|
||||||
|
expected: "ExcludesRune, '☻' ifadesini içeremez",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ContainsAny",
|
||||||
|
expected: "ContainsAny, '!@#$' karakterlerinden en az birini içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Contains",
|
||||||
|
expected: "Contains, 'purpose' metnini içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Base64",
|
||||||
|
expected: "Base64 geçerli bir Base64 karakter dizesi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Email",
|
||||||
|
expected: "Email geçerli bir e-posta adresi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URL",
|
||||||
|
expected: "URL geçerli bir URL olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URI",
|
||||||
|
expected: "URI geçerli bir URI olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBColorString",
|
||||||
|
expected: "RGBColorString geçerli bir RGB rengi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBAColorString",
|
||||||
|
expected: "RGBAColorString geçerli bir RGBA rengi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLColorString",
|
||||||
|
expected: "HSLColorString geçerli bir HSL rengi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLAColorString",
|
||||||
|
expected: "HSLAColorString geçerli bir HSLA rengi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexadecimalString",
|
||||||
|
expected: "HexadecimalString geçerli bir onaltılık olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexColorString",
|
||||||
|
expected: "HexColorString geçerli bir HEX rengi olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumberString",
|
||||||
|
expected: "NumberString geçerli bir sayı olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumericString",
|
||||||
|
expected: "NumericString geçerli bir sayısal değer olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphanumString",
|
||||||
|
expected: "AlphanumString yalnızca alfanümerik karakterler içerebilir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphaString",
|
||||||
|
expected: "AlphaString yalnızca alfabetik karakterler içerebilir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtFieldString",
|
||||||
|
expected: "LtFieldString, MaxString değerinden küçük olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteFieldString",
|
||||||
|
expected: "LteFieldString, MaxString değerinden küçük veya ona eşit olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtFieldString",
|
||||||
|
expected: "GtFieldString, MaxString değerinden büyük olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteFieldString",
|
||||||
|
expected: "GteFieldString, MaxString değerinden büyük veya ona eşit olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeFieldString",
|
||||||
|
expected: "NeFieldString, EqFieldString değerine eşit olmamalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtCSFieldString",
|
||||||
|
expected: "LtCSFieldString, Inner.LtCSFieldString değerinden küçük olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteCSFieldString",
|
||||||
|
expected: "LteCSFieldString, Inner.LteCSFieldString değerinden küçük veya ona eşit olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtCSFieldString",
|
||||||
|
expected: "GtCSFieldString, Inner.GtCSFieldString değerinden büyük olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteCSFieldString",
|
||||||
|
expected: "GteCSFieldString, Inner.GteCSFieldString değerinden küçük veya ona eşit olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeCSFieldString",
|
||||||
|
expected: "NeCSFieldString, Inner.NeCSFieldString değerine eşit olmamalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqCSFieldString",
|
||||||
|
expected: "EqCSFieldString, Inner.EqCSFieldString değerine eşit olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqFieldString",
|
||||||
|
expected: "EqFieldString, MaxString değerine eşit olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteString",
|
||||||
|
expected: "GteString en az 3 karakter uzunluğunda olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteNumber",
|
||||||
|
expected: "GteNumber, 5,56 veya daha büyük olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteMultiple",
|
||||||
|
expected: "GteMultiple en az 2 öğe içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteTime",
|
||||||
|
expected: "GteTime geçerli Tarih ve Saatten büyük veya ona eşit olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtString",
|
||||||
|
expected: "GtString, 3 karakter uzunluğundan fazla olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtNumber",
|
||||||
|
expected: "GtNumber, 5,56 değerinden büyük olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtMultiple",
|
||||||
|
expected: "GtMultiple, 2 öğeden daha fazla içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtTime",
|
||||||
|
expected: "GtTime geçerli Tarih ve Saatten büyük olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteString",
|
||||||
|
expected: "LteString en fazla 3 karakter uzunluğunda olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteNumber",
|
||||||
|
expected: "LteNumber, 5,56 veya daha az olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteMultiple",
|
||||||
|
expected: "LteMultiple, maksimum 2 öğe içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteTime",
|
||||||
|
expected: "LteTime geçerli Tarih ve Saate eşit veya daha küçük olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtString",
|
||||||
|
expected: "LtString, 3 karakter uzunluğundan daha az olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtNumber",
|
||||||
|
expected: "LtNumber, 5,56 değerinden küçük olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtMultiple",
|
||||||
|
expected: "LtMultiple, 2 öğeden daha az içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtTime",
|
||||||
|
expected: "LtTime geçerli Tarih ve Saatten daha az olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeString",
|
||||||
|
expected: "NeString, değerine eşit olmamalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeNumber",
|
||||||
|
expected: "NeNumber, 0.00 değerine eşit olmamalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeMultiple",
|
||||||
|
expected: "NeMultiple, 0 değerine eşit olmamalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqString",
|
||||||
|
expected: "EqString, 3 değerine eşit değil",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqNumber",
|
||||||
|
expected: "EqNumber, 2.33 değerine eşit değil",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqMultiple",
|
||||||
|
expected: "EqMultiple, 7 değerine eşit değil",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxString",
|
||||||
|
expected: "MaxString uzunluğu en fazla 3 karakter olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxNumber",
|
||||||
|
expected: "MaxNumber, 1.113,00 veya daha az olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxMultiple",
|
||||||
|
expected: "MaxMultiple maksimum 7 öğe içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinString",
|
||||||
|
expected: "MinString en az 1 karakter uzunluğunda olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinNumber",
|
||||||
|
expected: "MinNumber, 1.113,00 veya daha büyük olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinMultiple",
|
||||||
|
expected: "MinMultiple en az 7 öğe içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenString",
|
||||||
|
expected: "LenString uzunluğu 1 karakter olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenNumber",
|
||||||
|
expected: "LenNumber, 1.113,00 değerine eşit olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenMultiple",
|
||||||
|
expected: "LenMultiple, 7 öğe içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredString",
|
||||||
|
expected: "RequiredString zorunlu bir alandır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredNumber",
|
||||||
|
expected: "RequiredNumber zorunlu bir alandır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredMultiple",
|
||||||
|
expected: "RequiredMultiple zorunlu bir alandır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMinLen",
|
||||||
|
expected: "StrPtrMinLen en az 10 karakter uzunluğunda olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMaxLen",
|
||||||
|
expected: "StrPtrMaxLen uzunluğu en fazla 1 karakter olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLen",
|
||||||
|
expected: "StrPtrLen uzunluğu 2 karakter olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLt",
|
||||||
|
expected: "StrPtrLt, 1 karakter uzunluğundan daha az olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLte",
|
||||||
|
expected: "StrPtrLte en fazla 1 karakter uzunluğunda olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGt",
|
||||||
|
expected: "StrPtrGt, 10 karakter uzunluğundan fazla olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGte",
|
||||||
|
expected: "StrPtrGte en az 10 karakter uzunluğunda olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfString",
|
||||||
|
expected: "OneOfString, [red green]'dan biri olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfInt",
|
||||||
|
expected: "OneOfInt, [5 63]'dan biri olmalıdır",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueSlice",
|
||||||
|
expected: "UniqueSlice benzersiz değerler içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueArray",
|
||||||
|
expected: "UniqueArray benzersiz değerler içermelidir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UniqueMap",
|
||||||
|
expected: "UniqueMap benzersiz değerler içermelidir",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
|
||||||
|
var fe validator.FieldError
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
if tt.ns == e.Namespace() {
|
||||||
|
fe = e
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotEqual(t, fe, nil)
|
||||||
|
Equal(t, tt.expected, fe.Translate(trans))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,661 @@
|
||||||
|
package zh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
zhongwen "gin-valid/go-playground/locales/zh"
|
||||||
|
ut "gin-valid/go-playground/universal-translator"
|
||||||
|
"gin-valid/go-playground/validator/v10"
|
||||||
|
. "github.com/go-playground/assert/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTranslations(t *testing.T) {
|
||||||
|
|
||||||
|
zh := zhongwen.New()
|
||||||
|
uni := ut.New(zh, zh)
|
||||||
|
trans, _ := uni.GetTranslator("zh")
|
||||||
|
|
||||||
|
validate := validator.New()
|
||||||
|
|
||||||
|
err := RegisterDefaultTranslations(validate, trans)
|
||||||
|
Equal(t, err, nil)
|
||||||
|
|
||||||
|
type Inner struct {
|
||||||
|
EqCSFieldString string
|
||||||
|
NeCSFieldString string
|
||||||
|
GtCSFieldString string
|
||||||
|
GteCSFieldString string
|
||||||
|
LtCSFieldString string
|
||||||
|
LteCSFieldString string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Inner Inner
|
||||||
|
RequiredString string `validate:"required"`
|
||||||
|
RequiredNumber int `validate:"required"`
|
||||||
|
RequiredMultiple []string `validate:"required"`
|
||||||
|
LenString string `validate:"len=1"`
|
||||||
|
LenNumber float64 `validate:"len=1113.00"`
|
||||||
|
LenMultiple []string `validate:"len=7"`
|
||||||
|
MinString string `validate:"min=1"`
|
||||||
|
MinNumber float64 `validate:"min=1113.00"`
|
||||||
|
MinMultiple []string `validate:"min=7"`
|
||||||
|
MaxString string `validate:"max=3"`
|
||||||
|
MaxNumber float64 `validate:"max=1113.00"`
|
||||||
|
MaxMultiple []string `validate:"max=7"`
|
||||||
|
EqString string `validate:"eq=3"`
|
||||||
|
EqNumber float64 `validate:"eq=2.33"`
|
||||||
|
EqMultiple []string `validate:"eq=7"`
|
||||||
|
NeString string `validate:"ne="`
|
||||||
|
NeNumber float64 `validate:"ne=0.00"`
|
||||||
|
NeMultiple []string `validate:"ne=0"`
|
||||||
|
LtString string `validate:"lt=3"`
|
||||||
|
LtNumber float64 `validate:"lt=5.56"`
|
||||||
|
LtMultiple []string `validate:"lt=2"`
|
||||||
|
LtTime time.Time `validate:"lt"`
|
||||||
|
LteString string `validate:"lte=3"`
|
||||||
|
LteNumber float64 `validate:"lte=5.56"`
|
||||||
|
LteMultiple []string `validate:"lte=2"`
|
||||||
|
LteTime time.Time `validate:"lte"`
|
||||||
|
GtString string `validate:"gt=3"`
|
||||||
|
GtNumber float64 `validate:"gt=5.56"`
|
||||||
|
GtMultiple []string `validate:"gt=2"`
|
||||||
|
GtTime time.Time `validate:"gt"`
|
||||||
|
GteString string `validate:"gte=3"`
|
||||||
|
GteNumber float64 `validate:"gte=5.56"`
|
||||||
|
GteMultiple []string `validate:"gte=2"`
|
||||||
|
GteTime time.Time `validate:"gte"`
|
||||||
|
EqFieldString string `validate:"eqfield=MaxString"`
|
||||||
|
EqCSFieldString string `validate:"eqcsfield=Inner.EqCSFieldString"`
|
||||||
|
NeCSFieldString string `validate:"necsfield=Inner.NeCSFieldString"`
|
||||||
|
GtCSFieldString string `validate:"gtcsfield=Inner.GtCSFieldString"`
|
||||||
|
GteCSFieldString string `validate:"gtecsfield=Inner.GteCSFieldString"`
|
||||||
|
LtCSFieldString string `validate:"ltcsfield=Inner.LtCSFieldString"`
|
||||||
|
LteCSFieldString string `validate:"ltecsfield=Inner.LteCSFieldString"`
|
||||||
|
NeFieldString string `validate:"nefield=EqFieldString"`
|
||||||
|
GtFieldString string `validate:"gtfield=MaxString"`
|
||||||
|
GteFieldString string `validate:"gtefield=MaxString"`
|
||||||
|
LtFieldString string `validate:"ltfield=MaxString"`
|
||||||
|
LteFieldString string `validate:"ltefield=MaxString"`
|
||||||
|
AlphaString string `validate:"alpha"`
|
||||||
|
AlphanumString string `validate:"alphanum"`
|
||||||
|
NumericString string `validate:"numeric"`
|
||||||
|
NumberString string `validate:"number"`
|
||||||
|
HexadecimalString string `validate:"hexadecimal"`
|
||||||
|
HexColorString string `validate:"hexcolor"`
|
||||||
|
RGBColorString string `validate:"rgb"`
|
||||||
|
RGBAColorString string `validate:"rgba"`
|
||||||
|
HSLColorString string `validate:"hsl"`
|
||||||
|
HSLAColorString string `validate:"hsla"`
|
||||||
|
Email string `validate:"email"`
|
||||||
|
URL string `validate:"url"`
|
||||||
|
URI string `validate:"uri"`
|
||||||
|
Base64 string `validate:"base64"`
|
||||||
|
Contains string `validate:"contains=purpose"`
|
||||||
|
ContainsAny string `validate:"containsany=!@#$"`
|
||||||
|
Excludes string `validate:"excludes=text"`
|
||||||
|
ExcludesAll string `validate:"excludesall=!@#$"`
|
||||||
|
ExcludesRune string `validate:"excludesrune=☻"`
|
||||||
|
ISBN string `validate:"isbn"`
|
||||||
|
ISBN10 string `validate:"isbn10"`
|
||||||
|
ISBN13 string `validate:"isbn13"`
|
||||||
|
UUID string `validate:"uuid"`
|
||||||
|
UUID3 string `validate:"uuid3"`
|
||||||
|
UUID4 string `validate:"uuid4"`
|
||||||
|
UUID5 string `validate:"uuid5"`
|
||||||
|
ASCII string `validate:"ascii"`
|
||||||
|
PrintableASCII string `validate:"printascii"`
|
||||||
|
MultiByte string `validate:"multibyte"`
|
||||||
|
DataURI string `validate:"datauri"`
|
||||||
|
Latitude string `validate:"latitude"`
|
||||||
|
Longitude string `validate:"longitude"`
|
||||||
|
SSN string `validate:"ssn"`
|
||||||
|
IP string `validate:"ip"`
|
||||||
|
IPv4 string `validate:"ipv4"`
|
||||||
|
IPv6 string `validate:"ipv6"`
|
||||||
|
CIDR string `validate:"cidr"`
|
||||||
|
CIDRv4 string `validate:"cidrv4"`
|
||||||
|
CIDRv6 string `validate:"cidrv6"`
|
||||||
|
TCPAddr string `validate:"tcp_addr"`
|
||||||
|
TCPAddrv4 string `validate:"tcp4_addr"`
|
||||||
|
TCPAddrv6 string `validate:"tcp6_addr"`
|
||||||
|
UDPAddr string `validate:"udp_addr"`
|
||||||
|
UDPAddrv4 string `validate:"udp4_addr"`
|
||||||
|
UDPAddrv6 string `validate:"udp6_addr"`
|
||||||
|
IPAddr string `validate:"ip_addr"`
|
||||||
|
IPAddrv4 string `validate:"ip4_addr"`
|
||||||
|
IPAddrv6 string `validate:"ip6_addr"`
|
||||||
|
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
|
||||||
|
MAC string `validate:"mac"`
|
||||||
|
IsColor string `validate:"iscolor"`
|
||||||
|
StrPtrMinLen *string `validate:"min=10"`
|
||||||
|
StrPtrMaxLen *string `validate:"max=1"`
|
||||||
|
StrPtrLen *string `validate:"len=2"`
|
||||||
|
StrPtrLt *string `validate:"lt=1"`
|
||||||
|
StrPtrLte *string `validate:"lte=1"`
|
||||||
|
StrPtrGt *string `validate:"gt=10"`
|
||||||
|
StrPtrGte *string `validate:"gte=10"`
|
||||||
|
OneOfString string `validate:"oneof=red green"`
|
||||||
|
OneOfInt int `validate:"oneof=5 63"`
|
||||||
|
JsonString string `validate:"json"`
|
||||||
|
LowercaseString string `validate:"lowercase"`
|
||||||
|
UppercaseString string `validate:"uppercase"`
|
||||||
|
Datetime string `validate:"datetime=2006-01-02"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var test Test
|
||||||
|
|
||||||
|
test.Inner.EqCSFieldString = "1234"
|
||||||
|
test.Inner.GtCSFieldString = "1234"
|
||||||
|
test.Inner.GteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.MaxString = "1234"
|
||||||
|
test.MaxNumber = 2000
|
||||||
|
test.MaxMultiple = make([]string, 9)
|
||||||
|
|
||||||
|
test.LtString = "1234"
|
||||||
|
test.LtNumber = 6
|
||||||
|
test.LtMultiple = make([]string, 3)
|
||||||
|
test.LtTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LteString = "1234"
|
||||||
|
test.LteNumber = 6
|
||||||
|
test.LteMultiple = make([]string, 3)
|
||||||
|
test.LteTime = time.Now().Add(time.Hour * 24)
|
||||||
|
|
||||||
|
test.LtFieldString = "12345"
|
||||||
|
test.LteFieldString = "12345"
|
||||||
|
|
||||||
|
test.LtCSFieldString = "1234"
|
||||||
|
test.LteCSFieldString = "1234"
|
||||||
|
|
||||||
|
test.AlphaString = "abc3"
|
||||||
|
test.AlphanumString = "abc3!"
|
||||||
|
test.NumericString = "12E.00"
|
||||||
|
test.NumberString = "12E"
|
||||||
|
|
||||||
|
test.Excludes = "this is some test text"
|
||||||
|
test.ExcludesAll = "This is Great!"
|
||||||
|
test.ExcludesRune = "Love it ☻"
|
||||||
|
|
||||||
|
test.ASCII = "カタカナ"
|
||||||
|
test.PrintableASCII = "カタカナ"
|
||||||
|
|
||||||
|
test.MultiByte = "1234feerf"
|
||||||
|
|
||||||
|
s := "toolong"
|
||||||
|
test.StrPtrMaxLen = &s
|
||||||
|
test.StrPtrLen = &s
|
||||||
|
|
||||||
|
test.JsonString = "{\"foo\":\"bar\",}"
|
||||||
|
|
||||||
|
test.LowercaseString = "ABCDEFG"
|
||||||
|
test.UppercaseString = "abcdefg"
|
||||||
|
|
||||||
|
test.Datetime = "20060102"
|
||||||
|
|
||||||
|
err = validate.Struct(test)
|
||||||
|
NotEqual(t, err, nil)
|
||||||
|
|
||||||
|
errs, ok := err.(validator.ValidationErrors)
|
||||||
|
Equal(t, ok, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
ns string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ns: "Test.IsColor",
|
||||||
|
expected: "IsColor必须是一个有效的颜色",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MAC",
|
||||||
|
expected: "MAC必须是一个有效的MAC地址",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddr",
|
||||||
|
expected: "IPAddr必须是一个有效的IP地址",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv4",
|
||||||
|
expected: "IPAddrv4必须是一个有效的IPv4地址",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPAddrv6",
|
||||||
|
expected: "IPAddrv6必须是一个有效的IPv6地址",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddr",
|
||||||
|
expected: "UDPAddr必须是一个有效的UDP地址",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv4",
|
||||||
|
expected: "UDPAddrv4必须是一个有效的IPv4 UDP地址",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UDPAddrv6",
|
||||||
|
expected: "UDPAddrv6必须是一个有效的IPv6 UDP地址",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddr",
|
||||||
|
expected: "TCPAddr必须是一个有效的TCP地址",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv4",
|
||||||
|
expected: "TCPAddrv4必须是一个有效的IPv4 TCP地址",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.TCPAddrv6",
|
||||||
|
expected: "TCPAddrv6必须是一个有效的IPv6 TCP地址",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDR",
|
||||||
|
expected: "CIDR必须是一个有效的无类别域间路由(CIDR)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv4",
|
||||||
|
expected: "CIDRv4必须是一个包含IPv4地址的有效无类别域间路由(CIDR)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.CIDRv6",
|
||||||
|
expected: "CIDRv6必须是一个包含IPv6地址的有效无类别域间路由(CIDR)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.SSN",
|
||||||
|
expected: "SSN必须是一个有效的社会安全号码(SSN)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IP",
|
||||||
|
expected: "IP必须是一个有效的IP地址",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv4",
|
||||||
|
expected: "IPv4必须是一个有效的IPv4地址",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.IPv6",
|
||||||
|
expected: "IPv6必须是一个有效的IPv6地址",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.DataURI",
|
||||||
|
expected: "DataURI必须包含有效的数据URI",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Latitude",
|
||||||
|
expected: "Latitude必须包含有效的纬度坐标",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Longitude",
|
||||||
|
expected: "Longitude必须包含有效的经度坐标",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MultiByte",
|
||||||
|
expected: "MultiByte必须包含多字节字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ASCII",
|
||||||
|
expected: "ASCII必须只包含ascii字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.PrintableASCII",
|
||||||
|
expected: "PrintableASCII必须只包含可打印的ascii字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID",
|
||||||
|
expected: "UUID必须是一个有效的UUID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID3",
|
||||||
|
expected: "UUID3必须是一个有效的V3 UUID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID4",
|
||||||
|
expected: "UUID4必须是一个有效的V4 UUID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UUID5",
|
||||||
|
expected: "UUID5必须是一个有效的V5 UUID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN",
|
||||||
|
expected: "ISBN必须是一个有效的ISBN编号",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN10",
|
||||||
|
expected: "ISBN10必须是一个有效的ISBN-10编号",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ISBN13",
|
||||||
|
expected: "ISBN13必须是一个有效的ISBN-13编号",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Excludes",
|
||||||
|
expected: "Excludes不能包含文本'text'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesAll",
|
||||||
|
expected: "ExcludesAll不能包含以下任何字符'!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ExcludesRune",
|
||||||
|
expected: "ExcludesRune不能包含'☻'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.ContainsAny",
|
||||||
|
expected: "ContainsAny必须包含至少一个以下字符'!@#$'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Contains",
|
||||||
|
expected: "Contains必须包含文本'purpose'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Base64",
|
||||||
|
expected: "Base64必须是一个有效的Base64字符串",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Email",
|
||||||
|
expected: "Email必须是一个有效的邮箱",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URL",
|
||||||
|
expected: "URL必须是一个有效的URL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.URI",
|
||||||
|
expected: "URI必须是一个有效的URI",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBColorString",
|
||||||
|
expected: "RGBColorString必须是一个有效的RGB颜色",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RGBAColorString",
|
||||||
|
expected: "RGBAColorString必须是一个有效的RGBA颜色",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLColorString",
|
||||||
|
expected: "HSLColorString必须是一个有效的HSL颜色",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HSLAColorString",
|
||||||
|
expected: "HSLAColorString必须是一个有效的HSLA颜色",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexadecimalString",
|
||||||
|
expected: "HexadecimalString必须是一个有效的十六进制",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.HexColorString",
|
||||||
|
expected: "HexColorString必须是一个有效的十六进制颜色",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumberString",
|
||||||
|
expected: "NumberString必须是一个有效的数字",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NumericString",
|
||||||
|
expected: "NumericString必须是一个有效的数值",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphanumString",
|
||||||
|
expected: "AlphanumString只能包含字母和数字",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.AlphaString",
|
||||||
|
expected: "AlphaString只能包含字母",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtFieldString",
|
||||||
|
expected: "LtFieldString必须小于MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteFieldString",
|
||||||
|
expected: "LteFieldString必须小于或等于MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtFieldString",
|
||||||
|
expected: "GtFieldString必须大于MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteFieldString",
|
||||||
|
expected: "GteFieldString必须大于或等于MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeFieldString",
|
||||||
|
expected: "NeFieldString不能等于EqFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtCSFieldString",
|
||||||
|
expected: "LtCSFieldString必须小于Inner.LtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteCSFieldString",
|
||||||
|
expected: "LteCSFieldString必须小于或等于Inner.LteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtCSFieldString",
|
||||||
|
expected: "GtCSFieldString必须大于Inner.GtCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteCSFieldString",
|
||||||
|
expected: "GteCSFieldString必须大于或等于Inner.GteCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeCSFieldString",
|
||||||
|
expected: "NeCSFieldString不能等于Inner.NeCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqCSFieldString",
|
||||||
|
expected: "EqCSFieldString必须等于Inner.EqCSFieldString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqFieldString",
|
||||||
|
expected: "EqFieldString必须等于MaxString",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteString",
|
||||||
|
expected: "GteString长度必须至少为3个字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteNumber",
|
||||||
|
expected: "GteNumber必须大于或等于5.56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteMultiple",
|
||||||
|
expected: "GteMultiple必须至少包含2项",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GteTime",
|
||||||
|
expected: "GteTime必须大于或等于当前日期和时间",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtString",
|
||||||
|
expected: "GtString长度必须大于3个字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtNumber",
|
||||||
|
expected: "GtNumber必须大于5.56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtMultiple",
|
||||||
|
expected: "GtMultiple必须大于2项",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.GtTime",
|
||||||
|
expected: "GtTime必须大于当前日期和时间",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteString",
|
||||||
|
expected: "LteString长度不能超过3个字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteNumber",
|
||||||
|
expected: "LteNumber必须小于或等于5.56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteMultiple",
|
||||||
|
expected: "LteMultiple最多只能包含2项",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LteTime",
|
||||||
|
expected: "LteTime必须小于或等于当前日期和时间",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtString",
|
||||||
|
expected: "LtString长度必须小于3个字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtNumber",
|
||||||
|
expected: "LtNumber必须小于5.56",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtMultiple",
|
||||||
|
expected: "LtMultiple必须包含少于2项",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LtTime",
|
||||||
|
expected: "LtTime必须小于当前日期和时间",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeString",
|
||||||
|
expected: "NeString不能等于",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeNumber",
|
||||||
|
expected: "NeNumber不能等于0.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.NeMultiple",
|
||||||
|
expected: "NeMultiple不能等于0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqString",
|
||||||
|
expected: "EqString不等于3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqNumber",
|
||||||
|
expected: "EqNumber不等于2.33",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.EqMultiple",
|
||||||
|
expected: "EqMultiple不等于7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxString",
|
||||||
|
expected: "MaxString长度不能超过3个字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxNumber",
|
||||||
|
expected: "MaxNumber必须小于或等于1,113.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MaxMultiple",
|
||||||
|
expected: "MaxMultiple最多只能包含7项",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinString",
|
||||||
|
expected: "MinString长度必须至少为1个字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinNumber",
|
||||||
|
expected: "MinNumber最小只能为1,113.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.MinMultiple",
|
||||||
|
expected: "MinMultiple必须至少包含7项",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenString",
|
||||||
|
expected: "LenString长度必须是1个字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenNumber",
|
||||||
|
expected: "LenNumber必须等于1,113.00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LenMultiple",
|
||||||
|
expected: "LenMultiple必须包含7项",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredString",
|
||||||
|
expected: "RequiredString为必填字段",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredNumber",
|
||||||
|
expected: "RequiredNumber为必填字段",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.RequiredMultiple",
|
||||||
|
expected: "RequiredMultiple为必填字段",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMinLen",
|
||||||
|
expected: "StrPtrMinLen长度必须至少为10个字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrMaxLen",
|
||||||
|
expected: "StrPtrMaxLen长度不能超过1个字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLen",
|
||||||
|
expected: "StrPtrLen长度必须是2个字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLt",
|
||||||
|
expected: "StrPtrLt长度必须小于1个字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrLte",
|
||||||
|
expected: "StrPtrLte长度不能超过1个字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGt",
|
||||||
|
expected: "StrPtrGt长度必须大于10个字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.StrPtrGte",
|
||||||
|
expected: "StrPtrGte长度必须至少为10个字符",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfString",
|
||||||
|
expected: "OneOfString必须是[red green]中的一个",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.OneOfInt",
|
||||||
|
expected: "OneOfInt必须是[5 63]中的一个",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.JsonString",
|
||||||
|
expected: "JsonString必须是一个JSON字符串",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.LowercaseString",
|
||||||
|
expected: "LowercaseString必须是小写字母",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.UppercaseString",
|
||||||
|
expected: "UppercaseString必须是大写字母",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ns: "Test.Datetime",
|
||||||
|
expected: "Datetime的格式必须是2006-01-02",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
|
||||||
|
var fe validator.FieldError
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
if tt.ns == e.Namespace() {
|
||||||
|
fe = e
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotEqual(t, fe, nil)
|
||||||
|
Equal(t, tt.expected, fe.Translate(trans))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue