mirror of https://github.com/prometheus/prometheus
Tobias Schmidt
9 years ago
14 changed files with 2103 additions and 0 deletions
@ -0,0 +1,65 @@ |
|||||||
|
// Copyright 2015 The Prometheus Authors
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package main |
||||||
|
|
||||||
|
import "testing" |
||||||
|
|
||||||
|
func TestParse(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
input []string |
||||||
|
valid bool |
||||||
|
}{ |
||||||
|
{ |
||||||
|
input: []string{}, |
||||||
|
valid: true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
input: []string{"-web.external-url", ""}, |
||||||
|
valid: true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
input: []string{"-web.external-url", "http://proxy.com/prometheus"}, |
||||||
|
valid: true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
input: []string{"-web.external-url", "'https://url/prometheus'"}, |
||||||
|
valid: false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
input: []string{"-storage.remote.influxdb-url", ""}, |
||||||
|
valid: true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
input: []string{"-storage.remote.influxdb-url", "http://localhost:8086/"}, |
||||||
|
valid: true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
input: []string{"-storage.remote.influxdb-url", "'https://some-url/'"}, |
||||||
|
valid: false, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
for i, test := range tests { |
||||||
|
// reset "immutable" config
|
||||||
|
cfg.prometheusURL = "" |
||||||
|
cfg.influxdbURL = "" |
||||||
|
|
||||||
|
err := parse(test.input) |
||||||
|
if test.valid && err != nil { |
||||||
|
t.Errorf("%d. expected input to be valid, got %s", i, err) |
||||||
|
} else if !test.valid && err == nil { |
||||||
|
t.Errorf("%d. expected input to be invalid", i) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
The MIT License (MIT) |
||||||
|
|
||||||
|
Copyright (c) 2014 Alex Saskevich |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all |
||||||
|
copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
SOFTWARE. |
@ -0,0 +1,285 @@ |
|||||||
|
govalidator |
||||||
|
=========== |
||||||
|
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![GoDoc](https://godoc.org/github.com/asaskevich/govalidator?status.png)](https://godoc.org/github.com/asaskevich/govalidator) [![Coverage Status](https://img.shields.io/coveralls/asaskevich/govalidator.svg)](https://coveralls.io/r/asaskevich/govalidator?branch=master) [![views](https://sourcegraph.com/api/repos/github.com/asaskevich/govalidator/.counters/views.png)](https://sourcegraph.com/github.com/asaskevich/govalidator) |
||||||
|
[![wercker status](https://app.wercker.com/status/1ec990b09ea86c910d5f08b0e02c6043/s "wercker status")](https://app.wercker.com/project/bykey/1ec990b09ea86c910d5f08b0e02c6043) |
||||||
|
[![Build Status](https://travis-ci.org/asaskevich/govalidator.svg?branch=master)](https://travis-ci.org/asaskevich/govalidator) |
||||||
|
|
||||||
|
A package of validators and sanitizers for strings, structs and collections. Based on [validator.js](https://github.com/chriso/validator.js). |
||||||
|
|
||||||
|
#### Installation |
||||||
|
Make sure that Go is installed on your computer. |
||||||
|
Type the following command in your terminal: |
||||||
|
|
||||||
|
go get github.com/asaskevich/govalidator |
||||||
|
|
||||||
|
After it the package is ready to use. |
||||||
|
|
||||||
|
#### Import package in your project |
||||||
|
Add following line in your `*.go` file: |
||||||
|
```go |
||||||
|
import "github.com/asaskevich/govalidator" |
||||||
|
``` |
||||||
|
If you unhappy to use long `govalidator`, you can do something like this: |
||||||
|
```go |
||||||
|
import ( |
||||||
|
valid "github.com/asaskevich/govalidator" |
||||||
|
) |
||||||
|
``` |
||||||
|
|
||||||
|
#### List of functions: |
||||||
|
```go |
||||||
|
func Abs(value float64) float64 |
||||||
|
func BlackList(str, chars string) string |
||||||
|
func ByteLength(str string, params ...string) bool |
||||||
|
func StringLength(str string, params ...string) bool |
||||||
|
func StringMatches(s string, params ...string) bool |
||||||
|
func CamelCaseToUnderscore(str string) string |
||||||
|
func Contains(str, substring string) bool |
||||||
|
func Count(array []interface{}, iterator ConditionIterator) int |
||||||
|
func Each(array []interface{}, iterator Iterator) |
||||||
|
func ErrorByField(e error, field string) string |
||||||
|
func Filter(array []interface{}, iterator ConditionIterator) []interface{} |
||||||
|
func Find(array []interface{}, iterator ConditionIterator) interface{} |
||||||
|
func GetLine(s string, index int) (string, error) |
||||||
|
func GetLines(s string) []string |
||||||
|
func InRange(value, left, right float64) bool |
||||||
|
func IsASCII(str string) bool |
||||||
|
func IsAlpha(str string) bool |
||||||
|
func IsAlphanumeric(str string) bool |
||||||
|
func IsBase64(str string) bool |
||||||
|
func IsByteLength(str string, min, max int) bool |
||||||
|
func IsCreditCard(str string) bool |
||||||
|
func IsDataURI(str string) bool |
||||||
|
func IsDivisibleBy(str, num string) bool |
||||||
|
func IsEmail(str string) bool |
||||||
|
func IsFilePath(str string) (bool, int) |
||||||
|
func IsFloat(str string) bool |
||||||
|
func IsFullWidth(str string) bool |
||||||
|
func IsHalfWidth(str string) bool |
||||||
|
func IsHexadecimal(str string) bool |
||||||
|
func IsHexcolor(str string) bool |
||||||
|
func IsIP(str string) bool |
||||||
|
func IsIPv4(str string) bool |
||||||
|
func IsIPv6(str string) bool |
||||||
|
func IsISBN(str string, version int) bool |
||||||
|
func IsISBN10(str string) bool |
||||||
|
func IsISBN13(str string) bool |
||||||
|
func IsISO3166Alpha2(str string) bool |
||||||
|
func IsISO3166Alpha3(str string) bool |
||||||
|
func IsInt(str string) bool |
||||||
|
func IsJSON(str string) bool |
||||||
|
func IsLatitude(str string) bool |
||||||
|
func IsLongitude(str string) bool |
||||||
|
func IsLowerCase(str string) bool |
||||||
|
func IsMAC(str string) bool |
||||||
|
func IsMongoID(str string) bool |
||||||
|
func IsMultibyte(str string) bool |
||||||
|
func IsNatural(value float64) bool |
||||||
|
func IsNegative(value float64) bool |
||||||
|
func IsNonNegative(value float64) bool |
||||||
|
func IsNonPositive(value float64) bool |
||||||
|
func IsNull(str string) bool |
||||||
|
func IsNumeric(str string) bool |
||||||
|
func IsPositive(value float64) bool |
||||||
|
func IsPrintableASCII(str string) bool |
||||||
|
func IsRGBcolor(str string) bool |
||||||
|
func IsRequestURI(rawurl string) bool |
||||||
|
func IsRequestURL(rawurl string) bool |
||||||
|
func IsSSN(str string) bool |
||||||
|
func IsSemver(str string) bool |
||||||
|
func IsURL(str string) bool |
||||||
|
func IsUTFDigit(str string) bool |
||||||
|
func IsUTFLetter(str string) bool |
||||||
|
func IsUTFLetterNumeric(str string) bool |
||||||
|
func IsUTFNumeric(str string) bool |
||||||
|
func IsUUID(str string) bool |
||||||
|
func IsUUIDv3(str string) bool |
||||||
|
func IsUUIDv4(str string) bool |
||||||
|
func IsUUIDv5(str string) bool |
||||||
|
func IsUpperCase(str string) bool |
||||||
|
func IsVariableWidth(str string) bool |
||||||
|
func IsWhole(value float64) bool |
||||||
|
func LeftTrim(str, chars string) string |
||||||
|
func Map(array []interface{}, iterator ResultIterator) []interface{} |
||||||
|
func Matches(str, pattern string) bool |
||||||
|
func NormalizeEmail(str string) (string, error) |
||||||
|
func RemoveTags(s string) string |
||||||
|
func ReplacePattern(str, pattern, replace string) string |
||||||
|
func Reverse(s string) string |
||||||
|
func RightTrim(str, chars string) string |
||||||
|
func SafeFileName(str string) string |
||||||
|
func Sign(value float64) float64 |
||||||
|
func StripLow(str string, keepNewLines bool) string |
||||||
|
func ToBoolean(str string) (bool, error) |
||||||
|
func ToFloat(str string) (float64, error) |
||||||
|
func ToInt(str string) (int64, error) |
||||||
|
func ToJSON(obj interface{}) (string, error) |
||||||
|
func ToString(obj interface{}) string |
||||||
|
func Trim(str, chars string) string |
||||||
|
func Truncate(str string, length int, ending string) string |
||||||
|
func UnderscoreToCamelCase(s string) string |
||||||
|
func ValidateStruct(s interface{}) (bool, error) |
||||||
|
func WhiteList(str, chars string) string |
||||||
|
type ConditionIterator |
||||||
|
type Error |
||||||
|
func (e Error) Error() string |
||||||
|
type Errors |
||||||
|
func (es Errors) Error() string |
||||||
|
type ISO3166Entry |
||||||
|
type Iterator |
||||||
|
type ParamValidator |
||||||
|
type ResultIterator |
||||||
|
type UnsupportedTypeError |
||||||
|
func (e *UnsupportedTypeError) Error() string |
||||||
|
type Validator |
||||||
|
``` |
||||||
|
|
||||||
|
#### Examples |
||||||
|
###### IsURL |
||||||
|
```go |
||||||
|
println(govalidator.IsURL(`http://user@pass:domain.com/path/page`)) |
||||||
|
``` |
||||||
|
###### ToString |
||||||
|
```go |
||||||
|
type User struct { |
||||||
|
FirstName string |
||||||
|
LastName string |
||||||
|
} |
||||||
|
|
||||||
|
str, _ := govalidator.ToString(&User{"John", "Juan"}) |
||||||
|
println(str) |
||||||
|
``` |
||||||
|
###### Each, Map, Filter, Count for slices |
||||||
|
Each iterates over the slice/array and calls Iterator for every item |
||||||
|
```go |
||||||
|
data := []interface{}{1, 2, 3, 4, 5} |
||||||
|
var fn govalidator.Iterator = func(value interface{}, index int) { |
||||||
|
println(value.(int)) |
||||||
|
} |
||||||
|
govalidator.Each(data, fn) |
||||||
|
``` |
||||||
|
```go |
||||||
|
data := []interface{}{1, 2, 3, 4, 5} |
||||||
|
var fn govalidator.ResultIterator = func(value interface{}, index int) interface{} { |
||||||
|
return value.(int) * 3 |
||||||
|
} |
||||||
|
_ = govalidator.Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15} |
||||||
|
``` |
||||||
|
```go |
||||||
|
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} |
||||||
|
var fn govalidator.ConditionIterator = func(value interface{}, index int) bool { |
||||||
|
return value.(int)%2 == 0 |
||||||
|
} |
||||||
|
_ = govalidator.Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10} |
||||||
|
_ = govalidator.Count(data, fn) // result = 5 |
||||||
|
``` |
||||||
|
###### ValidateStruct [#2](https://github.com/asaskevich/govalidator/pull/2) |
||||||
|
If you want to validate structs, you can use tag `valid` for any field in your structure. All validators used with this field in one tag are separated by comma. If you want to skip validation, place `-` in your tag. If you need a validator that is not on the list below, you can add it like this: |
||||||
|
```go |
||||||
|
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool { |
||||||
|
return str == "duck" |
||||||
|
}) |
||||||
|
``` |
||||||
|
Here is a list of available validators for struct fields (validator - used function): |
||||||
|
```go |
||||||
|
"alpha": IsAlpha, |
||||||
|
"alphanum": IsAlphanumeric, |
||||||
|
"ascii": IsASCII, |
||||||
|
"base64": IsBase64, |
||||||
|
"creditcard": IsCreditCard, |
||||||
|
"datauri": IsDataURI, |
||||||
|
"email": IsEmail, |
||||||
|
"float": IsFloat, |
||||||
|
"fullwidth": IsFullWidth, |
||||||
|
"halfwidth": IsHalfWidth, |
||||||
|
"hexadecimal": IsHexadecimal, |
||||||
|
"hexcolor": IsHexcolor, |
||||||
|
"int": IsInt, |
||||||
|
"ip": IsIP, |
||||||
|
"ipv4": IsIPv4, |
||||||
|
"ipv6": IsIPv6, |
||||||
|
"isbn10": IsISBN10, |
||||||
|
"isbn13": IsISBN13, |
||||||
|
"json": IsJSON, |
||||||
|
"latitude": IsLatitude, |
||||||
|
"longitude": IsLongitude, |
||||||
|
"lowercase": IsLowerCase, |
||||||
|
"mac": IsMAC, |
||||||
|
"multibyte": IsMultibyte, |
||||||
|
"null": IsNull, |
||||||
|
"numeric": IsNumeric, |
||||||
|
"printableascii": IsPrintableASCII, |
||||||
|
"requri": IsRequestURI, |
||||||
|
"requrl": IsRequestURL, |
||||||
|
"rgbcolor": IsRGBcolor, |
||||||
|
"ssn": IsSSN, |
||||||
|
"semver": IsSemver, |
||||||
|
"uppercase": IsUpperCase, |
||||||
|
"url": IsURL, |
||||||
|
"utfdigit": IsUTFDigit, |
||||||
|
"utfletter": IsUTFLetter, |
||||||
|
"utfletternum": IsUTFLetterNumeric, |
||||||
|
"utfnumeric": IsUTFNumeric, |
||||||
|
"uuid": IsUUID, |
||||||
|
"uuidv3": IsUUIDv3, |
||||||
|
"uuidv4": IsUUIDv4, |
||||||
|
"uuidv5": IsUUIDv5, |
||||||
|
"variablewidth": IsVariableWidth, |
||||||
|
``` |
||||||
|
Validators with parameters |
||||||
|
|
||||||
|
```go |
||||||
|
"length(min|max)": ByteLength, |
||||||
|
"matches(pattern)": StringMatches, |
||||||
|
``` |
||||||
|
|
||||||
|
And here is small example of usage: |
||||||
|
```go |
||||||
|
type Post struct { |
||||||
|
Title string `valid:"alphanum,required"` |
||||||
|
Message string `valid:"duck,ascii"` |
||||||
|
AuthorIP string `valid:"ipv4"` |
||||||
|
Date string `valid:"-"` |
||||||
|
} |
||||||
|
post := &Post{ |
||||||
|
Title: "My Example Post", |
||||||
|
Message: "duck", |
||||||
|
AuthorIP: "123.234.54.3", |
||||||
|
} |
||||||
|
|
||||||
|
// Add your own struct validation tags |
||||||
|
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool { |
||||||
|
return str == "duck" |
||||||
|
}) |
||||||
|
|
||||||
|
result, err := govalidator.ValidateStruct(post) |
||||||
|
if err != nil { |
||||||
|
println("error: " + err.Error()) |
||||||
|
} |
||||||
|
println(result) |
||||||
|
``` |
||||||
|
###### WhiteList |
||||||
|
```go |
||||||
|
// Remove all characters from string ignoring characters between "a" and "z" |
||||||
|
println(govalidator.WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa") |
||||||
|
``` |
||||||
|
|
||||||
|
#### Notes |
||||||
|
Documentation is available here: [godoc.org](https://godoc.org/github.com/asaskevich/govalidator). |
||||||
|
Full information about code coverage is also available here: [govalidator on gocover.io](http://gocover.io/github.com/asaskevich/govalidator). |
||||||
|
|
||||||
|
#### Support |
||||||
|
If you do have a contribution for the package feel free to put up a Pull Request or open Issue. |
||||||
|
|
||||||
|
#### Special thanks to [contributors](https://github.com/asaskevich/govalidator/graphs/contributors) |
||||||
|
* [Attila Oláh](https://github.com/attilaolah) |
||||||
|
* [Daniel Korner](https://github.com/Dadie) |
||||||
|
* [Steven Wilkin](https://github.com/stevenwilkin) |
||||||
|
* [Deiwin Sarjas](https://github.com/deiwin) |
||||||
|
* [Noah Shibley](https://github.com/slugmobile) |
||||||
|
* [Nathan Davies](https://github.com/nathj07) |
||||||
|
* [Matt Sanford](https://github.com/mzsanford) |
||||||
|
* [Simon ccl1115](https://github.com/ccl1115) |
||||||
|
|
||||||
|
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/asaskevich/govalidator/trend.png)](https://bitdeli.com/free "Bitdeli Badge") |
||||||
|
|
@ -0,0 +1,58 @@ |
|||||||
|
package govalidator |
||||||
|
|
||||||
|
// Iterator is the function that accepts element of slice/array and its index
|
||||||
|
type Iterator func(interface{}, int) |
||||||
|
|
||||||
|
// ResultIterator is the function that accepts element of slice/array and its index and returns any result
|
||||||
|
type ResultIterator func(interface{}, int) interface{} |
||||||
|
|
||||||
|
// ConditionIterator is the function that accepts element of slice/array and its index and returns boolean
|
||||||
|
type ConditionIterator func(interface{}, int) bool |
||||||
|
|
||||||
|
// Each iterates over the slice and apply Iterator to every item
|
||||||
|
func Each(array []interface{}, iterator Iterator) { |
||||||
|
for index, data := range array { |
||||||
|
iterator(data, index) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result.
|
||||||
|
func Map(array []interface{}, iterator ResultIterator) []interface{} { |
||||||
|
var result []interface{} = make([]interface{}, len(array)) |
||||||
|
for index, data := range array { |
||||||
|
result[index] = iterator(data, index) |
||||||
|
} |
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
// Find iterates over the slice and apply ConditionIterator to every item. Returns first item that meet ConditionIterator or nil otherwise.
|
||||||
|
func Find(array []interface{}, iterator ConditionIterator) interface{} { |
||||||
|
for index, data := range array { |
||||||
|
if iterator(data, index) { |
||||||
|
return data |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice.
|
||||||
|
func Filter(array []interface{}, iterator ConditionIterator) []interface{} { |
||||||
|
var result []interface{} = make([]interface{}, 0) |
||||||
|
for index, data := range array { |
||||||
|
if iterator(data, index) { |
||||||
|
result = append(result, data) |
||||||
|
} |
||||||
|
} |
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
// Count iterates over the slice and apply ConditionIterator to every item. Returns count of items that meets ConditionIterator.
|
||||||
|
func Count(array []interface{}, iterator ConditionIterator) int { |
||||||
|
count := 0 |
||||||
|
for index, data := range array { |
||||||
|
if iterator(data, index) { |
||||||
|
count = count + 1 |
||||||
|
} |
||||||
|
} |
||||||
|
return count |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
package govalidator |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"strconv" |
||||||
|
) |
||||||
|
|
||||||
|
// ToString convert the input to a string.
|
||||||
|
func ToString(obj interface{}) string { |
||||||
|
res := fmt.Sprintf("%v", obj) |
||||||
|
return string(res) |
||||||
|
} |
||||||
|
|
||||||
|
// ToJSON convert the input to a valid JSON string
|
||||||
|
func ToJSON(obj interface{}) (string, error) { |
||||||
|
res, err := json.Marshal(obj) |
||||||
|
if err != nil { |
||||||
|
res = []byte("") |
||||||
|
} |
||||||
|
return string(res), err |
||||||
|
} |
||||||
|
|
||||||
|
// ToFloat convert the input string to a float, or 0.0 if the input is not a float.
|
||||||
|
func ToFloat(str string) (float64, error) { |
||||||
|
res, err := strconv.ParseFloat(str, 64) |
||||||
|
if err != nil { |
||||||
|
res = 0.0 |
||||||
|
} |
||||||
|
return res, err |
||||||
|
} |
||||||
|
|
||||||
|
// ToInt convert the input string to an integer, or 0 if the input is not an integer.
|
||||||
|
func ToInt(str string) (int64, error) { |
||||||
|
res, err := strconv.ParseInt(str, 0, 64) |
||||||
|
if err != nil { |
||||||
|
res = 0 |
||||||
|
} |
||||||
|
return res, err |
||||||
|
} |
||||||
|
|
||||||
|
// ToBoolean convert the input string to a boolean.
|
||||||
|
func ToBoolean(str string) (bool, error) { |
||||||
|
res, err := strconv.ParseBool(str) |
||||||
|
if err != nil { |
||||||
|
res = false |
||||||
|
} |
||||||
|
return res, err |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package govalidator |
||||||
|
|
||||||
|
type Errors []error |
||||||
|
|
||||||
|
func (es Errors) Errors() []error { |
||||||
|
return es |
||||||
|
} |
||||||
|
|
||||||
|
func (es Errors) Error() string { |
||||||
|
var err string |
||||||
|
for _, e := range es { |
||||||
|
err += e.Error() + ";" |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
type Error struct { |
||||||
|
Name string |
||||||
|
Err error |
||||||
|
} |
||||||
|
|
||||||
|
func (e Error) Error() string { |
||||||
|
return e.Name + ": " + e.Err.Error() |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
package govalidator |
||||||
|
|
||||||
|
import "math" |
||||||
|
|
||||||
|
// Abs returns absolute value of number
|
||||||
|
func Abs(value float64) float64 { |
||||||
|
return value * Sign(value) |
||||||
|
} |
||||||
|
|
||||||
|
// Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise
|
||||||
|
func Sign(value float64) float64 { |
||||||
|
if value > 0 { |
||||||
|
return 1 |
||||||
|
} else if value < 0 { |
||||||
|
return -1 |
||||||
|
} else { |
||||||
|
return 0 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// IsNegative returns true if value < 0
|
||||||
|
func IsNegative(value float64) bool { |
||||||
|
return value < 0 |
||||||
|
} |
||||||
|
|
||||||
|
// IsPositive returns true if value > 0
|
||||||
|
func IsPositive(value float64) bool { |
||||||
|
return value > 0 |
||||||
|
} |
||||||
|
|
||||||
|
// IsNonNegative returns true if value >= 0
|
||||||
|
func IsNonNegative(value float64) bool { |
||||||
|
return value >= 0 |
||||||
|
} |
||||||
|
|
||||||
|
// IsNonPositive returns true if value <= 0
|
||||||
|
func IsNonPositive(value float64) bool { |
||||||
|
return value <= 0 |
||||||
|
} |
||||||
|
|
||||||
|
// InRange returns true if value lies between left and right border
|
||||||
|
func InRange(value, left, right float64) bool { |
||||||
|
if left > right { |
||||||
|
left, right = right, left |
||||||
|
} |
||||||
|
return value >= left && value <= right |
||||||
|
} |
||||||
|
|
||||||
|
// IsWhole returns true if value is whole number
|
||||||
|
func IsWhole(value float64) bool { |
||||||
|
return Abs(math.Remainder(value, 1)) == 0 |
||||||
|
} |
||||||
|
|
||||||
|
// IsNatural returns true if value is natural number (positive and whole)
|
||||||
|
func IsNatural(value float64) bool { |
||||||
|
return IsWhole(value) && IsPositive(value) |
||||||
|
} |
@ -0,0 +1,81 @@ |
|||||||
|
package govalidator |
||||||
|
|
||||||
|
import "regexp" |
||||||
|
|
||||||
|
// Basic regular expressions for validating strings
|
||||||
|
const ( |
||||||
|
Email string = "^(((([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}])))\\.?$" |
||||||
|
CreditCard string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})$" |
||||||
|
ISBN10 string = "^(?:[0-9]{9}X|[0-9]{10})$" |
||||||
|
ISBN13 string = "^(?:[0-9]{13})$" |
||||||
|
UUID3 string = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$" |
||||||
|
UUID4 string = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" |
||||||
|
UUID5 string = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" |
||||||
|
UUID string = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" |
||||||
|
Alpha string = "^[a-zA-Z]+$" |
||||||
|
Alphanumeric string = "^[a-zA-Z0-9]+$" |
||||||
|
Numeric string = "^[-+]?[0-9]+$" |
||||||
|
Int string = "^(?:[-+]?(?:0|[1-9][0-9]*))$" |
||||||
|
Float string = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$" |
||||||
|
Hexadecimal string = "^[0-9a-fA-F]+$" |
||||||
|
Hexcolor string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" |
||||||
|
RGBcolor string = "^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])\\s*\\)$" |
||||||
|
ASCII string = "^[\x00-\x7F]+$" |
||||||
|
Multibyte string = "[^\x00-\x7F]" |
||||||
|
FullWidth string = "[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]" |
||||||
|
HalfWidth string = "[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]" |
||||||
|
Base64 string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" |
||||||
|
PrintableASCII string = "^[\x20-\x7E]+$" |
||||||
|
DataURI string = "^data:.+\\/(.+);base64$" |
||||||
|
Latitude string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" |
||||||
|
Longitude string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" |
||||||
|
URL string = `^((ftp|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$` |
||||||
|
SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$` |
||||||
|
WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$` |
||||||
|
UnixPath string = `^((?:\/[a-zA-Z0-9\.\:]+(?:_[a-zA-Z0-9\:\.]+)*(?:\-[\:a-zA-Z0-9\.]+)*)+\/?)$` |
||||||
|
Semver string = "^v?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(-(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\\+[0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*)?$" |
||||||
|
tagName string = "valid" |
||||||
|
) |
||||||
|
|
||||||
|
// Used by IsFilePath func
|
||||||
|
const ( |
||||||
|
// Unknown is unresolved OS type
|
||||||
|
Unknown = iota |
||||||
|
// Win is Windows type
|
||||||
|
Win |
||||||
|
// Unix is *nix OS types
|
||||||
|
Unix |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
rxEmail = regexp.MustCompile(Email) |
||||||
|
rxCreditCard = regexp.MustCompile(CreditCard) |
||||||
|
rxISBN10 = regexp.MustCompile(ISBN10) |
||||||
|
rxISBN13 = regexp.MustCompile(ISBN13) |
||||||
|
rxUUID3 = regexp.MustCompile(UUID3) |
||||||
|
rxUUID4 = regexp.MustCompile(UUID4) |
||||||
|
rxUUID5 = regexp.MustCompile(UUID5) |
||||||
|
rxUUID = regexp.MustCompile(UUID) |
||||||
|
rxAlpha = regexp.MustCompile(Alpha) |
||||||
|
rxAlphanumeric = regexp.MustCompile(Alphanumeric) |
||||||
|
rxNumeric = regexp.MustCompile(Numeric) |
||||||
|
rxInt = regexp.MustCompile(Int) |
||||||
|
rxFloat = regexp.MustCompile(Float) |
||||||
|
rxHexadecimal = regexp.MustCompile(Hexadecimal) |
||||||
|
rxHexcolor = regexp.MustCompile(Hexcolor) |
||||||
|
rxRGBcolor = regexp.MustCompile(RGBcolor) |
||||||
|
rxASCII = regexp.MustCompile(ASCII) |
||||||
|
rxPrintableASCII = regexp.MustCompile(PrintableASCII) |
||||||
|
rxMultibyte = regexp.MustCompile(Multibyte) |
||||||
|
rxFullWidth = regexp.MustCompile(FullWidth) |
||||||
|
rxHalfWidth = regexp.MustCompile(HalfWidth) |
||||||
|
rxBase64 = regexp.MustCompile(Base64) |
||||||
|
rxDataURI = regexp.MustCompile(DataURI) |
||||||
|
rxLatitude = regexp.MustCompile(Latitude) |
||||||
|
rxLongitude = regexp.MustCompile(Longitude) |
||||||
|
rxURL = regexp.MustCompile(URL) |
||||||
|
rxSSN = regexp.MustCompile(SSN) |
||||||
|
rxWinPath = regexp.MustCompile(WinPath) |
||||||
|
rxUnixPath = regexp.MustCompile(UnixPath) |
||||||
|
rxSemver = regexp.MustCompile(Semver) |
||||||
|
) |
@ -0,0 +1,352 @@ |
|||||||
|
package govalidator |
||||||
|
|
||||||
|
import ( |
||||||
|
"reflect" |
||||||
|
"regexp" |
||||||
|
) |
||||||
|
|
||||||
|
// Validator is a wrapper for a validator function that returns bool and accepts string.
|
||||||
|
type Validator func(str string) bool |
||||||
|
|
||||||
|
// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type.
|
||||||
|
type CustomTypeValidator func(i interface{}) bool |
||||||
|
|
||||||
|
// ParamValidator is a wrapper for validator functions that accepts additional parameters.
|
||||||
|
type ParamValidator func(str string, params ...string) bool |
||||||
|
type tagOptions []string |
||||||
|
|
||||||
|
// UnsupportedTypeError is a wrapper for reflect.Type
|
||||||
|
type UnsupportedTypeError struct { |
||||||
|
Type reflect.Type |
||||||
|
} |
||||||
|
|
||||||
|
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
|
||||||
|
// It implements the methods to sort by string.
|
||||||
|
type stringValues []reflect.Value |
||||||
|
|
||||||
|
// ParamTagMap is a map of functions accept variants parameters
|
||||||
|
var ParamTagMap = map[string]ParamValidator{ |
||||||
|
"length": ByteLength, |
||||||
|
"stringlength": StringLength, |
||||||
|
"matches": StringMatches, |
||||||
|
} |
||||||
|
|
||||||
|
var ParamTagRegexMap = map[string]*regexp.Regexp{ |
||||||
|
"length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"), |
||||||
|
"stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"), |
||||||
|
"matches": regexp.MustCompile(`matches\(([^)]+)\)`), |
||||||
|
} |
||||||
|
|
||||||
|
// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function.
|
||||||
|
// Use this to validate compound or custom types that need to be handled as a whole, e.g.
|
||||||
|
// `type UUID [16]byte` (this would be handled as an array of bytes).
|
||||||
|
var CustomTypeTagMap = map[string]CustomTypeValidator{} |
||||||
|
|
||||||
|
// TagMap is a map of functions, that can be used as tags for ValidateStruct function.
|
||||||
|
var TagMap = map[string]Validator{ |
||||||
|
"email": IsEmail, |
||||||
|
"url": IsURL, |
||||||
|
"requrl": IsRequestURL, |
||||||
|
"requri": IsRequestURI, |
||||||
|
"alpha": IsAlpha, |
||||||
|
"utfletter": IsUTFLetter, |
||||||
|
"alphanum": IsAlphanumeric, |
||||||
|
"utfletternum": IsUTFLetterNumeric, |
||||||
|
"numeric": IsNumeric, |
||||||
|
"utfnumeric": IsUTFNumeric, |
||||||
|
"utfdigit": IsUTFDigit, |
||||||
|
"hexadecimal": IsHexadecimal, |
||||||
|
"hexcolor": IsHexcolor, |
||||||
|
"rgbcolor": IsRGBcolor, |
||||||
|
"lowercase": IsLowerCase, |
||||||
|
"uppercase": IsUpperCase, |
||||||
|
"int": IsInt, |
||||||
|
"float": IsFloat, |
||||||
|
"null": IsNull, |
||||||
|
"uuid": IsUUID, |
||||||
|
"uuidv3": IsUUIDv3, |
||||||
|
"uuidv4": IsUUIDv4, |
||||||
|
"uuidv5": IsUUIDv5, |
||||||
|
"creditcard": IsCreditCard, |
||||||
|
"isbn10": IsISBN10, |
||||||
|
"isbn13": IsISBN13, |
||||||
|
"json": IsJSON, |
||||||
|
"multibyte": IsMultibyte, |
||||||
|
"ascii": IsASCII, |
||||||
|
"printableascii": IsPrintableASCII, |
||||||
|
"fullwidth": IsFullWidth, |
||||||
|
"halfwidth": IsHalfWidth, |
||||||
|
"variablewidth": IsVariableWidth, |
||||||
|
"base64": IsBase64, |
||||||
|
"datauri": IsDataURI, |
||||||
|
"ip": IsIP, |
||||||
|
"ipv4": IsIPv4, |
||||||
|
"ipv6": IsIPv6, |
||||||
|
"mac": IsMAC, |
||||||
|
"latitude": IsLatitude, |
||||||
|
"longitude": IsLongitude, |
||||||
|
"ssn": IsSSN, |
||||||
|
"semver": IsSemver, |
||||||
|
} |
||||||
|
|
||||||
|
// ISO3166Entry stores country codes
|
||||||
|
type ISO3166Entry struct { |
||||||
|
EnglishShortName string |
||||||
|
FrenchShortName string |
||||||
|
Alpha2Code string |
||||||
|
Alpha3Code string |
||||||
|
Numeric string |
||||||
|
} |
||||||
|
|
||||||
|
//ISO3166List based on https://www.iso.org/obp/ui/#search/code/ Code Type "Officially Assigned Codes"
|
||||||
|
var ISO3166List = []ISO3166Entry{ |
||||||
|
{"Afghanistan", "Afghanistan (l')", "AF", "AFG", "004"}, |
||||||
|
{"Albania", "Albanie (l')", "AL", "ALB", "008"}, |
||||||
|
{"Antarctica", "Antarctique (l')", "AQ", "ATA", "010"}, |
||||||
|
{"Algeria", "Algérie (l')", "DZ", "DZA", "012"}, |
||||||
|
{"American Samoa", "Samoa américaines (les)", "AS", "ASM", "016"}, |
||||||
|
{"Andorra", "Andorre (l')", "AD", "AND", "020"}, |
||||||
|
{"Angola", "Angola (l')", "AO", "AGO", "024"}, |
||||||
|
{"Antigua and Barbuda", "Antigua-et-Barbuda", "AG", "ATG", "028"}, |
||||||
|
{"Azerbaijan", "Azerbaïdjan (l')", "AZ", "AZE", "031"}, |
||||||
|
{"Argentina", "Argentine (l')", "AR", "ARG", "032"}, |
||||||
|
{"Australia", "Australie (l')", "AU", "AUS", "036"}, |
||||||
|
{"Austria", "Autriche (l')", "AT", "AUT", "040"}, |
||||||
|
{"Bahamas (the)", "Bahamas (les)", "BS", "BHS", "044"}, |
||||||
|
{"Bahrain", "Bahreïn", "BH", "BHR", "048"}, |
||||||
|
{"Bangladesh", "Bangladesh (le)", "BD", "BGD", "050"}, |
||||||
|
{"Armenia", "Arménie (l')", "AM", "ARM", "051"}, |
||||||
|
{"Barbados", "Barbade (la)", "BB", "BRB", "052"}, |
||||||
|
{"Belgium", "Belgique (la)", "BE", "BEL", "056"}, |
||||||
|
{"Bermuda", "Bermudes (les)", "BM", "BMU", "060"}, |
||||||
|
{"Bhutan", "Bhoutan (le)", "BT", "BTN", "064"}, |
||||||
|
{"Bolivia (Plurinational State of)", "Bolivie (État plurinational de)", "BO", "BOL", "068"}, |
||||||
|
{"Bosnia and Herzegovina", "Bosnie-Herzégovine (la)", "BA", "BIH", "070"}, |
||||||
|
{"Botswana", "Botswana (le)", "BW", "BWA", "072"}, |
||||||
|
{"Bouvet Island", "Bouvet (l'Île)", "BV", "BVT", "074"}, |
||||||
|
{"Brazil", "Brésil (le)", "BR", "BRA", "076"}, |
||||||
|
{"Belize", "Belize (le)", "BZ", "BLZ", "084"}, |
||||||
|
{"British Indian Ocean Territory (the)", "Indien (le Territoire britannique de l'océan)", "IO", "IOT", "086"}, |
||||||
|
{"Solomon Islands", "Salomon (Îles)", "SB", "SLB", "090"}, |
||||||
|
{"Virgin Islands (British)", "Vierges britanniques (les Îles)", "VG", "VGB", "092"}, |
||||||
|
{"Brunei Darussalam", "Brunéi Darussalam (le)", "BN", "BRN", "096"}, |
||||||
|
{"Bulgaria", "Bulgarie (la)", "BG", "BGR", "100"}, |
||||||
|
{"Myanmar", "Myanmar (le)", "MM", "MMR", "104"}, |
||||||
|
{"Burundi", "Burundi (le)", "BI", "BDI", "108"}, |
||||||
|
{"Belarus", "Bélarus (le)", "BY", "BLR", "112"}, |
||||||
|
{"Cambodia", "Cambodge (le)", "KH", "KHM", "116"}, |
||||||
|
{"Cameroon", "Cameroun (le)", "CM", "CMR", "120"}, |
||||||
|
{"Canada", "Canada (le)", "CA", "CAN", "124"}, |
||||||
|
{"Cabo Verde", "Cabo Verde", "CV", "CPV", "132"}, |
||||||
|
{"Cayman Islands (the)", "Caïmans (les Îles)", "KY", "CYM", "136"}, |
||||||
|
{"Central African Republic (the)", "République centrafricaine (la)", "CF", "CAF", "140"}, |
||||||
|
{"Sri Lanka", "Sri Lanka", "LK", "LKA", "144"}, |
||||||
|
{"Chad", "Tchad (le)", "TD", "TCD", "148"}, |
||||||
|
{"Chile", "Chili (le)", "CL", "CHL", "152"}, |
||||||
|
{"China", "Chine (la)", "CN", "CHN", "156"}, |
||||||
|
{"Taiwan (Province of China)", "Taïwan (Province de Chine)", "TW", "TWN", "158"}, |
||||||
|
{"Christmas Island", "Christmas (l'Île)", "CX", "CXR", "162"}, |
||||||
|
{"Cocos (Keeling) Islands (the)", "Cocos (les Îles)/ Keeling (les Îles)", "CC", "CCK", "166"}, |
||||||
|
{"Colombia", "Colombie (la)", "CO", "COL", "170"}, |
||||||
|
{"Comoros (the)", "Comores (les)", "KM", "COM", "174"}, |
||||||
|
{"Mayotte", "Mayotte", "YT", "MYT", "175"}, |
||||||
|
{"Congo (the)", "Congo (le)", "CG", "COG", "178"}, |
||||||
|
{"Congo (the Democratic Republic of the)", "Congo (la République démocratique du)", "CD", "COD", "180"}, |
||||||
|
{"Cook Islands (the)", "Cook (les Îles)", "CK", "COK", "184"}, |
||||||
|
{"Costa Rica", "Costa Rica (le)", "CR", "CRI", "188"}, |
||||||
|
{"Croatia", "Croatie (la)", "HR", "HRV", "191"}, |
||||||
|
{"Cuba", "Cuba", "CU", "CUB", "192"}, |
||||||
|
{"Cyprus", "Chypre", "CY", "CYP", "196"}, |
||||||
|
{"Czech Republic (the)", "tchèque (la République)", "CZ", "CZE", "203"}, |
||||||
|
{"Benin", "Bénin (le)", "BJ", "BEN", "204"}, |
||||||
|
{"Denmark", "Danemark (le)", "DK", "DNK", "208"}, |
||||||
|
{"Dominica", "Dominique (la)", "DM", "DMA", "212"}, |
||||||
|
{"Dominican Republic (the)", "dominicaine (la République)", "DO", "DOM", "214"}, |
||||||
|
{"Ecuador", "Équateur (l')", "EC", "ECU", "218"}, |
||||||
|
{"El Salvador", "El Salvador", "SV", "SLV", "222"}, |
||||||
|
{"Equatorial Guinea", "Guinée équatoriale (la)", "GQ", "GNQ", "226"}, |
||||||
|
{"Ethiopia", "Éthiopie (l')", "ET", "ETH", "231"}, |
||||||
|
{"Eritrea", "Érythrée (l')", "ER", "ERI", "232"}, |
||||||
|
{"Estonia", "Estonie (l')", "EE", "EST", "233"}, |
||||||
|
{"Faroe Islands (the)", "Féroé (les Îles)", "FO", "FRO", "234"}, |
||||||
|
{"Falkland Islands (the) [Malvinas]", "Falkland (les Îles)/Malouines (les Îles)", "FK", "FLK", "238"}, |
||||||
|
{"South Georgia and the South Sandwich Islands", "Géorgie du Sud-et-les Îles Sandwich du Sud (la)", "GS", "SGS", "239"}, |
||||||
|
{"Fiji", "Fidji (les)", "FJ", "FJI", "242"}, |
||||||
|
{"Finland", "Finlande (la)", "FI", "FIN", "246"}, |
||||||
|
{"Åland Islands", "Åland(les Îles)", "AX", "ALA", "248"}, |
||||||
|
{"France", "France (la)", "FR", "FRA", "250"}, |
||||||
|
{"French Guiana", "Guyane française (la )", "GF", "GUF", "254"}, |
||||||
|
{"French Polynesia", "Polynésie française (la)", "PF", "PYF", "258"}, |
||||||
|
{"French Southern Territories (the)", "Terres australes françaises (les)", "TF", "ATF", "260"}, |
||||||
|
{"Djibouti", "Djibouti", "DJ", "DJI", "262"}, |
||||||
|
{"Gabon", "Gabon (le)", "GA", "GAB", "266"}, |
||||||
|
{"Georgia", "Géorgie (la)", "GE", "GEO", "268"}, |
||||||
|
{"Gambia (the)", "Gambie (la)", "GM", "GMB", "270"}, |
||||||
|
{"Palestine, State of", "Palestine, État de", "PS", "PSE", "275"}, |
||||||
|
{"Germany", "Allemagne (l')", "DE", "DEU", "276"}, |
||||||
|
{"Ghana", "Ghana (le)", "GH", "GHA", "288"}, |
||||||
|
{"Gibraltar", "Gibraltar", "GI", "GIB", "292"}, |
||||||
|
{"Kiribati", "Kiribati", "KI", "KIR", "296"}, |
||||||
|
{"Greece", "Grèce (la)", "GR", "GRC", "300"}, |
||||||
|
{"Greenland", "Groenland (le)", "GL", "GRL", "304"}, |
||||||
|
{"Grenada", "Grenade (la)", "GD", "GRD", "308"}, |
||||||
|
{"Guadeloupe", "Guadeloupe (la)", "GP", "GLP", "312"}, |
||||||
|
{"Guam", "Guam", "GU", "GUM", "316"}, |
||||||
|
{"Guatemala", "Guatemala (le)", "GT", "GTM", "320"}, |
||||||
|
{"Guinea", "Guinée (la)", "GN", "GIN", "324"}, |
||||||
|
{"Guyana", "Guyana (le)", "GY", "GUY", "328"}, |
||||||
|
{"Haiti", "Haïti", "HT", "HTI", "332"}, |
||||||
|
{"Heard Island and McDonald Islands", "Heard-et-Îles MacDonald (l'Île)", "HM", "HMD", "334"}, |
||||||
|
{"Holy See (the)", "Saint-Siège (le)", "VA", "VAT", "336"}, |
||||||
|
{"Honduras", "Honduras (le)", "HN", "HND", "340"}, |
||||||
|
{"Hong Kong", "Hong Kong", "HK", "HKG", "344"}, |
||||||
|
{"Hungary", "Hongrie (la)", "HU", "HUN", "348"}, |
||||||
|
{"Iceland", "Islande (l')", "IS", "ISL", "352"}, |
||||||
|
{"India", "Inde (l')", "IN", "IND", "356"}, |
||||||
|
{"Indonesia", "Indonésie (l')", "ID", "IDN", "360"}, |
||||||
|
{"Iran (Islamic Republic of)", "Iran (République Islamique d')", "IR", "IRN", "364"}, |
||||||
|
{"Iraq", "Iraq (l')", "IQ", "IRQ", "368"}, |
||||||
|
{"Ireland", "Irlande (l')", "IE", "IRL", "372"}, |
||||||
|
{"Israel", "Israël", "IL", "ISR", "376"}, |
||||||
|
{"Italy", "Italie (l')", "IT", "ITA", "380"}, |
||||||
|
{"Côte d'Ivoire", "Côte d'Ivoire (la)", "CI", "CIV", "384"}, |
||||||
|
{"Jamaica", "Jamaïque (la)", "JM", "JAM", "388"}, |
||||||
|
{"Japan", "Japon (le)", "JP", "JPN", "392"}, |
||||||
|
{"Kazakhstan", "Kazakhstan (le)", "KZ", "KAZ", "398"}, |
||||||
|
{"Jordan", "Jordanie (la)", "JO", "JOR", "400"}, |
||||||
|
{"Kenya", "Kenya (le)", "KE", "KEN", "404"}, |
||||||
|
{"Korea (the Democratic People's Republic of)", "Corée (la République populaire démocratique de)", "KP", "PRK", "408"}, |
||||||
|
{"Korea (the Republic of)", "Corée (la République de)", "KR", "KOR", "410"}, |
||||||
|
{"Kuwait", "Koweït (le)", "KW", "KWT", "414"}, |
||||||
|
{"Kyrgyzstan", "Kirghizistan (le)", "KG", "KGZ", "417"}, |
||||||
|
{"Lao People's Democratic Republic (the)", "Lao, République démocratique populaire", "LA", "LAO", "418"}, |
||||||
|
{"Lebanon", "Liban (le)", "LB", "LBN", "422"}, |
||||||
|
{"Lesotho", "Lesotho (le)", "LS", "LSO", "426"}, |
||||||
|
{"Latvia", "Lettonie (la)", "LV", "LVA", "428"}, |
||||||
|
{"Liberia", "Libéria (le)", "LR", "LBR", "430"}, |
||||||
|
{"Libya", "Libye (la)", "LY", "LBY", "434"}, |
||||||
|
{"Liechtenstein", "Liechtenstein (le)", "LI", "LIE", "438"}, |
||||||
|
{"Lithuania", "Lituanie (la)", "LT", "LTU", "440"}, |
||||||
|
{"Luxembourg", "Luxembourg (le)", "LU", "LUX", "442"}, |
||||||
|
{"Macao", "Macao", "MO", "MAC", "446"}, |
||||||
|
{"Madagascar", "Madagascar", "MG", "MDG", "450"}, |
||||||
|
{"Malawi", "Malawi (le)", "MW", "MWI", "454"}, |
||||||
|
{"Malaysia", "Malaisie (la)", "MY", "MYS", "458"}, |
||||||
|
{"Maldives", "Maldives (les)", "MV", "MDV", "462"}, |
||||||
|
{"Mali", "Mali (le)", "ML", "MLI", "466"}, |
||||||
|
{"Malta", "Malte", "MT", "MLT", "470"}, |
||||||
|
{"Martinique", "Martinique (la)", "MQ", "MTQ", "474"}, |
||||||
|
{"Mauritania", "Mauritanie (la)", "MR", "MRT", "478"}, |
||||||
|
{"Mauritius", "Maurice", "MU", "MUS", "480"}, |
||||||
|
{"Mexico", "Mexique (le)", "MX", "MEX", "484"}, |
||||||
|
{"Monaco", "Monaco", "MC", "MCO", "492"}, |
||||||
|
{"Mongolia", "Mongolie (la)", "MN", "MNG", "496"}, |
||||||
|
{"Moldova (the Republic of)", "Moldova , République de", "MD", "MDA", "498"}, |
||||||
|
{"Montenegro", "Monténégro (le)", "ME", "MNE", "499"}, |
||||||
|
{"Montserrat", "Montserrat", "MS", "MSR", "500"}, |
||||||
|
{"Morocco", "Maroc (le)", "MA", "MAR", "504"}, |
||||||
|
{"Mozambique", "Mozambique (le)", "MZ", "MOZ", "508"}, |
||||||
|
{"Oman", "Oman", "OM", "OMN", "512"}, |
||||||
|
{"Namibia", "Namibie (la)", "NA", "NAM", "516"}, |
||||||
|
{"Nauru", "Nauru", "NR", "NRU", "520"}, |
||||||
|
{"Nepal", "Népal (le)", "NP", "NPL", "524"}, |
||||||
|
{"Netherlands (the)", "Pays-Bas (les)", "NL", "NLD", "528"}, |
||||||
|
{"Curaçao", "Curaçao", "CW", "CUW", "531"}, |
||||||
|
{"Aruba", "Aruba", "AW", "ABW", "533"}, |
||||||
|
{"Sint Maarten (Dutch part)", "Saint-Martin (partie néerlandaise)", "SX", "SXM", "534"}, |
||||||
|
{"Bonaire, Sint Eustatius and Saba", "Bonaire, Saint-Eustache et Saba", "BQ", "BES", "535"}, |
||||||
|
{"New Caledonia", "Nouvelle-Calédonie (la)", "NC", "NCL", "540"}, |
||||||
|
{"Vanuatu", "Vanuatu (le)", "VU", "VUT", "548"}, |
||||||
|
{"New Zealand", "Nouvelle-Zélande (la)", "NZ", "NZL", "554"}, |
||||||
|
{"Nicaragua", "Nicaragua (le)", "NI", "NIC", "558"}, |
||||||
|
{"Niger (the)", "Niger (le)", "NE", "NER", "562"}, |
||||||
|
{"Nigeria", "Nigéria (le)", "NG", "NGA", "566"}, |
||||||
|
{"Niue", "Niue", "NU", "NIU", "570"}, |
||||||
|
{"Norfolk Island", "Norfolk (l'Île)", "NF", "NFK", "574"}, |
||||||
|
{"Norway", "Norvège (la)", "NO", "NOR", "578"}, |
||||||
|
{"Northern Mariana Islands (the)", "Mariannes du Nord (les Îles)", "MP", "MNP", "580"}, |
||||||
|
{"United States Minor Outlying Islands (the)", "Îles mineures éloignées des États-Unis (les)", "UM", "UMI", "581"}, |
||||||
|
{"Micronesia (Federated States of)", "Micronésie (États fédérés de)", "FM", "FSM", "583"}, |
||||||
|
{"Marshall Islands (the)", "Marshall (Îles)", "MH", "MHL", "584"}, |
||||||
|
{"Palau", "Palaos (les)", "PW", "PLW", "585"}, |
||||||
|
{"Pakistan", "Pakistan (le)", "PK", "PAK", "586"}, |
||||||
|
{"Panama", "Panama (le)", "PA", "PAN", "591"}, |
||||||
|
{"Papua New Guinea", "Papouasie-Nouvelle-Guinée (la)", "PG", "PNG", "598"}, |
||||||
|
{"Paraguay", "Paraguay (le)", "PY", "PRY", "600"}, |
||||||
|
{"Peru", "Pérou (le)", "PE", "PER", "604"}, |
||||||
|
{"Philippines (the)", "Philippines (les)", "PH", "PHL", "608"}, |
||||||
|
{"Pitcairn", "Pitcairn", "PN", "PCN", "612"}, |
||||||
|
{"Poland", "Pologne (la)", "PL", "POL", "616"}, |
||||||
|
{"Portugal", "Portugal (le)", "PT", "PRT", "620"}, |
||||||
|
{"Guinea-Bissau", "Guinée-Bissau (la)", "GW", "GNB", "624"}, |
||||||
|
{"Timor-Leste", "Timor-Leste (le)", "TL", "TLS", "626"}, |
||||||
|
{"Puerto Rico", "Porto Rico", "PR", "PRI", "630"}, |
||||||
|
{"Qatar", "Qatar (le)", "QA", "QAT", "634"}, |
||||||
|
{"Réunion", "Réunion (La)", "RE", "REU", "638"}, |
||||||
|
{"Romania", "Roumanie (la)", "RO", "ROU", "642"}, |
||||||
|
{"Russian Federation (the)", "Russie (la Fédération de)", "RU", "RUS", "643"}, |
||||||
|
{"Rwanda", "Rwanda (le)", "RW", "RWA", "646"}, |
||||||
|
{"Saint Barthélemy", "Saint-Barthélemy", "BL", "BLM", "652"}, |
||||||
|
{"Saint Helena, Ascension and Tristan da Cunha", "Sainte-Hélène, Ascension et Tristan da Cunha", "SH", "SHN", "654"}, |
||||||
|
{"Saint Kitts and Nevis", "Saint-Kitts-et-Nevis", "KN", "KNA", "659"}, |
||||||
|
{"Anguilla", "Anguilla", "AI", "AIA", "660"}, |
||||||
|
{"Saint Lucia", "Sainte-Lucie", "LC", "LCA", "662"}, |
||||||
|
{"Saint Martin (French part)", "Saint-Martin (partie française)", "MF", "MAF", "663"}, |
||||||
|
{"Saint Pierre and Miquelon", "Saint-Pierre-et-Miquelon", "PM", "SPM", "666"}, |
||||||
|
{"Saint Vincent and the Grenadines", "Saint-Vincent-et-les Grenadines", "VC", "VCT", "670"}, |
||||||
|
{"San Marino", "Saint-Marin", "SM", "SMR", "674"}, |
||||||
|
{"Sao Tome and Principe", "Sao Tomé-et-Principe", "ST", "STP", "678"}, |
||||||
|
{"Saudi Arabia", "Arabie saoudite (l')", "SA", "SAU", "682"}, |
||||||
|
{"Senegal", "Sénégal (le)", "SN", "SEN", "686"}, |
||||||
|
{"Serbia", "Serbie (la)", "RS", "SRB", "688"}, |
||||||
|
{"Seychelles", "Seychelles (les)", "SC", "SYC", "690"}, |
||||||
|
{"Sierra Leone", "Sierra Leone (la)", "SL", "SLE", "694"}, |
||||||
|
{"Singapore", "Singapour", "SG", "SGP", "702"}, |
||||||
|
{"Slovakia", "Slovaquie (la)", "SK", "SVK", "703"}, |
||||||
|
{"Viet Nam", "Viet Nam (le)", "VN", "VNM", "704"}, |
||||||
|
{"Slovenia", "Slovénie (la)", "SI", "SVN", "705"}, |
||||||
|
{"Somalia", "Somalie (la)", "SO", "SOM", "706"}, |
||||||
|
{"South Africa", "Afrique du Sud (l')", "ZA", "ZAF", "710"}, |
||||||
|
{"Zimbabwe", "Zimbabwe (le)", "ZW", "ZWE", "716"}, |
||||||
|
{"Spain", "Espagne (l')", "ES", "ESP", "724"}, |
||||||
|
{"South Sudan", "Soudan du Sud (le)", "SS", "SSD", "728"}, |
||||||
|
{"Sudan (the)", "Soudan (le)", "SD", "SDN", "729"}, |
||||||
|
{"Western Sahara*", "Sahara occidental (le)*", "EH", "ESH", "732"}, |
||||||
|
{"Suriname", "Suriname (le)", "SR", "SUR", "740"}, |
||||||
|
{"Svalbard and Jan Mayen", "Svalbard et l'Île Jan Mayen (le)", "SJ", "SJM", "744"}, |
||||||
|
{"Swaziland", "Swaziland (le)", "SZ", "SWZ", "748"}, |
||||||
|
{"Sweden", "Suède (la)", "SE", "SWE", "752"}, |
||||||
|
{"Switzerland", "Suisse (la)", "CH", "CHE", "756"}, |
||||||
|
{"Syrian Arab Republic", "République arabe syrienne (la)", "SY", "SYR", "760"}, |
||||||
|
{"Tajikistan", "Tadjikistan (le)", "TJ", "TJK", "762"}, |
||||||
|
{"Thailand", "Thaïlande (la)", "TH", "THA", "764"}, |
||||||
|
{"Togo", "Togo (le)", "TG", "TGO", "768"}, |
||||||
|
{"Tokelau", "Tokelau (les)", "TK", "TKL", "772"}, |
||||||
|
{"Tonga", "Tonga (les)", "TO", "TON", "776"}, |
||||||
|
{"Trinidad and Tobago", "Trinité-et-Tobago (la)", "TT", "TTO", "780"}, |
||||||
|
{"United Arab Emirates (the)", "Émirats arabes unis (les)", "AE", "ARE", "784"}, |
||||||
|
{"Tunisia", "Tunisie (la)", "TN", "TUN", "788"}, |
||||||
|
{"Turkey", "Turquie (la)", "TR", "TUR", "792"}, |
||||||
|
{"Turkmenistan", "Turkménistan (le)", "TM", "TKM", "795"}, |
||||||
|
{"Turks and Caicos Islands (the)", "Turks-et-Caïcos (les Îles)", "TC", "TCA", "796"}, |
||||||
|
{"Tuvalu", "Tuvalu (les)", "TV", "TUV", "798"}, |
||||||
|
{"Uganda", "Ouganda (l')", "UG", "UGA", "800"}, |
||||||
|
{"Ukraine", "Ukraine (l')", "UA", "UKR", "804"}, |
||||||
|
{"Macedonia (the former Yugoslav Republic of)", "Macédoine (l'ex‑République yougoslave de)", "MK", "MKD", "807"}, |
||||||
|
{"Egypt", "Égypte (l')", "EG", "EGY", "818"}, |
||||||
|
{"United Kingdom of Great Britain and Northern Ireland (the)", "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord (le)", "GB", "GBR", "826"}, |
||||||
|
{"Guernsey", "Guernesey", "GG", "GGY", "831"}, |
||||||
|
{"Jersey", "Jersey", "JE", "JEY", "832"}, |
||||||
|
{"Isle of Man", "Île de Man", "IM", "IMN", "833"}, |
||||||
|
{"Tanzania, United Republic of", "Tanzanie, République-Unie de", "TZ", "TZA", "834"}, |
||||||
|
{"United States of America (the)", "États-Unis d'Amérique (les)", "US", "USA", "840"}, |
||||||
|
{"Virgin Islands (U.S.)", "Vierges des États-Unis (les Îles)", "VI", "VIR", "850"}, |
||||||
|
{"Burkina Faso", "Burkina Faso (le)", "BF", "BFA", "854"}, |
||||||
|
{"Uruguay", "Uruguay (l')", "UY", "URY", "858"}, |
||||||
|
{"Uzbekistan", "Ouzbékistan (l')", "UZ", "UZB", "860"}, |
||||||
|
{"Venezuela (Bolivarian Republic of)", "Venezuela (République bolivarienne du)", "VE", "VEN", "862"}, |
||||||
|
{"Wallis and Futuna", "Wallis-et-Futuna", "WF", "WLF", "876"}, |
||||||
|
{"Samoa", "Samoa (le)", "WS", "WSM", "882"}, |
||||||
|
{"Yemen", "Yémen (le)", "YE", "YEM", "887"}, |
||||||
|
{"Zambia", "Zambie (la)", "ZM", "ZMB", "894"}, |
||||||
|
} |
@ -0,0 +1,214 @@ |
|||||||
|
package govalidator |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"html" |
||||||
|
"path" |
||||||
|
"regexp" |
||||||
|
"strings" |
||||||
|
"unicode" |
||||||
|
) |
||||||
|
|
||||||
|
// Contains check if the string contains the substring.
|
||||||
|
func Contains(str, substring string) bool { |
||||||
|
return strings.Contains(str, substring) |
||||||
|
} |
||||||
|
|
||||||
|
// Matches check if string matches the pattern (pattern is regular expression)
|
||||||
|
// In case of error return false
|
||||||
|
func Matches(str, pattern string) bool { |
||||||
|
match, _ := regexp.MatchString(pattern, str) |
||||||
|
return match |
||||||
|
} |
||||||
|
|
||||||
|
// LeftTrim trim characters from the left-side of the input.
|
||||||
|
// If second argument is empty, it's will be remove leading spaces.
|
||||||
|
func LeftTrim(str, chars string) string { |
||||||
|
pattern := "" |
||||||
|
if chars == "" { |
||||||
|
pattern = "^\\s+" |
||||||
|
} else { |
||||||
|
pattern = "^[" + chars + "]+" |
||||||
|
} |
||||||
|
r, _ := regexp.Compile(pattern) |
||||||
|
return string(r.ReplaceAll([]byte(str), []byte(""))) |
||||||
|
} |
||||||
|
|
||||||
|
// RightTrim trim characters from the right-side of the input.
|
||||||
|
// If second argument is empty, it's will be remove spaces.
|
||||||
|
func RightTrim(str, chars string) string { |
||||||
|
pattern := "" |
||||||
|
if chars == "" { |
||||||
|
pattern = "\\s+$" |
||||||
|
} else { |
||||||
|
pattern = "[" + chars + "]+$" |
||||||
|
} |
||||||
|
r, _ := regexp.Compile(pattern) |
||||||
|
return string(r.ReplaceAll([]byte(str), []byte(""))) |
||||||
|
} |
||||||
|
|
||||||
|
// Trim trim characters from both sides of the input.
|
||||||
|
// If second argument is empty, it's will be remove spaces.
|
||||||
|
func Trim(str, chars string) string { |
||||||
|
return LeftTrim(RightTrim(str, chars), chars) |
||||||
|
} |
||||||
|
|
||||||
|
// WhiteList remove characters that do not appear in the whitelist.
|
||||||
|
func WhiteList(str, chars string) string { |
||||||
|
pattern := "[^" + chars + "]+" |
||||||
|
r, _ := regexp.Compile(pattern) |
||||||
|
return string(r.ReplaceAll([]byte(str), []byte(""))) |
||||||
|
} |
||||||
|
|
||||||
|
// BlackList remove characters that appear in the blacklist.
|
||||||
|
func BlackList(str, chars string) string { |
||||||
|
pattern := "[" + chars + "]+" |
||||||
|
r, _ := regexp.Compile(pattern) |
||||||
|
return string(r.ReplaceAll([]byte(str), []byte(""))) |
||||||
|
} |
||||||
|
|
||||||
|
// StripLow remove characters with a numerical value < 32 and 127, mostly control characters.
|
||||||
|
// If keep_new_lines is true, newline characters are preserved (\n and \r, hex 0xA and 0xD).
|
||||||
|
func StripLow(str string, keepNewLines bool) string { |
||||||
|
chars := "" |
||||||
|
if keepNewLines { |
||||||
|
chars = "\x00-\x09\x0B\x0C\x0E-\x1F\x7F" |
||||||
|
} else { |
||||||
|
chars = "\x00-\x1F\x7F" |
||||||
|
} |
||||||
|
return BlackList(str, chars) |
||||||
|
} |
||||||
|
|
||||||
|
// ReplacePattern replace regular expression pattern in string
|
||||||
|
func ReplacePattern(str, pattern, replace string) string { |
||||||
|
r, _ := regexp.Compile(pattern) |
||||||
|
return string(r.ReplaceAll([]byte(str), []byte(replace))) |
||||||
|
} |
||||||
|
|
||||||
|
// Escape replace <, >, & and " with HTML entities.
|
||||||
|
var Escape = html.EscapeString |
||||||
|
|
||||||
|
func addSegment(inrune, segment []rune) []rune { |
||||||
|
if len(segment) == 0 { |
||||||
|
return inrune |
||||||
|
} |
||||||
|
if len(inrune) != 0 { |
||||||
|
inrune = append(inrune, '_') |
||||||
|
} |
||||||
|
inrune = append(inrune, segment...) |
||||||
|
return inrune |
||||||
|
} |
||||||
|
|
||||||
|
// UnderscoreToCamelCase converts from underscore separated form to camel case form.
|
||||||
|
// Ex.: my_func => MyFunc
|
||||||
|
func UnderscoreToCamelCase(s string) string { |
||||||
|
return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1) |
||||||
|
} |
||||||
|
|
||||||
|
// CamelCaseToUnderscore converts from camel case form to underscore separated form.
|
||||||
|
// Ex.: MyFunc => my_func
|
||||||
|
func CamelCaseToUnderscore(str string) string { |
||||||
|
var output []rune |
||||||
|
var segment []rune |
||||||
|
for _, r := range str { |
||||||
|
if !unicode.IsLower(r) { |
||||||
|
output = addSegment(output, segment) |
||||||
|
segment = nil |
||||||
|
} |
||||||
|
segment = append(segment, unicode.ToLower(r)) |
||||||
|
} |
||||||
|
output = addSegment(output, segment) |
||||||
|
return string(output) |
||||||
|
} |
||||||
|
|
||||||
|
// Reverse return reversed string
|
||||||
|
func Reverse(s string) string { |
||||||
|
r := []rune(s) |
||||||
|
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 { |
||||||
|
r[i], r[j] = r[j], r[i] |
||||||
|
} |
||||||
|
return string(r) |
||||||
|
} |
||||||
|
|
||||||
|
// GetLines split string by "\n" and return array of lines
|
||||||
|
func GetLines(s string) []string { |
||||||
|
return strings.Split(s, "\n") |
||||||
|
} |
||||||
|
|
||||||
|
// GetLine return specified line of multiline string
|
||||||
|
func GetLine(s string, index int) (string, error) { |
||||||
|
lines := GetLines(s) |
||||||
|
if index < 0 || index >= len(lines) { |
||||||
|
return "", errors.New("line index out of bounds") |
||||||
|
} |
||||||
|
return lines[index], nil |
||||||
|
} |
||||||
|
|
||||||
|
// RemoveTags remove all tags from HTML string
|
||||||
|
func RemoveTags(s string) string { |
||||||
|
return ReplacePattern(s, "<[^>]*>", "") |
||||||
|
} |
||||||
|
|
||||||
|
// SafeFileName return safe string that can be used in file names
|
||||||
|
func SafeFileName(str string) string { |
||||||
|
name := strings.ToLower(str) |
||||||
|
name = path.Clean(path.Base(name)) |
||||||
|
name = strings.Trim(name, " ") |
||||||
|
separators, err := regexp.Compile(`[ &_=+:]`) |
||||||
|
if err == nil { |
||||||
|
name = separators.ReplaceAllString(name, "-") |
||||||
|
} |
||||||
|
legal, err := regexp.Compile(`[^[:alnum:]-.]`) |
||||||
|
if err == nil { |
||||||
|
name = legal.ReplaceAllString(name, "") |
||||||
|
} |
||||||
|
for strings.Contains(name, "--") { |
||||||
|
name = strings.Replace(name, "--", "-", -1) |
||||||
|
} |
||||||
|
return name |
||||||
|
} |
||||||
|
|
||||||
|
// NormalizeEmail canonicalize an email address.
|
||||||
|
// The local part of the email address is lowercased for all domains; the hostname is always lowercased and
|
||||||
|
// the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail).
|
||||||
|
// Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and
|
||||||
|
// are stripped of tags (e.g. some.one+tag@gmail.com becomes someone@gmail.com) and all @googlemail.com addresses are
|
||||||
|
// normalized to @gmail.com.
|
||||||
|
func NormalizeEmail(str string) (string, error) { |
||||||
|
if !IsEmail(str) { |
||||||
|
return "", fmt.Errorf("%s is not an email", str) |
||||||
|
} |
||||||
|
parts := strings.Split(str, "@") |
||||||
|
parts[0] = strings.ToLower(parts[0]) |
||||||
|
parts[1] = strings.ToLower(parts[1]) |
||||||
|
if parts[1] == "gmail.com" || parts[1] == "googlemail.com" { |
||||||
|
parts[1] = "gmail.com" |
||||||
|
parts[0] = strings.Split(ReplacePattern(parts[0], `\.`, ""), "+")[0] |
||||||
|
} |
||||||
|
return strings.Join(parts, "@"), nil |
||||||
|
} |
||||||
|
|
||||||
|
// Will truncate a string closest length without breaking words.
|
||||||
|
func Truncate(str string, length int, ending string) string { |
||||||
|
var aftstr, befstr string |
||||||
|
if len(str) > length { |
||||||
|
words := strings.Fields(str) |
||||||
|
before, present := 0, 0 |
||||||
|
for i := range words { |
||||||
|
befstr = aftstr |
||||||
|
before = present |
||||||
|
aftstr = aftstr + words[i] + " " |
||||||
|
present = len(aftstr) |
||||||
|
if present > length && i != 0 { |
||||||
|
if (length - before) < (present - length) { |
||||||
|
return Trim(befstr, " /\\.,\"'#!?&@+-") + ending |
||||||
|
} else { |
||||||
|
return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return str |
||||||
|
} |
@ -0,0 +1,868 @@ |
|||||||
|
// Package govalidator is package of validators and sanitizers for strings, structs and collections.
|
||||||
|
package govalidator |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"net" |
||||||
|
"net/url" |
||||||
|
"reflect" |
||||||
|
"regexp" |
||||||
|
"sort" |
||||||
|
"strings" |
||||||
|
"unicode" |
||||||
|
"unicode/utf8" |
||||||
|
) |
||||||
|
|
||||||
|
var fieldsRequiredByDefault bool |
||||||
|
|
||||||
|
// SetFieldsRequiredByDefault causes validation to fail when struct fields
|
||||||
|
// do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`).
|
||||||
|
// This struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
|
||||||
|
// type exampleStruct struct {
|
||||||
|
// Name string ``
|
||||||
|
// Email string `valid:"email"`
|
||||||
|
// This, however, will only fail when Email is empty or an invalid email address:
|
||||||
|
// type exampleStruct2 struct {
|
||||||
|
// Name string `valid:"-"`
|
||||||
|
// Email string `valid:"email"`
|
||||||
|
// Lastly, this will only fail when Email is an invalid email address but not when it's empty:
|
||||||
|
// type exampleStruct2 struct {
|
||||||
|
// Name string `valid:"-"`
|
||||||
|
// Email string `valid:"email,optional"`
|
||||||
|
func SetFieldsRequiredByDefault(value bool) { |
||||||
|
fieldsRequiredByDefault = value |
||||||
|
} |
||||||
|
|
||||||
|
// IsEmail check if the string is an email.
|
||||||
|
func IsEmail(str string) bool { |
||||||
|
// TODO uppercase letters are not supported
|
||||||
|
return rxEmail.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsURL check if the string is an URL.
|
||||||
|
func IsURL(str string) bool { |
||||||
|
if str == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") { |
||||||
|
return false |
||||||
|
} |
||||||
|
u, err := url.Parse(str) |
||||||
|
if err != nil { |
||||||
|
return false |
||||||
|
} |
||||||
|
if strings.HasPrefix(u.Host, ".") { |
||||||
|
return false |
||||||
|
} |
||||||
|
if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) { |
||||||
|
return false |
||||||
|
} |
||||||
|
return rxURL.MatchString(str) |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// IsRequestURL check if the string rawurl, assuming
|
||||||
|
// it was recieved in an HTTP request, is a valid
|
||||||
|
// URL confirm to RFC 3986
|
||||||
|
func IsRequestURL(rawurl string) bool { |
||||||
|
url, err := url.ParseRequestURI(rawurl) |
||||||
|
if err != nil { |
||||||
|
return false //Couldn't even parse the rawurl
|
||||||
|
} |
||||||
|
if len(url.Scheme) == 0 { |
||||||
|
return false //No Scheme found
|
||||||
|
} |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
// IsRequestURI check if the string rawurl, assuming
|
||||||
|
// it was recieved in an HTTP request, is an
|
||||||
|
// absolute URI or an absolute path.
|
||||||
|
func IsRequestURI(rawurl string) bool { |
||||||
|
_, err := url.ParseRequestURI(rawurl) |
||||||
|
return err == nil |
||||||
|
} |
||||||
|
|
||||||
|
// IsAlpha check if the string contains only letters (a-zA-Z). Empty string is valid.
|
||||||
|
func IsAlpha(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
return rxAlpha.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
//IsUTFLetter check if the string contains only unicode letter characters.
|
||||||
|
//Similar to IsAlpha but for all languages. Empty string is valid.
|
||||||
|
func IsUTFLetter(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
for _, c := range str { |
||||||
|
if !unicode.IsLetter(c) { |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
return true |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// IsAlphanumeric check if the string contains only letters and numbers. Empty string is valid.
|
||||||
|
func IsAlphanumeric(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
return rxAlphanumeric.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsUTFLetterNumeric check if the string contains only unicode letters and numbers. Empty string is valid.
|
||||||
|
func IsUTFLetterNumeric(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
for _, c := range str { |
||||||
|
if !unicode.IsLetter(c) && !unicode.IsNumber(c) { //letters && numbers are ok
|
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
return true |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// IsNumeric check if the string contains only numbers. Empty string is valid.
|
||||||
|
func IsNumeric(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
return rxNumeric.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsUTFNumeric check if the string contains only unicode numbers of any kind.
|
||||||
|
// Numbers can be 0-9 but also Fractions ¾,Roman Ⅸ and Hangzhou 〩. Empty string is valid.
|
||||||
|
func IsUTFNumeric(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
if strings.IndexAny(str, "+-") > 0 { |
||||||
|
return false |
||||||
|
} |
||||||
|
if len(str) > 1 { |
||||||
|
str = strings.TrimPrefix(str, "-") |
||||||
|
str = strings.TrimPrefix(str, "+") |
||||||
|
} |
||||||
|
for _, c := range str { |
||||||
|
if unicode.IsNumber(c) == false { //numbers && minus sign are ok
|
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
return true |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// IsUTFDigit check if the string contains only unicode radix-10 decimal digits. Empty string is valid.
|
||||||
|
func IsUTFDigit(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
if strings.IndexAny(str, "+-") > 0 { |
||||||
|
return false |
||||||
|
} |
||||||
|
if len(str) > 1 { |
||||||
|
str = strings.TrimPrefix(str, "-") |
||||||
|
str = strings.TrimPrefix(str, "+") |
||||||
|
} |
||||||
|
for _, c := range str { |
||||||
|
if !unicode.IsDigit(c) { //digits && minus sign are ok
|
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
return true |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// IsHexadecimal check if the string is a hexadecimal number.
|
||||||
|
func IsHexadecimal(str string) bool { |
||||||
|
return rxHexadecimal.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsHexcolor check if the string is a hexadecimal color.
|
||||||
|
func IsHexcolor(str string) bool { |
||||||
|
return rxHexcolor.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsRGBcolor check if the string is a valid RGB color in form rgb(RRR, GGG, BBB).
|
||||||
|
func IsRGBcolor(str string) bool { |
||||||
|
return rxRGBcolor.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsLowerCase check if the string is lowercase. Empty string is valid.
|
||||||
|
func IsLowerCase(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
return str == strings.ToLower(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsUpperCase check if the string is uppercase. Empty string is valid.
|
||||||
|
func IsUpperCase(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
return str == strings.ToUpper(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsInt check if the string is an integer. Empty string is valid.
|
||||||
|
func IsInt(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
return rxInt.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsFloat check if the string is a float.
|
||||||
|
func IsFloat(str string) bool { |
||||||
|
return str != "" && rxFloat.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsDivisibleBy check if the string is a number that's divisible by another.
|
||||||
|
// If second argument is not valid integer or zero, it's return false.
|
||||||
|
// Otherwise, if first argument is not valid integer or zero, it's return true (Invalid string converts to zero).
|
||||||
|
func IsDivisibleBy(str, num string) bool { |
||||||
|
f, _ := ToFloat(str) |
||||||
|
p := int64(f) |
||||||
|
q, _ := ToInt(num) |
||||||
|
if q == 0 { |
||||||
|
return false |
||||||
|
} |
||||||
|
return (p == 0) || (p%q == 0) |
||||||
|
} |
||||||
|
|
||||||
|
// IsNull check if the string is null.
|
||||||
|
func IsNull(str string) bool { |
||||||
|
return len(str) == 0 |
||||||
|
} |
||||||
|
|
||||||
|
// IsByteLength check if the string's length (in bytes) falls in a range.
|
||||||
|
func IsByteLength(str string, min, max int) bool { |
||||||
|
return len(str) >= min && len(str) <= max |
||||||
|
} |
||||||
|
|
||||||
|
// IsUUIDv3 check if the string is a UUID version 3.
|
||||||
|
func IsUUIDv3(str string) bool { |
||||||
|
return rxUUID3.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsUUIDv4 check if the string is a UUID version 4.
|
||||||
|
func IsUUIDv4(str string) bool { |
||||||
|
return rxUUID4.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsUUIDv5 check if the string is a UUID version 5.
|
||||||
|
func IsUUIDv5(str string) bool { |
||||||
|
return rxUUID5.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsUUID check if the string is a UUID (version 3, 4 or 5).
|
||||||
|
func IsUUID(str string) bool { |
||||||
|
return rxUUID.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsCreditCard check if the string is a credit card.
|
||||||
|
func IsCreditCard(str string) bool { |
||||||
|
r, _ := regexp.Compile("[^0-9]+") |
||||||
|
sanitized := r.ReplaceAll([]byte(str), []byte("")) |
||||||
|
if !rxCreditCard.MatchString(string(sanitized)) { |
||||||
|
return false |
||||||
|
} |
||||||
|
var sum int64 |
||||||
|
var digit string |
||||||
|
var tmpNum int64 |
||||||
|
var shouldDouble bool |
||||||
|
for i := len(sanitized) - 1; i >= 0; i-- { |
||||||
|
digit = string(sanitized[i:(i + 1)]) |
||||||
|
tmpNum, _ = ToInt(digit) |
||||||
|
if shouldDouble { |
||||||
|
tmpNum *= 2 |
||||||
|
if tmpNum >= 10 { |
||||||
|
sum += ((tmpNum % 10) + 1) |
||||||
|
} else { |
||||||
|
sum += tmpNum |
||||||
|
} |
||||||
|
} else { |
||||||
|
sum += tmpNum |
||||||
|
} |
||||||
|
shouldDouble = !shouldDouble |
||||||
|
} |
||||||
|
|
||||||
|
if sum%10 == 0 { |
||||||
|
return true |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// IsISBN10 check if the string is an ISBN version 10.
|
||||||
|
func IsISBN10(str string) bool { |
||||||
|
return IsISBN(str, 10) |
||||||
|
} |
||||||
|
|
||||||
|
// IsISBN13 check if the string is an ISBN version 13.
|
||||||
|
func IsISBN13(str string) bool { |
||||||
|
return IsISBN(str, 13) |
||||||
|
} |
||||||
|
|
||||||
|
// IsISBN check if the string is an ISBN (version 10 or 13).
|
||||||
|
// If version value is not equal to 10 or 13, it will be check both variants.
|
||||||
|
func IsISBN(str string, version int) bool { |
||||||
|
r, _ := regexp.Compile("[\\s-]+") |
||||||
|
sanitized := r.ReplaceAll([]byte(str), []byte("")) |
||||||
|
var checksum int32 |
||||||
|
var i int32 |
||||||
|
if version == 10 { |
||||||
|
if !rxISBN10.MatchString(string(sanitized)) { |
||||||
|
return false |
||||||
|
} |
||||||
|
for i = 0; i < 9; i++ { |
||||||
|
checksum += (i + 1) * int32(sanitized[i]-'0') |
||||||
|
} |
||||||
|
if sanitized[9] == 'X' { |
||||||
|
checksum += 10 * 10 |
||||||
|
} else { |
||||||
|
checksum += 10 * int32(sanitized[9]-'0') |
||||||
|
} |
||||||
|
if checksum%11 == 0 { |
||||||
|
return true |
||||||
|
} |
||||||
|
return false |
||||||
|
} else if version == 13 { |
||||||
|
if !rxISBN13.MatchString(string(sanitized)) { |
||||||
|
return false |
||||||
|
} |
||||||
|
factor := []int32{1, 3} |
||||||
|
for i = 0; i < 12; i++ { |
||||||
|
checksum += factor[i%2] * int32(sanitized[i]-'0') |
||||||
|
} |
||||||
|
if (int32(sanitized[12]-'0'))-((10-(checksum%10))%10) == 0 { |
||||||
|
return true |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
return IsISBN(str, 10) || IsISBN(str, 13) |
||||||
|
} |
||||||
|
|
||||||
|
// IsJSON check if the string is valid JSON (note: uses json.Unmarshal).
|
||||||
|
func IsJSON(str string) bool { |
||||||
|
var js json.RawMessage |
||||||
|
return json.Unmarshal([]byte(str), &js) == nil |
||||||
|
} |
||||||
|
|
||||||
|
// IsMultibyte check if the string contains one or more multibyte chars. Empty string is valid.
|
||||||
|
func IsMultibyte(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
return rxMultibyte.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsASCII check if the string contains ASCII chars only. Empty string is valid.
|
||||||
|
func IsASCII(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
return rxASCII.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsPrintableASCII check if the string contains printable ASCII chars only. Empty string is valid.
|
||||||
|
func IsPrintableASCII(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
return rxPrintableASCII.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsFullWidth check if the string contains any full-width chars. Empty string is valid.
|
||||||
|
func IsFullWidth(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
return rxFullWidth.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsHalfWidth check if the string contains any half-width chars. Empty string is valid.
|
||||||
|
func IsHalfWidth(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
return rxHalfWidth.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsVariableWidth check if the string contains a mixture of full and half-width chars. Empty string is valid.
|
||||||
|
func IsVariableWidth(str string) bool { |
||||||
|
if IsNull(str) { |
||||||
|
return true |
||||||
|
} |
||||||
|
return rxHalfWidth.MatchString(str) && rxFullWidth.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsBase64 check if a string is base64 encoded.
|
||||||
|
func IsBase64(str string) bool { |
||||||
|
return rxBase64.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsFilePath check is a string is Win or Unix file path and returns it's type.
|
||||||
|
func IsFilePath(str string) (bool, int) { |
||||||
|
if rxWinPath.MatchString(str) { |
||||||
|
//check windows path limit see:
|
||||||
|
// http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath
|
||||||
|
if len(str[3:]) > 32767 { |
||||||
|
return false, Win |
||||||
|
} |
||||||
|
return true, Win |
||||||
|
} else if rxUnixPath.MatchString(str) { |
||||||
|
return true, Unix |
||||||
|
} |
||||||
|
return false, Unknown |
||||||
|
} |
||||||
|
|
||||||
|
// IsDataURI checks if a string is base64 encoded data URI such as an image
|
||||||
|
func IsDataURI(str string) bool { |
||||||
|
dataURI := strings.Split(str, ",") |
||||||
|
if !rxDataURI.MatchString(dataURI[0]) { |
||||||
|
return false |
||||||
|
} |
||||||
|
return IsBase64(dataURI[1]) |
||||||
|
} |
||||||
|
|
||||||
|
// IsISO3166Alpha2 checks if a string is valid two-letter country code
|
||||||
|
func IsISO3166Alpha2(str string) bool { |
||||||
|
for _, entry := range ISO3166List { |
||||||
|
if str == entry.Alpha2Code { |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// IsISO3166Alpha3 checks if a string is valid three-letter country code
|
||||||
|
func IsISO3166Alpha3(str string) bool { |
||||||
|
for _, entry := range ISO3166List { |
||||||
|
if str == entry.Alpha3Code { |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// IsIP checks if a string is either IP version 4 or 6.
|
||||||
|
func IsIP(str string) bool { |
||||||
|
return net.ParseIP(str) != nil |
||||||
|
} |
||||||
|
|
||||||
|
// IsIPv4 check if the string is an IP version 4.
|
||||||
|
func IsIPv4(str string) bool { |
||||||
|
ip := net.ParseIP(str) |
||||||
|
return ip != nil && strings.Contains(str, ".") |
||||||
|
} |
||||||
|
|
||||||
|
// IsIPv6 check if the string is an IP version 6.
|
||||||
|
func IsIPv6(str string) bool { |
||||||
|
ip := net.ParseIP(str) |
||||||
|
return ip != nil && strings.Contains(str, ":") |
||||||
|
} |
||||||
|
|
||||||
|
// IsMAC check if a string is valid MAC address.
|
||||||
|
// Possible MAC formats:
|
||||||
|
// 01:23:45:67:89:ab
|
||||||
|
// 01:23:45:67:89:ab:cd:ef
|
||||||
|
// 01-23-45-67-89-ab
|
||||||
|
// 01-23-45-67-89-ab-cd-ef
|
||||||
|
// 0123.4567.89ab
|
||||||
|
// 0123.4567.89ab.cdef
|
||||||
|
func IsMAC(str string) bool { |
||||||
|
_, err := net.ParseMAC(str) |
||||||
|
return err == nil |
||||||
|
} |
||||||
|
|
||||||
|
// IsMongoID check if the string is a valid hex-encoded representation of a MongoDB ObjectId.
|
||||||
|
func IsMongoID(str string) bool { |
||||||
|
return rxHexadecimal.MatchString(str) && (len(str) == 24) |
||||||
|
} |
||||||
|
|
||||||
|
// IsLatitude check if a string is valid latitude.
|
||||||
|
func IsLatitude(str string) bool { |
||||||
|
return rxLatitude.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsLongitude check if a string is valid longitude.
|
||||||
|
func IsLongitude(str string) bool { |
||||||
|
return rxLongitude.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// ValidateStruct use tags for fields
|
||||||
|
func ValidateStruct(s interface{}) (bool, error) { |
||||||
|
if s == nil { |
||||||
|
return true, nil |
||||||
|
} |
||||||
|
result := true |
||||||
|
var err error |
||||||
|
val := reflect.ValueOf(s) |
||||||
|
if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr { |
||||||
|
val = val.Elem() |
||||||
|
} |
||||||
|
// we only accept structs
|
||||||
|
if val.Kind() != reflect.Struct { |
||||||
|
return false, fmt.Errorf("function only accepts structs; got %s", val.Kind()) |
||||||
|
} |
||||||
|
var errs Errors |
||||||
|
for i := 0; i < val.NumField(); i++ { |
||||||
|
valueField := val.Field(i) |
||||||
|
typeField := val.Type().Field(i) |
||||||
|
if typeField.PkgPath != "" { |
||||||
|
continue // Private field
|
||||||
|
} |
||||||
|
resultField, err := typeCheck(valueField, typeField) |
||||||
|
if err != nil { |
||||||
|
errs = append(errs, err) |
||||||
|
} |
||||||
|
result = result && resultField |
||||||
|
} |
||||||
|
if len(errs) > 0 { |
||||||
|
err = errs |
||||||
|
} |
||||||
|
return result, err |
||||||
|
} |
||||||
|
|
||||||
|
// parseTag splits a struct field's tag into its
|
||||||
|
// comma-separated options.
|
||||||
|
func parseTag(tag string) tagOptions { |
||||||
|
split := strings.SplitN(tag, ",", -1) |
||||||
|
return tagOptions(split) |
||||||
|
} |
||||||
|
|
||||||
|
func isValidTag(s string) bool { |
||||||
|
if s == "" { |
||||||
|
return false |
||||||
|
} |
||||||
|
for _, c := range s { |
||||||
|
switch { |
||||||
|
case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): |
||||||
|
// Backslash and quote chars are reserved, but
|
||||||
|
// otherwise any punctuation chars are allowed
|
||||||
|
// in a tag name.
|
||||||
|
default: |
||||||
|
if !unicode.IsLetter(c) && !unicode.IsDigit(c) { |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
// IsSSN will validate the given string as a U.S. Social Security Number
|
||||||
|
func IsSSN(str string) bool { |
||||||
|
if str == "" || len(str) != 11 { |
||||||
|
return false |
||||||
|
} |
||||||
|
return rxSSN.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// IsSemver check if string is valid semantic version
|
||||||
|
func IsSemver(str string) bool { |
||||||
|
return rxSemver.MatchString(str) |
||||||
|
} |
||||||
|
|
||||||
|
// ByteLength check string's length
|
||||||
|
func ByteLength(str string, params ...string) bool { |
||||||
|
if len(params) == 2 { |
||||||
|
min, _ := ToInt(params[0]) |
||||||
|
max, _ := ToInt(params[1]) |
||||||
|
return len(str) >= int(min) && len(str) <= int(max) |
||||||
|
} |
||||||
|
|
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// StringMatches checks if a string matches a given pattern.
|
||||||
|
func StringMatches(s string, params ...string) bool { |
||||||
|
if len(params) == 1 { |
||||||
|
pattern := params[0] |
||||||
|
return Matches(s, pattern) |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// StringLength check string's length (including multi byte strings)
|
||||||
|
func StringLength(str string, params ...string) bool { |
||||||
|
|
||||||
|
if len(params) == 2 { |
||||||
|
strLength := utf8.RuneCountInString(str) |
||||||
|
min, _ := ToInt(params[0]) |
||||||
|
max, _ := ToInt(params[1]) |
||||||
|
return strLength >= int(min) && strLength <= int(max) |
||||||
|
} |
||||||
|
|
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// Contains returns whether checks that a comma-separated list of options
|
||||||
|
// contains a particular substr flag. substr must be surrounded by a
|
||||||
|
// string boundary or commas.
|
||||||
|
func (opts tagOptions) contains(optionName string) bool { |
||||||
|
for i := range opts { |
||||||
|
tagOpt := opts[i] |
||||||
|
if tagOpt == optionName { |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
func checkRequired(v reflect.Value, t reflect.StructField, options tagOptions) (bool, error) { |
||||||
|
if options.contains("required") { |
||||||
|
err := fmt.Errorf("non zero value required") |
||||||
|
return false, Error{t.Name, err} |
||||||
|
} else if fieldsRequiredByDefault && !options.contains("optional") { |
||||||
|
err := fmt.Errorf("All fields are required to at least have one validation defined") |
||||||
|
return false, Error{t.Name, err} |
||||||
|
} |
||||||
|
// not required and empty is valid
|
||||||
|
return true, nil |
||||||
|
} |
||||||
|
|
||||||
|
func typeCheck(v reflect.Value, t reflect.StructField) (bool, error) { |
||||||
|
if !v.IsValid() { |
||||||
|
return false, nil |
||||||
|
} |
||||||
|
|
||||||
|
tag := t.Tag.Get(tagName) |
||||||
|
|
||||||
|
// Check if the field should be ignored
|
||||||
|
switch tag { |
||||||
|
case "": |
||||||
|
if !fieldsRequiredByDefault { |
||||||
|
return true, nil |
||||||
|
} |
||||||
|
err := fmt.Errorf("All fields are required to at least have one validation defined") |
||||||
|
return false, Error{t.Name, err} |
||||||
|
case "-": |
||||||
|
return true, nil |
||||||
|
} |
||||||
|
|
||||||
|
options := parseTag(tag) |
||||||
|
for i := range options { |
||||||
|
tagOpt := options[i] |
||||||
|
if ok := isValidTag(tagOpt); !ok { |
||||||
|
continue |
||||||
|
} |
||||||
|
if validatefunc, ok := CustomTypeTagMap[tagOpt]; ok { |
||||||
|
options = append(options[:i], options[i+1:]...) // we found our custom validator, so remove it from the options
|
||||||
|
if result := validatefunc(v.Interface()); !result { |
||||||
|
return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), tagOpt)} |
||||||
|
} |
||||||
|
return true, nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if isEmptyValue(v) { |
||||||
|
// an empty value is not validated, check only required
|
||||||
|
return checkRequired(v, t, options) |
||||||
|
} |
||||||
|
|
||||||
|
switch v.Kind() { |
||||||
|
case reflect.Bool, |
||||||
|
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, |
||||||
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, |
||||||
|
reflect.Float32, reflect.Float64, |
||||||
|
reflect.String: |
||||||
|
// for each tag option check the map of validator functions
|
||||||
|
for i := range options { |
||||||
|
tagOpt := options[i] |
||||||
|
negate := false |
||||||
|
// Check wether the tag looks like '!something' or 'something'
|
||||||
|
if len(tagOpt) > 0 && tagOpt[0] == '!' { |
||||||
|
tagOpt = string(tagOpt[1:]) |
||||||
|
negate = true |
||||||
|
} |
||||||
|
if ok := isValidTag(tagOpt); !ok { |
||||||
|
err := fmt.Errorf("Unknown Validator %s", tagOpt) |
||||||
|
return false, Error{t.Name, err} |
||||||
|
} |
||||||
|
|
||||||
|
// Check for param validators
|
||||||
|
for key, value := range ParamTagRegexMap { |
||||||
|
ps := value.FindStringSubmatch(tagOpt) |
||||||
|
if len(ps) > 0 { |
||||||
|
if validatefunc, ok := ParamTagMap[key]; ok { |
||||||
|
switch v.Kind() { |
||||||
|
case reflect.String: |
||||||
|
field := fmt.Sprint(v) // make value into string, then validate with regex
|
||||||
|
if result := validatefunc(field, ps[1:]...); !result && !negate || result && negate { |
||||||
|
var err error |
||||||
|
if !negate { |
||||||
|
err = fmt.Errorf("%s does not validate as %s", field, tagOpt) |
||||||
|
} else { |
||||||
|
err = fmt.Errorf("%s does validate as %s", field, tagOpt) |
||||||
|
} |
||||||
|
return false, Error{t.Name, err} |
||||||
|
} |
||||||
|
default: |
||||||
|
//Not Yet Supported Types (Fail here!)
|
||||||
|
err := fmt.Errorf("Validator %s doesn't support kind %s", tagOpt, v.Kind()) |
||||||
|
return false, Error{t.Name, err} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if validatefunc, ok := TagMap[tagOpt]; ok { |
||||||
|
switch v.Kind() { |
||||||
|
case reflect.String: |
||||||
|
field := fmt.Sprint(v) // make value into string, then validate with regex
|
||||||
|
if result := validatefunc(field); !result && !negate || result && negate { |
||||||
|
var err error |
||||||
|
if !negate { |
||||||
|
err = fmt.Errorf("%s does not validate as %s", field, tagOpt) |
||||||
|
} else { |
||||||
|
err = fmt.Errorf("%s does validate as %s", field, tagOpt) |
||||||
|
} |
||||||
|
return false, Error{t.Name, err} |
||||||
|
} |
||||||
|
default: |
||||||
|
//Not Yet Supported Types (Fail here!)
|
||||||
|
err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", tagOpt, v.Kind(), v) |
||||||
|
return false, Error{t.Name, err} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return true, nil |
||||||
|
case reflect.Map: |
||||||
|
if v.Type().Key().Kind() != reflect.String { |
||||||
|
return false, &UnsupportedTypeError{v.Type()} |
||||||
|
} |
||||||
|
var sv stringValues |
||||||
|
sv = v.MapKeys() |
||||||
|
sort.Sort(sv) |
||||||
|
result := true |
||||||
|
for _, k := range sv { |
||||||
|
resultItem, err := ValidateStruct(v.MapIndex(k).Interface()) |
||||||
|
if err != nil { |
||||||
|
return false, err |
||||||
|
} |
||||||
|
result = result && resultItem |
||||||
|
} |
||||||
|
return result, nil |
||||||
|
case reflect.Slice: |
||||||
|
result := true |
||||||
|
for i := 0; i < v.Len(); i++ { |
||||||
|
var resultItem bool |
||||||
|
var err error |
||||||
|
if v.Index(i).Kind() != reflect.Struct { |
||||||
|
resultItem, err = typeCheck(v.Index(i), t) |
||||||
|
if err != nil { |
||||||
|
return false, err |
||||||
|
} |
||||||
|
} else { |
||||||
|
resultItem, err = ValidateStruct(v.Index(i).Interface()) |
||||||
|
if err != nil { |
||||||
|
return false, err |
||||||
|
} |
||||||
|
} |
||||||
|
result = result && resultItem |
||||||
|
} |
||||||
|
return result, nil |
||||||
|
case reflect.Array: |
||||||
|
result := true |
||||||
|
for i := 0; i < v.Len(); i++ { |
||||||
|
var resultItem bool |
||||||
|
var err error |
||||||
|
if v.Index(i).Kind() != reflect.Struct { |
||||||
|
resultItem, err = typeCheck(v.Index(i), t) |
||||||
|
if err != nil { |
||||||
|
return false, err |
||||||
|
} |
||||||
|
} else { |
||||||
|
resultItem, err = ValidateStruct(v.Index(i).Interface()) |
||||||
|
if err != nil { |
||||||
|
return false, err |
||||||
|
} |
||||||
|
} |
||||||
|
result = result && resultItem |
||||||
|
} |
||||||
|
return result, nil |
||||||
|
case reflect.Interface: |
||||||
|
// If the value is an interface then encode its element
|
||||||
|
if v.IsNil() { |
||||||
|
return true, nil |
||||||
|
} |
||||||
|
return ValidateStruct(v.Interface()) |
||||||
|
case reflect.Ptr: |
||||||
|
// If the value is a pointer then check its element
|
||||||
|
if v.IsNil() { |
||||||
|
return true, nil |
||||||
|
} |
||||||
|
return typeCheck(v.Elem(), t) |
||||||
|
case reflect.Struct: |
||||||
|
return ValidateStruct(v.Interface()) |
||||||
|
default: |
||||||
|
return false, &UnsupportedTypeError{v.Type()} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func isEmptyValue(v reflect.Value) bool { |
||||||
|
switch v.Kind() { |
||||||
|
case reflect.String, reflect.Array: |
||||||
|
return v.Len() == 0 |
||||||
|
case reflect.Map, reflect.Slice: |
||||||
|
return v.Len() == 0 || v.IsNil() |
||||||
|
case reflect.Bool: |
||||||
|
return !v.Bool() |
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
||||||
|
return v.Int() == 0 |
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: |
||||||
|
return v.Uint() == 0 |
||||||
|
case reflect.Float32, reflect.Float64: |
||||||
|
return v.Float() == 0 |
||||||
|
case reflect.Interface, reflect.Ptr: |
||||||
|
return v.IsNil() |
||||||
|
} |
||||||
|
|
||||||
|
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) |
||||||
|
} |
||||||
|
|
||||||
|
// ErrorByField returns error for specified field of the struct
|
||||||
|
// validated by ValidateStruct or empty string if there are no errors
|
||||||
|
// or this field doesn't exists or doesn't have any errors.
|
||||||
|
func ErrorByField(e error, field string) string { |
||||||
|
if e == nil { |
||||||
|
return "" |
||||||
|
} |
||||||
|
return ErrorsByField(e)[field] |
||||||
|
} |
||||||
|
|
||||||
|
// ErrorsByField returns map of errors of the struct validated
|
||||||
|
// by ValidateStruct or empty map if there are no errors.
|
||||||
|
func ErrorsByField(e error) map[string]string { |
||||||
|
m := make(map[string]string) |
||||||
|
if e == nil { |
||||||
|
return m |
||||||
|
} |
||||||
|
// prototype for ValidateStruct
|
||||||
|
|
||||||
|
switch e.(type) { |
||||||
|
case Error: |
||||||
|
m[e.(Error).Name] = e.(Error).Err.Error() |
||||||
|
case Errors: |
||||||
|
for _, item := range e.(Errors).Errors() { |
||||||
|
m[item.(Error).Name] = item.(Error).Err.Error() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return m |
||||||
|
} |
||||||
|
|
||||||
|
// Error returns string equivalent for reflect.Type
|
||||||
|
func (e *UnsupportedTypeError) Error() string { |
||||||
|
return "validator: unsupported type: " + e.Type.String() |
||||||
|
} |
||||||
|
|
||||||
|
func (sv stringValues) Len() int { return len(sv) } |
||||||
|
func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } |
||||||
|
func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) } |
||||||
|
func (sv stringValues) get(i int) string { return sv[i].String() } |
@ -0,0 +1,15 @@ |
|||||||
|
box: wercker/golang |
||||||
|
build: |
||||||
|
steps: |
||||||
|
- setup-go-workspace |
||||||
|
|
||||||
|
- script: |
||||||
|
name: go get |
||||||
|
code: | |
||||||
|
go version |
||||||
|
go get -t ./... |
||||||
|
|
||||||
|
- script: |
||||||
|
name: go test |
||||||
|
code: | |
||||||
|
go test -race ./... |
Loading…
Reference in new issue