删掉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">![Project status](https://img.shields.io/badge/version-0.13.0-green.svg)
|
|
||||||
[![Build Status](https://travis-ci.org/go-playground/locales.svg?branch=master)](https://travis-ci.org/go-playground/locales)
|
|
||||||
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/locales)](https://goreportcard.com/report/github.com/go-playground/locales)
|
|
||||||
[![GoDoc](https://godoc.org/github.com/go-playground/locales?status.svg)](https://godoc.org/github.com/go-playground/locales)
|
|
||||||
![License](https://img.shields.io/dub/l/vibe-d.svg)
|
|
||||||
[![Gitter](https://badges.gitter.im/go-playground/locales.svg)](https://gitter.im/go-playground/locales?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
|
||||||
|
|
||||||
Locales is a set of locales generated from the [Unicode CLDR Project](http://cldr.unicode.org/) which can be used independently or within
|
|
||||||
an i18n package; these were built for use with, but not exclusive to, [Universal Translator](https://github.com/go-playground/universal-translator).
|
|
||||||
|
|
||||||
Features
|
|
||||||
--------
|
|
||||||
- [x] Rules generated from the latest [CLDR](http://cldr.unicode.org/index/downloads) data, v31.0.1
|
|
||||||
- [x] Contains Cardinal, Ordinal and Range Plural Rules
|
|
||||||
- [x] Contains Month, Weekday and Timezone translations built in
|
|
||||||
- [x] Contains Date & Time formatting functions
|
|
||||||
- [x] Contains Number, Currency, Accounting and Percent formatting functions
|
|
||||||
- [x] Supports the "Gregorian" calendar only ( my time isn't unlimited, had to draw the line somewhere )
|
|
||||||
|
|
||||||
Full Tests
|
|
||||||
--------------------
|
|
||||||
I could sure use your help adding tests for every locale, it is a huge undertaking and I just don't have the free time to do it all at the moment;
|
|
||||||
any help would be **greatly appreciated!!!!** please see [issue](https://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">![Project status](https://img.shields.io/badge/version-0.17.0-green.svg)
|
|
||||||
[![Build Status](https://travis-ci.org/go-playground/universal-translator.svg?branch=master)](https://travis-ci.org/go-playground/universal-translator)
|
|
||||||
[![Coverage Status](https://coveralls.io/repos/github/go-playground/universal-translator/badge.svg)](https://coveralls.io/github/go-playground/universal-translator)
|
|
||||||
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/universal-translator)](https://goreportcard.com/report/github.com/go-playground/universal-translator)
|
|
||||||
[![GoDoc](https://godoc.org/github.com/go-playground/universal-translator?status.svg)](https://godoc.org/github.com/go-playground/universal-translator)
|
|
||||||
![License](https://img.shields.io/dub/l/vibe-d.svg)
|
|
||||||
[![Gitter](https://badges.gitter.im/go-playground/universal-translator.svg)](https://gitter.im/go-playground/universal-translator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
|
||||||
|
|
||||||
Universal Translator is an i18n Translator for Go/Golang using CLDR data + pluralization rules
|
|
||||||
|
|
||||||
Why another i18n library?
|
|
||||||
--------------------------
|
|
||||||
Because none of the plural rules seem to be correct out there, including the previous implementation of this package,
|
|
||||||
so I took it upon myself to create [locales](https://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 [![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) for any issues, questions or possible enhancements to the package.
|
|
|
@ -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">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
![Project status](https://img.shields.io/badge/version-10.4.1-green.svg)
|
|
||||||
[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
|
|
||||||
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
|
|
||||||
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
|
|
||||||
[![GoDoc](https://godoc.org/github.com/go-playground/validator?status.svg)](https://pkg.go.dev/github.com/go-playground/validator/v10)
|
|
||||||
![License](https://img.shields.io/dub/l/vibe-d.svg)
|
|
||||||
|
|
||||||
Package validator implements value validations for structs and individual fields based on tags.
|
|
||||||
|
|
||||||
It has the following **unique** features:
|
|
||||||
|
|
||||||
- Cross Field and Cross Struct validations by using validation tags or custom validators.
|
|
||||||
- Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated.
|
|
||||||
- Ability to dive into both map keys and values for validation
|
|
||||||
- Handles type interface by determining it's underlying type prior to validation.
|
|
||||||
- Handles custom field types such as sql driver Valuer see [Valuer](https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29)
|
|
||||||
- Alias validation tags, which allows for mapping of several validations to a single tag for easier defining of validations on structs
|
|
||||||
- Extraction of custom defined Field Name e.g. can specify to extract the JSON name while validating and have it available in the resulting FieldError
|
|
||||||
- Customizable i18n aware error messages.
|
|
||||||
- Default validator for the [gin](https://github.com/gin-gonic/gin) web framework; upgrading from v8 to v9 in gin see [here](https://github.com/go-playground/validator/tree/master/_examples/gin-upgrading-overriding)
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
Use go get.
|
|
||||||
|
|
||||||
go get 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