diff --git a/.idea/Go-Tool.iml b/.idea/Go-Tool.iml index 0fb91b3..2bcd8a2 100644 --- a/.idea/Go-Tool.iml +++ b/.idea/Go-Tool.iml @@ -3,6 +3,7 @@ + diff --git a/test/jdkContainer/SourceAnalysis/heap.go b/SourceAnalysisAndTool/jdkContainer/sourceAnalysis/heap.go similarity index 99% rename from test/jdkContainer/SourceAnalysis/heap.go rename to SourceAnalysisAndTool/jdkContainer/sourceAnalysis/heap.go index 73a4b83..5783359 100644 --- a/test/jdkContainer/SourceAnalysis/heap.go +++ b/SourceAnalysisAndTool/jdkContainer/sourceAnalysis/heap.go @@ -15,7 +15,7 @@ // implementation; the file example_pq_test.go has the complete source. // //read note 拷贝一个代码过来解析.感觉在go里面拷贝代码简单多了,依赖可以直接使用。我在这边使用read note(自定义todo标识)来标识我的解析. -package SourceAnalysis +package sourceAnalysis import "sort" diff --git a/test/jdkContainer/SourceAnalysis/list.go b/SourceAnalysisAndTool/jdkContainer/sourceAnalysis/list.go similarity index 99% rename from test/jdkContainer/SourceAnalysis/list.go rename to SourceAnalysisAndTool/jdkContainer/sourceAnalysis/list.go index 0afdff1..8b65bf8 100644 --- a/test/jdkContainer/SourceAnalysis/list.go +++ b/SourceAnalysisAndTool/jdkContainer/sourceAnalysis/list.go @@ -9,7 +9,7 @@ // // do something with e.Value // } // -package SourceAnalysis +package sourceAnalysis // Element is an element of a linked list. //read note list中对应的元素,list相当是通过一个链表来链接所有的Element元素. diff --git a/test/jdkContainer/SourceAnalysis/ring.go b/SourceAnalysisAndTool/jdkContainer/sourceAnalysis/ring.go similarity index 99% rename from test/jdkContainer/SourceAnalysis/ring.go rename to SourceAnalysisAndTool/jdkContainer/sourceAnalysis/ring.go index 80b1036..063a361 100644 --- a/test/jdkContainer/SourceAnalysis/ring.go +++ b/SourceAnalysisAndTool/jdkContainer/sourceAnalysis/ring.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package ring implements operations on circular lists. -package SourceAnalysis +package sourceAnalysis // A Ring is an element of a circular list, or ring. // Rings do not have a beginning or end; a pointer to any ring element diff --git a/test/jdkContainer/testHeap/HeapTool.go b/SourceAnalysisAndTool/jdkContainer/testHeap/HeapTool.go similarity index 100% rename from test/jdkContainer/testHeap/HeapTool.go rename to SourceAnalysisAndTool/jdkContainer/testHeap/HeapTool.go diff --git a/test/jdkContainer/testHeap/heap_test.go b/SourceAnalysisAndTool/jdkContainer/testHeap/heap_test.go similarity index 100% rename from test/jdkContainer/testHeap/heap_test.go rename to SourceAnalysisAndTool/jdkContainer/testHeap/heap_test.go diff --git a/test/jdkContainer/testHeap/list_test.go b/SourceAnalysisAndTool/jdkContainer/testHeap/list_test.go similarity index 100% rename from test/jdkContainer/testHeap/list_test.go rename to SourceAnalysisAndTool/jdkContainer/testHeap/list_test.go diff --git a/test/jdkContainer/testHeap/ring_test.go b/SourceAnalysisAndTool/jdkContainer/testHeap/ring_test.go similarity index 100% rename from test/jdkContainer/testHeap/ring_test.go rename to SourceAnalysisAndTool/jdkContainer/testHeap/ring_test.go diff --git a/SourceAnalysisAndTool/validator.v8/2、结构体缓存获取.jpg b/SourceAnalysisAndTool/validator.v8/2、结构体缓存获取.jpg new file mode 100644 index 0000000..8eadd1a Binary files /dev/null and b/SourceAnalysisAndTool/validator.v8/2、结构体缓存获取.jpg differ diff --git a/SourceAnalysisAndTool/validator.v8/readme.md b/SourceAnalysisAndTool/validator.v8/readme.md new file mode 100644 index 0000000..29bb391 --- /dev/null +++ b/SourceAnalysisAndTool/validator.v8/readme.md @@ -0,0 +1,5 @@ +我的分析文章可参考:[Go源码解析:validator.v8](https://blog.csdn.net/qq_34326321/article/details/111030128) + +流程图: ![image](2、结构体缓存获取.jpg) + +流程图地址:[地址](https://www.processon.com/view/link/5fd6dabb63768906e6db0d25) \ No newline at end of file diff --git a/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/.gitignore b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/.gitignore new file mode 100644 index 0000000..792ca00 --- /dev/null +++ b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/.gitignore @@ -0,0 +1,29 @@ +# 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 +*.test +*.out +*.txt +cover.html +README.html \ No newline at end of file diff --git a/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/LICENSE b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/LICENSE new file mode 100644 index 0000000..6a2ae9a --- /dev/null +++ b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Dean Karn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/README.md b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/README.md new file mode 100644 index 0000000..d3bd9b0 --- /dev/null +++ b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/README.md @@ -0,0 +1,366 @@ +Package validator +================ +[![Join the chat at https://gitter.im/bluesuncorp/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-8.18.2-green.svg) +[![Build Status](https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/530054/badge.svg)](https://semaphoreci.com/joeybloggs/validator) +[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=v8&service=github)](https://coveralls.io/github/go-playground/validator?branch=v8) +[![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/gopkg.in/go-playground/validator.v8?status.svg)](https://godoc.org/gopkg.in/go-playground/validator.v8) +![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. +- 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 + +Installation +------------ + +Use go get. + + go get gopkg.in/go-playground/validator.v8 + +or to update + + go get -u gopkg.in/go-playground/validator.v8 + +Then import the validator package into your own code. + + import "gopkg.in/go-playground/validator.v8" + +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 returns nil or ValidationErrors as type error; so in you code all you need to do +is check if the error returned is not nil, and if it's not type cast it to type ValidationErrors +like so: + +```go +err := validate.Struct(mystruct) +validationErrors := err.(validator.ValidationErrors) + ``` + +Usage and documentation +------ + +Please see http://godoc.org/gopkg.in/go-playground/validator.v8 for detailed usage docs. + +##### Examples: + +Struct & Field validation +```go +package main + +import ( + "fmt" + + "gopkg.in/go-playground/validator.v8" +) + +// User contains user information +type User struct { + FirstName string `validate:"required"` + LastName string `validate:"required"` + Age uint8 `validate:"gte=0,lte=130"` + Email string `validate:"required,email"` + FavouriteColor string `validate:"hexcolor|rgb|rgba"` + Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage... +} + +// Address houses a users address information +type Address struct { + Street string `validate:"required"` + City string `validate:"required"` + Planet string `validate:"required"` + Phone string `validate:"required"` +} + +var validate *validator.Validate + +func main() { + + config := &validator.Config{TagName: "validate"} + + validate = validator.New(config) + + validateStruct() + validateField() +} + +func validateStruct() { + + address := &Address{ + Street: "Eavesdown Docks", + Planet: "Persphone", + Phone: "none", + } + + user := &User{ + FirstName: "Badger", + LastName: "Smith", + Age: 135, + Email: "Badger.Smith@gmail.com", + FavouriteColor: "#000", + Addresses: []*Address{address}, + } + + // returns nil or ValidationErrors ( map[string]*FieldError ) + errs := validate.Struct(user) + + if errs != nil { + + fmt.Println(errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag + // Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag + err := errs.(validator.ValidationErrors)["User.Addresses[0].City"] + fmt.Println(err.Field) // output: City + fmt.Println(err.Tag) // output: required + fmt.Println(err.Kind) // output: string + fmt.Println(err.Type) // output: string + fmt.Println(err.Param) // output: + fmt.Println(err.Value) // output: + + // from here you can create your own error messages in whatever language you wish + return + } + + // save user to database +} + +func validateField() { + myEmail := "joeybloggs.gmail.com" + + errs := validate.Field(myEmail, "required,email") + + if errs != nil { + fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag + return + } + + // email ok, move on +} +``` + +Custom Field Type +```go +package main + +import ( + "database/sql" + "database/sql/driver" + "fmt" + "reflect" + + "gopkg.in/go-playground/validator.v8" +) + +// DbBackedUser User struct +type DbBackedUser struct { + Name sql.NullString `validate:"required"` + Age sql.NullInt64 `validate:"required"` +} + +func main() { + + config := &validator.Config{TagName: "validate"} + + validate := validator.New(config) + + // register all sql.Null* types to use the ValidateValuer CustomTypeFunc + validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{}) + + x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}} + errs := validate.Struct(x) + + if len(errs.(validator.ValidationErrors)) > 0 { + fmt.Printf("Errs:\n%+v\n", errs) + } +} + +// ValidateValuer implements validator.CustomTypeFunc +func ValidateValuer(field reflect.Value) interface{} { + if valuer, ok := field.Interface().(driver.Valuer); ok { + val, err := valuer.Value() + if err == nil { + return val + } + // handle the error how you want + } + return nil +} +``` + +Struct Level Validation +```go +package main + +import ( + "fmt" + "reflect" + + "gopkg.in/go-playground/validator.v8" +) + +// User contains user information +type User struct { + FirstName string `json:"fname"` + LastName string `json:"lname"` + Age uint8 `validate:"gte=0,lte=130"` + Email string `validate:"required,email"` + FavouriteColor string `validate:"hexcolor|rgb|rgba"` + Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage... +} + +// Address houses a users address information +type Address struct { + Street string `validate:"required"` + City string `validate:"required"` + Planet string `validate:"required"` + Phone string `validate:"required"` +} + +var validate *validator.Validate + +func main() { + + config := &validator.Config{TagName: "validate"} + + validate = validator.New(config) + validate.RegisterStructValidation(UserStructLevelValidation, User{}) + + validateStruct() +} + +// UserStructLevelValidation contains custom struct level validations that don't always +// make sense at the field validation level. For Example this function validates that either +// FirstName or LastName exist; could have done that with a custom field validation but then +// would have had to add it to both fields duplicating the logic + overhead, this way it's +// only validated once. +// +// NOTE: you may ask why wouldn't I just do this outside of validator, because doing this way +// hooks right into validator and you can combine with validation tags and still have a +// common error output format. +func UserStructLevelValidation(v *validator.Validate, structLevel *validator.StructLevel) { + + user := structLevel.CurrentStruct.Interface().(User) + + if len(user.FirstName) == 0 && len(user.LastName) == 0 { + structLevel.ReportError(reflect.ValueOf(user.FirstName), "FirstName", "fname", "fnameorlname") + structLevel.ReportError(reflect.ValueOf(user.LastName), "LastName", "lname", "fnameorlname") + } + + // plus can to more, even with different tag than "fnameorlname" +} + +func validateStruct() { + + address := &Address{ + Street: "Eavesdown Docks", + Planet: "Persphone", + Phone: "none", + City: "Unknown", + } + + user := &User{ + FirstName: "", + LastName: "", + Age: 45, + Email: "Badger.Smith@gmail.com", + FavouriteColor: "#000", + Addresses: []*Address{address}, + } + + // returns nil or ValidationErrors ( map[string]*FieldError ) + errs := validate.Struct(user) + + if errs != nil { + + fmt.Println(errs) // output: Key: 'User.LastName' Error:Field validation for 'LastName' failed on the 'fnameorlname' tag + // Key: 'User.FirstName' Error:Field validation for 'FirstName' failed on the 'fnameorlname' tag + err := errs.(validator.ValidationErrors)["User.FirstName"] + fmt.Println(err.Field) // output: FirstName + fmt.Println(err.Tag) // output: fnameorlname + fmt.Println(err.Kind) // output: string + fmt.Println(err.Type) // output: string + fmt.Println(err.Param) // output: + fmt.Println(err.Value) // output: + + // from here you can create your own error messages in whatever language you wish + return + } + + // save user to database +} +``` + +Benchmarks +------ +###### Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3 using Go version go1.5.3 darwin/amd64 +```go +PASS +BenchmarkFieldSuccess-8 20000000 118 ns/op 0 B/op 0 allocs/op +BenchmarkFieldFailure-8 2000000 758 ns/op 432 B/op 4 allocs/op +BenchmarkFieldDiveSuccess-8 500000 2471 ns/op 464 B/op 28 allocs/op +BenchmarkFieldDiveFailure-8 500000 3172 ns/op 896 B/op 32 allocs/op +BenchmarkFieldCustomTypeSuccess-8 5000000 300 ns/op 32 B/op 2 allocs/op +BenchmarkFieldCustomTypeFailure-8 2000000 775 ns/op 432 B/op 4 allocs/op +BenchmarkFieldOrTagSuccess-8 1000000 1122 ns/op 4 B/op 1 allocs/op +BenchmarkFieldOrTagFailure-8 1000000 1167 ns/op 448 B/op 6 allocs/op +BenchmarkStructLevelValidationSuccess-8 3000000 548 ns/op 160 B/op 5 allocs/op +BenchmarkStructLevelValidationFailure-8 3000000 558 ns/op 160 B/op 5 allocs/op +BenchmarkStructSimpleCustomTypeSuccess-8 2000000 623 ns/op 36 B/op 3 allocs/op +BenchmarkStructSimpleCustomTypeFailure-8 1000000 1381 ns/op 640 B/op 9 allocs/op +BenchmarkStructPartialSuccess-8 1000000 1036 ns/op 272 B/op 9 allocs/op +BenchmarkStructPartialFailure-8 1000000 1734 ns/op 730 B/op 14 allocs/op +BenchmarkStructExceptSuccess-8 2000000 888 ns/op 250 B/op 7 allocs/op +BenchmarkStructExceptFailure-8 1000000 1036 ns/op 272 B/op 9 allocs/op +BenchmarkStructSimpleCrossFieldSuccess-8 2000000 773 ns/op 80 B/op 4 allocs/op +BenchmarkStructSimpleCrossFieldFailure-8 1000000 1487 ns/op 536 B/op 9 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 1000000 1261 ns/op 112 B/op 7 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldFailure-8 1000000 2055 ns/op 576 B/op 12 allocs/op +BenchmarkStructSimpleSuccess-8 3000000 519 ns/op 4 B/op 1 allocs/op +BenchmarkStructSimpleFailure-8 1000000 1429 ns/op 640 B/op 9 allocs/op +BenchmarkStructSimpleSuccessParallel-8 10000000 146 ns/op 4 B/op 1 allocs/op +BenchmarkStructSimpleFailureParallel-8 2000000 551 ns/op 640 B/op 9 allocs/op +BenchmarkStructComplexSuccess-8 500000 3269 ns/op 244 B/op 15 allocs/op +BenchmarkStructComplexFailure-8 200000 8436 ns/op 3609 B/op 60 allocs/op +BenchmarkStructComplexSuccessParallel-8 1000000 1024 ns/op 244 B/op 15 allocs/op +BenchmarkStructComplexFailureParallel-8 500000 3536 ns/op 3609 B/op 60 allocs/op +``` + +Complimentary Software +---------------------- + +Here is a list of software that compliments 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. +* [Conform](https://github.com/leebenson/conform) - Trims, sanitizes & scrubs data based on struct tags. + +How to Contribute +------ + +There will always be a development branch for each version i.e. `v1-development`. In order to contribute, +please make your pull requests against those branches. + +If the changes being proposed or requested are breaking changes, please create an issue, for discussion +or create a pull request against the highest development branch for example this package has a +v1 and v1-development branch however, there will also be a v2-development branch even though v2 doesn't exist yet. + +I strongly encourage everyone whom creates a custom validation function to contribute them and +help make this package even better. + +License +------ +Distributed under MIT License, please see license file in code for more details. diff --git a/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/baked_in.go b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/baked_in.go new file mode 100644 index 0000000..558616e --- /dev/null +++ b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/baked_in.go @@ -0,0 +1,1429 @@ +package validator + +import ( + "fmt" + "net" + "net/url" + "reflect" + "strings" + "time" + "unicode/utf8" +) + +// BakedInAliasValidators is a default mapping of a single validationstag that +// defines a common or complex set of validation(s) to simplify +// adding validation to structs. i.e. set key "_ageok" and the tags +// are "gt=0,lte=130" or key "_preferredname" and tags "omitempty,gt=0,lte=60" +//read note 别名标签,通过别名定义规则,默认是iscolor +// 它的作用比较神奇,就是说通过iscolor这个标签就可以判断后面一组的规则 +// 比如说这边 iscolor对应的是 hexcolor|rgb|rgba|hsl|hsla +// 如果我在tag中使用iscolor标签,则validator会去校验 是否满足 hexcolor | 是否满足 rgb | 是否满足 rgba | 是否满足 hsl | 是否满足 hsla (|:或者) +var bakedInAliasValidators = map[string]string{ + "iscolor": "hexcolor|rgb|rgba|hsl|hsla", +} + +// BakedInValidators is the default map of ValidationFunc +// you can add, remove or even replace items to suite your needs, +// or even disregard and use your own map if so desired. +//read note 默认的校验映射map. key是tag标签,value是对应tag标签的处理类 +// 具体校验实现可参考:https://blog.csdn.net/weixin_30896511/article/details/98428064 +var bakedInValidators = map[string]Func{ + "required": HasValue, //read note 参数需要有值 + "len": HasLengthOf, //read note 数值类型判断的是值相等, Map Slice Array判断的是长度相等,string判断的是Rune数组长度相等 + "min": HasMinOf, //read note 数值类型判断的是值【大于等于】min, Map Slice Array判断的是长度【大于等于】min,string判断的是Rune数组长度【大于等于】min + "max": HasMaxOf, //read note 数值类型判断的是值【小于等于】max, Map Slice Array判断的是长度【小于等于】max,string判断的是Rune数组长度【小于等于】max + "eq": IsEq, //read note 字符串或者数值类型,判断的是值【相等】,Map Slice Array判断长度是否【相等】 + "ne": IsNe, //read note 字符串或者数值类型,判断的是值【不相等】,Map Slice Array判断长度是否【不相等】 + "lt": IsLt, //read note 数值类型判断的是值【小于】lt, Map Slice Array判断的是长度【小于】lt,string判断的是Rune数组长度【小于】lt + "lte": IsLte, //类似max的操作,但是这边多了一步处理就是判断如果是【time.Time】,比较时间在当前时间之前或者等于当前时间 + "gt": IsGt, //read note 数值类型判断的是值【大于】gt, Map Slice Array判断的是长度【大于】gt,string判断的是Rune数组长度【大于】gt + "gte": IsGte, //类似min的操作,但是这边多了一步处理就是判断如果是【time.Time】,比较时间在当前时间之后或者等于当前时间 + + //read note 判断关联字段(标签标识关联字段的名字)和当前字段的值的关系,数值是判断值相等,Map Slice Array判断长度,如果有time.Time判断时间和当前时间前后关系,否则判断reflect.String + "eqfield": IsEqField, + "eqcsfield": IsEqCrossStructField, + "necsfield": IsNeCrossStructField, + "gtcsfield": IsGtCrossStructField, + "gtecsfield": IsGteCrossStructField, + "ltcsfield": IsLtCrossStructField, + "ltecsfield": IsLteCrossStructField, + "nefield": IsNeField, + "gtefield": IsGteField, + "gtfield": IsGtField, + "ltefield": IsLteField, + "ltfield": IsLtField, + + "alpha": IsAlpha, //这将验证字符串值是否仅包含ASCII字母字符 + "alphanum": IsAlphanum, //这将验证字符串值是否仅包含ASCII字母数字字符 + + "numeric": IsNumeric, //这将验证字符串值是否包含基本数值。基本排除指数等...对于整数或浮点数,它返回true。 + "number": IsNumber, //判断是否Number + "hexadecimal": IsHexadecimal, //这将验证字符串值是否包含有效的十六进制 + + "hexcolor": IsHEXColor, //这验证字符串值包含有效的十六进制颜色,包括#标签(#) + "rgb": IsRGB, //这将验证字符串值是否包含有效的rgb颜色 + "rgba": IsRGBA, //这将验证字符串值是否包含有效的rgba颜色 + "hsl": IsHSL, //这将验证字符串值是否包含有效的hsl颜色 + "hsla": IsHSLA, //这将验证字符串值是否包含有效的hsla颜色 + + "email": IsEmail, //这验证字符串值包含有效的电子邮件这可能不符合任何rfc标准的所有可能性,但任何电子邮件提供商都不接受所有可能性 + "url": IsURL, //这会验证字符串值是否包含有效的url这将接受golang请求uri接受的任何url,但必须包含一个模式,例如http://或rtmp:// + "uri": IsURI, //这验证了字符串值包含有效的uri。这将接受uri接受的golang请求的任何uri + "base64": IsBase64, //这将验证字符串值是否包含有效的base64值。虽然空字符串是有效的base64,但这会将空字符串报告为错误,如果您希望接受空字符串作为有效字符,则可以将此字符串与omitempty标记一起使用。 + + //read note 验证字符串是否【包含】/【不包含】对应的子字符串(这个方法看源码是处理Field.String()是否包含对应的子串,而如果不是字符串的话返回的是,不知道这个会不会被对比) + "contains": Contains, + "containsany": ContainsAny, + "containsrune": ContainsRune, + "excludes": Excludes, + "excludesall": ExcludesAll, + "excludesrune": ExcludesRune, + + //read note 校验字符串是否有效的isbn值 + "isbn": IsISBN, + "isbn10": IsISBN10, + "isbn13": IsISBN13, + + //read note 校验字符串是否有效的uuid值 + "uuid": IsUUID, + "uuid3": IsUUID3, + "uuid4": IsUUID4, + "uuid5": IsUUID5, + + "ascii": IsASCII, //这将验证字符串值是否仅包含ASCII字符。注意:如果字符串为空,则验证为true + "printascii": IsPrintableASCII, //这将验证字符串值是否仅包含可打印的ASCII字符。注意:如果字符串为空,则验证为true。 + "multibyte": HasMultiByteCharacter, //这将验证字符串值是否包含一个或多个多字节字符。注意:如果字符串为空,则验证为true + "datauri": IsDataURI, //这将验证字符串值是否包含有效的DataURI。注意:这也将验证数据部分是否有效base64 + "latitude": IsLatitude, //这将验证字符串值是否包含有效的纬度。 + "longitude": IsLongitude, //这将验证字符串值是否包含有效经度。 + "ssn": IsSSN, //这将验证字符串值是否包含有效的美国社会安全号码。 + "ipv4": IsIPv4, //这将验证字符串值是否包含有效的v4 IP地址 + "ipv6": IsIPv6, //这将验证字符串值是否包含有效的v6 IP地址 + "ip": IsIP, //这将验证字符串值是否包含有效的IP地址 + "cidrv4": IsCIDRv4, //这将验证字符串值是否包含有效的v4 CIDR地址 + "cidrv6": IsCIDRv6, //这将验证字符串值是否包含有效的v6 CIDR地址 + "cidr": IsCIDR, //这将验证字符串值是否包含有效的CIDR地址 + "tcp4_addr": IsTCP4AddrResolvable, //这将验证字符串值是否包含有效的可解析v4 TCP地址 + "tcp6_addr": IsTCP6AddrResolvable, //这将验证字符串值是否包含有效的可解析v6 TCP地址 + "tcp_addr": IsTCPAddrResolvable, //这将验证字符串值是否包含有效的可解析TCP地址 + "udp4_addr": IsUDP4AddrResolvable, //这将验证字符串值是否包含有效的可解析v4 UDP地址 + "udp6_addr": IsUDP6AddrResolvable, //这将验证字符串值是否包含有效的可解析v6 UDP地址 + "udp_addr": IsUDPAddrResolvable, //这将验证字符串值是否包含有效的可解析UDP地址 + "ip4_addr": IsIP4AddrResolvable, //这将验证字符串值是否包含有效的可解析v4 IP地址 + "ip6_addr": IsIP6AddrResolvable, //这将验证字符串值是否包含有效的可解析v6 IP地址 + "ip_addr": IsIPAddrResolvable, //这将验证字符串值是否包含有效的可解析IP地址 + "unix_addr": IsUnixAddrResolvable, //这将验证字符串值是否包含有效的Unix地址 + "mac": IsMAC, //这将验证字符串值是否包含有效的MAC地址 +} + +// IsMAC is the validation function for validating if the field's value is a valid MAC address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsMAC(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + _, err := net.ParseMAC(field.String()) + return err == nil +} + +// IsCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsCIDRv4(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + ip, _, err := net.ParseCIDR(field.String()) + + return err == nil && ip.To4() != nil +} + +// IsCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsCIDRv6(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + ip, _, err := net.ParseCIDR(field.String()) + + return err == nil && ip.To4() == nil +} + +// IsCIDR is the validation function for validating if the field's value is a valid v4 or v6 CIDR address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsCIDR(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + _, _, err := net.ParseCIDR(field.String()) + + return err == nil +} + +// IsIPv4 is the validation function for validating if a value is a valid v4 IP address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsIPv4(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + ip := net.ParseIP(field.String()) + + return ip != nil && ip.To4() != nil +} + +// IsIPv6 is the validation function for validating if the field's value is a valid v6 IP address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsIPv6(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + ip := net.ParseIP(field.String()) + + return ip != nil && ip.To4() == nil +} + +// IsIP is the validation function for validating if the field's value is a valid v4 or v6 IP address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsIP(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + ip := net.ParseIP(field.String()) + + return ip != nil +} + +// IsSSN is the validation function for validating if the field's value is a valid SSN. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsSSN(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + if field.Len() != 11 { + return false + } + + return sSNRegex.MatchString(field.String()) +} + +// IsLongitude is the validation function for validating if the field's value is a valid longitude coordinate. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsLongitude(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return longitudeRegex.MatchString(field.String()) +} + +// IsLatitude is the validation function for validating if the field's value is a valid latitude coordinate. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsLatitude(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return latitudeRegex.MatchString(field.String()) +} + +// IsDataURI is the validation function for validating if the field's value is a valid data URI. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsDataURI(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + uri := strings.SplitN(field.String(), ",", 2) + + if len(uri) != 2 { + return false + } + + if !dataURIRegex.MatchString(uri[0]) { + return false + } + + fld := reflect.ValueOf(uri[1]) + + return IsBase64(v, topStruct, currentStructOrField, fld, fld.Type(), fld.Kind(), param) +} + +// HasMultiByteCharacter is the validation function for validating if the field's value has a multi byte character. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func HasMultiByteCharacter(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + if field.Len() == 0 { + return true + } + + return multibyteRegex.MatchString(field.String()) +} + +// IsPrintableASCII is the validation function for validating if the field's value is a valid printable ASCII character. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsPrintableASCII(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return printableASCIIRegex.MatchString(field.String()) +} + +// IsASCII is the validation function for validating if the field's value is a valid ASCII character. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsASCII(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return aSCIIRegex.MatchString(field.String()) +} + +// IsUUID5 is the validation function for validating if the field's value is a valid v5 UUID. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsUUID5(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return uUID5Regex.MatchString(field.String()) +} + +// IsUUID4 is the validation function for validating if the field's value is a valid v4 UUID. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsUUID4(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return uUID4Regex.MatchString(field.String()) +} + +// IsUUID3 is the validation function for validating if the field's value is a valid v3 UUID. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsUUID3(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return uUID3Regex.MatchString(field.String()) +} + +// IsUUID is the validation function for validating if the field's value is a valid UUID of any version. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsUUID(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return uUIDRegex.MatchString(field.String()) +} + +// IsISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsISBN(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return IsISBN10(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) || IsISBN13(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) +} + +// IsISBN13 is the validation function for validating if the field's value is a valid v13 ISBN. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsISBN13(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + s := strings.Replace(strings.Replace(field.String(), "-", "", 4), " ", "", 4) + + if !iSBN13Regex.MatchString(s) { + return false + } + + var checksum int32 + var i int32 + + factor := []int32{1, 3} + + for i = 0; i < 12; i++ { + checksum += factor[i%2] * int32(s[i]-'0') + } + + return (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0 +} + +// IsISBN10 is the validation function for validating if the field's value is a valid v10 ISBN. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsISBN10(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + s := strings.Replace(strings.Replace(field.String(), "-", "", 3), " ", "", 3) + + if !iSBN10Regex.MatchString(s) { + return false + } + + var checksum int32 + var i int32 + + for i = 0; i < 9; i++ { + checksum += (i + 1) * int32(s[i]-'0') + } + + if s[9] == 'X' { + checksum += 10 * 10 + } else { + checksum += 10 * int32(s[9]-'0') + } + + return checksum%11 == 0 +} + +// ExcludesRune is the validation function for validating that the field's value does not contain the rune specified within the param. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func ExcludesRune(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return !ContainsRune(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) +} + +// ExcludesAll is the validation function for validating that the field's value does not contain any of the characters specified within the param. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func ExcludesAll(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return !ContainsAny(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) +} + +// Excludes is the validation function for validating that the field's value does not contain the text specified within the param. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func Excludes(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return !Contains(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) +} + +// ContainsRune is the validation function for validating that the field's value contains the rune specified within the param. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func ContainsRune(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + r, _ := utf8.DecodeRuneInString(param) + + return strings.ContainsRune(field.String(), r) +} + +// ContainsAny is the validation function for validating that the field's value contains any of the characters specified within the param. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func ContainsAny(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return strings.ContainsAny(field.String(), param) +} + +// Contains is the validation function for validating that the field's value contains the text specified within the param. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func Contains(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return strings.Contains(field.String(), param) +} + +// IsNeField is the validation function for validating if the current field's value is not equal to the field specified by the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsNeField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param) + + if !ok || currentKind != fieldKind { + return true + } + + switch fieldKind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() != currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() != currentField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() != currentField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) != int64(currentField.Len()) + + case reflect.Struct: + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return true + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return !fieldTime.Equal(t) + } + + } + + // default reflect.String: + return field.String() != currentField.String() +} + +// IsNe is the validation function for validating that the field's value does not equal the provided param value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsNe(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return !IsEq(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) +} + +// IsLteCrossStructField is the validation function for validating if the current field's value is less than or equal to the field, within a separate struct, specified by the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsLteCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + topField, topKind, ok := v.GetStructFieldOK(topStruct, param) + if !ok || topKind != fieldKind { + return false + } + + switch fieldKind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() <= topField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() <= topField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() <= topField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) <= int64(topField.Len()) + + case reflect.Struct: + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + fieldTime := field.Interface().(time.Time) + topTime := topField.Interface().(time.Time) + + return fieldTime.Before(topTime) || fieldTime.Equal(topTime) + } + } + + // default reflect.String: + return field.String() <= topField.String() +} + +// IsLtCrossStructField is the validation function for validating if the current field's value is less than the field, within a separate struct, specified by the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsLtCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + topField, topKind, ok := v.GetStructFieldOK(topStruct, param) + if !ok || topKind != fieldKind { + return false + } + + switch fieldKind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() < topField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() < topField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() < topField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) < int64(topField.Len()) + + case reflect.Struct: + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + fieldTime := field.Interface().(time.Time) + topTime := topField.Interface().(time.Time) + + return fieldTime.Before(topTime) + } + } + + // default reflect.String: + return field.String() < topField.String() +} + +// IsGteCrossStructField is the validation function for validating if the current field's value is greater than or equal to the field, within a separate struct, specified by the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsGteCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + topField, topKind, ok := v.GetStructFieldOK(topStruct, param) + if !ok || topKind != fieldKind { + return false + } + + switch fieldKind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() >= topField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() >= topField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() >= topField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) >= int64(topField.Len()) + + case reflect.Struct: + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + fieldTime := field.Interface().(time.Time) + topTime := topField.Interface().(time.Time) + + return fieldTime.After(topTime) || fieldTime.Equal(topTime) + } + } + + // default reflect.String: + return field.String() >= topField.String() +} + +// IsGtCrossStructField is the validation function for validating if the current field's value is greater than the field, within a separate struct, specified by the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsGtCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + topField, topKind, ok := v.GetStructFieldOK(topStruct, param) + if !ok || topKind != fieldKind { + return false + } + + switch fieldKind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() > topField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() > topField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() > topField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) > int64(topField.Len()) + + case reflect.Struct: + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + fieldTime := field.Interface().(time.Time) + topTime := topField.Interface().(time.Time) + + return fieldTime.After(topTime) + } + } + + // default reflect.String: + return field.String() > topField.String() +} + +// IsNeCrossStructField is the validation function for validating that the current field's value is not equal to the field, within a separate struct, specified by the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsNeCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + topField, currentKind, ok := v.GetStructFieldOK(topStruct, param) + if !ok || currentKind != fieldKind { + return true + } + + switch fieldKind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return topField.Int() != field.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return topField.Uint() != field.Uint() + + case reflect.Float32, reflect.Float64: + return topField.Float() != field.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(topField.Len()) != int64(field.Len()) + + case reflect.Struct: + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return true + } + + if fieldType == timeType { + + t := field.Interface().(time.Time) + fieldTime := topField.Interface().(time.Time) + + return !fieldTime.Equal(t) + } + } + + // default reflect.String: + return topField.String() != field.String() +} + +// IsEqCrossStructField is the validation function for validating that the current field's value is equal to the field, within a separate struct, specified by the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsEqCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + topField, topKind, ok := v.GetStructFieldOK(topStruct, param) + if !ok || topKind != fieldKind { + return false + } + + switch fieldKind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return topField.Int() == field.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return topField.Uint() == field.Uint() + + case reflect.Float32, reflect.Float64: + return topField.Float() == field.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(topField.Len()) == int64(field.Len()) + + case reflect.Struct: + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + t := field.Interface().(time.Time) + fieldTime := topField.Interface().(time.Time) + + return fieldTime.Equal(t) + } + } + + // default reflect.String: + return topField.String() == field.String() +} + +// IsEqField is the validation function for validating if the current field's value is equal to the field specified by the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsEqField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param) + if !ok || currentKind != fieldKind { + return false + } + + switch fieldKind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() == currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() == currentField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() == currentField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) == int64(currentField.Len()) + + case reflect.Struct: + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.Equal(t) + } + + } + + // default reflect.String: + return field.String() == currentField.String() +} + +// IsEq is the validation function for validating if the current field's value is equal to the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsEq(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + switch fieldKind { + + case reflect.String: + return field.String() == param + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) == p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return field.Int() == p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() == p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() == p + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsBase64 is the validation function for validating if the current field's value is a valid base 64. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsBase64(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return base64Regex.MatchString(field.String()) +} + +// IsURI is the validation function for validating if the current field's value is a valid URI. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsURI(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + switch fieldKind { + + case reflect.String: + + s := field.String() + + // checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195 + // emulate browser and strip the '#' suffix prior to validation. see issue-#237 + if i := strings.Index(s, "#"); i > -1 { + s = s[:i] + } + + if s == blank { + return false + } + + _, err := url.ParseRequestURI(s) + + return err == nil + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsURL is the validation function for validating if the current field's value is a valid URL. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsURL(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + switch fieldKind { + + case reflect.String: + + var i int + s := field.String() + + // checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195 + // emulate browser and strip the '#' suffix prior to validation. see issue-#237 + if i = strings.Index(s, "#"); i > -1 { + s = s[:i] + } + + if s == blank { + return false + } + + url, err := url.ParseRequestURI(s) + + if err != nil || url.Scheme == blank { + return false + } + + return err == nil + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsEmail is the validation function for validating if the current field's value is a valid email address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsEmail(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return emailRegex.MatchString(field.String()) +} + +// IsHSLA is the validation function for validating if the current field's value is a valid HSLA color. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsHSLA(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return hslaRegex.MatchString(field.String()) +} + +// IsHSL is the validation function for validating if the current field's value is a valid HSL color. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsHSL(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return hslRegex.MatchString(field.String()) +} + +// IsRGBA is the validation function for validating if the current field's value is a valid RGBA color. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsRGBA(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return rgbaRegex.MatchString(field.String()) +} + +// IsRGB is the validation function for validating if the current field's value is a valid RGB color. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsRGB(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return rgbRegex.MatchString(field.String()) +} + +// IsHEXColor is the validation function for validating if the current field's value is a valid HEX color. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsHEXColor(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return hexcolorRegex.MatchString(field.String()) +} + +// IsHexadecimal is the validation function for validating if the current field's value is a valid hexadecimal. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsHexadecimal(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return hexadecimalRegex.MatchString(field.String()) +} + +// IsNumber is the validation function for validating if the current field's value is a valid number. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsNumber(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return numberRegex.MatchString(field.String()) +} + +// IsNumeric is the validation function for validating if the current field's value is a valid numeric value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsNumeric(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return numericRegex.MatchString(field.String()) +} + +// IsAlphanum is the validation function for validating if the current field's value is a valid alphanumeric value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsAlphanum(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return alphaNumericRegex.MatchString(field.String()) +} + +// IsAlpha is the validation function for validating if the current field's value is a valid alpha value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsAlpha(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return alphaRegex.MatchString(field.String()) +} + +// HasValue is the validation function for validating if the current field's value is not the default static value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func HasValue(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + switch fieldKind { + case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: + return !field.IsNil() + default: + return field.IsValid() && field.Interface() != reflect.Zero(fieldType).Interface() + } +} + +// IsGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsGteField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param) + if !ok || currentKind != fieldKind { + return false + } + + switch fieldKind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return field.Int() >= currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return field.Uint() >= currentField.Uint() + + case reflect.Float32, reflect.Float64: + + return field.Float() >= currentField.Float() + + case reflect.Struct: + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.After(t) || fieldTime.Equal(t) + } + } + + // default reflect.String + return len(field.String()) >= len(currentField.String()) +} + +// IsGtField is the validation function for validating if the current field's value is greater than the field specified by the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsGtField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param) + if !ok || currentKind != fieldKind { + return false + } + + switch fieldKind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return field.Int() > currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return field.Uint() > currentField.Uint() + + case reflect.Float32, reflect.Float64: + + return field.Float() > currentField.Float() + + case reflect.Struct: + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.After(t) + } + } + + // default reflect.String + return len(field.String()) > len(currentField.String()) +} + +// IsGte is the validation function for validating if the current field's value is greater than or equal to the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsGte(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + switch fieldKind { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) >= p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) >= p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return field.Int() >= p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() >= p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() >= p + + case reflect.Struct: + + if fieldType == timeType || fieldType == timePtrType { + + now := time.Now().UTC() + t := field.Interface().(time.Time) + + return t.After(now) || t.Equal(now) + } + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsGt is the validation function for validating if the current field's value is greater than the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsGt(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + switch fieldKind { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) > p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) > p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return field.Int() > p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() > p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() > p + case reflect.Struct: + + if fieldType == timeType || fieldType == timePtrType { + + return field.Interface().(time.Time).After(time.Now().UTC()) + } + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// HasLengthOf is the validation function for validating if the current field's value is equal to the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func HasLengthOf(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + switch fieldKind { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) == p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) == p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return field.Int() == p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() == p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() == p + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// HasMinOf is the validation function for validating if the current field's value is greater than or equal to the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func HasMinOf(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + return IsGte(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) +} + +// IsLteField is the validation function for validating if the current field's value is less than or equal to the field specified by the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsLteField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param) + if !ok || currentKind != fieldKind { + return false + } + + switch fieldKind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return field.Int() <= currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return field.Uint() <= currentField.Uint() + + case reflect.Float32, reflect.Float64: + + return field.Float() <= currentField.Float() + + case reflect.Struct: + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.Before(t) || fieldTime.Equal(t) + } + } + + // default reflect.String + return len(field.String()) <= len(currentField.String()) +} + +// IsLtField is the validation function for validating if the current field's value is less than the field specified by the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsLtField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param) + if !ok || currentKind != fieldKind { + return false + } + + switch fieldKind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return field.Int() < currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return field.Uint() < currentField.Uint() + + case reflect.Float32, reflect.Float64: + + return field.Float() < currentField.Float() + + case reflect.Struct: + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.Before(t) + } + } + + // default reflect.String + return len(field.String()) < len(currentField.String()) +} + +// IsLte is the validation function for validating if the current field's value is less than or equal to the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsLte(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + switch fieldKind { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) <= p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) <= p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return field.Int() <= p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() <= p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() <= p + + case reflect.Struct: + + if fieldType == timeType || fieldType == timePtrType { + + now := time.Now().UTC() + t := field.Interface().(time.Time) + + return t.Before(now) || t.Equal(now) + } + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsLt is the validation function for validating if the current field's value is less than the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsLt(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + switch fieldKind { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) < p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) < p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return field.Int() < p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() < p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() < p + + case reflect.Struct: + + if fieldType == timeType || fieldType == timePtrType { + + return field.Interface().(time.Time).Before(time.Now().UTC()) + } + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// HasMaxOf is the validation function for validating if the current field's value is less than or equal to the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func HasMaxOf(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + return IsLte(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) +} + +// IsTCP4AddrResolvable is the validation function for validating if the field's value is a resolvable tcp4 address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsTCP4AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + if !isIP4Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { + return false + } + + _, err := net.ResolveTCPAddr("tcp4", field.String()) + return err == nil +} + +// IsTCP6AddrResolvable is the validation function for validating if the field's value is a resolvable tcp6 address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsTCP6AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + if !isIP6Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { + return false + } + + _, err := net.ResolveTCPAddr("tcp6", field.String()) + return err == nil +} + +// IsTCPAddrResolvable is the validation function for validating if the field's value is a resolvable tcp address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsTCPAddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + if !isIP4Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) && + !isIP6Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { + return false + } + + _, err := net.ResolveTCPAddr("tcp", field.String()) + return err == nil +} + +// IsUDP4AddrResolvable is the validation function for validating if the field's value is a resolvable udp4 address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsUDP4AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + if !isIP4Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { + return false + } + + _, err := net.ResolveUDPAddr("udp4", field.String()) + return err == nil +} + +// IsUDP6AddrResolvable is the validation function for validating if the field's value is a resolvable udp6 address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsUDP6AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + if !isIP6Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { + return false + } + + _, err := net.ResolveUDPAddr("udp6", field.String()) + return err == nil +} + +// IsUDPAddrResolvable is the validation function for validating if the field's value is a resolvable udp address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsUDPAddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + if !isIP4Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) && + !isIP6Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { + return false + } + + _, err := net.ResolveUDPAddr("udp", field.String()) + return err == nil +} + +// IsIP4AddrResolvable is the validation function for validating if the field's value is a resolvable ip4 address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsIP4AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + if !IsIPv4(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { + return false + } + + _, err := net.ResolveIPAddr("ip4", field.String()) + return err == nil +} + +// IsIP6AddrResolvable is the validation function for validating if the field's value is a resolvable ip6 address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsIP6AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + if !IsIPv6(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { + return false + } + + _, err := net.ResolveIPAddr("ip6", field.String()) + return err == nil +} + +// IsIPAddrResolvable is the validation function for validating if the field's value is a resolvable ip address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsIPAddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + if !IsIP(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { + return false + } + + _, err := net.ResolveIPAddr("ip", field.String()) + return err == nil +} + +// IsUnixAddrResolvable is the validation function for validating if the field's value is a resolvable unix address. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func IsUnixAddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + _, err := net.ResolveUnixAddr("unix", field.String()) + return err == nil +} + +func isIP4Addr(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + val := field.String() + + if idx := strings.LastIndex(val, ":"); idx != -1 { + val = val[0:idx] + } + + if !IsIPv4(v, topStruct, currentStructOrField, reflect.ValueOf(val), fieldType, fieldKind, param) { + return false + } + + return true +} + +func isIP6Addr(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + val := field.String() + + if idx := strings.LastIndex(val, ":"); idx != -1 { + if idx != 0 && val[idx-1:idx] == "]" { + val = val[1 : idx-1] + } + } + + if !IsIPv6(v, topStruct, currentStructOrField, reflect.ValueOf(val), fieldType, fieldKind, param) { + return false + } + + return true +} diff --git a/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/cache.go b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/cache.go new file mode 100644 index 0000000..b3a8720 --- /dev/null +++ b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/cache.go @@ -0,0 +1,285 @@ +package validator + +import ( + "fmt" + "reflect" + "strings" + "sync" + "sync/atomic" +) + +type tagType uint8 + +const ( + typeDefault tagType = iota + typeOmitEmpty + typeNoStructLevel + typeStructOnly + typeDive + typeOr + typeExists +) + +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 map[int]*cField //结构体对应的字段map + fn StructLevelFunc //结构体校验器 (结构体类型->结构体校验器) +} + +type cField struct { + Idx int //字段下标 + Name string //字段名 + AltName string // + cTags *cTag //Field对应的cTag规则,是一个链表(一串的规则). +} + +type cTag struct { + tag string //标签 + aliasTag string // + actualAliasTag string // + param string //如果是比较类型的标签,这里存放的是比较的值,比如说 min=10,这里存放的是【10】这个值 + hasAlias bool //是否有别名校验器标签 + typeof tagType //对应的tagType + hasTag bool //是否存在tag标签 + fn Func //当前cTag对应的【tag标签校验器】 + next *cTag //下一个cTag标签 +} + +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. + //read note 从缓存里面获取对应结构体类型的处理. + cs, ok := v.structCache.Get(typ) + if ok { + return cs + } + + //read note 如果缓存里面拿不到的话,就要从结构的Field中去解析 + cs = &cStruct{Name: sName, fields: make(map[int]*cField), fn: v.structLevelFuncs[typ]} + + numFields := current.NumField() + + var ctag *cTag + var fld reflect.StructField + var tag string + var customName string + + //read note 进行字段的处理 + for i := 0; i < numFields; i++ { + + fld = typ.Field(i) + + //read note 内嵌类型和小写的处理,直接pass + // 类似这种的: + /* type Derive struct { + Base ---内嵌 + } + */ + if !fld.Anonymous && fld.PkgPath != blank { + continue + } + + //read note 获取tag(通过之前Config设置的【tagName】属性去获取对应的tag) + tag = fld.Tag.Get(v.tagName) + + //read note 如果是忽略【-】的话,跳过 + if tag == skipValidationTag { + continue + } + + customName = fld.Name + + //read note config中【fieldNameTag】的处理,会设置到【cField】的【AltName】字段上,应该只是错误输出用的 + if v.fieldNameTag != blank { + + name := strings.SplitN(fld.Tag.Get(v.fieldNameTag), ",", 2)[0] + + // dash check is for json "-" (aka skipValidationTag) means don't output in json + if name != "" && name != skipValidationTag { + 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 + + //read note struct层的校验规则和Field层的校验规则不能共用(标签可能相同,但是别名会不一样..) + + if len(tag) > 0 { + ctag, _ = v.parseFieldTagsRecursive(tag, fld.Name, blank, 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[i] = &cField{Idx: i, Name: fld.Name, AltName: customName, cTags: ctag} + } + + 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 + var ok bool + noAlias := len(alias) == 0 + //read note:这边会把tag根据【,】进行分割处理,得到对应的tag组,所以我们在写的时候可以写入,分割的标签 + tags := strings.Split(tag, tagSeparator) + + for i := 0; i < len(tags); i++ { + + t = tags[i] + + if noAlias { + alias = t + } + + //read note 如果有别名校验器,则回去查找别名校验器(第一个和后面的设置不一样.调用next和没有调用next的区别) + if v.hasAliasValidators { + // check map for alias and process new tags, otherwise process as usual + if tagsVal, found := v.aliasValidators[t]; found { + + if i == 0 { + //read note 对别名校验器进行解析(一个子递归的过程.)除第一个校验器外,后面的校验器是通过一个next指向的链表连接起来 + firstCtag, current = v.parseFieldTagsRecursive(tagsVal, fieldName, t, true) + } else { + next, curr := v.parseFieldTagsRecursive(tagsVal, fieldName, t, true) + current.next, current = next, curr + + } + + continue + } + } + + //read note 设置对应的默认标签(第一个和后面的设置不一样.调用next和没有调用next的区别) + if i == 0 { + current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true} + firstCtag = current + } else { + current.next = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true} + current = current.next + } + + //read note 判断用 【,】分割后的标签,前面几个是处理特殊标签的.如果不是特殊标签,则往下处理设置tag的值 + switch t { + case diveTag: + current.typeof = typeDive + continue + + case omitempty: + current.typeof = typeOmitEmpty + continue + + case structOnlyTag: + current.typeof = typeStructOnly + continue + + case noStructLevelTag: + current.typeof = typeNoStructLevel + continue + + case existsTag: + current.typeof = typeExists + continue + + default: + // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C" + orVals := strings.Split(t, orSeparator) + + //read note 或的条件进行拆分. + + for j := 0; j < len(orVals); j++ { + //read note 解析【=】标签 + vals := strings.SplitN(orVals[j], tagKeySeparator, 2) + + if noAlias { + alias = vals[0] + current.aliasTag = alias + } else { + current.actualAliasTag = t + } + + //read note 如果或的标签成立,也就是说 A|B|C ABC会组装成一个链(非头节点需要往下一个节点去处理,所以这边需要把current指向它的next) + if j > 0 { + current.next = &cTag{aliasTag: alias, actualAliasTag: current.actualAliasTag, hasAlias: hasAlias, hasTag: true} + current = current.next + } + + current.tag = vals[0] + if len(current.tag) == 0 { + panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName))) + } + + //read note 查找对应的校验规则 + if current.fn, ok = v.validationFuncs[current.tag]; !ok { + panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, fieldName))) + } + + //read note 设置为typeOr.这个应该在后面处理对应字段的时候会拿到并且通过or的方式进行处理 + if len(orVals) > 1 { + current.typeof = typeOr + } + + if len(vals) > 1 { + current.param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1) + } + } + } + } + + return +} diff --git a/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/doc.go b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/doc.go new file mode 100644 index 0000000..fdede2c --- /dev/null +++ b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/doc.go @@ -0,0 +1,852 @@ +/* +Package validator implements value validations for structs and individual fields +based on tags. + +It can also handle Cross-Field and Cross-Struct validation for nested structs +and has the ability to dive into arrays and maps of any type. + +Why not a better error message? +Because this library intends for you to handle your own error messages. + +Why should I handle my own errors? +Many reasons. We built an internationalized application and needed to know the +field, and what validation failed so we could provide a localized error. + + if fieldErr.Field == "Name" { + switch fieldErr.ErrorTag + case "required": + return "Translated string based on field + error" + default: + return "Translated string based on field" + } + + +Validation Functions Return Type error + +Doing things this way is actually the way the standard library does, see the +file.Open method here: + + https://golang.org/pkg/os/#Open. + +The authors 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 returns 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 +type cast it to type ValidationErrors like so err.(validator.ValidationErrors). + +Custom Functions + +Custom functions can be added. Example: + + // Structure + func customFunc(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { + + if whatever { + return false + } + + return true + } + + validate.RegisterValidation("custom tag name", customFunc) + // NOTES: using the same tag name as an existing function + // will overwrite the existing one + +Cross-Field Validation + +Cross-Field Validation can be done via the following tags: + - eqfield + - nefield + - gtfield + - gtefield + - ltfield + - ltefield + - eqcsfield + - necsfield + - gtcsfield + - ftecsfield + - ltcsfield + - ltecsfield + +If, however, some custom cross-field validation is required, it can be done +using a custom validation. + +Why not just have cross-fields validation tags (i.e. only eqcsfield and not +eqfield)? + +The reason is efficiency. If you want to check a field within the same struct +"eqfield" only has to find the field on the same struct (1 level). But, if we +used "eqcsfield" it could be multiple levels down. Example: + + type Inner struct { + StartDate time.Time + } + + type Outer struct { + InnerStructField *Inner + CreatedAt time.Time `validate:"ltecsfield=InnerStructField.StartDate"` + } + + now := time.Now() + + inner := &Inner{ + StartDate: now, + } + + outer := &Outer{ + InnerStructField: inner, + CreatedAt: now, + } + + errs := validate.Struct(outer) + + // NOTE: when calling validate.Struct(val) topStruct will be the top level struct passed + // into the function + // when calling validate.FieldWithValue(val, field, tag) val will be + // whatever you pass, struct, field... + // when calling validate.Field(field, tag) val will be nil + +Multiple Validators + +Multiple validators on a field will process in the order defined. Example: + + type Test struct { + Field `validate:"max=10,min=1"` + } + + // max will be checked then min + +Bad Validator definitions are not handled by the library. Example: + + type Test struct { + Field `validate:"min=10,max=0"` + } + + // this definition of min max will never succeed + +Using Validator Tags + +Baked In Cross-Field validation only compares fields on the same struct. +If Cross-Field + Cross-Struct validation is needed you should implement your +own custom validator. + +Comma (",") is the default separator of validation tags. If you wish to +have a comma included within the parameter (i.e. excludesall=,) you will need to +use the UTF-8 hex representation 0x2C, which is replaced in the code as a comma, +so the above will become excludesall=0x2C. + + type Test struct { + Field `validate:"excludesall=,"` // BAD! Do not include a comma. + Field `validate:"excludesall=0x2C"` // GOOD! Use the UTF-8 hex representation. + } + +Pipe ("|") is the default separator of validation tags. If you wish to +have a pipe included within the parameter i.e. excludesall=| you will need to +use the UTF-8 hex representation 0x7C, which is replaced in the code as a pipe, +so the above will become excludesall=0x7C + + type Test struct { + Field `validate:"excludesall=|"` // BAD! Do not include a a pipe! + Field `validate:"excludesall=0x7C"` // GOOD! Use the UTF-8 hex representation. + } + + +Baked In Validators and Tags + +Here is a list of the current built in validators: + + +Skip Field + +Tells the validation to skip this struct field; this is particularly +handy in ignoring embedded structs from being validated. (Usage: -) + Usage: - + + +Or Operator + +This is the 'or' operator allowing multiple validators to be used and +accepted. (Usage: rbg|rgba) <-- this would allow either rgb or rgba +colors to be accepted. This can also be combined with 'and' for example +( Usage: omitempty,rgb|rgba) + + Usage: | + +StructOnly + +When a field that is a nested struct is encountered, and contains this flag +any validation on the nested struct will be run, but none of the nested +struct fields will be validated. This is usefull if inside of you program +you know the struct will be valid, but need to verify it has been assigned. +NOTE: only "required" and "omitempty" can be used on a struct itself. + + Usage: structonly + +NoStructLevel + +Same as structonly tag except that any struct level validations will not run. + + Usage: nostructlevel + +Exists + +Is a special tag without a validation function attached. It is used when a field +is a Pointer, Interface or Invalid and you wish to validate that it exists. +Example: want to ensure a bool exists if you define the bool as a pointer and +use exists it will ensure there is a value; couldn't use required as it would +fail when the bool was false. exists will fail is the value is a Pointer, Interface +or Invalid and is nil. + + Usage: exists + +Omit Empty + +Allows conditional validation, for example if a field is not set with +a value (Determined by the "required" validator) then other validation +such as min or max won't run, but if a value is set validation will run. + + Usage: omitempty + +Dive + +This tells the validator to dive into a slice, array or map and validate that +level of the slice, array or map with the validation tags that follow. +Multidimensional nesting is also supported, each level you wish to dive will +require another dive tag. + + Usage: dive + +Example #1 + + [][]string with validation tag "gt=0,dive,len=1,dive,required" + // gt=0 will be applied to [] + // len=1 will be applied to []string + // required will be applied to string + +Example #2 + + [][]string with validation tag "gt=0,dive,dive,required" + // gt=0 will be applied to [] + // []string will be spared validation + // required will be applied to string + +Required + +This validates that the value is not the data types default zero value. +For numbers ensures value is not zero. For strings ensures value is +not "". For slices, maps, pointers, interfaces, channels and functions +ensures the value is not nil. + + Usage: required + +Length + +For numbers, max will ensure that the value is +equal to the parameter given. For strings, it checks that +the string length is exactly that number of characters. For slices, +arrays, and maps, validates the number of items. + + Usage: len=10 + +Maximum + +For numbers, max will ensure that the value is +less than or equal to the parameter given. For strings, it checks +that the string length is at most that number of characters. For +slices, arrays, and maps, validates the number of items. + + Usage: max=10 + +Mininum + +For numbers, min will ensure that the value is +greater or equal to the parameter given. For strings, it checks that +the string length is at least that number of characters. For slices, +arrays, and maps, validates the number of items. + + Usage: min=10 + +Equals + +For strings & numbers, eq will ensure that the value is +equal to the parameter given. For slices, arrays, and maps, +validates the number of items. + + Usage: eq=10 + +Not Equal + +For strings & numbers, ne will ensure that the value is not +equal to the parameter given. For slices, arrays, and maps, +validates the number of items. + + Usage: ne=10 + +Greater Than + +For numbers, this will ensure that the value is greater than the +parameter given. For strings, it checks that the string length +is greater than that number of characters. For slices, arrays +and maps it validates the number of items. + +Example #1 + + Usage: gt=10 + +Example #2 (time.Time) + +For time.Time ensures the time value is greater than time.Now.UTC(). + + Usage: gt + +Greater Than or Equal + +Same as 'min' above. Kept both to make terminology with 'len' easier. + + +Example #1 + + Usage: gte=10 + +Example #2 (time.Time) + +For time.Time ensures the time value is greater than or equal to time.Now.UTC(). + + Usage: gte + +Less Than + +For numbers, this will ensure that the value is less than the parameter given. +For strings, it checks that the string length is less than that number of +characters. For slices, arrays, and maps it validates the number of items. + +Example #1 + + Usage: lt=10 + +Example #2 (time.Time) +For time.Time ensures the time value is less than time.Now.UTC(). + + Usage: lt + +Less Than or Equal + +Same as 'max' above. Kept both to make terminology with 'len' easier. + +Example #1 + + Usage: lte=10 + +Example #2 (time.Time) + +For time.Time ensures the time value is less than or equal to time.Now.UTC(). + + Usage: lte + +Field Equals Another Field + +This will validate the field value against another fields value either within +a struct or passed in field. + +Example #1: + + // Validation on Password field using: + Usage: eqfield=ConfirmPassword + +Example #2: + + // Validating by field: + validate.FieldWithValue(password, confirmpassword, "eqfield") + +Field Equals Another Field (relative) + +This does the same as eqfield except that it validates the field provided relative +to the top level struct. + + Usage: eqcsfield=InnerStructField.Field) + +Field Does Not Equal Another Field + +This will validate the field value against another fields value either within +a struct or passed in field. + +Examples: + + // Confirm two colors are not the same: + // + // Validation on Color field: + Usage: nefield=Color2 + + // Validating by field: + validate.FieldWithValue(color1, color2, "nefield") + +Field Does Not Equal Another Field (relative) + +This does the same as nefield except that it validates the field provided +relative to the top level struct. + + Usage: necsfield=InnerStructField.Field + +Field Greater Than Another Field + +Only valid for Numbers and time.Time types, this will validate the field value +against another fields value either within a struct or passed in field. +usage examples are for validation of a Start and End date: + +Example #1: + + // Validation on End field using: + validate.Struct Usage(gtfield=Start) + +Example #2: + + // Validating by field: + validate.FieldWithValue(start, end, "gtfield") + + +Field Greater Than Another Relative Field + +This does the same as gtfield except that it validates the field provided +relative to the top level struct. + + Usage: gtcsfield=InnerStructField.Field + +Field Greater Than or Equal To Another Field + +Only valid for Numbers and time.Time types, this will validate the field value +against another fields value either within a struct or passed in field. +usage examples are for validation of a Start and End date: + +Example #1: + + // Validation on End field using: + validate.Struct Usage(gtefield=Start) + +Example #2: + + // Validating by field: + validate.FieldWithValue(start, end, "gtefield") + +Field Greater Than or Equal To Another Relative Field + +This does the same as gtefield except that it validates the field provided relative +to the top level struct. + + Usage: gtecsfield=InnerStructField.Field + +Less Than Another Field + +Only valid for Numbers and time.Time types, this will validate the field value +against another fields value either within a struct or passed in field. +usage examples are for validation of a Start and End date: + +Example #1: + + // Validation on End field using: + validate.Struct Usage(ltfield=Start) + +Example #2: + + // Validating by field: + validate.FieldWithValue(start, end, "ltfield") + +Less Than Another Relative Field + +This does the same as ltfield except that it validates the field provided relative +to the top level struct. + + Usage: ltcsfield=InnerStructField.Field + +Less Than or Equal To Another Field + +Only valid for Numbers and time.Time types, this will validate the field value +against another fields value either within a struct or passed in field. +usage examples are for validation of a Start and End date: + +Example #1: + + // Validation on End field using: + validate.Struct Usage(ltefield=Start) + +Example #2: + + // Validating by field: + validate.FieldWithValue(start, end, "ltefield") + +Less Than or Equal To Another Relative Field + +This does the same as ltefield except that it validates the field provided relative +to the top level struct. + + Usage: ltecsfield=InnerStructField.Field + +Alpha Only + +This validates that a string value contains alpha characters only + + Usage: alpha + +Alphanumeric + +This validates that a string value contains alphanumeric characters only + + Usage: alphanum + +Numeric + +This validates that a string value contains a basic numeric value. +basic excludes exponents etc... + + Usage: numeric + +Hexadecimal String + +This validates that a string value contains a valid hexadecimal. + + Usage: hexadecimal + +Hexcolor String + +This validates that a string value contains a valid hex color including +hashtag (#) + + Usage: hexcolor + +RGB String + +This validates that a string value contains a valid rgb color + + Usage: rgb + +RGBA String + +This validates that a string value contains a valid rgba color + + Usage: rgba + +HSL String + +This validates that a string value contains a valid hsl color + + Usage: hsl + +HSLA String + +This validates that a string value contains a valid hsla color + + Usage: hsla + +E-mail String + +This validates that a string value contains a valid email +This may not conform to all possibilities of any rfc standard, but neither +does any email provider accept all posibilities. + + Usage: email + +URL String + +This validates that a string value contains a valid url +This will accept any url the golang request uri accepts but must contain +a schema for example http:// or rtmp:// + + Usage: url + +URI String + +This validates that a string value contains a valid uri +This will accept any uri the golang request uri accepts + + Usage: uri + +Base64 String + +This validates that a string value contains a valid base64 value. +Although an empty string is valid base64 this will report an empty string +as an error, if you wish to accept an empty string as valid you can use +this with the omitempty tag. + + Usage: base64 + +Contains + +This validates that a string value contains the substring value. + + Usage: contains=@ + +Contains Any + +This validates that a string value contains any Unicode code points +in the substring value. + + Usage: containsany=!@#? + +Contains Rune + +This validates that a string value contains the supplied rune value. + + Usage: containsrune=@ + +Excludes + +This validates that a string value does not contain the substring value. + + Usage: excludes=@ + +Excludes All + +This validates that a string value does not contain any Unicode code +points in the substring value. + + Usage: excludesall=!@#? + +Excludes Rune + +This validates that a string value does not contain the supplied rune value. + + Usage: excludesrune=@ + +International Standard Book Number + +This validates that a string value contains a valid isbn10 or isbn13 value. + + Usage: isbn + +International Standard Book Number 10 + +This validates that a string value contains a valid isbn10 value. + + Usage: isbn10 + +International Standard Book Number 13 + +This validates that a string value contains a valid isbn13 value. + + Usage: isbn13 + + +Universally Unique Identifier UUID + +This validates that a string value contains a valid UUID. + + Usage: uuid + +Universally Unique Identifier UUID v3 + +This validates that a string value contains a valid version 3 UUID. + + Usage: uuid3 + +Universally Unique Identifier UUID v4 + +This validates that a string value contains a valid version 4 UUID. + + Usage: uuid4 + +Universally Unique Identifier UUID v5 + +This validates that a string value contains a valid version 5 UUID. + + Usage: uuid5 + +ASCII + +This validates that a string value contains only ASCII characters. +NOTE: if the string is blank, this validates as true. + + Usage: ascii + +Printable ASCII + +This validates that a string value contains only printable ASCII characters. +NOTE: if the string is blank, this validates as true. + + Usage: asciiprint + +Multi-Byte Characters + +This validates that a string value contains one or more multibyte characters. +NOTE: if the string is blank, this validates as true. + + Usage: multibyte + +Data URL + +This validates that a string value contains a valid DataURI. +NOTE: this will also validate that the data portion is valid base64 + + Usage: datauri + +Latitude + +This validates that a string value contains a valid latitude. + + Usage: latitude + +Longitude + +This validates that a string value contains a valid longitude. + + Usage: longitude + +Social Security Number SSN + +This validates that a string value contains a valid U.S. Social Security Number. + + Usage: ssn + +Internet Protocol Address IP + +This validates that a string value contains a valid IP Adress. + + Usage: ip + +Internet Protocol Address IPv4 + +This validates that a string value contains a valid v4 IP Adress. + + Usage: ipv4 + +Internet Protocol Address IPv6 + +This validates that a string value contains a valid v6 IP Adress. + + Usage: ipv6 + +Classless Inter-Domain Routing CIDR + +This validates that a string value contains a valid CIDR Adress. + + Usage: cidr + +Classless Inter-Domain Routing CIDRv4 + +This validates that a string value contains a valid v4 CIDR Adress. + + Usage: cidrv4 + +Classless Inter-Domain Routing CIDRv6 + +This validates that a string value contains a valid v6 CIDR Adress. + + Usage: cidrv6 + +Transmission Control Protocol Address TCP + +This validates that a string value contains a valid resolvable TCP Adress. + + Usage: tcp_addr + +Transmission Control Protocol Address TCPv4 + +This validates that a string value contains a valid resolvable v4 TCP Adress. + + Usage: tcp4_addr + +Transmission Control Protocol Address TCPv6 + +This validates that a string value contains a valid resolvable v6 TCP Adress. + + Usage: tcp6_addr + +User Datagram Protocol Address UDP + +This validates that a string value contains a valid resolvable UDP Adress. + + Usage: udp_addr + +User Datagram Protocol Address UDPv4 + +This validates that a string value contains a valid resolvable v4 UDP Adress. + + Usage: udp4_addr + +User Datagram Protocol Address UDPv6 + +This validates that a string value contains a valid resolvable v6 UDP Adress. + + Usage: udp6_addr + +Internet Protocol Address IP + +This validates that a string value contains a valid resolvable IP Adress. + + Usage: ip_addr + +Internet Protocol Address IPv4 + +This validates that a string value contains a valid resolvable v4 IP Adress. + + Usage: ip4_addr + +Internet Protocol Address IPv6 + +This validates that a string value contains a valid resolvable v6 IP Adress. + + Usage: ip6_addr + +Unix domain socket end point Address + +This validates that a string value contains a valid Unix Adress. + + Usage: unix_addr + +Media Access Control Address MAC + +This validates that a string value contains a valid MAC Adress. + + Usage: mac + +Note: See Go's ParseMAC for accepted formats and types: + + http://golang.org/src/net/mac.go?s=866:918#L29 + +Alias Validators and Tags + +NOTE: When returning an error, the tag returned in "FieldError" will be +the alias tag unless the dive tag is part of the alias. Everything after the +dive tag is not reported as the alias tag. Also, the "ActualTag" in the before +case will be the actual tag within the alias that failed. + +Here is a list of the current built in alias tags: + + "iscolor" + alias is "hexcolor|rgb|rgba|hsl|hsla" (Usage: iscolor) + +Validator notes: + + regex + a regex validator won't be added because commas and = signs can be part + of a regex which conflict with the validation definitions. Although + workarounds can be made, they take away from using pure regex's. + Furthermore it's quick and dirty but the regex's become harder to + maintain and are not reusable, so it's as much a programming philosiphy + as anything. + + In place of this new validator functions should be created; a regex can + be used within the validator function and even be precompiled for better + efficiency within regexes.go. + + And the best reason, you can submit a pull request and we can keep on + adding to the validation library of this package! + +Panics + +This package panics when bad input is provided, this is by design, bad code like +that should not make it to production. + + type Test struct { + TestField string `validate:"nonexistantfunction=1"` + } + + t := &Test{ + TestField: "Test" + } + + validate.Struct(t) // this will panic +*/ +package validator diff --git a/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/logo.png b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/logo.png new file mode 100644 index 0000000..355000f Binary files /dev/null and b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/logo.png differ diff --git a/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/regexes.go b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/regexes.go new file mode 100644 index 0000000..83ae198 --- /dev/null +++ b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/regexes.go @@ -0,0 +1,59 @@ +package validator + +import "regexp" + +const ( + alphaRegexString = "^[a-zA-Z]+$" + alphaNumericRegexString = "^[a-zA-Z0-9]+$" + numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$" + numberRegexString = "^[0-9]+$" + hexadecimalRegexString = "^[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}])))\\.?$" + base64RegexString = "^(?:[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}$" + aSCIIRegexString = "^[\x00-\x7F]*$" + printableASCIIRegexString = "^[\x20-\x7E]*$" + multibyteRegexString = "[^\x00-\x7F]" + dataURIRegexString = "^data:.+\\/(.+);base64$" + latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" + longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" + sSNRegexString = `^\d{3}[- ]?\d{2}[- ]?\d{4}$` +) + +var ( + alphaRegex = regexp.MustCompile(alphaRegexString) + alphaNumericRegex = regexp.MustCompile(alphaNumericRegexString) + 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) + emailRegex = regexp.MustCompile(emailRegexString) + base64Regex = regexp.MustCompile(base64RegexString) + iSBN10Regex = regexp.MustCompile(iSBN10RegexString) + iSBN13Regex = regexp.MustCompile(iSBN13RegexString) + uUID3Regex = regexp.MustCompile(uUID3RegexString) + uUID4Regex = regexp.MustCompile(uUID4RegexString) + uUID5Regex = regexp.MustCompile(uUID5RegexString) + uUIDRegex = regexp.MustCompile(uUIDRegexString) + 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) +) diff --git a/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/util.go b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/util.go new file mode 100644 index 0000000..08f691c --- /dev/null +++ b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/util.go @@ -0,0 +1,253 @@ +package validator + +import ( + "reflect" + "strconv" + "strings" +) + +const ( + blank = "" + 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 ( + restrictedTags = map[string]struct{}{ + diveTag: {}, + existsTag: {}, + structOnlyTag: {}, + omitempty: {}, + skipValidationTag: {}, + utf8HexComma: {}, + utf8Pipe: {}, + noStructLevelTag: {}, + } +) + +// 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. +// it is exposed for use within you Custom Functions +func (v *Validate) ExtractType(current reflect.Value) (reflect.Value, reflect.Kind) { + + val, k, _ := v.extractTypeInternal(current, false) + return val, k +} + +// only exists to not break backward compatibility, needed to return the third param for a bug fix internally +func (v *Validate) extractTypeInternal(current reflect.Value, nullable bool) (reflect.Value, reflect.Kind, bool) { + + switch current.Kind() { + case reflect.Ptr: + + nullable = true + + if current.IsNil() { + return current, reflect.Ptr, nullable + } + + return v.extractTypeInternal(current.Elem(), nullable) + + case reflect.Interface: + + nullable = true + + if current.IsNil() { + return current, reflect.Interface, nullable + } + + return v.extractTypeInternal(current.Elem(), nullable) + + case reflect.Invalid: + return current, reflect.Invalid, nullable + + default: + + if v.hasCustomFuncs { + + if fn, ok := v.customTypeFuncs[current.Type()]; ok { + //read note 如果有自定义的类型校验器,则这边会先去调用一下类型校验器,然后在返回对应的结构体值,继续往回上一层,往下校验 + return v.extractTypeInternal(reflect.ValueOf(fn(current)), nullable) + } + } + + return current, current.Kind(), nullable + } +} + +// GetStructFieldOK 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) GetStructFieldOK(current reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) { + + current, kind := v.ExtractType(current) + + if kind == reflect.Invalid { + return current, kind, false + } + + if namespace == blank { + return current, kind, true + } + + switch kind { + + case reflect.Ptr, reflect.Interface: + + return current, kind, false + + case reflect.Struct: + + typ := current.Type() + fld := namespace + ns := namespace + + if typ != timeType && typ != timePtrType { + + idx := strings.Index(namespace, namespaceSeparator) + + if idx != -1 { + fld = namespace[:idx] + ns = namespace[idx+1:] + } else { + ns = blank + } + + bracketIdx := strings.Index(fld, leftBracket) + if bracketIdx != -1 { + fld = fld[:bracketIdx] + + ns = namespace[bracketIdx:] + } + + current = current.FieldByName(fld) + + return v.GetStructFieldOK(current, ns) + } + + 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 current, kind, false + } + + startIdx := idx2 + 1 + + if startIdx < len(namespace) { + if namespace[startIdx:startIdx+1] == namespaceSeparator { + startIdx++ + } + } + + return v.GetStructFieldOK(current.Index(arrIdx), namespace[startIdx:]) + + 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) + return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(i)), namespace[endIdx+1:]) + case reflect.Int8: + i, _ := strconv.ParseInt(key, 10, 8) + return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(int8(i))), namespace[endIdx+1:]) + case reflect.Int16: + i, _ := strconv.ParseInt(key, 10, 16) + return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(int16(i))), namespace[endIdx+1:]) + case reflect.Int32: + i, _ := strconv.ParseInt(key, 10, 32) + return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(int32(i))), namespace[endIdx+1:]) + case reflect.Int64: + i, _ := strconv.ParseInt(key, 10, 64) + return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(i)), namespace[endIdx+1:]) + case reflect.Uint: + i, _ := strconv.ParseUint(key, 10, 0) + return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(uint(i))), namespace[endIdx+1:]) + case reflect.Uint8: + i, _ := strconv.ParseUint(key, 10, 8) + return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(uint8(i))), namespace[endIdx+1:]) + case reflect.Uint16: + i, _ := strconv.ParseUint(key, 10, 16) + return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(uint16(i))), namespace[endIdx+1:]) + case reflect.Uint32: + i, _ := strconv.ParseUint(key, 10, 32) + return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(uint32(i))), namespace[endIdx+1:]) + case reflect.Uint64: + i, _ := strconv.ParseUint(key, 10, 64) + return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(i)), namespace[endIdx+1:]) + case reflect.Float32: + f, _ := strconv.ParseFloat(key, 32) + return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(float32(f))), namespace[endIdx+1:]) + case reflect.Float64: + f, _ := strconv.ParseFloat(key, 64) + return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(f)), namespace[endIdx+1:]) + case reflect.Bool: + b, _ := strconv.ParseBool(key) + return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(b)), namespace[endIdx+1:]) + + // reflect.Type = string + default: + return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(key)), namespace[endIdx+1:]) + } + } + + // 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 +} + +// 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 +} + +func panicIf(err error) { + if err != nil { + panic(err.Error()) + } +} diff --git a/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/validator.go b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/validator.go new file mode 100644 index 0000000..fde5326 --- /dev/null +++ b/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8/validator.go @@ -0,0 +1,829 @@ +/** + * Package validator + * + * MISC: + * - anonymous structs - they don't have names so expect the Struct name within StructErrors to be blank + * + */ + +package validator + +import ( + "bytes" + "errors" + "fmt" + "reflect" + "strings" + "sync" + "time" +) + +const ( + utf8HexComma = "0x2C" //read note 在tag里面代表【,】;需要这么书写,直接写【,】会被识别成分割符 + utf8Pipe = "0x7C" //read note 在tag里面代表【|】;需要这么书写,直接写【|】会被识别成条件符 + tagSeparator = "," //read note tag标签分割符 + orSeparator = "|" //read note tag判断条件【或】 + tagKeySeparator = "=" //read note tag里面需要等值 使用= + + structOnlyTag = "structonly" //read note 结构体上有的tagType,因为结构体的特殊性,他在获取cTag的时候是从第二个开始的 + _ // 也就是说第二个才是生效的,所以当只书写structonly的时候,他会略过这个tag,那么必须跟着前面的一个标签才行,比如说 `valid="require,structonly"`,这样才会生效 + noStructLevelTag = "nostructlevel" // 同上 + + omitempty = "omitempty" //read note 如果字段未设值则忽略它 + skipValidationTag = "-" //read note 忽略字段 + diveTag = "dive" //read note 深入到slice, array or map 里面去校验里面的字段是否正确,每一层都需要多一个【div】来标识 + _ //read note 比如说 [][]string "gt=0,dive,dive,required" gt是校验[][]string长度,第一个div是校验[]string,第二个div是校验string + existsTag = "exists" //read note 校验值存在即可(除非是nil则会报错),和require的区别是说,require对默认值也会报错 + + //read note 数组结构的名称组成 + arrayIndexFieldName = "%s" + leftBracket + "%d" + rightBracket + mapIndexFieldName = "%s" + leftBracket + "%v" + rightBracket + + //read note 字段校验错误信息打印 + fieldErrMsg = "Key: '%s' Error:Field validation for '%s' failed on the '%s' tag" + //read note 报错信息(非字段校验错误) + invalidValidation = "Invalid validation tag on field %s" + undefinedValidation = "Undefined validation function on field %s" + validatorNotInitialized = "Validator instance not initialized" + fieldNameRequired = "Field Name Required" + tagRequired = "Tag Required" +) + +var ( + timeType = reflect.TypeOf(time.Time{}) + timePtrType = reflect.TypeOf(&time.Time{}) + defaultCField = new(cField) +) + +// StructLevel contains all of the information and helper methods +// for reporting errors during struct level validation +type StructLevel struct { + TopStruct reflect.Value + CurrentStruct reflect.Value + errPrefix string + nsPrefix string + errs ValidationErrors + v *Validate +} + +// ReportValidationErrors accepts the key relative to the top level struct and validatin errors. +// Example: had a triple nested struct User, ContactInfo, Country and ran errs := validate.Struct(country) +// from within a User struct level validation would call this method like so: +// ReportValidationErrors("ContactInfo.", errs) +// NOTE: relativeKey can contain both the Field Relative and Custom name relative paths +// i.e. ReportValidationErrors("ContactInfo.|cInfo", errs) where cInfo represents say the JSON name of +// the relative path; this will be split into 2 variables in the next valiator version. +func (sl *StructLevel) ReportValidationErrors(relativeKey string, errs ValidationErrors) { + for _, e := range errs { + + idx := strings.Index(relativeKey, "|") + var rel string + var cRel string + + if idx != -1 { + rel = relativeKey[:idx] + cRel = relativeKey[idx+1:] + } else { + rel = relativeKey + } + + key := sl.errPrefix + rel + e.Field + + e.FieldNamespace = key + e.NameNamespace = sl.nsPrefix + cRel + e.Name + + sl.errs[key] = e + } +} + +// ReportError reports an error just by passing the field and tag information +// NOTE: tag can be an existing validation tag or just something you make up +// and precess on the flip side it's up to you. +func (sl *StructLevel) ReportError(field reflect.Value, fieldName string, customName string, tag string) { + + field, kind := sl.v.ExtractType(field) + + if fieldName == blank { + panic(fieldNameRequired) + } + + if customName == blank { + customName = fieldName + } + + if tag == blank { + panic(tagRequired) + } + + ns := sl.errPrefix + fieldName + + switch kind { + case reflect.Invalid: + sl.errs[ns] = &FieldError{ + FieldNamespace: ns, + NameNamespace: sl.nsPrefix + customName, + Name: customName, + Field: fieldName, + Tag: tag, + ActualTag: tag, + Param: blank, + Kind: kind, + } + default: + sl.errs[ns] = &FieldError{ + FieldNamespace: ns, + NameNamespace: sl.nsPrefix + customName, + Name: customName, + Field: fieldName, + Tag: tag, + ActualTag: tag, + Param: blank, + Value: field.Interface(), + Kind: kind, + Type: field.Type(), + } + } +} + +// Validate contains the validator settings passed in using the Config struct +type Validate struct { + tagName string //校验起作用的tag名 + fieldNameTag string // + validationFuncs map[string]Func //规则类型的校验 【tag标签】-> 校验规则 + structLevelFuncs map[reflect.Type]StructLevelFunc //规则结构体的校验 【结构体类型】-> 校验规则 + customTypeFuncs map[reflect.Type]CustomTypeFunc //类型校验器 【数据类型】-> 校验规则 + aliasValidators map[string]string //别名校验器 【别名匹配规则组合】-> 校验规则 + hasCustomFuncs bool //是否存在类型校验器 + hasAliasValidators bool //是否有别名校验器 + hasStructLevelFuncs bool //是否有结构体校验器 + tagCache *tagCache //tag对应的【校验规则方法】的缓存 + structCache *structCache //结构体对应的【校验规则方法】的缓存 + errsPool *sync.Pool //校验错误奖池 +} + +func (v *Validate) initCheck() { + if v == nil { + panic(validatorNotInitialized) + } +} + +// Config contains the options that a Validator instance will use. +// It is passed to the New() function +type Config struct { + TagName string + FieldNameTag string +} + +// 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{} + +// Func accepts all values needed for file and cross field validation +// v = validator instance, needed but some built in functions for it's custom types +// topStruct = top level struct when validating by struct otherwise nil +// currentStruct = current level struct when validating by struct otherwise optional comparison value +// field = field value for validation +// param = parameter used in validation i.e. gt=0 param would be 0 +type Func func(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldtype reflect.Type, fieldKind reflect.Kind, param string) bool + +// StructLevelFunc accepts all values needed for struct level validation +type StructLevelFunc func(v *Validate, structLevel *StructLevel) + +// ValidationErrors is a type of map[string]*FieldError +// it exists to allow for multiple errors to be passed from this library +// and yet still subscribe to the error interface +type ValidationErrors map[string]*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 map +func (ve ValidationErrors) Error() string { + + buff := bytes.NewBufferString(blank) + + for key, err := range ve { + buff.WriteString(fmt.Sprintf(fieldErrMsg, key, err.Field, err.Tag)) + buff.WriteString("\n") + } + + return strings.TrimSpace(buff.String()) +} + +// FieldError contains a single field's validation error along +// with other properties that may be needed for error message creation +type FieldError struct { + FieldNamespace string + NameNamespace string + Field string + Name string + Tag string + ActualTag string + Kind reflect.Kind + Type reflect.Type + Param string + Value interface{} +} + +// New creates a new Validate instance for use. +func New(config *Config) *Validate { + + //read note tag校验器缓存,初始化 + tc := new(tagCache) + tc.m.Store(make(map[string]*cTag)) + + //read note 结构体缓存,初始化一个Map,防止后面添加的时候报错 + sc := new(structCache) + sc.m.Store(make(map[reflect.Type]*cStruct)) + + v := &Validate{ + tagName: config.TagName, + fieldNameTag: config.FieldNameTag, + tagCache: tc, + structCache: sc, + //read note 自定义一个错误池 + errsPool: &sync.Pool{New: func() interface{} { + return ValidationErrors{} + }}} + + //read note 设置别名规则类(一组) 可参考: baked_in.go/bakedInAliasValidators + if len(v.aliasValidators) == 0 { + // must copy alias validators for separate validations to be used in each validator instance + v.aliasValidators = map[string]string{} + for k, val := range bakedInAliasValidators { + //read note 默认别名校验器注册 + v.RegisterAliasValidation(k, val) + } + } + + //read note 设置默认的校验方法.可参考: baked_in.go/bakedInValidators + if len(v.validationFuncs) == 0 { + // must copy validators for separate validations to be used in each instance + v.validationFuncs = map[string]Func{} + for k, val := range bakedInValidators { + //read note 默认tag校验器注册 + v.RegisterValidation(k, val) + } + } + + return v +} + +// 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.initCheck() + + if v.structLevelFuncs == nil { + v.structLevelFuncs = map[reflect.Type]StructLevelFunc{} + } + + for _, t := range types { + v.structLevelFuncs[reflect.TypeOf(t)] = fn + fmt.Println(reflect.TypeOf(t)) + } + + v.hasStructLevelFuncs = true +} + +// RegisterValidation adds a validation Func to a Validate's map of validators denoted by the key +// NOTE: if the key already exists, the previous validation function will be replaced. +// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation +func (v *Validate) RegisterValidation(key string, fn Func) error { + v.initCheck() + + if key == blank { + return errors.New("Function Key cannot be empty") + } + + if fn == nil { + return errors.New("Function cannot be empty") + } + + _, ok := restrictedTags[key] + + if ok || strings.ContainsAny(key, restrictedTagChars) { + panic(fmt.Sprintf(restrictedTagErr, key)) + } + + v.validationFuncs[key] = fn + + return nil +} + +// 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 +//read note 注册类型的校验器,应该在校验之前就注册完成,因为该注册方法不是线程安全的. +func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) { + v.initCheck() + + if v.customTypeFuncs == nil { + v.customTypeFuncs = map[reflect.Type]CustomTypeFunc{} + } + + for _, t := range types { + v.customTypeFuncs[reflect.TypeOf(t)] = fn + } + + v.hasCustomFuncs = true +} + +// RegisterAliasValidation registers a mapping of a single validationstag that +// defines a common or complex set of validation(s) to simplify adding validation +// to structs. NOTE: when returning an error the tag returned in FieldError will be +// the alias tag unless the dive tag is part of the alias; everything after the +// dive tag is not reported as the alias tag. Also the ActualTag in the before case +// will be the actual tag within the alias that failed. +// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation +func (v *Validate) RegisterAliasValidation(alias, tags string) { + v.initCheck() + + _, ok := restrictedTags[alias] + + if ok || strings.ContainsAny(alias, restrictedTagChars) { + panic(fmt.Sprintf(restrictedAliasErr, alias)) + } + + v.aliasValidators[alias] = tags + v.hasAliasValidators = true +} + +// Field validates a single field using tag style validation and returns nil or ValidationErrors as type error. +// You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors. +// NOTE: it returns ValidationErrors instead of a single FieldError because this can also +// validate Array, Slice and maps fields which may contain more than one error +//read note +func (v *Validate) Field(field interface{}, tag string) error { + v.initCheck() + + if len(tag) == 0 || tag == skipValidationTag { + return nil + } + + errs := v.errsPool.Get().(ValidationErrors) + fieldVal := reflect.ValueOf(field) + + //read note 从tag缓存中获取 tag标签对应的校验方法 + ctag, ok := v.tagCache.Get(tag) + if !ok { + //read note 加锁 + 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. + //read note 加锁之后在这边再获取一次,因为在上一次判断到加锁的过程中,可能有函数已经把对应方法加载进来了 + ctag, ok = v.tagCache.Get(tag) + if !ok { + //read note + ctag, _ = v.parseFieldTagsRecursive(tag, blank, blank, false) + //read note 进行标签设值 + v.tagCache.Set(tag, ctag) + } + } + + v.traverseField(fieldVal, fieldVal, fieldVal, blank, blank, errs, false, false, nil, nil, defaultCField, ctag) + + //read note 判断校验错误是否存在 + if len(errs) == 0 { + v.errsPool.Put(errs) + return nil + } + + return errs +} + +// FieldWithValue validates a single field, against another fields value using tag style validation and returns nil or ValidationErrors. +// You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors. +// NOTE: it returns ValidationErrors instead of a single FieldError because this can also +// validate Array, Slice and maps fields which may contain more than one error +func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string) error { + v.initCheck() + + if len(tag) == 0 || tag == skipValidationTag { + return nil + } + + errs := v.errsPool.Get().(ValidationErrors) + topVal := reflect.ValueOf(val) + + ctag, ok := v.tagCache.Get(tag) + if !ok { + 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, ok = v.tagCache.Get(tag) + if !ok { + ctag, _ = v.parseFieldTagsRecursive(tag, blank, blank, false) + v.tagCache.Set(tag, ctag) + } + } + + v.traverseField(topVal, topVal, reflect.ValueOf(field), blank, blank, errs, false, false, nil, nil, defaultCField, ctag) + + if len(errs) == 0 { + v.errsPool.Put(errs) + return nil + } + + return errs +} + +// StructPartial validates the fields passed in only, ignoring all others. +// Fields may be provided in a namespaced fashion relative to the struct provided +// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name and returns nil or ValidationErrors as error +// You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors. +func (v *Validate) StructPartial(current interface{}, fields ...string) error { + v.initCheck() + + sv, _ := v.ExtractType(reflect.ValueOf(current)) + name := sv.Type().Name() + m := map[string]struct{}{} + + if fields != nil { + for _, k := range fields { + + flds := strings.Split(k, namespaceSeparator) + if len(flds) > 0 { + + key := name + namespaceSeparator + for _, s := range flds { + + idx := strings.Index(s, leftBracket) + + if idx != -1 { + for idx != -1 { + key += s[:idx] + m[key] = struct{}{} + + idx2 := strings.Index(s, rightBracket) + idx2++ + key += s[idx:idx2] + m[key] = struct{}{} + s = s[idx2:] + idx = strings.Index(s, leftBracket) + } + } else { + + key += s + m[key] = struct{}{} + } + + key += namespaceSeparator + } + } + } + } + + errs := v.errsPool.Get().(ValidationErrors) + + v.ensureValidStruct(sv, sv, sv, blank, blank, errs, true, len(m) != 0, false, m, false) + + if len(errs) == 0 { + v.errsPool.Put(errs) + return nil + } + + return errs +} + +// 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 and returns nil or ValidationErrors as error +// You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors. +func (v *Validate) StructExcept(current interface{}, fields ...string) error { + v.initCheck() + + sv, _ := v.ExtractType(reflect.ValueOf(current)) + name := sv.Type().Name() + m := map[string]struct{}{} + + for _, key := range fields { + m[name+namespaceSeparator+key] = struct{}{} + } + + errs := v.errsPool.Get().(ValidationErrors) + + v.ensureValidStruct(sv, sv, sv, blank, blank, errs, true, len(m) != 0, true, m, false) + + if len(errs) == 0 { + v.errsPool.Put(errs) + return nil + } + + return errs +} + +// Struct validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified. +// it returns nil or ValidationErrors as error. +// You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors. +func (v *Validate) Struct(current interface{}) error { + v.initCheck() + + errs := v.errsPool.Get().(ValidationErrors) + sv := reflect.ValueOf(current) + + //read note 进行结构体校验 + v.ensureValidStruct(sv, sv, sv, blank, blank, errs, true, false, false, nil, false) + + //read note 校验之后,对校验错误进行处理. + if len(errs) == 0 { + v.errsPool.Put(errs) + return nil + } + + return errs +} + +func (v *Validate) ensureValidStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, useStructName bool, partial bool, exclude bool, includeExclude map[string]struct{}, isStructOnly bool) { + + //read note 指针处理成结构体Value(返回其指向的实际结构体) + if current.Kind() == reflect.Ptr && !current.IsNil() { + current = current.Elem() + } + + //read note 结构体类型校验. + if current.Kind() != reflect.Struct && current.Kind() != reflect.Interface { + panic("value passed for validation is not a struct") + } + + //read note 校验结构体 + v.tranverseStruct(topStruct, currentStruct, current, errPrefix, nsPrefix, errs, useStructName, partial, exclude, includeExclude, nil, nil) +} + +// tranverseStruct traverses a structs fields and then passes them to be validated by traverseField +func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, useStructName bool, partial bool, exclude bool, includeExclude map[string]struct{}, cs *cStruct, ct *cTag) { + + var ok bool + first := len(nsPrefix) == 0 + typ := current.Type() + + //read note 结构体校验器_缓存 + cs, ok = v.structCache.Get(typ) + if !ok { + cs = v.extractStructCache(current, typ.Name()) + } + + if useStructName { + errPrefix += cs.Name + namespaceSeparator + + if len(v.fieldNameTag) != 0 { + nsPrefix += cs.Name + namespaceSeparator + } + } + + // structonly tag present don't tranverseFields + // but must still check and run below struct level validation + // if present + //read note 【structonly】标签会忽略掉Field的校验 + if first || ct == nil || ct.typeof != typeStructOnly { + + for _, f := range cs.fields { + + if partial { + + _, ok = includeExclude[errPrefix+f.Name] + + if (ok && exclude) || (!ok && !exclude) { + continue + } + } + + v.traverseField(topStruct, currentStruct, current.Field(f.Idx), errPrefix, nsPrefix, errs, partial, exclude, includeExclude, cs, f, f.cTags) + } + } + + // check if any struct level validations, after all field validations already checked. + //read note 如果结构体层的校验存在的话,需要进行调用.(这边是规则类校验) + if cs.fn != nil { + cs.fn(v, &StructLevel{v: v, TopStruct: topStruct, CurrentStruct: current, errPrefix: errPrefix, nsPrefix: nsPrefix, errs: errs}) + } +} + +// 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(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, partial bool, exclude bool, includeExclude map[string]struct{}, cs *cStruct, cf *cField, ct *cTag) { + + //read note 处理Ptr、Interface、Invalid(validator自定义)、自定义结构体类型. + // 返回参数为:值 、 数据源类型、nullable(用来校验 【结构体】的【omiEmpty】标签...) + current, kind, nullable := v.extractTypeInternal(current, false) + var typ reflect.Type + + switch kind { + //read note 处理地址、Interface、Invalid是直接往校验错误池中添加一个Field错误? + case reflect.Ptr, reflect.Interface, reflect.Invalid: + + if ct == nil { + return + } + + if ct.typeof == typeOmitEmpty { + return + } + + if ct.hasTag { + + ns := errPrefix + cf.Name + + //read note 如果是Invalid,需要往校验错误池中添加一个Field错误 + if kind == reflect.Invalid { + errs[ns] = &FieldError{ + FieldNamespace: ns, + NameNamespace: nsPrefix + cf.AltName, + Name: cf.AltName, + Field: cf.Name, + Tag: ct.aliasTag, + ActualTag: ct.tag, + Param: ct.param, + Kind: kind, + } + return + } + + errs[ns] = &FieldError{ + FieldNamespace: ns, + NameNamespace: nsPrefix + cf.AltName, + Name: cf.AltName, + Field: cf.Name, + Tag: ct.aliasTag, + ActualTag: ct.tag, + Param: ct.param, + Value: current.Interface(), + Kind: kind, + Type: current.Type(), + } + + return + } + + case reflect.Struct: + typ = current.Type() + + if typ != timeType { + + //read note 对于结构体,这边有一个比较特殊的处理就是cTag是从第二个位置开始,也就是之前一直尝试structonly不成功,就是需要在structonly前面加一个tag才行。 + // 前一个对于struct应该是不生效才对,第二个才会对struct生效 + if ct != nil { + ct = ct.next + } + + if ct != nil && ct.typeof == typeNoStructLevel { + return + } + + //read note 如果上面的cTag没有生效/没有tagType为【typeNoStructLevel】的标签.就直接进入这个结构体 + v.tranverseStruct(topStruct, current, current, errPrefix+cf.Name+namespaceSeparator, nsPrefix+cf.AltName+namespaceSeparator, errs, false, partial, exclude, includeExclude, cs, ct) + return + } + } + + if !ct.hasTag { + return + } + + typ = current.Type() + + //read note 进行字段Field的校验,这边的话是一个for的循环,在不同的标签中去找到对应的处理方式 +OUTER: + for { + if ct == nil { + return + } + + switch ct.typeof { + + case typeExists: + ct = ct.next + continue + + case typeOmitEmpty: + + if !nullable && !HasValue(v, topStruct, currentStruct, current, typ, kind, blank) { + return + } + + ct = ct.next + continue + + case typeDive: + + ct = ct.next + + // traverse slice or map here + // or panic ;) + switch kind { + //read note 【dive】标签处理Slice 和 Array:往数组中的每一个元素进行处理 + case reflect.Slice, reflect.Array: + + for i := 0; i < current.Len(); i++ { + v.traverseField(topStruct, currentStruct, current.Index(i), errPrefix, nsPrefix, errs, partial, exclude, includeExclude, cs, &cField{Name: fmt.Sprintf(arrayIndexFieldName, cf.Name, i), AltName: fmt.Sprintf(arrayIndexFieldName, cf.AltName, i)}, ct) + } + //read note 【dive】标签处理 Map:往Map中的每一个元素进行处理 + case reflect.Map: + for _, key := range current.MapKeys() { + v.traverseField(topStruct, currentStruct, current.MapIndex(key), errPrefix, nsPrefix, errs, partial, exclude, includeExclude, cs, &cField{Name: fmt.Sprintf(mapIndexFieldName, cf.Name, key.Interface()), AltName: fmt.Sprintf(mapIndexFieldName, cf.AltName, key.Interface())}, ct) + } + //read note 【div】只能标注在 Slice 、 Map 、Array 上,不然就是报错. + 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: + + //read note 【|】的处理 + errTag := blank + + for { + + //read note 如果结果是true的话,因为【|】标签只要有一个成立就可以,所以可以直接排除掉其他 【|】标签了 + if ct.fn(v, topStruct, currentStruct, current, typ, kind, ct.param) { + + // drain rest of the 'or' values, then continue or leave + //read note 排除其他 【|】标签,直到下一个Field的非【|】标签再进入处理. + for { + + ct = ct.next + + if ct == nil { + return + } + + if ct.typeof != typeOr { + continue OUTER + } + } + } + + errTag += orSeparator + ct.tag + + //read note 没有下一个cTag的处理器.处理一i西安错误信息,返回. + if ct.next == nil { + // if we get here, no valid 'or' value and no more tags + + ns := errPrefix + cf.Name + + if ct.hasAlias { + errs[ns] = &FieldError{ + FieldNamespace: ns, + NameNamespace: nsPrefix + cf.AltName, + Name: cf.AltName, + Field: cf.Name, + Tag: ct.aliasTag, + ActualTag: ct.actualAliasTag, + Value: current.Interface(), + Type: typ, + Kind: kind, + } + } else { + errs[errPrefix+cf.Name] = &FieldError{ + FieldNamespace: ns, + NameNamespace: nsPrefix + cf.AltName, + Name: cf.AltName, + Field: cf.Name, + Tag: errTag[1:], + ActualTag: errTag[1:], + Value: current.Interface(), + Type: typ, + Kind: kind, + } + } + + return + } + + ct = ct.next + } + + default: + //read note 剩下的标签处理.类似一个循环的处理 + if !ct.fn(v, topStruct, currentStruct, current, typ, kind, ct.param) { + + ns := errPrefix + cf.Name + + errs[ns] = &FieldError{ + FieldNamespace: ns, + NameNamespace: nsPrefix + cf.AltName, + Name: cf.AltName, + Field: cf.Name, + Tag: ct.aliasTag, + ActualTag: ct.tag, + Value: current.Interface(), + Param: ct.param, + Type: typ, + Kind: kind, + } + + return + + } + + ct = ct.next + } + } +} diff --git a/SourceAnalysisAndTool/validator.v8/testValidator/Data.go b/SourceAnalysisAndTool/validator.v8/testValidator/Data.go new file mode 100644 index 0000000..57bcb23 --- /dev/null +++ b/SourceAnalysisAndTool/validator.v8/testValidator/Data.go @@ -0,0 +1,33 @@ +/* + * @Author : huangzj + * @Time : 2020/12/11 16:16 + * @Description: + */ + +package testValidator + +type Ower struct { + Age int `validate:"min=10"` +} + +type Cat struct { + Age int `validate:"min=10"` + Ower Ower +} + +type Man struct { + Name string `validate:"ipe"` +} + +type Woman struct { + Name string `validate:"diy=5"` +} + +type Person struct { + Name string + M map[string]int `validate:"min=10"` + H int `validate:"max=20"` + Exist bool `validate:"exists"` + Require bool `validate:"required"` + Cat Cat +} diff --git a/SourceAnalysisAndTool/validator.v8/testValidator/Validator_test.go b/SourceAnalysisAndTool/validator.v8/testValidator/Validator_test.go new file mode 100644 index 0000000..d6a98bc --- /dev/null +++ b/SourceAnalysisAndTool/validator.v8/testValidator/Validator_test.go @@ -0,0 +1,158 @@ +/* + * @Author : huangzj + * @Time : 2020/12/11 16:16 + * @Description: + */ + +package testValidator + +import ( + "Go-Tool/SourceAnalysisAndTool/validator.v8/sourceAnalysis/gopkg.in/go-playground/validator.v8" + "fmt" + "reflect" + "strconv" + "strings" + "testing" +) + +func TestValidator(t *testing.T) { + fmt.Println("测试别名校验器") + testAlias() + + fmt.Println() + fmt.Println("测试默认和自定义tag校验器") + testTag() + + fmt.Println() + fmt.Println("测试字段校验器") + testField() + + fmt.Println() + fmt.Println("测试结构体校验器") + testStruct() + +} + +func testStruct() { + //添加结构体校验,对结构体进行处理之后,validator的标签还是会起作用 + config := &validator.Config{ + TagName: "validate", + } + valid := validator.New(config) + valid.RegisterStructValidation(func(v *validator.Validate, structLevel *validator.StructLevel) { + c := structLevel.CurrentStruct.Interface().(Cat) + if c.Age < 100 { + fmt.Println("猫的岁数不能小于100") + } + }, Cat{}) + p := Person{ + Name: "192.168.0.1", + M: map[string]int{"A": 1}, + H: 2000, + Exist: false, + Require: false, + Cat: Cat{ + Age: 0, + Ower: Ower{}, + }, + } + + err := valid.Struct(p) + fmt.Println(err) +} + +func testField() { + //如果注册了对应的Field,则会调用自定义的方法,不会再使用validator定义的标签 + //这边测试了结构体和基本类型,都是如上述描述的这么处理 + config := &validator.Config{ + TagName: "validate", + } + valid := validator.New(config) + valid.RegisterAliasValidation("ipe", "ip|ipv4|ipv6") + + valid.RegisterCustomTypeFunc(func(field reflect.Value) interface{} { + d := field.Interface().(Cat) + if d.Age < 50 { + fmt.Println("狗的岁数不能小于50") + } + return field + }, Cat{}) + + p := Person{ + Name: "192.168.0.1", + M: map[string]int{"A": 1}, + H: 2000, + Exist: false, + Require: false, + Cat: Cat{ + Age: 0, + Ower: Ower{}, + }, + } + err := valid.Struct(p) + fmt.Println(err) + + valid.RegisterCustomTypeFunc(func(field reflect.Value) interface{} { + d := field.Interface().(int) + if d != 10 { + fmt.Println("测试一下") + } + return field + }, 1) + + err1 := valid.Struct(p) + fmt.Println(err1) + +} + +func testTag() { + w := Woman{ + Name: "my name is Woman", + } + + w1 := Woman{ + Name: "my name is wo-man", + } + + config := &validator.Config{ + TagName: "validate", + } + valid := validator.New(config) + _ = valid.RegisterValidation("diy", func(v *validator.Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldtype reflect.Type, fieldKind reflect.Kind, param string) bool { + s := field.Interface().(string) + + i, _ := strconv.ParseInt(param, 0, 64) + if int64(len(s)) < i || strings.Contains(s, "Woman") { + fmt.Println("校验一下") + return false + } + return true + }) + + err := valid.Struct(w) + fmt.Println(err) + + err1 := valid.Struct(w1) + fmt.Println(err1) +} + +func testAlias() { + m := Man{ + Name: "192.168.0.1", + } + + m1 := Man{ + Name: "213123123", + } + + config := &validator.Config{ + TagName: "validate", + } + valid := validator.New(config) + valid.RegisterAliasValidation("ipe", "ip|ipv4|ipv6") + err := valid.Struct(m) + fmt.Println(err) + + err1 := valid.Struct(m1) + fmt.Println(err1) +} diff --git a/go.mod b/go.mod index 7855d5e..eed629d 100644 --- a/go.mod +++ b/go.mod @@ -4,16 +4,18 @@ go 1.13 require ( github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75 // indirect - github.com/ahmetalpbalkan/go-linq v2.0.0-rc0+incompatible - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/ahmetalpbalkan/go-linq v2.0.0-rc0+incompatible // indirect + github.com/ahmetb/go-linq v2.0.0-rc0+incompatible github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 // indirect github.com/go-ini/ini v1.57.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/json-iterator/go v1.1.10 github.com/kardianos/govendor v1.0.9 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/stretchr/testify v1.5.1 // indirect golang.org/x/sys v0.0.0-20200428200454-593003d681fa // indirect golang.org/x/tools v0.0.0-20200429213335-127c98bd7927 // indirect + gopkg.in/go-playground/validator.v8 v8.18.2 // indirect gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/yaml.v2 v2.2.8 // indirect ) diff --git a/go.sum b/go.sum index c4f711e..1beaffa 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75 h1:xGHheKK44eC6K0u5X github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ= github.com/ahmetalpbalkan/go-linq v2.0.0-rc0+incompatible h1:8q+6K5jJ2RCTD7v7VxMfuUdHyU22GZi+qMY0hpLTnVc= github.com/ahmetalpbalkan/go-linq v2.0.0-rc0+incompatible/go.mod h1:IBvGwXyZVvXuN8KIbGCK53k+fzCKOmAdYhobMdJXOck= +github.com/ahmetb/go-linq v2.0.0-rc0+incompatible h1:H8yA1M8T+1/pyPRUwSOyc30MFrtiP9vgJ2s+lgdaQlM= +github.com/ahmetb/go-linq v2.0.0-rc0+incompatible/go.mod h1:PFffvbdbtw+QTB0WKRP0cNht7vnCfnGlEpak/DVg5cY= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -10,16 +12,24 @@ github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3N github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU= github.com/go-ini/ini v1.57.0 h1:Qwzj3wZQW+Plax5Ntj+GYe07DfGj1OH+aL1nMTMaNow= github.com/go-ini/ini v1.57.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/kardianos/govendor v1.0.9 h1:WOH3FcVI9eOgnIZYg96iwUwrL4eOVx+aQ66oyX2R8Yc= github.com/kardianos/govendor v1.0.9/go.mod h1:yvmR6q9ZZ7nSF5Wvh40v0wfP+3TwwL8zYQp+itoZSVM= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -45,6 +55,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= diff --git a/readme.md b/readme.md index b6c7ce3..c655803 100644 --- a/readme.md +++ b/readme.md @@ -66,6 +66,8 @@ 2020/12/7 :添加container包使用示例和源码分析 +2020/12/14:添加validator.v8源码解析和使用示例 + # mod vendor模式加载包 通过go mod的方式加载的github上面的包会有报红的问题,但是包本身是可以运行的,这样就是会有一个问题,如果你想要点击去看方法的内容,没办法做到 diff --git a/vendor/github.com/ahmetb/go-linq/.travis.yml b/vendor/github.com/ahmetb/go-linq/.travis.yml index 157e59f..6587753 100644 --- a/vendor/github.com/ahmetb/go-linq/.travis.yml +++ b/vendor/github.com/ahmetb/go-linq/.travis.yml @@ -2,13 +2,14 @@ sudo: false language: go go: + - 1.5 - 1.7 before_install: - go get github.com/mattn/goveralls - go get golang.org/x/tools/cmd/cover - go get -u github.com/golang/lint/golint - + script: - go vet -x ./... - golint ./... diff --git a/vendor/github.com/ahmetb/go-linq/LICENSE b/vendor/github.com/ahmetb/go-linq/LICENSE index c1ab55f..71dd824 100644 --- a/vendor/github.com/ahmetb/go-linq/LICENSE +++ b/vendor/github.com/ahmetb/go-linq/LICENSE @@ -1,201 +1,14 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +Copyright 2016 Ahmet Alp Balkan - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - 1. Definitions. + http://www.apache.org/licenses/LICENSE-2.0 - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2016 Ahmet Alp Balkan - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/ahmetb/go-linq/README.md b/vendor/github.com/ahmetb/go-linq/README.md index 4e9f12b..475464c 100644 --- a/vendor/github.com/ahmetb/go-linq/README.md +++ b/vendor/github.com/ahmetb/go-linq/README.md @@ -1,21 +1,16 @@ # go-linq [![GoDoc](https://godoc.org/github.com/ahmetalpbalkan/go-linq?status.svg)](https://godoc.org/github.com/ahmetalpbalkan/go-linq) [![Build Status](https://travis-ci.org/ahmetalpbalkan/go-linq.svg?branch=master)](https://travis-ci.org/ahmetalpbalkan/go-linq) [![Coverage Status](https://coveralls.io/repos/github/ahmetalpbalkan/go-linq/badge.svg?branch=master)](https://coveralls.io/github/ahmetalpbalkan/go-linq?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/ahmetalpbalkan/go-linq)](https://goreportcard.com/report/github.com/ahmetalpbalkan/go-linq) A powerful language integrated query (LINQ) library for Go. -* Written in vanilla Go, no dependencies! -* Complete lazy evaluation with iterator pattern +* Written in vanilla Go! * Safe for concurrent use -* Supports generic functions to make your code cleaner and free of type assertions +* Complete lazy evaluation with iterator pattern * Supports arrays, slices, maps, strings, channels and custom collections +(collection needs to implement `Iterable` interface and element - `Comparable` +interface) ## Installation $ go get github.com/ahmetalpbalkan/go-linq -We recommend using a dependency manager (e.g. [govendor][govendor] or -[godep][godep]) to maintain a local copy of this package in your project. - -[govendor]: https://github.com/kardianos/govendor -[godep]: https://github.com/tools/godep/ - > :warning: :warning: `go-linq` has recently introduced _breaking API changes_ > with v2.0.0. See [release notes](#release-notes) for details. v2.0.0 comes with > a refined interface, dramatically increased performance and memory efficiency, @@ -31,20 +26,16 @@ Usage is as easy as chaining methods like: `From(slice)` `.Where(predicate)` `.Select(selector)` `.Union(data)` -**Example 1: Find all owners of cars manufactured after 2015** - +**Example: Find all owners of cars manufactured from 2015** ```go import . "github.com/ahmetalpbalkan/go-linq" type Car struct { - year int + id, year int owner, model string } -... - - -var owners []string +owners := []string{} From(cars).Where(func(c interface{}) bool { return c.(Car).year >= 2015 @@ -53,21 +44,7 @@ From(cars).Where(func(c interface{}) bool { }).ToSlice(&owners) ``` -Or, you can use generic functions, like `WhereT` and `SelectT` to simplify your code -(at a performance penalty): - -```go -var owners []string - -From(cars).WhereT(func(c Car) bool { - return c.year >= 2015 -}).SelectT(func(c Car) string { - return c.owner -}).ToSlice(&owners) -``` - -**Example 2: Find the author who has written the most books** - +**Example: Find the author who has written the most books** ```go import . "github.com/ahmetalpbalkan/go-linq" @@ -94,8 +71,7 @@ author := From(books).SelectMany( // make a flat array of authors }).First() // take the first author ``` -**Example 3: Implement a custom method that leaves only values greater than the specified threshold** - +**Example: Implement a custom method that leaves only values greater than the specified threshold** ```go type MyQuery Query @@ -120,67 +96,10 @@ func (q MyQuery) GreaterThan(threshold int) Query { result := MyQuery(Range(1,10)).GreaterThan(5).Results() ``` -## Generic Functions - -Although Go doesn't implement generics, with some reflection tricks, you can use go-linq without -typing `interface{}`s and type assertions. This will introduce a performance penalty (5x-10x slower) -but will yield in a cleaner and more readable code. - -Methods with `T` suffix (such as `WhereT`) accept functions with generic types. So instead of - - .Select(func(v interface{}) interface{} {...}) - -you can type: - - .SelectT(func(v YourType) YourOtherType {...}) - -This will make your code free of `interface{}` and type assertions. - -**Example 4: "MapReduce" in a slice of string sentences to list the top 5 most used words using generic functions** - -```go -var results []string - -From(sentences). - // split sentences to words - SelectManyT(func(sentence string) Query { - return From(strings.Split(sentence, " ")) - }). - // group the words - GroupByT( - func(word string) string { return word }, - func(word string) string { return word }, - ). - // order by count - OrderByDescendingT(func(wordGroup Group) int { - return len(wordGroup.Group) - }). - // order by the word - ThenByT(func(wordGroup Group) string { - return wordGroup.Key.(string) - }). - Take(5). // take the top 5 - // project the words using the index as rank - SelectIndexedT(func(index int, wordGroup Group) string { - return fmt.Sprintf("Rank: #%d, Word: %s, Counts: %d", index+1, wordGroup.Key, len(wordGroup.Group)) - }). - ToSlice(&results) -``` - -**More examples** can be found in the [documentation](https://godoc.org/github.com/ahmetalpbalkan/go-linq). +**More examples** can be found in [documentation](https://godoc.org/github.com/ahmetalpbalkan/go-linq). ## Release Notes - ~~~ -v3.0.0 (2017-01-10) -* Breaking change: ToSlice() now overwrites existing slice starting - from index 0 and grows/reslices it as needed. -* Generic methods support (thanks @cleitonmarx!) - - Accepting parametrized functions was originally proposed in #26 - - You can now avoid type assertions and interface{}s - - Functions with generic methods are named as "MethodNameT" and - signature for the existing LINQ methods are unchanged. -* Added ForEach(), ForEachIndexed() and AggregateWithSeedBy(). v2.0.0 (2016-09-02) * IMPORTANT: This release is a BREAKING CHANGE. The old version diff --git a/vendor/github.com/ahmetb/go-linq/aggregate.go b/vendor/github.com/ahmetb/go-linq/aggregate.go index bb1975d..a8658c5 100644 --- a/vendor/github.com/ahmetb/go-linq/aggregate.go +++ b/vendor/github.com/ahmetb/go-linq/aggregate.go @@ -2,15 +2,16 @@ package linq // Aggregate applies an accumulator function over a sequence. // -// Aggregate method makes it simple to perform a calculation over a sequence of -// values. This method works by calling f() one time for each element in source -// except the first one. Each time f() is called, Aggregate passes both the -// element from the sequence and an aggregated value (as the first argument to -// f()). The first element of source is used as the initial aggregate value. The -// result of f() replaces the previous aggregated value. +// Aggregate method makes it simple to perform a calculation over a sequence of values. +// This method works by calling f() one time for each element in source +// except the first one. Each time f() is called, Aggregate passes both +// the element from the sequence and an aggregated value (as the first argument to f()). +// The first element of source is used as the initial aggregate value. +// The result of f() replaces the previous aggregated value. // // Aggregate returns the final result of f(). -func (q Query) Aggregate(f func(interface{}, interface{}) interface{}) interface{} { +func (q Query) Aggregate( + f func(interface{}, interface{}) interface{}) interface{} { next := q.Iterate() result, any := next() @@ -25,40 +26,21 @@ func (q Query) Aggregate(f func(interface{}, interface{}) interface{}) interface return result } -// AggregateT is the typed version of Aggregate. +// AggregateWithSeed applies an accumulator function over a sequence. +// The specified seed value is used as the initial accumulator value. // -// - f is of type: func(TSource, TSource) TSource -// -// NOTE: Aggregate has better performance than AggregateT. -func (q Query) AggregateT(f interface{}) interface{} { - fGenericFunc, err := newGenericFunc( - "AggregateT", "f", f, - simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - fFunc := func(result interface{}, current interface{}) interface{} { - return fGenericFunc.Call(result, current) - } - - return q.Aggregate(fFunc) -} - -// AggregateWithSeed applies an accumulator function over a sequence. The -// specified seed value is used as the initial accumulator value. -// -// Aggregate method makes it simple to perform a calculation over a sequence of -// values. This method works by calling f() one time for each element in source -// except the first one. Each time f() is called, Aggregate passes both the -// element from the sequence and an aggregated value (as the first argument to -// f()). The value of the seed parameter is used as the initial aggregate value. +// Aggregate method makes it simple to perform a calculation over a sequence of values. +// This method works by calling f() one time for each element in source +// except the first one. Each time f() is called, Aggregate passes both +// the element from the sequence and an aggregated value (as the first argument to f()). +// The value of the seed parameter is used as the initial aggregate value. // The result of f() replaces the previous aggregated value. // // Aggregate returns the final result of f(). -func (q Query) AggregateWithSeed(seed interface{}, - f func(interface{}, interface{}) interface{}) interface{} { +func (q Query) AggregateWithSeed( + seed interface{}, + f func(interface{}, interface{}) interface{}, +) interface{} { next := q.Iterate() result := seed @@ -69,90 +51,3 @@ func (q Query) AggregateWithSeed(seed interface{}, return result } - -// AggregateWithSeedT is the typed version of AggregateWithSeed. -// -// - f is of type "func(TAccumulate, TSource) TAccumulate" -// -// NOTE: AggregateWithSeed has better performance than -// AggregateWithSeedT. -func (q Query) AggregateWithSeedT(seed interface{}, - f interface{}) interface{} { - fGenericFunc, err := newGenericFunc( - "AggregateWithSeed", "f", f, - simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - fFunc := func(result interface{}, current interface{}) interface{} { - return fGenericFunc.Call(result, current) - } - - return q.AggregateWithSeed(seed, fFunc) -} - -// AggregateWithSeedBy applies an accumulator function over a sequence. The -// specified seed value is used as the initial accumulator value, and the -// specified function is used to select the result value. -// -// Aggregate method makes it simple to perform a calculation over a sequence of -// values. This method works by calling f() one time for each element in source. -// Each time func is called, Aggregate passes both the element from the sequence -// and an aggregated value (as the first argument to func). The value of the -// seed parameter is used as the initial aggregate value. The result of func -// replaces the previous aggregated value. -// -// The final result of func is passed to resultSelector to obtain the final -// result of Aggregate. -func (q Query) AggregateWithSeedBy(seed interface{}, - f func(interface{}, interface{}) interface{}, - resultSelector func(interface{}) interface{}) interface{} { - - next := q.Iterate() - result := seed - - for current, ok := next(); ok; current, ok = next() { - result = f(result, current) - } - - return resultSelector(result) -} - -// AggregateWithSeedByT is the typed version of AggregateWithSeedBy. -// -// - f is of type "func(TAccumulate, TSource) TAccumulate" -// - resultSelectorFn is of type "func(TAccumulate) TResult" -// -// NOTE: AggregateWithSeedBy has better performance than -// AggregateWithSeedByT. -func (q Query) AggregateWithSeedByT(seed interface{}, - f interface{}, - resultSelectorFn interface{}) interface{} { - fGenericFunc, err := newGenericFunc( - "AggregateWithSeedByT", "f", f, - simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - fFunc := func(result interface{}, current interface{}) interface{} { - return fGenericFunc.Call(result, current) - } - - resultSelectorGenericFunc, err := newGenericFunc( - "AggregateWithSeedByT", "resultSelectorFn", resultSelectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - resultSelectorFunc := func(result interface{}) interface{} { - return resultSelectorGenericFunc.Call(result) - } - - return q.AggregateWithSeedBy(seed, fFunc, resultSelectorFunc) -} diff --git a/vendor/github.com/ahmetb/go-linq/compare.go b/vendor/github.com/ahmetb/go-linq/compare.go index 3e0ffbf..c78e4aa 100644 --- a/vendor/github.com/ahmetb/go-linq/compare.go +++ b/vendor/github.com/ahmetb/go-linq/compare.go @@ -2,21 +2,21 @@ package linq type comparer func(interface{}, interface{}) int -// Comparable is an interface that has to be implemented by a custom collection -// elememts in order to work with linq. +// Comparable is an interface that has to be implemented by a +// custom collection elememts in order to work with linq. // // Example: -// func (f foo) CompareTo(c Comparable) int { -// a, b := f.f1, c.(foo).f1 +// func (f foo) CompareTo(c Comparable) int { +// a, b := f.f1, c.(foo).f1 // -// if a < b { -// return -1 -// } else if a > b { -// return 1 -// } +// if a < b { +// return -1 +// } else if a > b { +// return 1 +// } // -// return 0 -// } +// return 0 +// } type Comparable interface { CompareTo(Comparable) int } diff --git a/vendor/github.com/ahmetb/go-linq/concat.go b/vendor/github.com/ahmetb/go-linq/concat.go index 3101c56..6210126 100644 --- a/vendor/github.com/ahmetb/go-linq/concat.go +++ b/vendor/github.com/ahmetb/go-linq/concat.go @@ -1,7 +1,7 @@ package linq -// Append inserts an item to the end of a collection, so it becomes the last -// item. +// Append inserts an item to the end of a collection, +// so it becomes the last item. func (q Query) Append(item interface{}) Query { return Query{ Iterate: func() Iterator { @@ -28,8 +28,8 @@ func (q Query) Append(item interface{}) Query { // Concat concatenates two collections. // // The Concat method differs from the Union method because the Concat method -// returns all the original elements in the input sequences. The Union method -// returns only unique elements. +// returns all the original elements in the input sequences. +// The Union method returns only unique elements. func (q Query) Concat(q2 Query) Query { return Query{ Iterate: func() Iterator { @@ -53,8 +53,8 @@ func (q Query) Concat(q2 Query) Query { } } -// Prepend inserts an item to the beginning of a collection, so it becomes the -// first item. +// Prepend inserts an item to the beginning of a collection, +// so it becomes the first item. func (q Query) Prepend(item interface{}) Query { return Query{ Iterate: func() Iterator { diff --git a/vendor/github.com/ahmetb/go-linq/distinct.go b/vendor/github.com/ahmetb/go-linq/distinct.go index aaef37a..7a32c31 100644 --- a/vendor/github.com/ahmetb/go-linq/distinct.go +++ b/vendor/github.com/ahmetb/go-linq/distinct.go @@ -1,7 +1,7 @@ package linq -// Distinct method returns distinct elements from a collection. The result is an -// unordered collection that contains no duplicate values. +// Distinct method returns distinct elements from a collection. +// The result is an unordered collection that contains no duplicate values. func (q Query) Distinct() Query { return Query{ Iterate: func() Iterator { @@ -22,11 +22,11 @@ func (q Query) Distinct() Query { } } -// Distinct method returns distinct elements from a collection. The result is an -// ordered collection that contains no duplicate values. +// Distinct method returns distinct elements from a collection. +// The result is an ordered collection that contains no duplicate values. // // NOTE: Distinct method on OrderedQuery type has better performance than -// Distinct method on Query type. +// Distinct method on Query type func (oq OrderedQuery) Distinct() OrderedQuery { return OrderedQuery{ orders: oq.orders, @@ -73,26 +73,3 @@ func (q Query) DistinctBy(selector func(interface{}) interface{}) Query { }, } } - -// DistinctByT is the typed version of DistinctBy. -// -// - selectorFn is of type "func(TSource) TSource". -// -// NOTE: DistinctBy has better performance than DistinctByT. -func (q Query) DistinctByT(selectorFn interface{}) Query { - selectorFunc, ok := selectorFn.(func(interface{}) interface{}) - if !ok { - selectorGenericFunc, err := newGenericFunc( - "DistinctByT", "selectorFn", selectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - selectorFunc = func(item interface{}) interface{} { - return selectorGenericFunc.Call(item) - } - } - return q.DistinctBy(selectorFunc) -} diff --git a/vendor/github.com/ahmetb/go-linq/doc.go b/vendor/github.com/ahmetb/go-linq/doc.go index c89c4fe..8231f70 100644 --- a/vendor/github.com/ahmetb/go-linq/doc.go +++ b/vendor/github.com/ahmetb/go-linq/doc.go @@ -1,6 +1,5 @@ -// Package linq provides methods for querying and manipulating slices, arrays, -// maps, strings, channels and collections. +// Package linq provides methods for querying and manipulating +// slices, arrays, maps, strings, channels and collections. // -// Authors: Alexander Kalankhodzhaev (kalan), Ahmet Alp Balkan, Cleiton Marques -// Souza. +// Authors: Alexander Kalankhodzhaev (kalan), Ahmet Alp Balkan package linq diff --git a/vendor/github.com/ahmetb/go-linq/except.go b/vendor/github.com/ahmetb/go-linq/except.go index d146992..c34a6b4 100644 --- a/vendor/github.com/ahmetb/go-linq/except.go +++ b/vendor/github.com/ahmetb/go-linq/except.go @@ -1,7 +1,8 @@ package linq -// Except produces the set difference of two sequences. The set difference is -// the members of the first sequence that don't appear in the second sequence. +// Except produces the set difference of two sequences. +// The set difference is the members of the first sequence +// that don't appear in the second sequence. func (q Query) Except(q2 Query) Query { return Query{ Iterate: func() Iterator { @@ -26,11 +27,12 @@ func (q Query) Except(q2 Query) Query { } } -// ExceptBy invokes a transform function on each element of a collection and -// produces the set difference of two sequences. The set difference is the -// members of the first sequence that don't appear in the second sequence. -func (q Query) ExceptBy(q2 Query, - selector func(interface{}) interface{}) Query { +// ExceptBy invokes a transform function on each element of a collection +// and produces the set difference of two sequences. +// The set difference is the members of the first sequence +// that don't appear in the second sequence. +func (q Query) ExceptBy( + q2 Query, selector func(interface{}) interface{}) Query { return Query{ Iterate: func() Iterator { next := q.Iterate() @@ -55,25 +57,3 @@ func (q Query) ExceptBy(q2 Query, }, } } - -// ExceptByT is the typed version of ExceptBy. -// -// - selectorFn is of type "func(TSource) TSource" -// -// NOTE: ExceptBy has better performance than ExceptByT. -func (q Query) ExceptByT(q2 Query, - selectorFn interface{}) Query { - selectorGenericFunc, err := newGenericFunc( - "ExceptByT", "selectorFn", selectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - selectorFunc := func(item interface{}) interface{} { - return selectorGenericFunc.Call(item) - } - - return q.ExceptBy(q2, selectorFunc) -} diff --git a/vendor/github.com/ahmetb/go-linq/from.go b/vendor/github.com/ahmetb/go-linq/from.go index 21ee9f4..56ab451 100644 --- a/vendor/github.com/ahmetb/go-linq/from.go +++ b/vendor/github.com/ahmetb/go-linq/from.go @@ -5,30 +5,30 @@ import "reflect" // Iterator is an alias for function to iterate over data. type Iterator func() (item interface{}, ok bool) -// Query is the type returned from query functions. It can be iterated manually -// as shown in the example. +// Query is the type returned from query functions. +// It can be iterated manually as shown in the example. type Query struct { Iterate func() Iterator } -// KeyValue is a type that is used to iterate over a map (if query is created -// from a map). This type is also used by ToMap() method to output result of a -// query into a map. +// KeyValue is a type that is used to iterate over a map +// (if query is created from a map). This type is also used by +// ToMap() method to output result of a query into a map. type KeyValue struct { Key interface{} Value interface{} } -// Iterable is an interface that has to be implemented by a custom collection in -// order to work with linq. +// Iterable is an interface that has to be implemented by a +// custom collection in order to work with linq. type Iterable interface { Iterate() Iterator } -// From initializes a linq query with passed slice, array or map as the source. -// String, channel or struct implementing Iterable interface can be used as an -// input. In this case From delegates it to FromString, FromChannel and -// FromIterable internally. +// From initializes a linq query with passed slice, array or map +// as the source. String, channel or struct implementing Iterable +// interface can be used as an input. In this case From delegates it +// to FromString, FromChannel and FromIterable internally. func From(source interface{}) Query { src := reflect.ValueOf(source) @@ -84,8 +84,8 @@ func From(source interface{}) Query { } } -// FromChannel initializes a linq query with passed channel, linq iterates over -// channel until it is closed. +// FromChannel initializes a linq query with passed channel, +// linq iterates over channel until it is closed. func FromChannel(source <-chan interface{}) Query { return Query{ Iterate: func() Iterator { @@ -97,8 +97,8 @@ func FromChannel(source <-chan interface{}) Query { } } -// FromString initializes a linq query with passed string, linq iterates over -// runes of string. +// FromString initializes a linq query with passed string, +// linq iterates over runes of string. func FromString(source string) Query { runes := []rune(source) len := len(runes) @@ -120,8 +120,8 @@ func FromString(source string) Query { } } -// FromIterable initializes a linq query with custom collection passed. This -// collection has to implement Iterable interface, linq iterates over items, +// FromIterable initializes a linq query with custom collection passed. +// This collection has to implement Iterable interface, linq iterates over items, // that has to implement Comparable interface or be basic types. func FromIterable(source Iterable) Query { return Query{ diff --git a/vendor/github.com/ahmetb/go-linq/genericfunc.go b/vendor/github.com/ahmetb/go-linq/genericfunc.go deleted file mode 100644 index 77cba2d..0000000 --- a/vendor/github.com/ahmetb/go-linq/genericfunc.go +++ /dev/null @@ -1,138 +0,0 @@ -package linq - -import ( - "fmt" - "reflect" - "strings" -) - -// genericType represents a any reflect.Type. -type genericType int - -var genericTp = reflect.TypeOf(new(genericType)).Elem() - -// functionCache keeps genericFunc reflection objects in cache. -type functionCache struct { - MethodName string - ParamName string - FnValue reflect.Value - FnType reflect.Type - TypesIn []reflect.Type - TypesOut []reflect.Type -} - -// genericFunc is a type used to validate and call dynamic functions. -type genericFunc struct { - Cache *functionCache -} - -// Call calls a dynamic function. -func (g *genericFunc) Call(params ...interface{}) interface{} { - paramsIn := make([]reflect.Value, len(params)) - for i, param := range params { - paramsIn[i] = reflect.ValueOf(param) - } - paramsOut := g.Cache.FnValue.Call(paramsIn) - if len(paramsOut) >= 1 { - return paramsOut[0].Interface() - } - return nil -} - -// newGenericFunc instantiates a new genericFunc pointer -func newGenericFunc(methodName, paramName string, fn interface{}, validateFunc func(*functionCache) error) (*genericFunc, error) { - cache := &functionCache{} - cache.FnValue = reflect.ValueOf(fn) - - if cache.FnValue.Kind() != reflect.Func { - return nil, fmt.Errorf("%s: parameter [%s] is not a function type. It is a '%s'", methodName, paramName, cache.FnValue.Type()) - } - cache.MethodName = methodName - cache.ParamName = paramName - cache.FnType = cache.FnValue.Type() - numTypesIn := cache.FnType.NumIn() - cache.TypesIn = make([]reflect.Type, numTypesIn) - for i := 0; i < numTypesIn; i++ { - cache.TypesIn[i] = cache.FnType.In(i) - } - - numTypesOut := cache.FnType.NumOut() - cache.TypesOut = make([]reflect.Type, numTypesOut) - for i := 0; i < numTypesOut; i++ { - cache.TypesOut[i] = cache.FnType.Out(i) - } - if err := validateFunc(cache); err != nil { - return nil, err - } - - return &genericFunc{Cache: cache}, nil -} - -// simpleParamValidator creates a function to validate genericFunc based in the -// In and Out function parameters. -func simpleParamValidator(In []reflect.Type, Out []reflect.Type) func(cache *functionCache) error { - return func(cache *functionCache) error { - var isValid = func() bool { - if In != nil { - if len(In) != len(cache.TypesIn) { - return false - } - for i, paramIn := range In { - if paramIn != genericTp && paramIn != cache.TypesIn[i] { - return false - } - } - } - if Out != nil { - if len(Out) != len(cache.TypesOut) { - return false - } - for i, paramOut := range Out { - if paramOut != genericTp && paramOut != cache.TypesOut[i] { - return false - } - } - } - return true - } - - if !isValid() { - return fmt.Errorf("%s: parameter [%s] has a invalid function signature. Expected: '%s', actual: '%s'", cache.MethodName, cache.ParamName, formatFnSignature(In, Out), formatFnSignature(cache.TypesIn, cache.TypesOut)) - } - return nil - } -} - -// newElemTypeSlice creates a slice of items elem types. -func newElemTypeSlice(items ...interface{}) []reflect.Type { - typeList := make([]reflect.Type, len(items)) - for i, item := range items { - typeItem := reflect.TypeOf(item) - if typeItem.Kind() == reflect.Ptr { - typeList[i] = typeItem.Elem() - } - } - return typeList -} - -// formatFnSignature formats the func signature based in the parameters types. -func formatFnSignature(In []reflect.Type, Out []reflect.Type) string { - paramInNames := make([]string, len(In)) - for i, typeIn := range In { - if typeIn == genericTp { - paramInNames[i] = "T" - } else { - paramInNames[i] = typeIn.String() - } - - } - paramOutNames := make([]string, len(Out)) - for i, typeOut := range Out { - if typeOut == genericTp { - paramOutNames[i] = "T" - } else { - paramOutNames[i] = typeOut.String() - } - } - return fmt.Sprintf("func(%s)%s", strings.Join(paramInNames, ","), strings.Join(paramOutNames, ",")) -} diff --git a/vendor/github.com/ahmetb/go-linq/groupby.go b/vendor/github.com/ahmetb/go-linq/groupby.go index face510..2affbf8 100644 --- a/vendor/github.com/ahmetb/go-linq/groupby.go +++ b/vendor/github.com/ahmetb/go-linq/groupby.go @@ -6,11 +6,14 @@ type Group struct { Group []interface{} } -// GroupBy method groups the elements of a collection according to a specified -// key selector function and projects the elements for each group by using a -// specified function. -func (q Query) GroupBy(keySelector func(interface{}) interface{}, - elementSelector func(interface{}) interface{}) Query { +// GroupBy method groups the elements of a collection according +// to a specified key selector function and projects the elements for each group +// by using a specified function. +func (q Query) GroupBy( + keySelector func(interface{}) interface{}, + elementSelector func(interface{}) interface{}, +) Query { + return Query{ func() Iterator { next := q.Iterate() @@ -43,39 +46,3 @@ func (q Query) GroupBy(keySelector func(interface{}) interface{}, }, } } - -// GroupByT is the typed version of GroupBy. -// -// - keySelectorFn is of type "func(TSource) TKey" -// - elementSelectorFn is of type "func(TSource) TElement" -// -// NOTE: GroupBy has better performance than GroupByT. -func (q Query) GroupByT(keySelectorFn interface{}, - elementSelectorFn interface{}) Query { - keySelectorGenericFunc, err := newGenericFunc( - "GroupByT", "keySelectorFn", keySelectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - keySelectorFunc := func(item interface{}) interface{} { - return keySelectorGenericFunc.Call(item) - } - - elementSelectorGenericFunc, err := newGenericFunc( - "GroupByT", "elementSelectorFn", elementSelectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - elementSelectorFunc := func(item interface{}) interface{} { - return elementSelectorGenericFunc.Call(item) - - } - - return q.GroupBy(keySelectorFunc, elementSelectorFunc) -} diff --git a/vendor/github.com/ahmetb/go-linq/groupjoin.go b/vendor/github.com/ahmetb/go-linq/groupjoin.go index f9f8144..7a3b56b 100644 --- a/vendor/github.com/ahmetb/go-linq/groupjoin.go +++ b/vendor/github.com/ahmetb/go-linq/groupjoin.go @@ -1,27 +1,25 @@ package linq -import "reflect" - // GroupJoin correlates the elements of two collections based on key equality, // and groups the results. // -// This method produces hierarchical results, which means that elements from -// outer query are paired with collections of matching elements from inner. -// GroupJoin enables you to base your results on a whole set of matches for each -// element of outer query. +// This method produces hierarchical results, which means that elements from outer query +// are paired with collections of matching elements from inner. GroupJoin enables you +// to base your results on a whole set of matches for each element of outer query. // // The resultSelector function is called only one time for each outer element -// together with a collection of all the inner elements that match the outer -// element. This differs from the Join method, in which the result selector -// function is invoked on pairs that contain one element from outer and one -// element from inner. +// together with a collection of all the inner elements that match the outer element. +// This differs from the Join method, in which the result selector function is invoked +// on pairs that contain one element from outer and one element from inner. // -// GroupJoin preserves the order of the elements of outer, and for each element -// of outer, the order of the matching elements from inner. -func (q Query) GroupJoin(inner Query, +// GroupJoin preserves the order of the elements of outer, and for each element of outer, +// the order of the matching elements from inner. +func (q Query) GroupJoin( + inner Query, outerKeySelector func(interface{}) interface{}, innerKeySelector func(interface{}) interface{}, - resultSelector func(outer interface{}, inners []interface{}) interface{}) Query { + resultSelector func(outer interface{}, inners []interface{}) interface{}, +) Query { return Query{ Iterate: func() Iterator { @@ -50,58 +48,3 @@ func (q Query) GroupJoin(inner Query, }, } } - -// GroupJoinT is the typed version of GroupJoin. -// -// - inner: The query to join to the outer query. -// - outerKeySelectorFn is of type "func(TOuter) TKey" -// - innerKeySelectorFn is of type "func(TInner) TKey" -// - resultSelectorFn: is of type "func(TOuter, inners []TInner) TResult" -// -// NOTE: GroupJoin has better performance than GroupJoinT. -func (q Query) GroupJoinT(inner Query, - outerKeySelectorFn interface{}, - innerKeySelectorFn interface{}, - resultSelectorFn interface{}) Query { - outerKeySelectorGenericFunc, err := newGenericFunc( - "GroupJoinT", "outerKeySelectorFn", outerKeySelectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - outerKeySelectorFunc := func(item interface{}) interface{} { - return outerKeySelectorGenericFunc.Call(item) - } - - innerKeySelectorFuncGenericFunc, err := newGenericFunc( - "GroupJoinT", "innerKeySelectorFn", innerKeySelectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - innerKeySelectorFunc := func(item interface{}) interface{} { - return innerKeySelectorFuncGenericFunc.Call(item) - } - - resultSelectorGenericFunc, err := newGenericFunc( - "GroupJoinT", "resultSelectorFn", resultSelectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - resultSelectorFunc := func(outer interface{}, inners []interface{}) interface{} { - innerSliceType := reflect.MakeSlice(resultSelectorGenericFunc.Cache.TypesIn[1], 0, 0) - innersSlicePointer := reflect.New(innerSliceType.Type()) - From(inners).ToSlice(innersSlicePointer.Interface()) - innersTyped := reflect.Indirect(innersSlicePointer).Interface() - return resultSelectorGenericFunc.Call(outer, innersTyped) - } - - return q.GroupJoin(inner, outerKeySelectorFunc, innerKeySelectorFunc, resultSelectorFunc) -} diff --git a/vendor/github.com/ahmetb/go-linq/intersect.go b/vendor/github.com/ahmetb/go-linq/intersect.go index 511ed3b..6499bb8 100644 --- a/vendor/github.com/ahmetb/go-linq/intersect.go +++ b/vendor/github.com/ahmetb/go-linq/intersect.go @@ -2,8 +2,8 @@ package linq // Intersect produces the set intersection of the source collection and the // provided input collection. The intersection of two sets A and B is defined as -// the set that contains all the elements of A that also appear in B, but no -// other elements. +// the set that contains all the elements of A that also appear in B, +// but no other elements. func (q Query) Intersect(q2 Query) Query { return Query{ Iterate: func() Iterator { @@ -31,12 +31,14 @@ func (q Query) Intersect(q2 Query) Query { // IntersectBy produces the set intersection of the source collection and the // provided input collection. The intersection of two sets A and B is defined as -// the set that contains all the elements of A that also appear in B, but no -// other elements. +// the set that contains all the elements of A that also appear in B, +// but no other elements. // // IntersectBy invokes a transform function on each element of both collections. -func (q Query) IntersectBy(q2 Query, - selector func(interface{}) interface{}) Query { +func (q Query) IntersectBy( + q2 Query, + selector func(interface{}) interface{}, +) Query { return Query{ Iterate: func() Iterator { @@ -63,25 +65,3 @@ func (q Query) IntersectBy(q2 Query, }, } } - -// IntersectByT is the typed version of IntersectBy. -// -// - selectorFn is of type "func(TSource) TSource" -// -// NOTE: IntersectBy has better performance than IntersectByT. -func (q Query) IntersectByT(q2 Query, - selectorFn interface{}) Query { - selectorGenericFunc, err := newGenericFunc( - "IntersectByT", "selectorFn", selectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - selectorFunc := func(item interface{}) interface{} { - return selectorGenericFunc.Call(item) - } - - return q.IntersectBy(q2, selectorFunc) -} diff --git a/vendor/github.com/ahmetb/go-linq/join.go b/vendor/github.com/ahmetb/go-linq/join.go index 68211a2..95cb835 100644 --- a/vendor/github.com/ahmetb/go-linq/join.go +++ b/vendor/github.com/ahmetb/go-linq/join.go @@ -2,18 +2,20 @@ package linq // Join correlates the elements of two collection based on matching keys. // -// A join refers to the operation of correlating the elements of two sources of -// information based on a common key. Join brings the two information sources -// and the keys by which they are matched together in one method call. This -// differs from the use of SelectMany, which requires more than one method call +// A join refers to the operation of correlating the elements of two sources +// of information based on a common key. Join brings the two information sources +// and the keys by which they are matched together in one method call. +// This differs from the use of SelectMany, which requires more than one method call // to perform the same operation. // -// Join preserves the order of the elements of outer collection, and for each of -// these elements, the order of the matching elements of inner. -func (q Query) Join(inner Query, +// Join preserves the order of the elements of outer collection, +// and for each of these elements, the order of the matching elements of inner. +func (q Query) Join( + inner Query, outerKeySelector func(interface{}) interface{}, innerKeySelector func(interface{}) interface{}, - resultSelector func(outer interface{}, inner interface{}) interface{}) Query { + resultSelector func(outer interface{}, inner interface{}) interface{}, +) Query { return Query{ Iterate: func() Iterator { @@ -52,54 +54,3 @@ func (q Query) Join(inner Query, }, } } - -// JoinT is the typed version of Join. -// -// - outerKeySelectorFn is of type "func(TOuter) TKey" -// - innerKeySelectorFn is of type "func(TInner) TKey" -// - resultSelectorFn is of type "func(TOuter,TInner) TResult" -// -// NOTE: Join has better performance than JoinT. -func (q Query) JoinT(inner Query, - outerKeySelectorFn interface{}, - innerKeySelectorFn interface{}, - resultSelectorFn interface{}) Query { - outerKeySelectorGenericFunc, err := newGenericFunc( - "JoinT", "outerKeySelectorFn", outerKeySelectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - outerKeySelectorFunc := func(item interface{}) interface{} { - return outerKeySelectorGenericFunc.Call(item) - } - - innerKeySelectorFuncGenericFunc, err := newGenericFunc( - "JoinT", "innerKeySelectorFn", - innerKeySelectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - innerKeySelectorFunc := func(item interface{}) interface{} { - return innerKeySelectorFuncGenericFunc.Call(item) - } - - resultSelectorGenericFunc, err := newGenericFunc( - "JoinT", "resultSelectorFn", resultSelectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - resultSelectorFunc := func(outer interface{}, inner interface{}) interface{} { - return resultSelectorGenericFunc.Call(outer, inner) - } - - return q.Join(inner, outerKeySelectorFunc, innerKeySelectorFunc, resultSelectorFunc) -} diff --git a/vendor/github.com/ahmetb/go-linq/orderby.go b/vendor/github.com/ahmetb/go-linq/orderby.go index 110559d..ff7f5ce 100644 --- a/vendor/github.com/ahmetb/go-linq/orderby.go +++ b/vendor/github.com/ahmetb/go-linq/orderby.go @@ -8,17 +8,18 @@ type order struct { desc bool } -// OrderedQuery is the type returned from OrderBy, OrderByDescending ThenBy and -// ThenByDescending functions. +// OrderedQuery is the type returned from OrderBy, OrderByDescending +// ThenBy and ThenByDescending functions. type OrderedQuery struct { Query original Query orders []order } -// OrderBy sorts the elements of a collection in ascending order. Elements are -// sorted according to a key. -func (q Query) OrderBy(selector func(interface{}) interface{}) OrderedQuery { +// OrderBy sorts the elements of a collection in ascending order. +// Elements are sorted according to a key. +func (q Query) OrderBy( + selector func(interface{}) interface{}) OrderedQuery { return OrderedQuery{ orders: []order{{selector: selector}}, original: q, @@ -42,30 +43,10 @@ func (q Query) OrderBy(selector func(interface{}) interface{}) OrderedQuery { } } -// OrderByT is the typed version of OrderBy. -// -// - selectorFn is of type "func(TSource) TKey" -// -// NOTE: OrderBy has better performance than OrderByT. -func (q Query) OrderByT(selectorFn interface{}) OrderedQuery { - selectorGenericFunc, err := newGenericFunc( - "OrderByT", "selectorFn", selectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - selectorFunc := func(item interface{}) interface{} { - return selectorGenericFunc.Call(item) - } - - return q.OrderBy(selectorFunc) -} - // OrderByDescending sorts the elements of a collection in descending order. // Elements are sorted according to a key. -func (q Query) OrderByDescending(selector func(interface{}) interface{}) OrderedQuery { +func (q Query) OrderByDescending( + selector func(interface{}) interface{}) OrderedQuery { return OrderedQuery{ orders: []order{{selector: selector, desc: true}}, original: q, @@ -89,28 +70,9 @@ func (q Query) OrderByDescending(selector func(interface{}) interface{}) Ordered } } -// OrderByDescendingT is the typed version of OrderByDescending. -// - selectorFn is of type "func(TSource) TKey" -// NOTE: OrderByDescending has better performance than OrderByDescendingT. -func (q Query) OrderByDescendingT(selectorFn interface{}) OrderedQuery { - selectorGenericFunc, err := newGenericFunc( - "OrderByDescendingT", "selectorFn", selectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - selectorFunc := func(item interface{}) interface{} { - return selectorGenericFunc.Call(item) - } - - return q.OrderByDescending(selectorFunc) -} - -// ThenBy performs a subsequent ordering of the elements in a collection in -// ascending order. This method enables you to specify multiple sort criteria by -// applying any number of ThenBy or ThenByDescending methods. +// ThenBy performs a subsequent ordering of the elements in a collection +// in ascending order. This method enables you to specify multiple sort criteria +// by applying any number of ThenBy or ThenByDescending methods. func (oq OrderedQuery) ThenBy( selector func(interface{}) interface{}) OrderedQuery { return OrderedQuery{ @@ -136,29 +98,11 @@ func (oq OrderedQuery) ThenBy( } } -// ThenByT is the typed version of ThenBy. -// - selectorFn is of type "func(TSource) TKey" -// NOTE: ThenBy has better performance than ThenByT. -func (oq OrderedQuery) ThenByT(selectorFn interface{}) OrderedQuery { - selectorGenericFunc, err := newGenericFunc( - "ThenByT", "selectorFn", selectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - selectorFunc := func(item interface{}) interface{} { - return selectorGenericFunc.Call(item) - } - - return oq.ThenBy(selectorFunc) -} - -// ThenByDescending performs a subsequent ordering of the elements in a -// collection in descending order. This method enables you to specify multiple -// sort criteria by applying any number of ThenBy or ThenByDescending methods. -func (oq OrderedQuery) ThenByDescending(selector func(interface{}) interface{}) OrderedQuery { +// ThenByDescending performs a subsequent ordering of the elements in a collection +// in descending order. This method enables you to specify multiple sort criteria +// by applying any number of ThenBy or ThenByDescending methods. +func (oq OrderedQuery) ThenByDescending( + selector func(interface{}) interface{}) OrderedQuery { return OrderedQuery{ orders: append(oq.orders, order{selector: selector, desc: true}), original: oq.original, @@ -182,32 +126,10 @@ func (oq OrderedQuery) ThenByDescending(selector func(interface{}) interface{}) } } -// ThenByDescendingT is the typed version of ThenByDescending. -// - selectorFn is of type "func(TSource) TKey" -// NOTE: ThenByDescending has better performance than ThenByDescendingT. -func (oq OrderedQuery) ThenByDescendingT(selectorFn interface{}) OrderedQuery { - selectorFunc, ok := selectorFn.(func(interface{}) interface{}) - if !ok { - selectorGenericFunc, err := newGenericFunc( - "ThenByDescending", "selectorFn", selectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - selectorFunc = func(item interface{}) interface{} { - return selectorGenericFunc.Call(item) - } - } - return oq.ThenByDescending(selectorFunc) -} - -// Sort returns a new query by sorting elements with provided less function in -// ascending order. The comparer function should return true if the parameter i -// is less than j. While this method is uglier than chaining OrderBy, -// OrderByDescending, ThenBy and ThenByDescending methods, it's performance is -// much better. +// Sort returns a new query by sorting elements with provided less function +// in ascending order. The comparer function should return true if the parameter i +// is less than j. While this method is uglier than chaining OrderBy, OrderByDescending, +// ThenBy and ThenByDescending methods, it's performance is much better. func (q Query) Sort(less func(i, j interface{}) bool) Query { return Query{ Iterate: func() Iterator { @@ -228,25 +150,6 @@ func (q Query) Sort(less func(i, j interface{}) bool) Query { } } -// SortT is the typed version of Sort. -// - lessFn is of type "func(TSource,TSource) bool" -// NOTE: Sort has better performance than SortT. -func (q Query) SortT(lessFn interface{}) Query { - lessGenericFunc, err := newGenericFunc( - "SortT", "lessFn", lessFn, - simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(bool))), - ) - if err != nil { - panic(err) - } - - lessFunc := func(i, j interface{}) bool { - return lessGenericFunc.Call(i, j).(bool) - } - - return q.Sort(lessFunc) -} - type sorter struct { items []interface{} less func(i, j interface{}) bool @@ -270,10 +173,6 @@ func (q Query) sort(orders []order) (r []interface{}) { r = append(r, item) } - if len(r) == 0 { - return - } - for i, j := range orders { orders[i].compare = getComparer(j.selector(r[0])) } diff --git a/vendor/github.com/ahmetb/go-linq/result.go b/vendor/github.com/ahmetb/go-linq/result.go index 34e47ab..979be93 100644 --- a/vendor/github.com/ahmetb/go-linq/result.go +++ b/vendor/github.com/ahmetb/go-linq/result.go @@ -18,27 +18,6 @@ func (q Query) All(predicate func(interface{}) bool) bool { return true } -// AllT is the typed version of All. -// -// - predicateFn is of type "func(TSource) bool" -// -// NOTE: All has better performance than AllT. -func (q Query) AllT(predicateFn interface{}) bool { - - predicateGenericFunc, err := newGenericFunc( - "AllT", "predicateFn", predicateFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), - ) - if err != nil { - panic(err) - } - predicateFunc := func(item interface{}) bool { - return predicateGenericFunc.Call(item).(bool) - } - - return q.All(predicateFunc) -} - // Any determines whether any element of a collection exists. func (q Query) Any() bool { _, ok := q.Iterate()() @@ -58,28 +37,6 @@ func (q Query) AnyWith(predicate func(interface{}) bool) bool { return false } -// AnyWithT is the typed version of AnyWith. -// -// - predicateFn is of type "func(TSource) bool" -// -// NOTE: AnyWith has better performance than AnyWithT. -func (q Query) AnyWithT(predicateFn interface{}) bool { - - predicateGenericFunc, err := newGenericFunc( - "AnyWithT", "predicateFn", predicateFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), - ) - if err != nil { - panic(err) - } - - predicateFunc := func(item interface{}) bool { - return predicateGenericFunc.Call(item).(bool) - } - - return q.AnyWith(predicateFunc) -} - // Average computes the average of a collection of numeric values. func (q Query) Average() (r float64) { next := q.Iterate() @@ -147,8 +104,8 @@ func (q Query) Count() (r int) { return } -// CountWith returns a number that represents how many elements in the specified -// collection satisfy a condition. +// CountWith returns a number that represents how many elements +// in the specified collection satisfy a condition. func (q Query) CountWith(predicate func(interface{}) bool) (r int) { next := q.Iterate() @@ -161,36 +118,14 @@ func (q Query) CountWith(predicate func(interface{}) bool) (r int) { return } -// CountWithT is the typed version of CountWith. -// -// - predicateFn is of type "func(TSource) bool" -// -// NOTE: CountWith has better performance than CountWithT. -func (q Query) CountWithT(predicateFn interface{}) int { - - predicateGenericFunc, err := newGenericFunc( - "CountWithT", "predicateFn", predicateFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), - ) - if err != nil { - panic(err) - } - - predicateFunc := func(item interface{}) bool { - return predicateGenericFunc.Call(item).(bool) - } - - return q.CountWith(predicateFunc) -} - // First returns the first element of a collection. func (q Query) First() interface{} { item, _ := q.Iterate()() return item } -// FirstWith returns the first element of a collection that satisfies a -// specified condition. +// FirstWith returns the first element of a collection that satisfies +// a specified condition. func (q Query) FirstWith(predicate func(interface{}) bool) interface{} { next := q.Iterate() @@ -203,99 +138,6 @@ func (q Query) FirstWith(predicate func(interface{}) bool) interface{} { return nil } -// FirstWithT is the typed version of FirstWith. -// -// - predicateFn is of type "func(TSource) bool" -// -// NOTE: FirstWith has better performance than FirstWithT. -func (q Query) FirstWithT(predicateFn interface{}) interface{} { - - predicateGenericFunc, err := newGenericFunc( - "FirstWithT", "predicateFn", predicateFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), - ) - if err != nil { - panic(err) - } - - predicateFunc := func(item interface{}) bool { - return predicateGenericFunc.Call(item).(bool) - } - - return q.FirstWith(predicateFunc) -} - -// ForEach performs the specified action on each element of a collection. -func (q Query) ForEach(action func(interface{})) { - next := q.Iterate() - - for item, ok := next(); ok; item, ok = next() { - action(item) - } -} - -// ForEachT is the typed version of ForEach. -// -// - actionFn is of type "func(TSource)" -// -// NOTE: ForEach has better performance than ForEachT. -func (q Query) ForEachT(actionFn interface{}) { - actionGenericFunc, err := newGenericFunc( - "ForEachT", "actionFn", actionFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), nil), - ) - - if err != nil { - panic(err) - } - - actionFunc := func(item interface{}) { - actionGenericFunc.Call(item) - } - - q.ForEach(actionFunc) -} - -// ForEachIndexed performs the specified action on each element of a collection. -// -// The first argument to action represents the zero-based index of that -// element in the source collection. This can be useful if the elements are in a -// known order and you want to do something with an element at a particular -// index, for example. It can also be useful if you want to retrieve the index -// of one or more elements. The second argument to action represents the -// element to process. -func (q Query) ForEachIndexed(action func(int, interface{})) { - next := q.Iterate() - index := 0 - - for item, ok := next(); ok; item, ok = next() { - action(index, item) - index++ - } -} - -// ForEachIndexedT is the typed version of ForEachIndexed. -// -// - actionFn is of type "func(int, TSource)" -// -// NOTE: ForEachIndexed has better performance than ForEachIndexedT. -func (q Query) ForEachIndexedT(actionFn interface{}) { - actionGenericFunc, err := newGenericFunc( - "ForEachIndexedT", "actionFn", actionFn, - simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), nil), - ) - - if err != nil { - panic(err) - } - - actionFunc := func(index int, item interface{}) { - actionGenericFunc.Call(index, item) - } - - q.ForEachIndexed(actionFunc) -} - // Last returns the last element of a collection. func (q Query) Last() (r interface{}) { next := q.Iterate() @@ -307,8 +149,8 @@ func (q Query) Last() (r interface{}) { return } -// LastWith returns the last element of a collection that satisfies a specified -// condition. +// LastWith returns the last element of a collection that satisfies +// a specified condition. func (q Query) LastWith(predicate func(interface{}) bool) (r interface{}) { next := q.Iterate() @@ -321,28 +163,6 @@ func (q Query) LastWith(predicate func(interface{}) bool) (r interface{}) { return } -// LastWithT is the typed version of LastWith. -// -// - predicateFn is of type "func(TSource) bool" -// -// NOTE: LastWith has better performance than LastWithT. -func (q Query) LastWithT(predicateFn interface{}) interface{} { - - predicateGenericFunc, err := newGenericFunc( - "LastWithT", "predicateFn", predicateFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), - ) - if err != nil { - panic(err) - } - - predicateFunc := func(item interface{}) bool { - return predicateGenericFunc.Call(item).(bool) - } - - return q.LastWith(predicateFunc) -} - // Max returns the maximum value in a collection of values. func (q Query) Max() (r interface{}) { next := q.Iterate() @@ -410,8 +230,8 @@ func (q Query) SequenceEqual(q2 Query) bool { return !ok2 } -// Single returns the only element of a collection, and nil if there is not -// exactly one element in the collection. +// Single returns the only element of a collection, and nil +// if there is not exactly one element in the collection. func (q Query) Single() interface{} { next := q.Iterate() item, ok := next() @@ -427,8 +247,8 @@ func (q Query) Single() interface{} { return item } -// SingleWith returns the only element of a collection that satisfies a -// specified condition, and nil if more than one such element exists. +// SingleWith returns the only element of a collection that satisfies +// a specified condition, and nil if more than one such element exists. func (q Query) SingleWith(predicate func(interface{}) bool) (r interface{}) { next := q.Iterate() found := false @@ -447,31 +267,10 @@ func (q Query) SingleWith(predicate func(interface{}) bool) (r interface{}) { return } -// SingleWithT is the typed version of SingleWith. -// -// - predicateFn is of type "func(TSource) bool" -// -// NOTE: SingleWith has better performance than SingleWithT. -func (q Query) SingleWithT(predicateFn interface{}) interface{} { - predicateGenericFunc, err := newGenericFunc( - "SingleWithT", "predicateFn", predicateFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), - ) - if err != nil { - panic(err) - } - - predicateFunc := func(item interface{}) bool { - return predicateGenericFunc.Call(item).(bool) - } - - return q.SingleWith(predicateFunc) -} - // SumInts computes the sum of a collection of numeric values. // -// Values can be of any integer type: int, int8, int16, int32, int64. The result -// is int64. Method returns zero if collection contains no elements. +// Values can be of any integer type: int, int8, int16, int32, int64. +// The result is int64. Method returns zero if collection contains no elements. func (q Query) SumInts() (r int64) { next := q.Iterate() item, ok := next() @@ -491,9 +290,8 @@ func (q Query) SumInts() (r int64) { // SumUInts computes the sum of a collection of numeric values. // -// Values can be of any unsigned integer type: uint, uint8, uint16, uint32, -// uint64. The result is uint64. Method returns zero if collection contains no -// elements. +// Values can be of any unsigned integer type: uint, uint8, uint16, uint32, uint64. +// The result is uint64. Method returns zero if collection contains no elements. func (q Query) SumUInts() (r uint64) { next := q.Iterate() item, ok := next() @@ -532,8 +330,8 @@ func (q Query) SumFloats() (r float64) { return } -// ToChannel iterates over a collection and outputs each element to a channel, -// then closes it. +// ToChannel iterates over a collection and outputs each element +// to a channel, then closes it. func (q Query) ToChannel(result chan<- interface{}) { next := q.Iterate() @@ -545,9 +343,8 @@ func (q Query) ToChannel(result chan<- interface{}) { } // ToMap iterates over a collection and populates result map with elements. -// Collection elements have to be of KeyValue type to use this method. To -// populate a map with elements of different type use ToMapBy method. ToMap -// doesn't empty the result map before populating it. +// Collection elements have to be of KeyValue type to use this method. +// To populate a map with elements of different type use ToMapBy method. func (q Query) ToMap(result interface{}) { q.ToMapBy( result, @@ -559,14 +356,15 @@ func (q Query) ToMap(result interface{}) { }) } -// ToMapBy iterates over a collection and populates the result map with -// elements. Functions keySelector and valueSelector are executed for each -// element of the collection to generate key and value for the map. Generated -// key and value types must be assignable to the map's key and value types. -// ToMapBy doesn't empty the result map before populating it. -func (q Query) ToMapBy(result interface{}, +// ToMapBy iterates over a collection and populates result map with elements. +// Functions keySelector and valueSelector are executed for each element of the collection +// to generate key and value for the map. Generated key and value types must be assignable +// to the map's key and value types. +func (q Query) ToMapBy( + result interface{}, keySelector func(interface{}) interface{}, - valueSelector func(interface{}) interface{}) { + valueSelector func(interface{}) interface{}, +) { res := reflect.ValueOf(result) m := reflect.Indirect(res) next := q.Iterate() @@ -581,78 +379,16 @@ func (q Query) ToMapBy(result interface{}, res.Elem().Set(m) } -// ToMapByT is the typed version of ToMapBy. -// -// - keySelectorFn is of type "func(TSource)TKey" -// - valueSelectorFn is of type "func(TSource)TValue" -// -// NOTE: ToMapBy has better performance than ToMapByT. -func (q Query) ToMapByT(result interface{}, - keySelectorFn interface{}, valueSelectorFn interface{}) { - keySelectorGenericFunc, err := newGenericFunc( - "ToMapByT", "keySelectorFn", keySelectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - keySelectorFunc := func(item interface{}) interface{} { - return keySelectorGenericFunc.Call(item) - } - - valueSelectorGenericFunc, err := newGenericFunc( - "ToMapByT", "valueSelectorFn", valueSelectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - valueSelectorFunc := func(item interface{}) interface{} { - return valueSelectorGenericFunc.Call(item) - } - - q.ToMapBy(result, keySelectorFunc, valueSelectorFunc) -} - -// ToSlice iterates over a collection and saves the results in the slice pointed -// by v. It overwrites the existing slice, starting from index 0. -// -// If the slice pointed by v has sufficient capacity, v will be pointed to a -// resliced slice. If it does not, a new underlying array will be allocated and -// v will point to it. -func (q Query) ToSlice(v interface{}) { - res := reflect.ValueOf(v) +// ToSlice iterates over a collection and populates result slice with elements. +// Collection elements must be assignable to the slice's element type. +func (q Query) ToSlice(result interface{}) { + res := reflect.ValueOf(result) slice := reflect.Indirect(res) - - cap := slice.Cap() - res.Elem().Set(slice.Slice(0, cap)) // make len(slice)==cap(slice) from now on - next := q.Iterate() - index := 0 + for item, ok := next(); ok; item, ok = next() { - if index >= cap { - slice, cap = grow(slice) - } - slice.Index(index).Set(reflect.ValueOf(item)) - index++ + slice = reflect.Append(slice, reflect.ValueOf(item)) } - // reslice the len(res)==cap(res) actual res size - res.Elem().Set(slice.Slice(0, index)) -} - -// grow grows the slice s by doubling its capacity, then it returns the new -// slice (resliced to its full capacity) and the new capacity. -func grow(s reflect.Value) (v reflect.Value, newCap int) { - cap := s.Cap() - if cap == 0 { - cap = 1 - } else { - cap *= 2 - } - newSlice := reflect.MakeSlice(s.Type(), cap, cap) - reflect.Copy(newSlice, s) - return newSlice, cap + res.Elem().Set(slice) } diff --git a/vendor/github.com/ahmetb/go-linq/reverse.go b/vendor/github.com/ahmetb/go-linq/reverse.go index 8716c22..eaacb9f 100644 --- a/vendor/github.com/ahmetb/go-linq/reverse.go +++ b/vendor/github.com/ahmetb/go-linq/reverse.go @@ -2,9 +2,9 @@ package linq // Reverse inverts the order of the elements in a collection. // -// Unlike OrderBy, this sorting method does not consider the actual values -// themselves in determining the order. Rather, it just returns the elements in -// the reverse order from which they are produced by the underlying source. +// Unlike OrderBy, this sorting method does not consider the actual values themselves +// in determining the order. Rather, it just returns the elements in the reverse order +// from which they are produced by the underlying source. func (q Query) Reverse() Query { return Query{ Iterate: func() Iterator { diff --git a/vendor/github.com/ahmetb/go-linq/select.go b/vendor/github.com/ahmetb/go-linq/select.go index 5dc8cc7..e607229 100644 --- a/vendor/github.com/ahmetb/go-linq/select.go +++ b/vendor/github.com/ahmetb/go-linq/select.go @@ -1,16 +1,17 @@ package linq -// Select projects each element of a collection into a new form. Returns a query -// with the result of invoking the transform function on each element of -// original source. +// Select projects each element of a collection into a new form. +// Returns a query with the result of invoking the transform function +// on each element of original source. // -// This projection method requires the transform function, selector, to produce -// one value for each value in the source collection. If selector returns a -// value that is itself a collection, it is up to the consumer to traverse the -// subcollections manually. In such a situation, it might be better for your -// query to return a single coalesced collection of values. To achieve this, use -// the SelectMany method instead of Select. Although SelectMany works similarly -// to Select, it differs in that the transform function returns a collection +// This projection method requires the transform function, selector, +// to produce one value for each value in the source collection. +// If selector returns a value that is itself a collection, +// it is up to the consumer to traverse the subcollections manually. +// In such a situation, it might be better for your query to return a single +// coalesced collection of values. To achieve this, use the SelectMany method +// instead of Select. Although SelectMany works similarly to Select, +// it differs in that the transform function returns a collection // that is then expanded by SelectMany before it is returned. func (q Query) Select(selector func(interface{}) interface{}) Query { return Query{ @@ -30,44 +31,24 @@ func (q Query) Select(selector func(interface{}) interface{}) Query { } } -// SelectT is the typed version of Select. -// - selectorFn is of type "func(TSource)TResult" -// NOTE: Select has better performance than SelectT. -func (q Query) SelectT(selectorFn interface{}) Query { - - selectGenericFunc, err := newGenericFunc( - "SelectT", "selectorFn", selectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - selectorFunc := func(item interface{}) interface{} { - return selectGenericFunc.Call(item) - } - - return q.Select(selectorFunc) -} - -// SelectIndexed projects each element of a collection into a new form by -// incorporating the element's index. Returns a query with the result of -// invoking the transform function on each element of original source. +// SelectIndexed projects each element of a collection into a new form +// by incorporating the element's index. Returns a query with the result +// of invoking the transform function on each element of original source. // -// The first argument to selector represents the zero-based index of that -// element in the source collection. This can be useful if the elements are in a -// known order and you want to do something with an element at a particular -// index, for example. It can also be useful if you want to retrieve the index -// of one or more elements. The second argument to selector represents the -// element to process. +// The first argument to selector represents the zero-based index of that element +// in the source collection. This can be useful if the elements are in a known order +// and you want to do something with an element at a particular index, +// for example. It can also be useful if you want to retrieve the index of one +// or more elements. The second argument to selector represents the element to process. // -// This projection method requires the transform function, selector, to produce -// one value for each value in the source collection. If selector returns a -// value that is itself a collection, it is up to the consumer to traverse the -// subcollections manually. In such a situation, it might be better for your -// query to return a single coalesced collection of values. To achieve this, use -// the SelectMany method instead of Select. Although SelectMany works similarly -// to Select, it differs in that the transform function returns a collection +// This projection method requires the transform function, selector, +// to produce one value for each value in the source collection. +// If selector returns a value that is itself a collection, +// it is up to the consumer to traverse the subcollections manually. +// In such a situation, it might be better for your query to return a single +// coalesced collection of values. To achieve this, use the SelectMany method +// instead of Select. Although SelectMany works similarly to Select, +// it differs in that the transform function returns a collection // that is then expanded by SelectMany before it is returned. func (q Query) SelectIndexed(selector func(int, interface{}) interface{}) Query { return Query{ @@ -88,22 +69,3 @@ func (q Query) SelectIndexed(selector func(int, interface{}) interface{}) Query }, } } - -// SelectIndexedT is the typed version of SelectIndexed. -// - selectorFn is of type "func(int,TSource)TResult" -// NOTE: SelectIndexed has better performance than SelectIndexedT. -func (q Query) SelectIndexedT(selectorFn interface{}) Query { - selectGenericFunc, err := newGenericFunc( - "SelectIndexedT", "selectorFn", selectorFn, - simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - selectorFunc := func(index int, item interface{}) interface{} { - return selectGenericFunc.Call(index, item) - } - - return q.SelectIndexed(selectorFunc) -} diff --git a/vendor/github.com/ahmetb/go-linq/selectmany.go b/vendor/github.com/ahmetb/go-linq/selectmany.go index 74fa671..25ea8c3 100644 --- a/vendor/github.com/ahmetb/go-linq/selectmany.go +++ b/vendor/github.com/ahmetb/go-linq/selectmany.go @@ -32,37 +32,14 @@ func (q Query) SelectMany(selector func(interface{}) Query) Query { } } -// SelectManyT is the typed version of SelectMany. +// SelectManyIndexed projects each element of a collection to a Query, iterates and +// flattens the resulting collection into one collection. // -// - selectorFn is of type "func(TSource)Query" -// -// NOTE: SelectMany has better performance than SelectManyT. -func (q Query) SelectManyT(selectorFn interface{}) Query { - - selectManyGenericFunc, err := newGenericFunc( - "SelectManyT", "selectorFn", selectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(Query))), - ) - if err != nil { - panic(err) - } - - selectorFunc := func(inner interface{}) Query { - return selectManyGenericFunc.Call(inner).(Query) - } - return q.SelectMany(selectorFunc) - -} - -// SelectManyIndexed projects each element of a collection to a Query, iterates -// and flattens the resulting collection into one collection. -// -// The first argument to selector represents the zero-based index of that -// element in the source collection. This can be useful if the elements are in a -// known order and you want to do something with an element at a particular -// index, for example. It can also be useful if you want to retrieve the index -// of one or more elements. The second argument to selector represents the -// element to process. +// The first argument to selector represents the zero-based index of that element +// in the source collection. This can be useful if the elements are in a known order +// and you want to do something with an element at a particular index, for example. +// It can also be useful if you want to retrieve the index of one or more elements. +// The second argument to selector represents the element to process. func (q Query) SelectManyIndexed(selector func(int, interface{}) Query) Query { return Query{ Iterate: func() Iterator { @@ -95,33 +72,13 @@ func (q Query) SelectManyIndexed(selector func(int, interface{}) Query) Query { } } -// SelectManyIndexedT is the typed version of SelectManyIndexed. -// -// - selectorFn is of type "func(int,TSource)Query" -// -// NOTE: SelectManyIndexed has better performance than SelectManyIndexedT. -func (q Query) SelectManyIndexedT(selectorFn interface{}) Query { - - selectManyIndexedGenericFunc, err := newGenericFunc( - "SelectManyIndexedT", "selectorFn", selectorFn, - simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(Query))), - ) - if err != nil { - panic(err) - } - - selectorFunc := func(index int, inner interface{}) Query { - return selectManyIndexedGenericFunc.Call(index, inner).(Query) - } - - return q.SelectManyIndexed(selectorFunc) -} - // SelectManyBy projects each element of a collection to a Query, iterates and -// flattens the resulting collection into one collection, and invokes a result -// selector function on each element therein. -func (q Query) SelectManyBy(selector func(interface{}) Query, - resultSelector func(interface{}, interface{}) interface{}) Query { +// flattens the resulting collection into one collection, and invokes +// a result selector function on each element therein. +func (q Query) SelectManyBy( + selector func(interface{}) Query, + resultSelector func(interface{}, interface{}) interface{}, +) Query { return Query{ Iterate: func() Iterator { @@ -146,53 +103,18 @@ func (q Query) SelectManyBy(selector func(interface{}) Query, } } - item = resultSelector(item, outer) + item = resultSelector(outer, item) return } }, } } -// SelectManyByT is the typed version of SelectManyBy. -// -// - selectorFn is of type "func(TSource)Query" -// - resultSelectorFn is of type "func(TSource,TCollection)TResult" -// -// NOTE: SelectManyBy has better performance than SelectManyByT. -func (q Query) SelectManyByT(selectorFn interface{}, - resultSelectorFn interface{}) Query { - - selectorGenericFunc, err := newGenericFunc( - "SelectManyByT", "selectorFn", selectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(Query))), - ) - if err != nil { - panic(err) - } - - selectorFunc := func(outer interface{}) Query { - return selectorGenericFunc.Call(outer).(Query) - } - - resultSelectorGenericFunc, err := newGenericFunc( - "SelectManyByT", "resultSelectorFn", resultSelectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - resultSelectorFunc := func(outer interface{}, item interface{}) interface{} { - return resultSelectorGenericFunc.Call(outer, item) - } - - return q.SelectManyBy(selectorFunc, resultSelectorFunc) -} - -// SelectManyByIndexed projects each element of a collection to a Query, -// iterates and flattens the resulting collection into one collection, and -// invokes a result selector function on each element therein. The index of each -// source element is used in the intermediate projected form of that element. +// SelectManyByIndexed projects each element of a collection to a Query, iterates and +// flattens the resulting collection into one collection, and invokes +// a result selector function on each element therein. +// The index of each source element is used in the intermediate projected form +// of that element. func (q Query) SelectManyByIndexed(selector func(int, interface{}) Query, resultSelector func(interface{}, interface{}) interface{}) Query { @@ -221,45 +143,9 @@ func (q Query) SelectManyByIndexed(selector func(int, interface{}) Query, } } - item = resultSelector(item, outer) + item = resultSelector(outer, item) return } }, } } - -// SelectManyByIndexedT is the typed version of SelectManyByIndexed. -// -// - selectorFn is of type "func(int,TSource)Query" -// - resultSelectorFn is of type "func(TSource,TCollection)TResult" -// -// NOTE: SelectManyByIndexed has better performance than -// SelectManyByIndexedT. -func (q Query) SelectManyByIndexedT(selectorFn interface{}, - resultSelectorFn interface{}) Query { - selectorGenericFunc, err := newGenericFunc( - "SelectManyByIndexedT", "selectorFn", selectorFn, - simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(Query))), - ) - if err != nil { - panic(err) - } - - selectorFunc := func(index int, outer interface{}) Query { - return selectorGenericFunc.Call(index, outer).(Query) - } - - resultSelectorGenericFunc, err := newGenericFunc( - "SelectManyByIndexedT", "resultSelectorFn", resultSelectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - resultSelectorFunc := func(outer interface{}, item interface{}) interface{} { - return resultSelectorGenericFunc.Call(outer, item) - } - - return q.SelectManyByIndexed(selectorFunc, resultSelectorFunc) -} diff --git a/vendor/github.com/ahmetb/go-linq/skip.go b/vendor/github.com/ahmetb/go-linq/skip.go index 0fb05d3..1110ae5 100644 --- a/vendor/github.com/ahmetb/go-linq/skip.go +++ b/vendor/github.com/ahmetb/go-linq/skip.go @@ -1,7 +1,7 @@ package linq -// Skip bypasses a specified number of elements in a collection and then returns -// the remaining elements. +// Skip bypasses a specified number of elements in a collection +// and then returns the remaining elements. func (q Query) Skip(count int) Query { return Query{ Iterate: func() Iterator { @@ -22,13 +22,13 @@ func (q Query) Skip(count int) Query { } } -// SkipWhile bypasses elements in a collection as long as a specified condition -// is true and then returns the remaining elements. +// SkipWhile bypasses elements in a collection as long as a specified condition is true +// and then returns the remaining elements. // -// This method tests each element by using predicate and skips the element if -// the result is true. After the predicate function returns false for an -// element, that element and the remaining elements in source are returned and -// there are no more invocations of predicate. +// This method tests each element by using predicate and skips the element +// if the result is true. After the predicate function returns false for an element, +// that element and the remaining elements in source are returned +// and there are no more invocations of predicate. func (q Query) SkipWhile(predicate func(interface{}) bool) Query { return Query{ Iterate: func() Iterator { @@ -54,36 +54,14 @@ func (q Query) SkipWhile(predicate func(interface{}) bool) Query { } } -// SkipWhileT is the typed version of SkipWhile. +// SkipWhileIndexed bypasses elements in a collection as long as a specified condition +// is true and then returns the remaining elements. The element's index is used +// in the logic of the predicate function. // -// - predicateFn is of type "func(TSource)bool" -// -// NOTE: SkipWhile has better performance than SkipWhileT. -func (q Query) SkipWhileT(predicateFn interface{}) Query { - - predicateGenericFunc, err := newGenericFunc( - "SkipWhileT", "predicateFn", predicateFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), - ) - if err != nil { - panic(err) - } - - predicateFunc := func(item interface{}) bool { - return predicateGenericFunc.Call(item).(bool) - } - - return q.SkipWhile(predicateFunc) -} - -// SkipWhileIndexed bypasses elements in a collection as long as a specified -// condition is true and then returns the remaining elements. The element's -// index is used in the logic of the predicate function. -// -// This method tests each element by using predicate and skips the element if -// the result is true. After the predicate function returns false for an -// element, that element and the remaining elements in source are returned and -// there are no more invocations of predicate. +// This method tests each element by using predicate and skips the element +// if the result is true. After the predicate function returns false for an element, +// that element and the remaining elements in source are returned +// and there are no more invocations of predicate. func (q Query) SkipWhileIndexed(predicate func(int, interface{}) bool) Query { return Query{ Iterate: func() Iterator { @@ -111,24 +89,3 @@ func (q Query) SkipWhileIndexed(predicate func(int, interface{}) bool) Query { }, } } - -// SkipWhileIndexedT is the typed version of SkipWhileIndexed. -// -// - predicateFn is of type "func(int,TSource)bool" -// -// NOTE: SkipWhileIndexed has better performance than SkipWhileIndexedT. -func (q Query) SkipWhileIndexedT(predicateFn interface{}) Query { - predicateGenericFunc, err := newGenericFunc( - "SkipWhileIndexedT", "predicateFn", predicateFn, - simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(bool))), - ) - if err != nil { - panic(err) - } - - predicateFunc := func(index int, item interface{}) bool { - return predicateGenericFunc.Call(index, item).(bool) - } - - return q.SkipWhileIndexed(predicateFunc) -} diff --git a/vendor/github.com/ahmetb/go-linq/take.go b/vendor/github.com/ahmetb/go-linq/take.go index c2707e5..d21fc96 100644 --- a/vendor/github.com/ahmetb/go-linq/take.go +++ b/vendor/github.com/ahmetb/go-linq/take.go @@ -1,7 +1,6 @@ package linq -// Take returns a specified number of contiguous elements from the start of a -// collection. +// Take returns a specified number of contiguous elements from the start of a collection. func (q Query) Take(count int) Query { return Query{ Iterate: func() Iterator { @@ -20,8 +19,8 @@ func (q Query) Take(count int) Query { } } -// TakeWhile returns elements from a collection as long as a specified condition -// is true, and then skips the remaining elements. +// TakeWhile returns elements from a collection as long as a specified condition is true, +// and then skips the remaining elements. func (q Query) TakeWhile(predicate func(interface{}) bool) Query { return Query{ Iterate: func() Iterator { @@ -50,33 +49,10 @@ func (q Query) TakeWhile(predicate func(interface{}) bool) Query { } } -// TakeWhileT is the typed version of TakeWhile. -// -// - predicateFn is of type "func(TSource)bool" -// -// NOTE: TakeWhile has better performance than TakeWhileT. -func (q Query) TakeWhileT(predicateFn interface{}) Query { - - predicateGenericFunc, err := newGenericFunc( - "TakeWhileT", "predicateFn", predicateFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), - ) - if err != nil { - panic(err) - } - - predicateFunc := func(item interface{}) bool { - return predicateGenericFunc.Call(item).(bool) - } - - return q.TakeWhile(predicateFunc) -} - -// TakeWhileIndexed returns elements from a collection as long as a specified -// condition is true. The element's index is used in the logic of the predicate -// function. The first argument of predicate represents the zero-based index of -// the element within collection. The second argument represents the element to -// test. +// TakeWhileIndexed returns elements from a collection as long as a specified condition +// is true. The element's index is used in the logic of the predicate function. +// The first argument of predicate represents the zero-based index of the element +// within collection. The second argument represents the element to test. func (q Query) TakeWhileIndexed(predicate func(int, interface{}) bool) Query { return Query{ Iterate: func() Iterator { @@ -106,24 +82,3 @@ func (q Query) TakeWhileIndexed(predicate func(int, interface{}) bool) Query { }, } } - -// TakeWhileIndexedT is the typed version of TakeWhileIndexed. -// -// - predicateFn is of type "func(int,TSource)bool" -// -// NOTE: TakeWhileIndexed has better performance than TakeWhileIndexedT. -func (q Query) TakeWhileIndexedT(predicateFn interface{}) Query { - whereFunc, err := newGenericFunc( - "TakeWhileIndexedT", "predicateFn", predicateFn, - simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(bool))), - ) - if err != nil { - panic(err) - } - - predicateFunc := func(index int, item interface{}) bool { - return whereFunc.Call(index, item).(bool) - } - - return q.TakeWhileIndexed(predicateFunc) -} diff --git a/vendor/github.com/ahmetb/go-linq/union.go b/vendor/github.com/ahmetb/go-linq/union.go index 717b83b..9ca4b65 100644 --- a/vendor/github.com/ahmetb/go-linq/union.go +++ b/vendor/github.com/ahmetb/go-linq/union.go @@ -2,9 +2,10 @@ package linq // Union produces the set union of two collections. // -// This method excludes duplicates from the return set. This is different -// behavior to the Concat method, which returns all the elements in the input -// collection including duplicates. +// This method excludes duplicates from the return set. +// This is different behavior to the Concat method, +// which returns all the elements in the input collection +// including duplicates. func (q Query) Union(q2 Query) Query { return Query{ Iterate: func() Iterator { diff --git a/vendor/github.com/ahmetb/go-linq/where.go b/vendor/github.com/ahmetb/go-linq/where.go index 6fd4302..26d0bda 100644 --- a/vendor/github.com/ahmetb/go-linq/where.go +++ b/vendor/github.com/ahmetb/go-linq/where.go @@ -19,33 +19,11 @@ func (q Query) Where(predicate func(interface{}) bool) Query { } } -// WhereT is the typed version of Where. +// WhereIndexed filters a collection of values based on a predicate. +// Each element's index is used in the logic of the predicate function. // -// - predicateFn is of type "func(TSource)bool" -// -// NOTE: Where has better performance than WhereT. -func (q Query) WhereT(predicateFn interface{}) Query { - - predicateGenericFunc, err := newGenericFunc( - "WhereT", "predicateFn", predicateFn, - simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), - ) - if err != nil { - panic(err) - } - - predicateFunc := func(item interface{}) bool { - return predicateGenericFunc.Call(item).(bool) - } - - return q.Where(predicateFunc) -} - -// WhereIndexed filters a collection of values based on a predicate. Each -// element's index is used in the logic of the predicate function. -// -// The first argument represents the zero-based index of the element within -// collection. The second argument of predicate represents the element to test. +// The first argument represents the zero-based index of the element within collection. +// The second argument of predicate represents the element to test. func (q Query) WhereIndexed(predicate func(int, interface{}) bool) Query { return Query{ Iterate: func() Iterator { @@ -66,24 +44,3 @@ func (q Query) WhereIndexed(predicate func(int, interface{}) bool) Query { }, } } - -// WhereIndexedT is the typed version of WhereIndexed. -// -// - predicateFn is of type "func(int,TSource)bool" -// -// NOTE: WhereIndexed has better performance than WhereIndexedT. -func (q Query) WhereIndexedT(predicateFn interface{}) Query { - predicateGenericFunc, err := newGenericFunc( - "WhereIndexedT", "predicateFn", predicateFn, - simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(bool))), - ) - if err != nil { - panic(err) - } - - predicateFunc := func(index int, item interface{}) bool { - return predicateGenericFunc.Call(index, item).(bool) - } - - return q.WhereIndexed(predicateFunc) -} diff --git a/vendor/github.com/ahmetb/go-linq/zip.go b/vendor/github.com/ahmetb/go-linq/zip.go index da3dcd3..1314b9a 100644 --- a/vendor/github.com/ahmetb/go-linq/zip.go +++ b/vendor/github.com/ahmetb/go-linq/zip.go @@ -1,17 +1,19 @@ package linq -// Zip applies a specified function to the corresponding elements of two -// collections, producing a collection of the results. +// Zip applies a specified function to the corresponding elements +// of two collections, producing a collection of the results. // // The method steps through the two input collections, applying function -// resultSelector to corresponding elements of the two collections. The method -// returns a collection of the values that are returned by resultSelector. If -// the input collections do not have the same number of elements, the method -// combines elements until it reaches the end of one of the collections. For -// example, if one collection has three elements and the other one has four, the -// result collection has only three elements. -func (q Query) Zip(q2 Query, - resultSelector func(interface{}, interface{}) interface{}) Query { +// resultSelector to corresponding elements of the two collections. +// The method returns a collection of the values that are returned by resultSelector. +// If the input collections do not have the same number of elements, +// the method combines elements until it reaches the end of one of the collections. +// For example, if one collection has three elements and the other one has four, +// the result collection has only three elements. +func (q Query) Zip( + q2 Query, + resultSelector func(interface{}, interface{}) interface{}, +) Query { return Query{ Iterate: func() Iterator { @@ -31,25 +33,3 @@ func (q Query) Zip(q2 Query, }, } } - -// ZipT is the typed version of Zip. -// -// - resultSelectorFn is of type "func(TFirst,TSecond)TResult" -// -// NOTE: Zip has better performance than ZipT. -func (q Query) ZipT(q2 Query, - resultSelectorFn interface{}) Query { - resultSelectorGenericFunc, err := newGenericFunc( - "ZipT", "resultSelectorFn", resultSelectorFn, - simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), - ) - if err != nil { - panic(err) - } - - resultSelectorFunc := func(item1 interface{}, item2 interface{}) interface{} { - return resultSelectorGenericFunc.Call(item1, item2) - } - - return q.Zip(q2, resultSelectorFunc) -} diff --git a/vendor/modules.txt b/vendor/modules.txt index e549182..103f816 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,5 +1,5 @@ -# github.com/ahmetalpbalkan/go-linq v2.0.0-rc0+incompatible -github.com/ahmetalpbalkan/go-linq +# github.com/ahmetb/go-linq v2.0.0-rc0+incompatible +github.com/ahmetb/go-linq # github.com/go-ini/ini v1.57.0 github.com/go-ini/ini # github.com/json-iterator/go v1.1.10