删掉master
This commit is contained in:
		
							parent
							
								
									24c6a55374
								
							
						
					
					
						commit
						d0da12a22c
					
				
							
								
								
									
										62
									
								
								gin/b/b.go
								
								
								
								
							
							
						
						
									
										62
									
								
								gin/b/b.go
								
								
								
								
							|  | @ -1,62 +0,0 @@ | ||||||
| package b |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"git.ningdatech.com/ningda/gin_valid/gin/binding" |  | ||||||
| 	"git.ningdatech.com/ningda/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 |  | ||||||
| } |  | ||||||
|  | @ -1,116 +0,0 @@ | ||||||
| // 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://git.ningdatech.com/ningda/gin_valid/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://git.ningdatech.com/ningda/gin_valid/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) |  | ||||||
| } |  | ||||||
|  | @ -1,111 +0,0 @@ | ||||||
| // 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://git.ningdatech.com/ningda/gin_valid/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://git.ningdatech.com/ningda/gin_valid/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) |  | ||||||
| } |  | ||||||
|  | @ -1,73 +0,0 @@ | ||||||
| // 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 ( |  | ||||||
| 	"git.ningdatech.com/ningda/gin_valid/go-playground/locales/zh" |  | ||||||
| 	ut "git.ningdatech.com/ningda/gin_valid/go-playground/universal-translator" |  | ||||||
| 	zhTrans "git.ningdatech.com/ningda/gin_valid/go-playground/validator/v10/translations/zh" |  | ||||||
| 	"reflect" |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
| 
 |  | ||||||
| 	"git.ningdatech.com/ningda/gin_valid/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") |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
|  | @ -1,63 +0,0 @@ | ||||||
| // 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) |  | ||||||
| } |  | ||||||
|  | @ -1,392 +0,0 @@ | ||||||
| // 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" |  | ||||||
| 
 |  | ||||||
| 	"git.ningdatech.com/ningda/gin_valid/gin/internal/bytesconv" |  | ||||||
| 	"git.ningdatech.com/ningda/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 |  | ||||||
| } |  | ||||||
|  | @ -1,34 +0,0 @@ | ||||||
| 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) |  | ||||||
| } |  | ||||||
|  | @ -1,56 +0,0 @@ | ||||||
| // 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" |  | ||||||
| 
 |  | ||||||
| 	"git.ningdatech.com/ningda/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) |  | ||||||
| } |  | ||||||
|  | @ -1,37 +0,0 @@ | ||||||
| // 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) |  | ||||||
| } |  | ||||||
|  | @ -1,66 +0,0 @@ | ||||||
| // 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 |  | ||||||
| } |  | ||||||
|  | @ -1,36 +0,0 @@ | ||||||
| // 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)
 |  | ||||||
| } |  | ||||||
|  | @ -1,21 +0,0 @@ | ||||||
| // 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) |  | ||||||
| } |  | ||||||
|  | @ -1,18 +0,0 @@ | ||||||
| // 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) |  | ||||||
| } |  | ||||||
|  | @ -1,33 +0,0 @@ | ||||||
| // 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) |  | ||||||
| } |  | ||||||
|  | @ -1,35 +0,0 @@ | ||||||
| // 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) |  | ||||||
| } |  | ||||||
|  | @ -1,23 +0,0 @@ | ||||||
| // 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)) |  | ||||||
| } |  | ||||||
|  | @ -1,22 +0,0 @@ | ||||||
| // 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 |  | ||||||
| ) |  | ||||||
|  | @ -1,23 +0,0 @@ | ||||||
| // 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 |  | ||||||
| ) |  | ||||||
|  | @ -1,24 +0,0 @@ | ||||||
| # 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 |  | ||||||
|  | @ -1,26 +0,0 @@ | ||||||
| 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 |  | ||||||
|  | @ -1,21 +0,0 @@ | ||||||
| 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. |  | ||||||
|  | @ -1,172 +0,0 @@ | ||||||
| ## locales |  | ||||||
| <img align="right" src="https://raw.githubusercontent.com/go-playground/locales/master/logo.png"> |  | ||||||
| [](https://travis-ci.org/go-playground/locales) |  | ||||||
| [](https://goreportcard.com/report/github.com/go-playground/locales) |  | ||||||
| [](https://godoc.org/github.com/go-playground/locales) |  | ||||||
|  |  | ||||||
| [](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://github.com/go-playground/locales/issues/1) for details. |  | ||||||
| 
 |  | ||||||
| Installation |  | ||||||
| ----------- |  | ||||||
| 
 |  | ||||||
| Use go get  |  | ||||||
| 
 |  | ||||||
| ```shell |  | ||||||
| go get github.com/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" |  | ||||||
| 
 |  | ||||||
| 	"github.com/go-playground/locales/currency" |  | ||||||
| 	"github.com/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. |  | ||||||
|  | @ -1,308 +0,0 @@ | ||||||
| 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 |  | ||||||
| ) |  | ||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 36 KiB | 
|  | @ -1,293 +0,0 @@ | ||||||
| package locales |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"strconv" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"git.ningdatech.com/ningda/gin_valid/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 |  | ||||||
| } |  | ||||||
|  | @ -1,619 +0,0 @@ | ||||||
| package zh |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"math" |  | ||||||
| 	"strconv" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"git.ningdatech.com/ningda/gin_valid/go-playground/locales" |  | ||||||
| 	"git.ningdatech.com/ningda/gin_valid/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) |  | ||||||
| } |  | ||||||
|  | @ -1,25 +0,0 @@ | ||||||
| # 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 |  | ||||||
|  | @ -1,27 +0,0 @@ | ||||||
| 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 |  | ||||||
|  | @ -1,21 +0,0 @@ | ||||||
| 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. |  | ||||||
|  | @ -1,89 +0,0 @@ | ||||||
| ## universal-translator |  | ||||||
| <img align="right" src="https://raw.githubusercontent.com/go-playground/universal-translator/master/logo.png"> |  | ||||||
| [](https://travis-ci.org/go-playground/universal-translator) |  | ||||||
| [](https://coveralls.io/github/go-playground/universal-translator) |  | ||||||
| [](https://goreportcard.com/report/github.com/go-playground/universal-translator) |  | ||||||
| [](https://godoc.org/github.com/go-playground/universal-translator) |  | ||||||
|  |  | ||||||
| [](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://github.com/go-playground/locales) for everyone to use; this package  |  | ||||||
| is a thin wrapper around [locales](https://github.com/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://github.com/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://github.com/go-playground/locales/issues/1) for details. |  | ||||||
| 
 |  | ||||||
| License |  | ||||||
| ------ |  | ||||||
| Distributed under MIT License, please see license file in code for more details. |  | ||||||
|  | @ -1,148 +0,0 @@ | ||||||
| package ut |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 
 |  | ||||||
| 	"git.ningdatech.com/ningda/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) |  | ||||||
| } |  | ||||||
|  | @ -1,274 +0,0 @@ | ||||||
| package ut |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io/ioutil" |  | ||||||
| 	"os" |  | ||||||
| 	"path/filepath" |  | ||||||
| 
 |  | ||||||
| 	"io" |  | ||||||
| 
 |  | ||||||
| 	"git.ningdatech.com/ningda/gin_valid/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.
										
									
								
							| Before Width: | Height: | Size: 16 KiB | 
|  | @ -1,420 +0,0 @@ | ||||||
| package ut |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"strconv" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"git.ningdatech.com/ningda/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 |  | ||||||
| } |  | ||||||
|  | @ -1,113 +0,0 @@ | ||||||
| package ut |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"git.ningdatech.com/ningda/gin_valid/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 |  | ||||||
| } |  | ||||||
|  | @ -1,9 +0,0 @@ | ||||||
| # 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 [](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. |  | ||||||
|  | @ -1,13 +0,0 @@ | ||||||
| ### Package version eg. v8, v9:  |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ### Issue, Question or Enhancement: |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ### Code sample, to showcase or reproduce: |  | ||||||
| 
 |  | ||||||
| ```go |  | ||||||
| 
 |  | ||||||
| ``` |  | ||||||
|  | @ -1,13 +0,0 @@ | ||||||
| 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 |  | ||||||
|  | @ -1,30 +0,0 @@ | ||||||
| # 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 |  | ||||||
|  | @ -1,29 +0,0 @@ | ||||||
| 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 |  | ||||||
|  | @ -1,22 +0,0 @@ | ||||||
| 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. |  | ||||||
| 
 |  | ||||||
|  | @ -1,18 +0,0 @@ | ||||||
| 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 |  | ||||||
|  | @ -1,299 +0,0 @@ | ||||||
| Package validator |  | ||||||
| ================ |  | ||||||
| <img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">[](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) |  | ||||||
|  |  | ||||||
| [](https://travis-ci.org/go-playground/validator) |  | ||||||
| [](https://coveralls.io/github/go-playground/validator?branch=master) |  | ||||||
| [](https://goreportcard.com/report/github.com/go-playground/validator) |  | ||||||
| [](https://pkg.go.dev/github.com/go-playground/validator/v10) |  | ||||||
|  |  | ||||||
| 
 |  | ||||||
| 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 github.com/go-playground/validator/v10 |  | ||||||
| 
 |  | ||||||
| Then import the validator package into your own code. |  | ||||||
| 
 |  | ||||||
| 	import "github.com/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. |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -1,322 +0,0 @@ | ||||||
| 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 |  | ||||||
| } |  | ||||||
|  | @ -1,162 +0,0 @@ | ||||||
| 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
											
										
									
								
							|  | @ -1,295 +0,0 @@ | ||||||
| package validator |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"fmt" |  | ||||||
| 	"reflect" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	ut "git.ningdatech.com/ningda/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) |  | ||||||
| } |  | ||||||
|  | @ -1,119 +0,0 @@ | ||||||
| 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) |  | ||||||
| } |  | ||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 13 KiB | 
|  | @ -1,25 +0,0 @@ | ||||||
| package validators |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"reflect" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"git.ningdatech.com/ningda/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() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -1,101 +0,0 @@ | ||||||
| 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) |  | ||||||
| ) |  | ||||||
|  | @ -1,175 +0,0 @@ | ||||||
| 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) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -1,11 +0,0 @@ | ||||||
| package validator |  | ||||||
| 
 |  | ||||||
| import ut "git.ningdatech.com/ningda/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
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -1,288 +0,0 @@ | ||||||
| package validator |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"reflect" |  | ||||||
| 	"strconv" |  | ||||||
| 	"strings" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // extractTypeInternal gets the actual underlying type of field value.
 |  | ||||||
| // It will dive into pointers, customTypes and return you the
 |  | ||||||
| // underlying value and it's kind.
 |  | ||||||
| func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (reflect.Value, reflect.Kind, bool) { |  | ||||||
| 
 |  | ||||||
| BEGIN: |  | ||||||
| 	switch current.Kind() { |  | ||||||
| 	case reflect.Ptr: |  | ||||||
| 
 |  | ||||||
| 		nullable = true |  | ||||||
| 
 |  | ||||||
| 		if current.IsNil() { |  | ||||||
| 			return current, reflect.Ptr, nullable |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		current = current.Elem() |  | ||||||
| 		goto BEGIN |  | ||||||
| 
 |  | ||||||
| 	case reflect.Interface: |  | ||||||
| 
 |  | ||||||
| 		nullable = true |  | ||||||
| 
 |  | ||||||
| 		if current.IsNil() { |  | ||||||
| 			return current, reflect.Interface, nullable |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		current = current.Elem() |  | ||||||
| 		goto BEGIN |  | ||||||
| 
 |  | ||||||
| 	case reflect.Invalid: |  | ||||||
| 		return current, reflect.Invalid, nullable |  | ||||||
| 
 |  | ||||||
| 	default: |  | ||||||
| 
 |  | ||||||
| 		if v.v.hasCustomFuncs { |  | ||||||
| 
 |  | ||||||
| 			if fn, ok := v.v.customFuncs[current.Type()]; ok { |  | ||||||
| 				current = reflect.ValueOf(fn(current)) |  | ||||||
| 				goto BEGIN |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		return current, current.Kind(), nullable |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // getStructFieldOKInternal traverses a struct to retrieve a specific field denoted by the provided namespace 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.
 |  | ||||||
| func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, nullable bool, found bool) { |  | ||||||
| 	// val 是结构体, 如果 gtfield=filed1 这种field的比较,那 namespace就是入参 filed1
 |  | ||||||
| BEGIN: |  | ||||||
| 	current, kind, nullable = v.ExtractType(val) |  | ||||||
| 	if kind == reflect.Invalid { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if namespace == "" { |  | ||||||
| 		found = true |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	switch kind { |  | ||||||
| 
 |  | ||||||
| 	case reflect.Ptr, reflect.Interface: |  | ||||||
| 		return |  | ||||||
| 
 |  | ||||||
| 	case reflect.Struct: |  | ||||||
| 
 |  | ||||||
| 		typ := current.Type() |  | ||||||
| 		fld := namespace |  | ||||||
| 		var ns string |  | ||||||
| 
 |  | ||||||
| 		if typ != timeType { |  | ||||||
| 
 |  | ||||||
| 			idx := strings.Index(namespace, namespaceSeparator) |  | ||||||
| 
 |  | ||||||
| 			if idx != -1 { |  | ||||||
| 				fld = namespace[:idx] |  | ||||||
| 				ns = namespace[idx+1:] |  | ||||||
| 			} else { |  | ||||||
| 				ns = "" |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			bracketIdx := strings.Index(fld, leftBracket) |  | ||||||
| 			if bracketIdx != -1 { |  | ||||||
| 				fld = fld[:bracketIdx] |  | ||||||
| 
 |  | ||||||
| 				ns = namespace[bracketIdx:] |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			val = current.FieldByName(fld) |  | ||||||
| 			namespace = ns |  | ||||||
| 			goto BEGIN |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 	case reflect.Array, reflect.Slice: |  | ||||||
| 		idx := strings.Index(namespace, leftBracket) |  | ||||||
| 		idx2 := strings.Index(namespace, rightBracket) |  | ||||||
| 
 |  | ||||||
| 		arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2]) |  | ||||||
| 
 |  | ||||||
| 		if arrIdx >= current.Len() { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		startIdx := idx2 + 1 |  | ||||||
| 
 |  | ||||||
| 		if startIdx < len(namespace) { |  | ||||||
| 			if namespace[startIdx:startIdx+1] == namespaceSeparator { |  | ||||||
| 				startIdx++ |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		val = current.Index(arrIdx) |  | ||||||
| 		namespace = namespace[startIdx:] |  | ||||||
| 		goto BEGIN |  | ||||||
| 
 |  | ||||||
| 	case reflect.Map: |  | ||||||
| 		idx := strings.Index(namespace, leftBracket) + 1 |  | ||||||
| 		idx2 := strings.Index(namespace, rightBracket) |  | ||||||
| 
 |  | ||||||
| 		endIdx := idx2 |  | ||||||
| 
 |  | ||||||
| 		if endIdx+1 < len(namespace) { |  | ||||||
| 			if namespace[endIdx+1:endIdx+2] == namespaceSeparator { |  | ||||||
| 				endIdx++ |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		key := namespace[idx:idx2] |  | ||||||
| 
 |  | ||||||
| 		switch current.Type().Key().Kind() { |  | ||||||
| 		case reflect.Int: |  | ||||||
| 			i, _ := strconv.Atoi(key) |  | ||||||
| 			val = current.MapIndex(reflect.ValueOf(i)) |  | ||||||
| 			namespace = namespace[endIdx+1:] |  | ||||||
| 
 |  | ||||||
| 		case reflect.Int8: |  | ||||||
| 			i, _ := strconv.ParseInt(key, 10, 8) |  | ||||||
| 			val = current.MapIndex(reflect.ValueOf(int8(i))) |  | ||||||
| 			namespace = namespace[endIdx+1:] |  | ||||||
| 
 |  | ||||||
| 		case reflect.Int16: |  | ||||||
| 			i, _ := strconv.ParseInt(key, 10, 16) |  | ||||||
| 			val = current.MapIndex(reflect.ValueOf(int16(i))) |  | ||||||
| 			namespace = namespace[endIdx+1:] |  | ||||||
| 
 |  | ||||||
| 		case reflect.Int32: |  | ||||||
| 			i, _ := strconv.ParseInt(key, 10, 32) |  | ||||||
| 			val = current.MapIndex(reflect.ValueOf(int32(i))) |  | ||||||
| 			namespace = namespace[endIdx+1:] |  | ||||||
| 
 |  | ||||||
| 		case reflect.Int64: |  | ||||||
| 			i, _ := strconv.ParseInt(key, 10, 64) |  | ||||||
| 			val = current.MapIndex(reflect.ValueOf(i)) |  | ||||||
| 			namespace = namespace[endIdx+1:] |  | ||||||
| 
 |  | ||||||
| 		case reflect.Uint: |  | ||||||
| 			i, _ := strconv.ParseUint(key, 10, 0) |  | ||||||
| 			val = current.MapIndex(reflect.ValueOf(uint(i))) |  | ||||||
| 			namespace = namespace[endIdx+1:] |  | ||||||
| 
 |  | ||||||
| 		case reflect.Uint8: |  | ||||||
| 			i, _ := strconv.ParseUint(key, 10, 8) |  | ||||||
| 			val = current.MapIndex(reflect.ValueOf(uint8(i))) |  | ||||||
| 			namespace = namespace[endIdx+1:] |  | ||||||
| 
 |  | ||||||
| 		case reflect.Uint16: |  | ||||||
| 			i, _ := strconv.ParseUint(key, 10, 16) |  | ||||||
| 			val = current.MapIndex(reflect.ValueOf(uint16(i))) |  | ||||||
| 			namespace = namespace[endIdx+1:] |  | ||||||
| 
 |  | ||||||
| 		case reflect.Uint32: |  | ||||||
| 			i, _ := strconv.ParseUint(key, 10, 32) |  | ||||||
| 			val = current.MapIndex(reflect.ValueOf(uint32(i))) |  | ||||||
| 			namespace = namespace[endIdx+1:] |  | ||||||
| 
 |  | ||||||
| 		case reflect.Uint64: |  | ||||||
| 			i, _ := strconv.ParseUint(key, 10, 64) |  | ||||||
| 			val = current.MapIndex(reflect.ValueOf(i)) |  | ||||||
| 			namespace = namespace[endIdx+1:] |  | ||||||
| 
 |  | ||||||
| 		case reflect.Float32: |  | ||||||
| 			f, _ := strconv.ParseFloat(key, 32) |  | ||||||
| 			val = current.MapIndex(reflect.ValueOf(float32(f))) |  | ||||||
| 			namespace = namespace[endIdx+1:] |  | ||||||
| 
 |  | ||||||
| 		case reflect.Float64: |  | ||||||
| 			f, _ := strconv.ParseFloat(key, 64) |  | ||||||
| 			val = current.MapIndex(reflect.ValueOf(f)) |  | ||||||
| 			namespace = namespace[endIdx+1:] |  | ||||||
| 
 |  | ||||||
| 		case reflect.Bool: |  | ||||||
| 			b, _ := strconv.ParseBool(key) |  | ||||||
| 			val = current.MapIndex(reflect.ValueOf(b)) |  | ||||||
| 			namespace = namespace[endIdx+1:] |  | ||||||
| 
 |  | ||||||
| 		// reflect.Type = string
 |  | ||||||
| 		default: |  | ||||||
| 			val = current.MapIndex(reflect.ValueOf(key)) |  | ||||||
| 			namespace = namespace[endIdx+1:] |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		goto BEGIN |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// if got here there was more namespace, cannot go any deeper
 |  | ||||||
| 	panic("Invalid field namespace") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // asInt returns the parameter as a int64
 |  | ||||||
| // or panics if it can't convert
 |  | ||||||
| func asInt(param string) int64 { |  | ||||||
| 	i, err := strconv.ParseInt(param, 0, 64) |  | ||||||
| 	panicIf(err) |  | ||||||
| 
 |  | ||||||
| 	return i |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // asIntFromTimeDuration parses param as time.Duration and returns it as int64
 |  | ||||||
| // or panics on error.
 |  | ||||||
| func asIntFromTimeDuration(param string) int64 { |  | ||||||
| 	d, err := time.ParseDuration(param) |  | ||||||
| 	if err != nil { |  | ||||||
| 		// attempt parsing as an an integer assuming nanosecond precision
 |  | ||||||
| 		return asInt(param) |  | ||||||
| 	} |  | ||||||
| 	return int64(d) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // asIntFromType calls the proper function to parse param as int64,
 |  | ||||||
| // given a field's Type t.
 |  | ||||||
| func asIntFromType(t reflect.Type, param string) int64 { |  | ||||||
| 	switch t { |  | ||||||
| 	case timeDurationType: |  | ||||||
| 		return asIntFromTimeDuration(param) |  | ||||||
| 	default: |  | ||||||
| 		return asInt(param) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // asUint returns the parameter as a uint64
 |  | ||||||
| // or panics if it can't convert
 |  | ||||||
| func asUint(param string) uint64 { |  | ||||||
| 
 |  | ||||||
| 	i, err := strconv.ParseUint(param, 0, 64) |  | ||||||
| 	panicIf(err) |  | ||||||
| 
 |  | ||||||
| 	return i |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // asFloat returns the parameter as a float64
 |  | ||||||
| // or panics if it can't convert
 |  | ||||||
| func asFloat(param string) float64 { |  | ||||||
| 
 |  | ||||||
| 	i, err := strconv.ParseFloat(param, 64) |  | ||||||
| 	panicIf(err) |  | ||||||
| 
 |  | ||||||
| 	return i |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // asBool returns the parameter as a bool
 |  | ||||||
| // or panics if it can't convert
 |  | ||||||
| func asBool(param string) bool { |  | ||||||
| 
 |  | ||||||
| 	i, err := strconv.ParseBool(param) |  | ||||||
| 	panicIf(err) |  | ||||||
| 
 |  | ||||||
| 	return i |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func panicIf(err error) { |  | ||||||
| 	if err != nil { |  | ||||||
| 		panic(err.Error()) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -1,482 +0,0 @@ | ||||||
| package validator |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"fmt" |  | ||||||
| 	"reflect" |  | ||||||
| 	"strconv" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // per validate construct
 |  | ||||||
| type validate struct { |  | ||||||
| 	v        *Validate |  | ||||||
| 	top      reflect.Value |  | ||||||
| 	ns       []byte // ns是命名空间,见
 |  | ||||||
| 	actualNs []byte |  | ||||||
| 	// 如果想修改 错误信息的打印, 直接改这个错误的 .Error()方法应该就可以了, 不对不对,还有一个翻译在执行
 |  | ||||||
| 	errs           ValidationErrors    //校验失败的 记录错误的 结构体的 field[] , 依次验证field,所以可以加个判断,只要有一个错误,就停下来 //每次用完都会清零,在put回pool
 |  | ||||||
| 	includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise
 |  | ||||||
| 	ffn            FilterFunc |  | ||||||
| 	slflParent     reflect.Value // StructLevel & FieldLevel
 |  | ||||||
| 	slCurrent      reflect.Value // StructLevel & FieldLevel
 |  | ||||||
| 	flField        reflect.Value // StructLevel & FieldLevel
 |  | ||||||
| 	cf             *cField       // StructLevel & FieldLevel
 |  | ||||||
| 	ct             *cTag         // StructLevel & FieldLevel
 |  | ||||||
| 	misc           []byte        // misc reusable
 |  | ||||||
| 	str1           string        // misc reusable   // 真正显示的 错误,有别名就是别名
 |  | ||||||
| 	str2           string        // misc reusable // 失败的field信息,如 User.Name (结构体名称+field真名称),如果没有别名,就是str1
 |  | ||||||
| 	fldIsPointer   bool          // StructLevel & FieldLevel
 |  | ||||||
| 	isPartial      bool |  | ||||||
| 	hasExcludes    bool |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // parent and current will be the same the first run of validateStruct
 |  | ||||||
| func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) { |  | ||||||
| 
 |  | ||||||
| 	cs, ok := v.v.structCache.Get(typ) |  | ||||||
| 	if !ok { |  | ||||||
| 		cs = v.v.extractStructCache(current, typ.Name()) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if len(ns) == 0 && len(cs.name) != 0 { |  | ||||||
| 		// 但实际上,`:` 左边的我都不需要,所以还是改 翻译 比较好 , 而且可能会影响到 未知的错误
 |  | ||||||
| 		ns = append(ns, cs.name...) // 去掉这里的ns的内容,出现错误时,应该就就不会输出 struct.field:"err", 而是 field:"err",
 |  | ||||||
| 		ns = append(ns, '.') |  | ||||||
| 
 |  | ||||||
| 		structNs = append(structNs, cs.name...) |  | ||||||
| 		structNs = append(structNs, '.') |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// ct is nil on top level struct, and structs as fields that have no tag info
 |  | ||||||
| 	// so if nil or if not nil and the structonly tag isn't present
 |  | ||||||
| 	if ct == nil || ct.typeof != typeStructOnly { |  | ||||||
| 
 |  | ||||||
| 		var f *cField |  | ||||||
| 
 |  | ||||||
| 		for i := 0; i < len(cs.fields); i++ { |  | ||||||
| 			// yang 加个判断, 有一个验证不通过就退出
 |  | ||||||
| 			if len(v.errs) != 0 { |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 			// yang 修改分割线
 |  | ||||||
| 			f = cs.fields[i] |  | ||||||
| 
 |  | ||||||
| 			if v.isPartial { |  | ||||||
| 
 |  | ||||||
| 				if v.ffn != nil { |  | ||||||
| 					// used with StructFiltered
 |  | ||||||
| 					if v.ffn(append(structNs, f.name...)) { |  | ||||||
| 						continue |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 				} else { |  | ||||||
| 					// used with StructPartial & StructExcept
 |  | ||||||
| 					_, ok = v.includeExclude[string(append(structNs, f.name...))] |  | ||||||
| 
 |  | ||||||
| 					if (ok && v.hasExcludes) || (!ok && !v.hasExcludes) { |  | ||||||
| 						continue |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			v.traverseField(ctx, parent, current.Field(f.idx), ns, structNs, f, f.cTags) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// check if any struct level validations, after all field validations already checked.
 |  | ||||||
| 	// first iteration will have no info about nostructlevel tag, and is checked prior to
 |  | ||||||
| 	// calling the next iteration of validateStruct called from traverseField.
 |  | ||||||
| 	if cs.fn != nil { |  | ||||||
| 
 |  | ||||||
| 		v.slflParent = parent |  | ||||||
| 		v.slCurrent = current |  | ||||||
| 		v.ns = ns |  | ||||||
| 		v.actualNs = structNs |  | ||||||
| 
 |  | ||||||
| 		cs.fn(ctx, v) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options
 |  | ||||||
| func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) { |  | ||||||
| 	var typ reflect.Type |  | ||||||
| 	var kind reflect.Kind |  | ||||||
| 
 |  | ||||||
| 	current, kind, v.fldIsPointer = v.extractTypeInternal(current, false) |  | ||||||
| 
 |  | ||||||
| 	switch kind { |  | ||||||
| 	case reflect.Ptr, reflect.Interface, reflect.Invalid: |  | ||||||
| 
 |  | ||||||
| 		if ct == nil { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if ct.typeof == typeOmitEmpty || ct.typeof == typeIsDefault { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if ct.hasTag { |  | ||||||
| 			if kind == reflect.Invalid { |  | ||||||
| 				v.str1 = string(append(ns, cf.altName...)) |  | ||||||
| 				if v.v.hasTagNameFunc { |  | ||||||
| 					v.str2 = string(append(structNs, cf.name...)) |  | ||||||
| 				} else { |  | ||||||
| 					v.str2 = v.str1 |  | ||||||
| 				} |  | ||||||
| 				v.errs = append(v.errs, |  | ||||||
| 					&fieldError{ |  | ||||||
| 						v:              v.v, |  | ||||||
| 						tag:            ct.aliasTag, |  | ||||||
| 						actualTag:      ct.tag, |  | ||||||
| 						ns:             v.str1, |  | ||||||
| 						structNs:       v.str2, |  | ||||||
| 						fieldLen:       uint8(len(cf.altName)), |  | ||||||
| 						structfieldLen: uint8(len(cf.name)), |  | ||||||
| 						param:          ct.param, |  | ||||||
| 						kind:           kind, |  | ||||||
| 					}, |  | ||||||
| 				) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			v.str1 = string(append(ns, cf.altName...)) |  | ||||||
| 			if v.v.hasTagNameFunc { |  | ||||||
| 				v.str2 = string(append(structNs, cf.name...)) |  | ||||||
| 			} else { |  | ||||||
| 				v.str2 = v.str1 |  | ||||||
| 			} |  | ||||||
| 			if !ct.runValidationWhenNil { |  | ||||||
| 				v.errs = append(v.errs, |  | ||||||
| 					&fieldError{ |  | ||||||
| 						v:              v.v, |  | ||||||
| 						tag:            ct.aliasTag, |  | ||||||
| 						actualTag:      ct.tag, |  | ||||||
| 						ns:             v.str1, |  | ||||||
| 						structNs:       v.str2, |  | ||||||
| 						fieldLen:       uint8(len(cf.altName)), |  | ||||||
| 						structfieldLen: uint8(len(cf.name)), |  | ||||||
| 						value:          current.Interface(), |  | ||||||
| 						param:          ct.param, |  | ||||||
| 						kind:           kind, |  | ||||||
| 						typ:            current.Type(), |  | ||||||
| 					}, |  | ||||||
| 				) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 	case reflect.Struct: |  | ||||||
| 
 |  | ||||||
| 		typ = current.Type() |  | ||||||
| 
 |  | ||||||
| 		if typ != timeType { |  | ||||||
| 
 |  | ||||||
| 			if ct != nil { |  | ||||||
| 
 |  | ||||||
| 				if ct.typeof == typeStructOnly { |  | ||||||
| 					goto CONTINUE |  | ||||||
| 				} else if ct.typeof == typeIsDefault { |  | ||||||
| 					// set Field Level fields
 |  | ||||||
| 					v.slflParent = parent |  | ||||||
| 					v.flField = current |  | ||||||
| 					v.cf = cf |  | ||||||
| 					v.ct = ct |  | ||||||
| 
 |  | ||||||
| 					if !ct.fn(ctx, v) { |  | ||||||
| 						v.str1 = string(append(ns, cf.altName...)) |  | ||||||
| 
 |  | ||||||
| 						if v.v.hasTagNameFunc { |  | ||||||
| 							v.str2 = string(append(structNs, cf.name...)) |  | ||||||
| 						} else { |  | ||||||
| 							v.str2 = v.str1 |  | ||||||
| 						} |  | ||||||
| 
 |  | ||||||
| 						v.errs = append(v.errs, |  | ||||||
| 							&fieldError{ |  | ||||||
| 								v:              v.v, |  | ||||||
| 								tag:            ct.aliasTag, |  | ||||||
| 								actualTag:      ct.tag, |  | ||||||
| 								ns:             v.str1, |  | ||||||
| 								structNs:       v.str2, |  | ||||||
| 								fieldLen:       uint8(len(cf.altName)), |  | ||||||
| 								structfieldLen: uint8(len(cf.name)), |  | ||||||
| 								value:          current.Interface(), |  | ||||||
| 								param:          ct.param, |  | ||||||
| 								kind:           kind, |  | ||||||
| 								typ:            typ, |  | ||||||
| 							}, |  | ||||||
| 						) |  | ||||||
| 						return |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				ct = ct.next |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if ct != nil && ct.typeof == typeNoStructLevel { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 		CONTINUE: |  | ||||||
| 			// if len == 0 then validating using 'Var' or 'VarWithValue'
 |  | ||||||
| 			// Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
 |  | ||||||
| 			// VarWithField - this allows for validating against each field within the struct against a specific value
 |  | ||||||
| 			//                pretty handy in certain situations
 |  | ||||||
| 			if len(cf.name) > 0 { |  | ||||||
| 				ns = append(append(ns, cf.altName...), '.') |  | ||||||
| 				structNs = append(append(structNs, cf.name...), '.') |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			v.validateStruct(ctx, current, current, typ, ns, structNs, ct) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if !ct.hasTag { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	typ = current.Type() |  | ||||||
| 
 |  | ||||||
| OUTER: |  | ||||||
| 	for { |  | ||||||
| 		if ct == nil { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		switch ct.typeof { |  | ||||||
| 
 |  | ||||||
| 		case typeOmitEmpty: |  | ||||||
| 
 |  | ||||||
| 			// set Field Level fields
 |  | ||||||
| 			v.slflParent = parent |  | ||||||
| 			v.flField = current |  | ||||||
| 			v.cf = cf |  | ||||||
| 			v.ct = ct |  | ||||||
| 
 |  | ||||||
| 			if !hasValue(v) { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			ct = ct.next |  | ||||||
| 			continue |  | ||||||
| 
 |  | ||||||
| 		case typeEndKeys: |  | ||||||
| 			return |  | ||||||
| 
 |  | ||||||
| 		case typeDive: |  | ||||||
| 
 |  | ||||||
| 			ct = ct.next |  | ||||||
| 
 |  | ||||||
| 			// traverse slice or map here
 |  | ||||||
| 			// or panic ;)
 |  | ||||||
| 			switch kind { |  | ||||||
| 			case reflect.Slice, reflect.Array: |  | ||||||
| 
 |  | ||||||
| 				var i64 int64 |  | ||||||
| 				reusableCF := &cField{} |  | ||||||
| 
 |  | ||||||
| 				for i := 0; i < current.Len(); i++ { |  | ||||||
| 
 |  | ||||||
| 					i64 = int64(i) |  | ||||||
| 
 |  | ||||||
| 					v.misc = append(v.misc[0:0], cf.name...) |  | ||||||
| 					v.misc = append(v.misc, '[') |  | ||||||
| 					v.misc = strconv.AppendInt(v.misc, i64, 10) |  | ||||||
| 					v.misc = append(v.misc, ']') |  | ||||||
| 
 |  | ||||||
| 					reusableCF.name = string(v.misc) |  | ||||||
| 
 |  | ||||||
| 					if cf.namesEqual { |  | ||||||
| 						reusableCF.altName = reusableCF.name |  | ||||||
| 					} else { |  | ||||||
| 
 |  | ||||||
| 						v.misc = append(v.misc[0:0], cf.altName...) |  | ||||||
| 						v.misc = append(v.misc, '[') |  | ||||||
| 						v.misc = strconv.AppendInt(v.misc, i64, 10) |  | ||||||
| 						v.misc = append(v.misc, ']') |  | ||||||
| 
 |  | ||||||
| 						reusableCF.altName = string(v.misc) |  | ||||||
| 					} |  | ||||||
| 					v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct) |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 			case reflect.Map: |  | ||||||
| 
 |  | ||||||
| 				var pv string |  | ||||||
| 				reusableCF := &cField{} |  | ||||||
| 
 |  | ||||||
| 				for _, key := range current.MapKeys() { |  | ||||||
| 
 |  | ||||||
| 					pv = fmt.Sprintf("%v", key.Interface()) |  | ||||||
| 
 |  | ||||||
| 					v.misc = append(v.misc[0:0], cf.name...) |  | ||||||
| 					v.misc = append(v.misc, '[') |  | ||||||
| 					v.misc = append(v.misc, pv...) |  | ||||||
| 					v.misc = append(v.misc, ']') |  | ||||||
| 
 |  | ||||||
| 					reusableCF.name = string(v.misc) |  | ||||||
| 
 |  | ||||||
| 					if cf.namesEqual { |  | ||||||
| 						reusableCF.altName = reusableCF.name |  | ||||||
| 					} else { |  | ||||||
| 						v.misc = append(v.misc[0:0], cf.altName...) |  | ||||||
| 						v.misc = append(v.misc, '[') |  | ||||||
| 						v.misc = append(v.misc, pv...) |  | ||||||
| 						v.misc = append(v.misc, ']') |  | ||||||
| 
 |  | ||||||
| 						reusableCF.altName = string(v.misc) |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					if ct != nil && ct.typeof == typeKeys && ct.keys != nil { |  | ||||||
| 						v.traverseField(ctx, parent, key, ns, structNs, reusableCF, ct.keys) |  | ||||||
| 						// can be nil when just keys being validated
 |  | ||||||
| 						if ct.next != nil { |  | ||||||
| 							v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct.next) |  | ||||||
| 						} |  | ||||||
| 					} else { |  | ||||||
| 						v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct) |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 			default: |  | ||||||
| 				// throw error, if not a slice or map then should not have gotten here
 |  | ||||||
| 				// bad dive tag
 |  | ||||||
| 				panic("dive error! can't dive on a non slice or map") |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			return |  | ||||||
| 
 |  | ||||||
| 		case typeOr: |  | ||||||
| 
 |  | ||||||
| 			v.misc = v.misc[0:0] |  | ||||||
| 
 |  | ||||||
| 			for { |  | ||||||
| 
 |  | ||||||
| 				// set Field Level fields
 |  | ||||||
| 				v.slflParent = parent |  | ||||||
| 				v.flField = current |  | ||||||
| 				v.cf = cf |  | ||||||
| 				v.ct = ct |  | ||||||
| 
 |  | ||||||
| 				if ct.fn(ctx, v) { |  | ||||||
| 
 |  | ||||||
| 					// drain rest of the 'or' values, then continue or leave
 |  | ||||||
| 					for { |  | ||||||
| 
 |  | ||||||
| 						ct = ct.next |  | ||||||
| 
 |  | ||||||
| 						if ct == nil { |  | ||||||
| 							return |  | ||||||
| 						} |  | ||||||
| 
 |  | ||||||
| 						if ct.typeof != typeOr { |  | ||||||
| 							continue OUTER |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				v.misc = append(v.misc, '|') |  | ||||||
| 				v.misc = append(v.misc, ct.tag...) |  | ||||||
| 
 |  | ||||||
| 				if ct.hasParam { |  | ||||||
| 					v.misc = append(v.misc, '=') |  | ||||||
| 					v.misc = append(v.misc, ct.param...) |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				if ct.isBlockEnd || ct.next == nil { |  | ||||||
| 					// if we get here, no valid 'or' value and no more tags
 |  | ||||||
| 					v.str1 = string(append(ns, cf.altName...)) |  | ||||||
| 
 |  | ||||||
| 					if v.v.hasTagNameFunc { |  | ||||||
| 						v.str2 = string(append(structNs, cf.name...)) |  | ||||||
| 					} else { |  | ||||||
| 						v.str2 = v.str1 |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					if ct.hasAlias { |  | ||||||
| 
 |  | ||||||
| 						v.errs = append(v.errs, |  | ||||||
| 							&fieldError{ |  | ||||||
| 								v:              v.v, |  | ||||||
| 								tag:            ct.aliasTag, |  | ||||||
| 								actualTag:      ct.actualAliasTag, |  | ||||||
| 								ns:             v.str1, |  | ||||||
| 								structNs:       v.str2, |  | ||||||
| 								fieldLen:       uint8(len(cf.altName)), |  | ||||||
| 								structfieldLen: uint8(len(cf.name)), |  | ||||||
| 								value:          current.Interface(), |  | ||||||
| 								param:          ct.param, |  | ||||||
| 								kind:           kind, |  | ||||||
| 								typ:            typ, |  | ||||||
| 							}, |  | ||||||
| 						) |  | ||||||
| 
 |  | ||||||
| 					} else { |  | ||||||
| 
 |  | ||||||
| 						tVal := string(v.misc)[1:] |  | ||||||
| 
 |  | ||||||
| 						v.errs = append(v.errs, |  | ||||||
| 							&fieldError{ |  | ||||||
| 								v:              v.v, |  | ||||||
| 								tag:            tVal, |  | ||||||
| 								actualTag:      tVal, |  | ||||||
| 								ns:             v.str1, |  | ||||||
| 								structNs:       v.str2, |  | ||||||
| 								fieldLen:       uint8(len(cf.altName)), |  | ||||||
| 								structfieldLen: uint8(len(cf.name)), |  | ||||||
| 								value:          current.Interface(), |  | ||||||
| 								param:          ct.param, |  | ||||||
| 								kind:           kind, |  | ||||||
| 								typ:            typ, |  | ||||||
| 							}, |  | ||||||
| 						) |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					return |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				ct = ct.next |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 		default: |  | ||||||
| 
 |  | ||||||
| 			// set Field Level fields
 |  | ||||||
| 			v.slflParent = parent |  | ||||||
| 			v.flField = current |  | ||||||
| 			v.cf = cf |  | ||||||
| 			v.ct = ct |  | ||||||
| 
 |  | ||||||
| 			if !ct.fn(ctx, v) { // 这是 验证函数的进行校验, 不成功就会记录下来
 |  | ||||||
| 
 |  | ||||||
| 				v.str1 = string(append(ns, cf.altName...)) //如果不想要 命名空间,就不要拼接 ns
 |  | ||||||
| 
 |  | ||||||
| 				if v.v.hasTagNameFunc { |  | ||||||
| 					v.str2 = string(append(structNs, cf.name...)) |  | ||||||
| 				} else { |  | ||||||
| 					v.str2 = v.str1 |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				v.errs = append(v.errs, |  | ||||||
| 					&fieldError{ |  | ||||||
| 						v:              v.v, |  | ||||||
| 						tag:            ct.aliasTag, |  | ||||||
| 						actualTag:      ct.tag, |  | ||||||
| 						ns:             v.str1, |  | ||||||
| 						structNs:       v.str2, |  | ||||||
| 						fieldLen:       uint8(len(cf.altName)), |  | ||||||
| 						structfieldLen: uint8(len(cf.name)), |  | ||||||
| 						value:          current.Interface(), |  | ||||||
| 						param:          ct.param, |  | ||||||
| 						kind:           kind, |  | ||||||
| 						typ:            typ, |  | ||||||
| 					}, |  | ||||||
| 				) |  | ||||||
| 
 |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			ct = ct.next |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,619 +0,0 @@ | ||||||
| package validator |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"reflect" |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	ut "git.ningdatech.com/ningda/gin_valid/go-playground/universal-translator" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| const ( |  | ||||||
| 	defaultTagName        = "validate" |  | ||||||
| 	utf8HexComma          = "0x2C" |  | ||||||
| 	utf8Pipe              = "0x7C" |  | ||||||
| 	tagSeparator          = "," // tag参数 的分离符号,
 |  | ||||||
| 	orSeparator           = "|" // 或者 的分离符号
 |  | ||||||
| 	tagKeySeparator       = "=" |  | ||||||
| 	structOnlyTag         = "structonly" |  | ||||||
| 	noStructLevelTag      = "nostructlevel" |  | ||||||
| 	omitempty             = "omitempty" |  | ||||||
| 	isdefault             = "isdefault" |  | ||||||
| 	requiredWithoutAllTag = "required_without_all" |  | ||||||
| 	requiredWithoutTag    = "required_without" |  | ||||||
| 	requiredWithTag       = "required_with" |  | ||||||
| 	requiredWithAllTag    = "required_with_all" |  | ||||||
| 	requiredIfTag         = "required_if" |  | ||||||
| 	requiredUnlessTag     = "required_unless" |  | ||||||
| 	skipValidationTag     = "-" |  | ||||||
| 	diveTag               = "dive" |  | ||||||
| 	keysTag               = "keys" |  | ||||||
| 	endKeysTag            = "endkeys" |  | ||||||
| 	requiredTag           = "required" |  | ||||||
| 	namespaceSeparator    = "." |  | ||||||
| 	leftBracket           = "[" |  | ||||||
| 	rightBracket          = "]" |  | ||||||
| 	restrictedTagChars    = ".[],|=+()`~!@#$%^&*\\\"/?<>{}" |  | ||||||
| 	restrictedAliasErr    = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" |  | ||||||
| 	restrictedTagErr      = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var ( |  | ||||||
| 	timeDurationType = reflect.TypeOf(time.Duration(0)) |  | ||||||
| 	timeType         = reflect.TypeOf(time.Time{}) |  | ||||||
| 
 |  | ||||||
| 	defaultCField = &cField{namesEqual: true} |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // FilterFunc is the type used to filter fields using
 |  | ||||||
| // StructFiltered(...) function.
 |  | ||||||
| // returning true results in the field being filtered/skiped from
 |  | ||||||
| // validation
 |  | ||||||
| type FilterFunc func(ns []byte) bool |  | ||||||
| 
 |  | ||||||
| // CustomTypeFunc allows for overriding or adding custom field type handler functions
 |  | ||||||
| // field = field value of the type to return a value to be validated
 |  | ||||||
| // example Valuer from sql drive see https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29
 |  | ||||||
| type CustomTypeFunc func(field reflect.Value) interface{} |  | ||||||
| 
 |  | ||||||
| // TagNameFunc allows for adding of a custom tag name parser
 |  | ||||||
| type TagNameFunc func(field reflect.StructField) string |  | ||||||
| 
 |  | ||||||
| type internalValidationFuncWrapper struct { |  | ||||||
| 	fn                FuncCtx |  | ||||||
| 	runValidatinOnNil bool |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Validate contains the validator settings and cache
 |  | ||||||
| type Validate struct { |  | ||||||
| 	tagName          string |  | ||||||
| 	pool             *sync.Pool |  | ||||||
| 	hasCustomFuncs   bool |  | ||||||
| 	hasTagNameFunc   bool |  | ||||||
| 	tagNameFunc      TagNameFunc |  | ||||||
| 	structLevelFuncs map[reflect.Type]StructLevelFuncCtx |  | ||||||
| 	customFuncs      map[reflect.Type]CustomTypeFunc |  | ||||||
| 	aliases          map[string]string |  | ||||||
| 	validations      map[string]internalValidationFuncWrapper     // 每个 tag如 gte 都会对应一个func处理, 如果这个tag没有函数处理会报错
 |  | ||||||
| 	transTagFunc     map[ut.Translator]map[string]TranslationFunc // map[<locale>]map[<tag>]TranslationFunc
 |  | ||||||
| 	tagCache         *tagCache |  | ||||||
| 	structCache      *structCache |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // New returns a new instance of 'validate' with sane defaults.
 |  | ||||||
| func New() *Validate { |  | ||||||
| 
 |  | ||||||
| 	tc := new(tagCache) |  | ||||||
| 	tc.m.Store(make(map[string]*cTag)) |  | ||||||
| 
 |  | ||||||
| 	sc := new(structCache) |  | ||||||
| 	sc.m.Store(make(map[reflect.Type]*cStruct)) |  | ||||||
| 
 |  | ||||||
| 	v := &Validate{ |  | ||||||
| 		tagName:     defaultTagName, |  | ||||||
| 		aliases:     make(map[string]string, len(bakedInAliases)), |  | ||||||
| 		validations: make(map[string]internalValidationFuncWrapper, len(bakedInValidators)), |  | ||||||
| 		tagCache:    tc, |  | ||||||
| 		structCache: sc, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// must copy alias validators for separate validations to be used in each validator instance
 |  | ||||||
| 	for k, val := range bakedInAliases { |  | ||||||
| 		v.RegisterAlias(k, val) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// must copy validators for separate validations to be used in each instance
 |  | ||||||
| 	for k, val := range bakedInValidators { |  | ||||||
| 
 |  | ||||||
| 		switch k { |  | ||||||
| 		// these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour
 |  | ||||||
| 		case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag: |  | ||||||
| 			_ = v.registerValidation(k, wrapFunc(val), true, true) |  | ||||||
| 		default: |  | ||||||
| 			// no need to error check here, baked in will always be valid
 |  | ||||||
| 			_ = v.registerValidation(k, wrapFunc(val), true, false) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	v.pool = &sync.Pool{ |  | ||||||
| 		New: func() interface{} { |  | ||||||
| 			return &validate{ |  | ||||||
| 				v:        v, |  | ||||||
| 				ns:       make([]byte, 0, 64), |  | ||||||
| 				actualNs: make([]byte, 0, 64), |  | ||||||
| 				misc:     make([]byte, 32), |  | ||||||
| 			} |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return v |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // SetTagName allows for changing of the default tag name of 'validate'
 |  | ||||||
| func (v *Validate) SetTagName(name string) { |  | ||||||
| 	v.tagName = name |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RegisterTagNameFunc registers a function to get alternate names for StructFields.
 |  | ||||||
| //
 |  | ||||||
| // eg. to use the names which have been specified for JSON representations of structs, rather than normal Go field names:
 |  | ||||||
| //
 |  | ||||||
| //    validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
 |  | ||||||
| //        name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
 |  | ||||||
| //        if name == "-" {
 |  | ||||||
| //            return ""
 |  | ||||||
| //        }
 |  | ||||||
| //        return name
 |  | ||||||
| //    })
 |  | ||||||
| func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) { |  | ||||||
| 	v.tagNameFunc = fn |  | ||||||
| 	v.hasTagNameFunc = true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RegisterValidation adds a validation with the given tag
 |  | ||||||
| //
 |  | ||||||
| // NOTES:
 |  | ||||||
| // - if the key already exists, the previous validation function will be replaced.
 |  | ||||||
| // - this method is not thread-safe it is intended that these all be registered prior to any validation
 |  | ||||||
| func (v *Validate) RegisterValidation(tag string, fn Func, callValidationEvenIfNull ...bool) error { |  | ||||||
| 	return v.RegisterValidationCtx(tag, wrapFunc(fn), callValidationEvenIfNull...) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RegisterValidationCtx does the same as RegisterValidation on accepts a FuncCtx validation
 |  | ||||||
| // allowing context.Context validation support.
 |  | ||||||
| func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx, callValidationEvenIfNull ...bool) error { |  | ||||||
| 	var nilCheckable bool |  | ||||||
| 	if len(callValidationEvenIfNull) > 0 { |  | ||||||
| 		nilCheckable = callValidationEvenIfNull[0] |  | ||||||
| 	} |  | ||||||
| 	return v.registerValidation(tag, fn, false, nilCheckable) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error { |  | ||||||
| 	if len(tag) == 0 { |  | ||||||
| 		return errors.New("Function Key cannot be empty") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if fn == nil { |  | ||||||
| 		return errors.New("Function cannot be empty") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	_, ok := restrictedTags[tag] |  | ||||||
| 	if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) { |  | ||||||
| 		panic(fmt.Sprintf(restrictedTagErr, tag)) |  | ||||||
| 	} |  | ||||||
| 	v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidatinOnNil: nilCheckable} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RegisterAlias registers a mapping of a single validation tag that
 |  | ||||||
| // defines a common or complex set of validation(s) to simplify adding validation
 |  | ||||||
| // to structs.
 |  | ||||||
| //
 |  | ||||||
| // NOTE: this function is not thread-safe it is intended that these all be registered prior to any validation
 |  | ||||||
| func (v *Validate) RegisterAlias(alias, tags string) { |  | ||||||
| 
 |  | ||||||
| 	_, ok := restrictedTags[alias] |  | ||||||
| 
 |  | ||||||
| 	if ok || strings.ContainsAny(alias, restrictedTagChars) { |  | ||||||
| 		panic(fmt.Sprintf(restrictedAliasErr, alias)) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	v.aliases[alias] = tags |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RegisterStructValidation registers a StructLevelFunc against a number of types.
 |  | ||||||
| //
 |  | ||||||
| // NOTE:
 |  | ||||||
| // - this method is not thread-safe it is intended that these all be registered prior to any validation
 |  | ||||||
| func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interface{}) { |  | ||||||
| 	v.RegisterStructValidationCtx(wrapStructLevelFunc(fn), types...) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RegisterStructValidationCtx registers a StructLevelFuncCtx against a number of types and allows passing
 |  | ||||||
| // of contextual validation information via context.Context.
 |  | ||||||
| //
 |  | ||||||
| // NOTE:
 |  | ||||||
| // - this method is not thread-safe it is intended that these all be registered prior to any validation
 |  | ||||||
| func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...interface{}) { |  | ||||||
| 
 |  | ||||||
| 	if v.structLevelFuncs == nil { |  | ||||||
| 		v.structLevelFuncs = make(map[reflect.Type]StructLevelFuncCtx) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for _, t := range types { |  | ||||||
| 		tv := reflect.ValueOf(t) |  | ||||||
| 		if tv.Kind() == reflect.Ptr { |  | ||||||
| 			t = reflect.Indirect(tv).Interface() |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		v.structLevelFuncs[reflect.TypeOf(t)] = fn |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
 |  | ||||||
| //
 |  | ||||||
| // NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
 |  | ||||||
| func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) { |  | ||||||
| 
 |  | ||||||
| 	if v.customFuncs == nil { |  | ||||||
| 		v.customFuncs = make(map[reflect.Type]CustomTypeFunc) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for _, t := range types { |  | ||||||
| 		v.customFuncs[reflect.TypeOf(t)] = fn |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	v.hasCustomFuncs = true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RegisterTranslation registers translations against the provided tag.
 |  | ||||||
| func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) { |  | ||||||
| 
 |  | ||||||
| 	if v.transTagFunc == nil { |  | ||||||
| 		v.transTagFunc = make(map[ut.Translator]map[string]TranslationFunc) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if err = registerFn(trans); err != nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	m, ok := v.transTagFunc[trans] |  | ||||||
| 	if !ok { |  | ||||||
| 		m = make(map[string]TranslationFunc) |  | ||||||
| 		v.transTagFunc[trans] = m |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	m[tag] = translationFn |  | ||||||
| 
 |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Struct validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified.
 |  | ||||||
| //
 |  | ||||||
| // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
 |  | ||||||
| // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
 |  | ||||||
| func (v *Validate) Struct(s interface{}) error { |  | ||||||
| 	return v.StructCtx(context.Background(), s) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // StructCtx validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified
 |  | ||||||
| // and also allows passing of context.Context for contextual validation information.
 |  | ||||||
| //
 |  | ||||||
| // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
 |  | ||||||
| // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
 |  | ||||||
| func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) { |  | ||||||
| 
 |  | ||||||
| 	val := reflect.ValueOf(s) |  | ||||||
| 	top := val |  | ||||||
| 
 |  | ||||||
| 	if val.Kind() == reflect.Ptr && !val.IsNil() { |  | ||||||
| 		val = val.Elem() |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if val.Kind() != reflect.Struct || val.Type() == timeType { |  | ||||||
| 		return &InvalidValidationError{Type: reflect.TypeOf(s)} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// good to validate
 |  | ||||||
| 	vd := v.pool.Get().(*validate) |  | ||||||
| 	vd.top = top |  | ||||||
| 	vd.isPartial = false |  | ||||||
| 	// vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
 |  | ||||||
| 
 |  | ||||||
| 	vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil) |  | ||||||
| 
 |  | ||||||
| 	if len(vd.errs) > 0 { |  | ||||||
| 		err = vd.errs |  | ||||||
| 		vd.errs = nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	v.pool.Put(vd) |  | ||||||
| 
 |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // StructFiltered validates a structs exposed fields, that pass the FilterFunc check and automatically validates
 |  | ||||||
| // nested structs, unless otherwise specified.
 |  | ||||||
| //
 |  | ||||||
| // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
 |  | ||||||
| // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
 |  | ||||||
| func (v *Validate) StructFiltered(s interface{}, fn FilterFunc) error { |  | ||||||
| 	return v.StructFilteredCtx(context.Background(), s, fn) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // StructFilteredCtx validates a structs exposed fields, that pass the FilterFunc check and automatically validates
 |  | ||||||
| // nested structs, unless otherwise specified and also allows passing of contextual validation information via
 |  | ||||||
| // context.Context
 |  | ||||||
| //
 |  | ||||||
| // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
 |  | ||||||
| // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
 |  | ||||||
| func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn FilterFunc) (err error) { |  | ||||||
| 	val := reflect.ValueOf(s) |  | ||||||
| 	top := val |  | ||||||
| 
 |  | ||||||
| 	if val.Kind() == reflect.Ptr && !val.IsNil() { |  | ||||||
| 		val = val.Elem() |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if val.Kind() != reflect.Struct || val.Type() == timeType { |  | ||||||
| 		return &InvalidValidationError{Type: reflect.TypeOf(s)} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// good to validate
 |  | ||||||
| 	vd := v.pool.Get().(*validate) |  | ||||||
| 	vd.top = top |  | ||||||
| 	vd.isPartial = true |  | ||||||
| 	vd.ffn = fn |  | ||||||
| 	// vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
 |  | ||||||
| 
 |  | ||||||
| 	vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil) |  | ||||||
| 
 |  | ||||||
| 	if len(vd.errs) > 0 { |  | ||||||
| 		err = vd.errs |  | ||||||
| 		vd.errs = nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	v.pool.Put(vd) |  | ||||||
| 
 |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // StructPartial validates the fields passed in only, ignoring all others.
 |  | ||||||
| // Fields may be provided in a namespaced fashion relative to the  struct provided
 |  | ||||||
| // eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
 |  | ||||||
| //
 |  | ||||||
| // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
 |  | ||||||
| // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
 |  | ||||||
| func (v *Validate) StructPartial(s interface{}, fields ...string) error { |  | ||||||
| 	return v.StructPartialCtx(context.Background(), s, fields...) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // StructPartialCtx validates the fields passed in only, ignoring all others and allows passing of contextual
 |  | ||||||
| // validation validation information via context.Context
 |  | ||||||
| // Fields may be provided in a namespaced fashion relative to the  struct provided
 |  | ||||||
| // eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
 |  | ||||||
| //
 |  | ||||||
| // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
 |  | ||||||
| // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
 |  | ||||||
| func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields ...string) (err error) { |  | ||||||
| 	val := reflect.ValueOf(s) |  | ||||||
| 	top := val |  | ||||||
| 
 |  | ||||||
| 	if val.Kind() == reflect.Ptr && !val.IsNil() { |  | ||||||
| 		val = val.Elem() |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if val.Kind() != reflect.Struct || val.Type() == timeType { |  | ||||||
| 		return &InvalidValidationError{Type: reflect.TypeOf(s)} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// good to validate
 |  | ||||||
| 	vd := v.pool.Get().(*validate) |  | ||||||
| 	vd.top = top |  | ||||||
| 	vd.isPartial = true |  | ||||||
| 	vd.ffn = nil |  | ||||||
| 	vd.hasExcludes = false |  | ||||||
| 	vd.includeExclude = make(map[string]struct{}) |  | ||||||
| 
 |  | ||||||
| 	typ := val.Type() |  | ||||||
| 	name := typ.Name() |  | ||||||
| 
 |  | ||||||
| 	for _, k := range fields { |  | ||||||
| 
 |  | ||||||
| 		flds := strings.Split(k, namespaceSeparator) |  | ||||||
| 		if len(flds) > 0 { |  | ||||||
| 
 |  | ||||||
| 			vd.misc = append(vd.misc[0:0], name...) |  | ||||||
| 			vd.misc = append(vd.misc, '.') |  | ||||||
| 
 |  | ||||||
| 			for _, s := range flds { |  | ||||||
| 
 |  | ||||||
| 				idx := strings.Index(s, leftBracket) |  | ||||||
| 
 |  | ||||||
| 				if idx != -1 { |  | ||||||
| 					for idx != -1 { |  | ||||||
| 						vd.misc = append(vd.misc, s[:idx]...) |  | ||||||
| 						vd.includeExclude[string(vd.misc)] = struct{}{} |  | ||||||
| 
 |  | ||||||
| 						idx2 := strings.Index(s, rightBracket) |  | ||||||
| 						idx2++ |  | ||||||
| 						vd.misc = append(vd.misc, s[idx:idx2]...) |  | ||||||
| 						vd.includeExclude[string(vd.misc)] = struct{}{} |  | ||||||
| 						s = s[idx2:] |  | ||||||
| 						idx = strings.Index(s, leftBracket) |  | ||||||
| 					} |  | ||||||
| 				} else { |  | ||||||
| 
 |  | ||||||
| 					vd.misc = append(vd.misc, s...) |  | ||||||
| 					vd.includeExclude[string(vd.misc)] = struct{}{} |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				vd.misc = append(vd.misc, '.') |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil) |  | ||||||
| 
 |  | ||||||
| 	if len(vd.errs) > 0 { |  | ||||||
| 		err = vd.errs |  | ||||||
| 		vd.errs = nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	v.pool.Put(vd) |  | ||||||
| 
 |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // StructExcept validates all fields except the ones passed in.
 |  | ||||||
| // Fields may be provided in a namespaced fashion relative to the  struct provided
 |  | ||||||
| // i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
 |  | ||||||
| //
 |  | ||||||
| // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
 |  | ||||||
| // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
 |  | ||||||
| func (v *Validate) StructExcept(s interface{}, fields ...string) error { |  | ||||||
| 	return v.StructExceptCtx(context.Background(), s, fields...) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // StructExceptCtx validates all fields except the ones passed in and allows passing of contextual
 |  | ||||||
| // validation validation information via context.Context
 |  | ||||||
| // Fields may be provided in a namespaced fashion relative to the  struct provided
 |  | ||||||
| // i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
 |  | ||||||
| //
 |  | ||||||
| // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
 |  | ||||||
| // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
 |  | ||||||
| func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields ...string) (err error) { |  | ||||||
| 	val := reflect.ValueOf(s) |  | ||||||
| 	top := val |  | ||||||
| 
 |  | ||||||
| 	if val.Kind() == reflect.Ptr && !val.IsNil() { |  | ||||||
| 		val = val.Elem() |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if val.Kind() != reflect.Struct || val.Type() == timeType { |  | ||||||
| 		return &InvalidValidationError{Type: reflect.TypeOf(s)} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// good to validate
 |  | ||||||
| 	vd := v.pool.Get().(*validate) |  | ||||||
| 	vd.top = top |  | ||||||
| 	vd.isPartial = true |  | ||||||
| 	vd.ffn = nil |  | ||||||
| 	vd.hasExcludes = true |  | ||||||
| 	vd.includeExclude = make(map[string]struct{}) |  | ||||||
| 
 |  | ||||||
| 	typ := val.Type() |  | ||||||
| 	name := typ.Name() |  | ||||||
| 
 |  | ||||||
| 	for _, key := range fields { |  | ||||||
| 
 |  | ||||||
| 		vd.misc = vd.misc[0:0] |  | ||||||
| 
 |  | ||||||
| 		if len(name) > 0 { |  | ||||||
| 			vd.misc = append(vd.misc, name...) |  | ||||||
| 			vd.misc = append(vd.misc, '.') |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		vd.misc = append(vd.misc, key...) |  | ||||||
| 		vd.includeExclude[string(vd.misc)] = struct{}{} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil) |  | ||||||
| 
 |  | ||||||
| 	if len(vd.errs) > 0 { |  | ||||||
| 		err = vd.errs |  | ||||||
| 		vd.errs = nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	v.pool.Put(vd) |  | ||||||
| 
 |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Var validates a single variable using tag style validation.
 |  | ||||||
| // eg.
 |  | ||||||
| // var i int
 |  | ||||||
| // validate.Var(i, "gt=1,lt=10")
 |  | ||||||
| //
 |  | ||||||
| // WARNING: a struct can be passed for validation eg. time.Time is a struct or
 |  | ||||||
| // if you have a custom type and have registered a custom type handler, so must
 |  | ||||||
| // allow it; however unforeseen validations will occur if trying to validate a
 |  | ||||||
| // struct that is meant to be passed to 'validate.Struct'
 |  | ||||||
| //
 |  | ||||||
| // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
 |  | ||||||
| // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
 |  | ||||||
| // validate Array, Slice and maps fields which may contain more than one error
 |  | ||||||
| func (v *Validate) Var(field interface{}, tag string) error { |  | ||||||
| 	return v.VarCtx(context.Background(), field, tag) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // VarCtx validates a single variable using tag style validation and allows passing of contextual
 |  | ||||||
| // validation validation information via context.Context.
 |  | ||||||
| // eg.
 |  | ||||||
| // var i int
 |  | ||||||
| // validate.Var(i, "gt=1,lt=10")
 |  | ||||||
| //
 |  | ||||||
| // WARNING: a struct can be passed for validation eg. time.Time is a struct or
 |  | ||||||
| // if you have a custom type and have registered a custom type handler, so must
 |  | ||||||
| // allow it; however unforeseen validations will occur if trying to validate a
 |  | ||||||
| // struct that is meant to be passed to 'validate.Struct'
 |  | ||||||
| //
 |  | ||||||
| // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
 |  | ||||||
| // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
 |  | ||||||
| // validate Array, Slice and maps fields which may contain more than one error
 |  | ||||||
| func (v *Validate) VarCtx(ctx context.Context, field interface{}, tag string) (err error) { |  | ||||||
| 	if len(tag) == 0 || tag == skipValidationTag { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	ctag := v.fetchCacheTag(tag) |  | ||||||
| 	val := reflect.ValueOf(field) |  | ||||||
| 	vd := v.pool.Get().(*validate) |  | ||||||
| 	vd.top = val |  | ||||||
| 	vd.isPartial = false |  | ||||||
| 	vd.traverseField(ctx, val, val, vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag) |  | ||||||
| 
 |  | ||||||
| 	if len(vd.errs) > 0 { |  | ||||||
| 		err = vd.errs |  | ||||||
| 		vd.errs = nil |  | ||||||
| 	} |  | ||||||
| 	v.pool.Put(vd) |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // VarWithValue validates a single variable, against another variable/field's value using tag style validation
 |  | ||||||
| // eg.
 |  | ||||||
| // s1 := "abcd"
 |  | ||||||
| // s2 := "abcd"
 |  | ||||||
| // validate.VarWithValue(s1, s2, "eqcsfield") // returns true
 |  | ||||||
| //
 |  | ||||||
| // WARNING: a struct can be passed for validation eg. time.Time is a struct or
 |  | ||||||
| // if you have a custom type and have registered a custom type handler, so must
 |  | ||||||
| // allow it; however unforeseen validations will occur if trying to validate a
 |  | ||||||
| // struct that is meant to be passed to 'validate.Struct'
 |  | ||||||
| //
 |  | ||||||
| // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
 |  | ||||||
| // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
 |  | ||||||
| // validate Array, Slice and maps fields which may contain more than one error
 |  | ||||||
| func (v *Validate) VarWithValue(field interface{}, other interface{}, tag string) error { |  | ||||||
| 	return v.VarWithValueCtx(context.Background(), field, other, tag) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // VarWithValueCtx validates a single variable, against another variable/field's value using tag style validation and
 |  | ||||||
| // allows passing of contextual validation validation information via context.Context.
 |  | ||||||
| // eg.
 |  | ||||||
| // s1 := "abcd"
 |  | ||||||
| // s2 := "abcd"
 |  | ||||||
| // validate.VarWithValue(s1, s2, "eqcsfield") // returns true
 |  | ||||||
| //
 |  | ||||||
| // WARNING: a struct can be passed for validation eg. time.Time is a struct or
 |  | ||||||
| // if you have a custom type and have registered a custom type handler, so must
 |  | ||||||
| // allow it; however unforeseen validations will occur if trying to validate a
 |  | ||||||
| // struct that is meant to be passed to 'validate.Struct'
 |  | ||||||
| //
 |  | ||||||
| // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
 |  | ||||||
| // You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
 |  | ||||||
| // validate Array, Slice and maps fields which may contain more than one error
 |  | ||||||
| func (v *Validate) VarWithValueCtx(ctx context.Context, field interface{}, other interface{}, tag string) (err error) { |  | ||||||
| 	if len(tag) == 0 || tag == skipValidationTag { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	ctag := v.fetchCacheTag(tag) |  | ||||||
| 	otherVal := reflect.ValueOf(other) |  | ||||||
| 	vd := v.pool.Get().(*validate) |  | ||||||
| 	vd.top = otherVal |  | ||||||
| 	vd.isPartial = false |  | ||||||
| 	vd.traverseField(ctx, otherVal, reflect.ValueOf(field), vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag) |  | ||||||
| 
 |  | ||||||
| 	if len(vd.errs) > 0 { |  | ||||||
| 		err = vd.errs |  | ||||||
| 		vd.errs = nil |  | ||||||
| 	} |  | ||||||
| 	v.pool.Put(vd) |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
							
								
								
									
										13
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										13
									
								
								go.mod
								
								
								
								
							|  | @ -1,13 +0,0 @@ | ||||||
| module git.ningdatech.com/ningda/gin_valid |  | ||||||
| 
 |  | ||||||
| go 1.13 |  | ||||||
| 
 |  | ||||||
| require ( |  | ||||||
| 	github.com/golang/protobuf v1.4.3 |  | ||||||
| 	github.com/json-iterator/go v1.1.10 // indirect |  | ||||||
| 	github.com/leodido/go-urn v1.2.0 |  | ||||||
| 	github.com/ugorji/go v1.2.0 // indirect |  | ||||||
| 	github.com/ugorji/go/codec v1.2.0 |  | ||||||
| 	golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 |  | ||||||
| 	gopkg.in/yaml.v2 v2.4.0 |  | ||||||
| ) |  | ||||||
		Loading…
	
		Reference in New Issue