├── .editorconfig ├── .travis.yml ├── LICENSE ├── README.md ├── error.go ├── has.go ├── has_test.go ├── is.go ├── is_test.go ├── validate.go └── validate_test.go /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # all files 5 | [*] 6 | indent_style = tab 7 | indent_size = 4 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.9.x -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 syyong.x 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VValidator 2 | 3 | [![GoDoc](https://godoc.org/github.com/syyongx/vvalidator?status.svg)](https://godoc.org/github.com/syyongx/vvalidator) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/syyongx/vvalidator)](https://goreportcard.com/report/github.com/syyongx/vvalidator) 5 | [![MIT licensed][3]][4] 6 | 7 | [3]: https://img.shields.io/badge/license-MIT-blue.svg 8 | [4]: LICENSE 9 | 10 | Go parameter validator library. 11 | 12 | ## Download & Install 13 | ```shell 14 | go get github.com/syyongx/vvalidator 15 | ``` 16 | 17 | ## Apis 18 | ### validator 19 | ```go 20 | ValidateInt(data interface{}, key string, min, max int, def ... int) (int, error) 21 | ValidateIntp(data interface{}, key string, min, max int, code int, message string, def ... int) int 22 | ValidateInt64(data interface{}, key string, min, max int64, def ... int64) (int64, error) 23 | ValidateInt64p(data interface{}, key string, min, max int64, code int, message string, def ... int64) int64 24 | ValidateFloat(data interface{}, key string, min, max float64, def ... float64) (float64, error) 25 | ValidateFloatp(data interface{}, key string, min, max float64, code int, message string, def ... float64) float64 26 | ValidateString(data interface{}, key string, min, max int, def ... string) (string, error) 27 | ValidateStringp(data interface{}, key string, min, max int, code int, message string, def ... string) string 28 | ValidateStringWithPattern(data interface{}, key, pattern string, def ... string) (string, error) 29 | ValidateStringWithPatternp(data interface{}, key, pattern string, code int, message string, def ... string) string 30 | ValidateEnumInt(data interface{}, key string, validValues []int, def ... int) (int, error) 31 | ValidateEnumIntp(data interface{}, key string, validValues []int, code int, message string, def ... int) int 32 | ValidateEnumInt64(data interface{}, key string, validValues []int64, def ... int64) (int64, error) 33 | ValidateEnumInt64p(data interface{}, key string, validValues []int64, code int, message string, def ... int64) int64 34 | ValidateEnumString(data interface{}, key string, validValues []string, def ... string) (string, error) 35 | ValidateEnumStringp(data interface{}, key string, validValues []string, code int, message string, def ... string) string 36 | ValidateSlice(data interface{}, key, sep string, min, max int, def ... string) ([]string, error) 37 | ValidateSlicep(data interface{}, key, sep string, min, max int, code int, message string, def ... string) []string 38 | ``` 39 | 40 | ### is 41 | ```go 42 | IsNumeric(str string) bool 43 | IsInt(str string) bool 44 | IsFloat(str string) bool 45 | IsHexadecimal(str string) bool 46 | IsAlpha(str string) bool 47 | IsAlphanumeric(str string) bool 48 | IsIP(str string) bool 49 | IsIPv4(str string) bool 50 | IsIPv6(str string) bool 51 | IsLatitude(str string) bool 52 | IsLongitude(str string) bool 53 | IsBase64(str string) bool 54 | IsPort(str string) bool 55 | IsURL(str string) bool 56 | IsASCII(str string) bool 57 | IsPrintableASCII(str string) bool 58 | IsEmail(str string) bool 59 | IsWinPath(str string) bool 60 | IsUnixPath(str string) bool 61 | IsSemver(str string) bool 62 | IsFullWidth(str string) bool 63 | IsHalfWidth(str string) bool 64 | IsHash(str, algorithm string) bool 65 | IsMAC(str string) bool 66 | IsTime(str string, format string) bool 67 | IsRFC3339Time(str string) bool 68 | IsRFC3339WithoutZoneTime(str string) bool 69 | IsJSON(str string) bool 70 | IsUTFLetter(str string) bool 71 | IsUTFLetterNumeric(str string) bool 72 | IsHexColor(str string) bool 73 | IsRGBColor(str string) bool 74 | IsRGBAColor(str string) bool 75 | IsLowerCase(str string) bool 76 | IsUpperCase(str string) bool 77 | ``` 78 | 79 | ### has 80 | ```go 81 | HasLowerCase(str string) bool 82 | HasUpperCase(str string) bool 83 | ``` 84 | 85 | ## LICENSE 86 | VValidator source code is licensed under the [MIT](https://github.com/syyongx/vvalidator/blob/master/LICENSE) Licence. 87 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package vvalidator 2 | 3 | // DefaultCode default parameter error code 4 | var DefaultCode = 400 5 | 6 | // Error error struct 7 | type Error struct { 8 | Message string 9 | Code int 10 | CustomMessage string 11 | } 12 | 13 | // NewError returns the instance of Error 14 | func NewError(message string, code int, customMessage string) Error { 15 | return Error{ 16 | Message: message, 17 | Code: code, 18 | CustomMessage: customMessage, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /has.go: -------------------------------------------------------------------------------- 1 | package vvalidator 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | const ( 8 | // PatternHasLowerCase check has lower case 9 | PatternHasLowerCase = ".*[[:lower:]]" 10 | // PatternHasUpperCase check has upper case 11 | PatternHasUpperCase = ".*[[:upper:]]" 12 | ) 13 | 14 | // HasLowerCase check if the string contains at least 1 lowercase. 15 | func HasLowerCase(str string) bool { 16 | return regexp.MustCompile(PatternHasLowerCase).MatchString(str) 17 | } 18 | 19 | // HasUpperCase check if the string contains as least 1 uppercase. 20 | func HasUpperCase(str string) bool { 21 | return regexp.MustCompile(PatternHasUpperCase).MatchString(str) 22 | } 23 | -------------------------------------------------------------------------------- /has_test.go: -------------------------------------------------------------------------------- 1 | package vvalidator 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestHas(t *testing.T) { 8 | lc := HasLowerCase("aA") 9 | equal(t, true, lc) 10 | uc := HasUpperCase("aA") 11 | equal(t, true, uc) 12 | } 13 | -------------------------------------------------------------------------------- /is.go: -------------------------------------------------------------------------------- 1 | package vvalidator 2 | 3 | import ( 4 | "encoding/json" 5 | "net" 6 | "regexp" 7 | "strconv" 8 | "strings" 9 | "time" 10 | "unicode" 11 | ) 12 | 13 | const ( 14 | // PatternNumeric is numeric 15 | PatternNumeric = "^[0-9]+$" 16 | // PatternInt is int 17 | PatternInt = "^(?:[-+]?(?:0|[1-9][0-9]*))$" 18 | // PatternFloat is float 19 | PatternFloat = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$" 20 | // PatternHexadecimal is hexadecimal 21 | PatternHexadecimal = "^[0-9a-fA-F]+$" 22 | // PatternAlpha is alpha 23 | PatternAlpha = "^[a-zA-Z]+$" 24 | // PatternAlphanumeric is alphanumeric 25 | PatternAlphanumeric = "^[a-zA-Z0-9]+$" 26 | // PatternLatitude is latitude 27 | PatternLatitude = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" 28 | // PatternLongitude is longitude 29 | PatternLongitude = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" 30 | // PatternBase64 is base64 31 | PatternBase64 = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" 32 | // PatternASCII is ASCII 33 | PatternASCII = "^[\x00-\x7F]+$" 34 | // PatternPrintableASCII is printable ASCII 35 | PatternPrintableASCII = "^[\x20-\x7E]+$" 36 | // PatternIP is ip 37 | PatternIP = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))` 38 | // PatternURLSchema is URL schema 39 | PatternURLSchema = `((ftp|tcp|udp|wss?|https?):\/\/)` 40 | // PatternURLUsername is URL username 41 | PatternURLUsername = `(\S+(:\S*)?@)` 42 | // PatternURLPath is URL path 43 | PatternURLPath = `((\/|\?|#)[^\s]*)` 44 | // PatternURLPort is URL port 45 | PatternURLPort = `(:(\d{1,5}))` 46 | // PatternURLIP is URL ip 47 | PatternURLIP = `([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]))` 48 | // PatternURLSubdomain is URL subdomain 49 | PatternURLSubdomain = `((www\.)|([a-zA-Z0-9]([-\.][-\._a-zA-Z0-9]+)*))` 50 | // PatternURL is URL 51 | PatternURL = `^` + PatternURLSchema + `?` + PatternURLUsername + `?` + `((` + PatternURLIP + `|(\[` + PatternIP + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + PatternURLSubdomain + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + PatternURLPort + `?` + PatternURLPath + `?$` 52 | // PatternEmail is email 53 | PatternEmail = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" 54 | // PatternWinPath is win path 55 | PatternWinPath = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$` 56 | // PatternUnixPath is unix path 57 | PatternUnixPath = `^(/[^/\x00]*)+/?$` 58 | // PatternSemver is semver 59 | PatternSemver = "^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-]+)*)?$" 60 | // PatternFullWidth is full width 61 | PatternFullWidth = `[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]` 62 | // PatternHalfWidth is half width 63 | PatternHalfWidth = `[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]` 64 | // PatternHexColor is hex color 65 | PatternHexColor = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" 66 | // PatternRGBColor is RGB color 67 | PatternRGBColor = "^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*\\)$" 68 | // PatternRGBAColor is RGB color 69 | PatternRGBAColor = "^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])\\s*,\\s*((0\\.[0-9]{1})|(1\\.0)|(1))\\)$" 70 | ) 71 | 72 | // IsNumeric check if the string is numeric. 73 | func IsNumeric(str string) bool { 74 | return regexp.MustCompile(PatternNumeric).MatchString(str) 75 | } 76 | 77 | // IsInt check if the string is int. 78 | func IsInt(str string) bool { 79 | return regexp.MustCompile(PatternInt).MatchString(str) 80 | } 81 | 82 | // IsFloat check if the string is an float. 83 | func IsFloat(str string) bool { 84 | return regexp.MustCompile(PatternFloat).MatchString(str) 85 | } 86 | 87 | // IsHexadecimal check if the string is a hexadecimal number. 88 | func IsHexadecimal(str string) bool { 89 | return regexp.MustCompile(PatternHexadecimal).MatchString(str) 90 | } 91 | 92 | // IsAlpha checks if the string contains only letters (a-zA-Z). 93 | func IsAlpha(str string) bool { 94 | return regexp.MustCompile(PatternAlpha).MatchString(str) 95 | } 96 | 97 | // IsAlphanumeric checks if the string contains only letters(a-zA-Z) and numbers. 98 | func IsAlphanumeric(str string) bool { 99 | return regexp.MustCompile(PatternAlphanumeric).MatchString(str) 100 | } 101 | 102 | // IsIP checks if the string is valid IP. 103 | func IsIP(str string) bool { 104 | return net.ParseIP(str) != nil 105 | } 106 | 107 | // IsIPv4 checks if the string is valid IPv4. 108 | func IsIPv4(str string) bool { 109 | ip := net.ParseIP(str) 110 | if ip == nil { 111 | return false 112 | } 113 | return strings.Contains(str, ".") 114 | } 115 | 116 | // IsIPv6 checks if the string is valid IPv6. 117 | func IsIPv6(str string) bool { 118 | ip := net.ParseIP(str) 119 | if ip == nil { 120 | return false 121 | } 122 | return strings.Contains(str, ":") 123 | } 124 | 125 | // IsLatitude checks if the string is valid latitude. 126 | func IsLatitude(str string) bool { 127 | return regexp.MustCompile(PatternLatitude).MatchString(str) 128 | } 129 | 130 | // IsLongitude checks if the string is valid longitude. 131 | func IsLongitude(str string) bool { 132 | return regexp.MustCompile(PatternLongitude).MatchString(str) 133 | } 134 | 135 | // IsBase64 checks if the string is base64 encoded. 136 | func IsBase64(str string) bool { 137 | return regexp.MustCompile(PatternBase64).MatchString(str) 138 | } 139 | 140 | // IsPort checks if a string represents a valid port. 141 | func IsPort(str string) bool { 142 | if n, err := strconv.Atoi(str); err == nil && n > 0 && n < 65536 { 143 | return true 144 | } 145 | return false 146 | } 147 | 148 | // IsURL checks if the string is URL. 149 | func IsURL(str string) bool { 150 | return regexp.MustCompile(PatternURL).MatchString(str) 151 | } 152 | 153 | // IsASCII checks if the string is ASCII. 154 | func IsASCII(str string) bool { 155 | return regexp.MustCompile(PatternASCII).MatchString(str) 156 | } 157 | 158 | // IsPrintableASCII checks if the string is printable ASCII. 159 | func IsPrintableASCII(str string) bool { 160 | return regexp.MustCompile(PatternPrintableASCII).MatchString(str) 161 | } 162 | 163 | // IsEmail checks if the string is email. 164 | func IsEmail(str string) bool { 165 | return regexp.MustCompile(PatternEmail).MatchString(str) 166 | } 167 | 168 | // IsWinPath checks if the string is windows path. 169 | func IsWinPath(str string) bool { 170 | if regexp.MustCompile(PatternWinPath).MatchString(str) { 171 | // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath 172 | if len(str[3:]) > 32767 { 173 | return false 174 | } 175 | return true 176 | } 177 | return false 178 | } 179 | 180 | // IsUnixPath checks if the string is unix path. 181 | func IsUnixPath(str string) bool { 182 | return regexp.MustCompile(PatternUnixPath).MatchString(str) 183 | } 184 | 185 | // IsSemver checks if the string is valid Semantic Version. 186 | func IsSemver(str string) bool { 187 | return regexp.MustCompile(PatternSemver).MatchString(str) 188 | } 189 | 190 | // IsFullWidth checks if the string is contains any full-width chars. 191 | func IsFullWidth(str string) bool { 192 | return regexp.MustCompile(PatternFullWidth).MatchString(str) 193 | } 194 | 195 | // IsHalfWidth checks if the string is contains any half-width chars. 196 | func IsHalfWidth(str string) bool { 197 | return regexp.MustCompile(PatternHalfWidth).MatchString(str) 198 | } 199 | 200 | // IsHash checks if a string is a hash of type algorithm. 201 | // Algorithm is one of 202 | // [ 'md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 203 | // 'ripemd128', 'ripemd160', 'tiger128', 'tiger160', 'tiger192', 204 | // 'crc32', 'crc32b'] 205 | func IsHash(str, algorithm string) bool { 206 | length := "0" 207 | algo := strings.ToLower(algorithm) 208 | 209 | switch algo { 210 | case "crc32", "crc32b": 211 | length = "8" 212 | case "md5", "md4", "ripemd128", "tiger128": 213 | length = "32" 214 | case "sha1", "ripemd160", "tiger160": 215 | length = "40" 216 | case "tiger192": 217 | length = "48" 218 | case "sha256": 219 | length = "64" 220 | case "sha384": 221 | length = "96" 222 | case "sha512": 223 | length = "128" 224 | default: 225 | return false 226 | } 227 | 228 | return regexp.MustCompile("^[a-f0-9]{" + length + "}$").MatchString(str) 229 | } 230 | 231 | // IsMAC check if a string is valid MAC address. 232 | // Possible MAC formats: 233 | // 01:23:45:67:89:ab 234 | // 01:23:45:67:89:ab:cd:ef 235 | // 01-23-45-67-89-ab 236 | // 01-23-45-67-89-ab-cd-ef 237 | // 0123.4567.89ab 238 | // 0123.4567.89ab.cdef 239 | func IsMAC(str string) bool { 240 | _, err := net.ParseMAC(str) 241 | return err == nil 242 | } 243 | 244 | // IsTime check if string is valid according to given format 245 | func IsTime(str string, format string) bool { 246 | _, err := time.Parse(format, str) 247 | return err == nil 248 | } 249 | 250 | // IsRFC3339Time check if string is valid timestamp value according to RFC3339 251 | func IsRFC3339Time(str string) bool { 252 | return IsTime(str, time.RFC3339) 253 | } 254 | 255 | // IsRFC3339WithoutZoneTime check if string is valid timestamp value according to RFC3339 which excludes the timezone. 256 | func IsRFC3339WithoutZoneTime(str string) bool { 257 | return IsTime(str, "2006-01-02T15:04:05") 258 | } 259 | 260 | // IsJSON check if the string is valid JSON (note: uses json.Unmarshal). 261 | func IsJSON(str string) bool { 262 | var js json.RawMessage 263 | return json.Unmarshal([]byte(str), &js) == nil 264 | } 265 | 266 | //IsUTFLetter check if the string contains only unicode letter characters. 267 | //Similar to IsAlpha but for all languages. Empty string is valid. 268 | func IsUTFLetter(str string) bool { 269 | for _, c := range str { 270 | if !unicode.IsLetter(c) { 271 | return false 272 | } 273 | } 274 | return true 275 | } 276 | 277 | // IsUTFLetterNumeric check if the string contains only unicode letters and numbers. Empty string is valid. 278 | func IsUTFLetterNumeric(str string) bool { 279 | for _, c := range str { 280 | if !unicode.IsLetter(c) && !unicode.IsNumber(c) { //letters && numbers are ok 281 | return false 282 | } 283 | } 284 | return true 285 | } 286 | 287 | // IsHexColor check if the string is a hexadecimal color. 288 | func IsHexColor(str string) bool { 289 | return regexp.MustCompile(PatternHexColor).MatchString(str) 290 | } 291 | 292 | // IsRGBColor check if the string is a valid RGB color in form rgb(255, 255, 255). 293 | func IsRGBColor(str string) bool { 294 | return regexp.MustCompile(PatternRGBColor).MatchString(str) 295 | } 296 | 297 | // IsRGBAColor check if the string is a valid RGBA color in form rgb(255, 255, 255, 0.5). 298 | func IsRGBAColor(str string) bool { 299 | return regexp.MustCompile(PatternRGBAColor).MatchString(str) 300 | } 301 | 302 | // IsLowerCase check if the string is lowercase. 303 | func IsLowerCase(str string) bool { 304 | return str == strings.ToLower(str) 305 | } 306 | 307 | // IsUpperCase check if the string is uppercase. 308 | func IsUpperCase(str string) bool { 309 | return str == strings.ToUpper(str) 310 | } 311 | -------------------------------------------------------------------------------- /is_test.go: -------------------------------------------------------------------------------- 1 | package vvalidator 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestIs(t *testing.T) { 8 | ipv4 := IsIPv4("8.8.8.8") 9 | equal(t, true, ipv4) 10 | ipv6 := IsIPv6("2001:db8::68") 11 | equal(t, true, ipv6) 12 | rgb := IsRGBColor("rgb(255,255,255)") 13 | equal(t, true, rgb) 14 | rgba := IsRGBAColor("rgba(255,255,255,0.1)") 15 | equal(t, true, rgba) 16 | } 17 | -------------------------------------------------------------------------------- /validate.go: -------------------------------------------------------------------------------- 1 | package vvalidator 2 | 3 | import ( 4 | "errors" 5 | "regexp" 6 | "strconv" 7 | "strings" 8 | "unicode/utf8" 9 | ) 10 | 11 | // ValidateInt validate 32 bit integer 12 | func ValidateInt(data interface{}, key string, min, max int, def ...int) (int, error) { 13 | var defVal interface{} 14 | ldef := len(def) 15 | if ldef > 0 { 16 | defVal = def[0] 17 | } 18 | val, err := checkExist(data, key, defVal) 19 | if err != nil { 20 | return 0, err 21 | } 22 | 23 | switch val.(type) { 24 | case int: 25 | return val.(int), nil 26 | case string: 27 | value := val.(string) 28 | if !IsInt(value) { 29 | if ldef == 0 { 30 | return 0, errors.New(key + " must be an integer") 31 | } 32 | return def[0], nil 33 | } 34 | 35 | v, err := strconv.Atoi(value) 36 | if err != nil { 37 | if ldef == 0 { 38 | return 0, errors.New(key + " must be an integer") 39 | } 40 | return def[0], nil 41 | } 42 | if min != -1 && v < min { 43 | if ldef == 0 { 44 | return 0, errors.New(key + " is too small (minimum is " + strconv.Itoa(min) + ")") 45 | } 46 | return def[0], nil 47 | } 48 | if max != -1 && v > max { 49 | if ldef == 0 { 50 | return 0, errors.New(key + " is too big (maximum is " + strconv.Itoa(max) + ")") 51 | } 52 | return def[0], nil 53 | } 54 | return v, nil 55 | default: 56 | return 0, errors.New("type invalid, must be string or int") 57 | } 58 | } 59 | 60 | // ValidateIntp Validate 32 bit integer with custom error info. 61 | // if err != nil will panic. 62 | func ValidateIntp(data interface{}, key string, min, max int, code int, message string, def ...int) int { 63 | val, err := ValidateInt(data, key, min, max, def...) 64 | if err != nil { 65 | panic(NewError(err.Error(), code, message)) 66 | } 67 | return val 68 | } 69 | 70 | // ValidateInt64 Validate 64 bit integer. 71 | func ValidateInt64(data interface{}, key string, min, max int64, def ...int64) (int64, error) { 72 | var defVal interface{} 73 | ldef := len(def) 74 | if ldef > 0 { 75 | defVal = def[0] 76 | } 77 | val, err := checkExist(data, key, defVal) 78 | if err != nil { 79 | return 0, err 80 | } 81 | 82 | switch val.(type) { 83 | case int64: 84 | return val.(int64), nil 85 | case string: 86 | value := val.(string) 87 | if !IsInt(value) { 88 | if ldef == 0 { 89 | return 0, errors.New(key + " must be a valid interger") 90 | } 91 | return def[0], nil 92 | } 93 | 94 | v, err := strconv.ParseInt(value, 10, 64) 95 | if err != nil { 96 | if ldef == 0 { 97 | return 0, errors.New(key + " must be a valid interger") 98 | } 99 | return def[0], nil 100 | } 101 | if min != -1 && v < min { 102 | if ldef == 0 { 103 | return 0, errors.New(key + " is too small (minimum is " + strconv.FormatInt(min, 10) + ")") 104 | } 105 | return def[0], nil 106 | } 107 | if max != -1 && v > max { 108 | if ldef == 0 { 109 | return 0, errors.New(key + " is too big (maximum is " + strconv.FormatInt(max, 10) + ")") 110 | } 111 | return def[0], nil 112 | } 113 | return v, nil 114 | default: 115 | return 0, errors.New("type invalid, must be string or int64") 116 | } 117 | } 118 | 119 | // ValidateInt64p Validate 64 bit integer with custom error info. 120 | // if err != nil will panic. 121 | func ValidateInt64p(data interface{}, key string, min, max int64, code int, message string, def ...int64) int64 { 122 | val, err := ValidateInt64(data, key, min, max, def...) 123 | if err != nil { 124 | panic(NewError(err.Error(), code, message)) 125 | } 126 | return val 127 | } 128 | 129 | // ValidateFloat validate 64 bit float. 130 | func ValidateFloat(data interface{}, key string, min, max float64, def ...float64) (float64, error) { 131 | var defVal interface{} 132 | ldef := len(def) 133 | if ldef > 0 { 134 | defVal = def[0] 135 | } 136 | val, err := checkExist(data, key, defVal) 137 | if err != nil { 138 | return 0, err 139 | } 140 | 141 | switch val.(type) { 142 | case float64: 143 | return val.(float64), nil 144 | case string: 145 | value := val.(string) 146 | if !IsFloat(value) { 147 | if ldef == 0 { 148 | return 0, errors.New(key + " must be a valid float64") 149 | } 150 | return def[0], nil 151 | } 152 | 153 | v, err := strconv.ParseFloat(value, 64) 154 | if err != nil { 155 | if ldef == 0 { 156 | return 0, errors.New(key + " must be a valid float64") 157 | } 158 | return def[0], nil 159 | } 160 | if min != -1 && v < min { 161 | if ldef == 0 { 162 | return 0, errors.New(key + " is too small (minimum is " + strconv.FormatFloat(min, 'f', -1, 64) + ")") 163 | } 164 | return def[0], nil 165 | } 166 | if max != -1 && v > max { 167 | if ldef == 0 { 168 | return 0, errors.New(key + " is too big (maximum is " + strconv.FormatFloat(max, 'f', -1, 64) + ")") 169 | } 170 | return def[0], nil 171 | } 172 | return v, nil 173 | default: 174 | return 0, errors.New("type invalid, must be string or float64") 175 | } 176 | } 177 | 178 | // ValidateFloatp validate 64 bit float with custom error info. 179 | // if err != nil will panic. 180 | func ValidateFloatp(data interface{}, key string, min, max float64, code int, message string, def ...float64) float64 { 181 | val, err := ValidateFloat(data, key, min, max, def...) 182 | if err != nil { 183 | panic(NewError(err.Error(), code, message)) 184 | } 185 | return val 186 | } 187 | 188 | // ValidateString validate string. 189 | func ValidateString(data interface{}, key string, min, max int, def ...string) (string, error) { 190 | var defVal interface{} 191 | if len(def) > 0 { 192 | defVal = def[0] 193 | } 194 | val, err := checkExist(data, key, defVal) 195 | if err != nil { 196 | return "", err 197 | } 198 | 199 | length := utf8.RuneCountInString(val.(string)) 200 | if len(def) > 0 && length == 0 { 201 | return def[0], nil 202 | } 203 | if min != -1 && length < min { 204 | return "", errors.New(key + " is too short (minimum is " + strconv.Itoa(min) + " characters)") 205 | } 206 | if max != -1 && length > max { 207 | return "", errors.New(key + " is too long (maximum is " + strconv.Itoa(max) + " characters)") 208 | } 209 | return val.(string), nil 210 | } 211 | 212 | // ValidateStringp validate string with custom error info. 213 | // if err != nil will panic. 214 | func ValidateStringp(data interface{}, key string, min, max int, code int, message string, def ...string) string { 215 | val, err := ValidateString(data, key, min, max, def...) 216 | if err != nil { 217 | panic(NewError(err.Error(), code, message)) 218 | } 219 | return val 220 | } 221 | 222 | // ValidateStringWithPattern validate string with regexp pattern. 223 | func ValidateStringWithPattern(data interface{}, key, pattern string, def ...string) (string, error) { 224 | var defVal interface{} 225 | ldef := len(def) 226 | if ldef > 0 { 227 | defVal = def[0] 228 | } 229 | val, err := checkExist(data, key, defVal) 230 | if err != nil { 231 | return "", err 232 | } 233 | if !regexp.MustCompile(pattern).MatchString(val.(string)) { 234 | if ldef == 0 { 235 | return "", errors.New(key + " must be a valid string") 236 | } 237 | return def[0], nil 238 | } 239 | 240 | return val.(string), nil 241 | } 242 | 243 | // ValidateStringWithPatternp validateStringWithPatternp validate string with regex pattern. 244 | // if err != nil will panic. 245 | func ValidateStringWithPatternp(data interface{}, key, pattern string, code int, message string, def ...string) string { 246 | val, err := ValidateStringWithPattern(data, key, pattern, def...) 247 | if err != nil { 248 | panic(NewError(err.Error(), code, message)) 249 | } 250 | return val 251 | } 252 | 253 | // ValidateEnumInt validate enum int. 254 | func ValidateEnumInt(data interface{}, key string, validValues []int, def ...int) (int, error) { 255 | val, err := ValidateInt(data, key, -1, -1, def...) 256 | if err != nil { 257 | return 0, nil 258 | } 259 | for _, v := range validValues { 260 | if v == val { 261 | return val, nil 262 | } 263 | } 264 | return 0, errors.New(key + " is invalid") 265 | } 266 | 267 | // ValidateEnumIntp validate enum int with custom error info. 268 | // if err != nil will panic. 269 | func ValidateEnumIntp(data interface{}, key string, validValues []int, code int, message string, def ...int) int { 270 | val, err := ValidateEnumInt(data, key, validValues, def...) 271 | if err != nil { 272 | panic(NewError(err.Error(), code, message)) 273 | } 274 | return val 275 | } 276 | 277 | // ValidateEnumInt64 validate enum int64 278 | func ValidateEnumInt64(data interface{}, key string, validValues []int64, def ...int64) (int64, error) { 279 | val, err := ValidateInt64(data, key, -1, -1, def...) 280 | if err != nil { 281 | return 0, err 282 | } 283 | for _, v := range validValues { 284 | if v == val { 285 | return val, nil 286 | } 287 | } 288 | return 0, errors.New(key + " is invalid") 289 | } 290 | 291 | // ValidateEnumInt64p Validate enum int64 with panic. 292 | // if err != nil will panic. 293 | func ValidateEnumInt64p(data interface{}, key string, validValues []int64, code int, message string, def ...int64) int64 { 294 | val, err := ValidateEnumInt64(data, key, validValues, def...) 295 | if err != nil { 296 | panic(NewError(err.Error(), code, message)) 297 | } 298 | return val 299 | } 300 | 301 | // ValidateEnumString validate enum string 302 | func ValidateEnumString(data interface{}, key string, validValues []string, def ...string) (string, error) { 303 | val, err := ValidateString(data, key, -1, -1, def...) 304 | if err != nil { 305 | return "", nil 306 | } 307 | for _, v := range validValues { 308 | if v == val { 309 | return val, nil 310 | } 311 | } 312 | return "", errors.New(key + " is invalid") 313 | } 314 | 315 | // ValidateEnumStringp validate enum string with custom error info. 316 | // if err != nil will panic. 317 | func ValidateEnumStringp(data interface{}, key string, validValues []string, code int, message string, def ...string) string { 318 | val, err := ValidateEnumString(data, key, validValues, def...) 319 | if err != nil { 320 | panic(NewError(err.Error(), code, message)) 321 | } 322 | return val 323 | } 324 | 325 | // ValidateSlice validate slice. 326 | func ValidateSlice(data interface{}, key, sep string, min, max int, def ...string) ([]string, error) { 327 | var defVal interface{} 328 | if len(def) > 0 { 329 | defVal = def[0] 330 | } 331 | val, err := checkExist(data, key, defVal) 332 | if err != nil { 333 | return nil, err 334 | } 335 | 336 | vals := strings.Split(val.(string), sep) 337 | length := len(vals) 338 | if min != -1 && length < min { 339 | return nil, errors.New(key + " is too short (minimum is " + strconv.Itoa(min) + " elements)") 340 | } 341 | if max != -1 && length > max { 342 | return nil, errors.New(key + " is too long (maximum is " + strconv.Itoa(max) + " elements)") 343 | } 344 | return vals, nil 345 | } 346 | 347 | // ValidateSlicep validate slice with custom error info. 348 | // if err != nil will panic. 349 | func ValidateSlicep(data interface{}, key, sep string, min, max int, code int, message string, def ...string) []string { 350 | val, err := ValidateSlice(data, key, sep, min, max, def...) 351 | if err != nil { 352 | panic(NewError(err.Error(), code, message)) 353 | } 354 | return val 355 | } 356 | 357 | // Chekc exist 358 | func checkExist(data interface{}, key string, def interface{}) (interface{}, error) { 359 | var val string 360 | switch data.(type) { 361 | case string: 362 | val = data.(string) 363 | case map[string]string: 364 | if value, ok := data.(map[string]string)[key]; ok { 365 | val = value 366 | } else { 367 | if def == nil { 368 | return nil, errors.New(key + " is required") 369 | } 370 | return def, nil 371 | } 372 | default: 373 | return nil, errors.New("data type invalid, must be string or map[string]string") 374 | } 375 | 376 | if val == "" { 377 | if def == nil { 378 | return nil, errors.New(key + " can't be empty") 379 | } 380 | return def, nil 381 | } 382 | 383 | return val, nil 384 | } 385 | -------------------------------------------------------------------------------- /validate_test.go: -------------------------------------------------------------------------------- 1 | package vvalidator 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestValidate(t *testing.T) { 10 | defer func() { 11 | r := recover() 12 | if err, ok := r.(Error); ok { 13 | fmt.Println(err.Message) 14 | } 15 | }() 16 | 17 | params := map[string]string{ 18 | "uid": "123", 19 | "nickname": "fengmoti", 20 | "height": "1.5", 21 | } 22 | 23 | uid1, err1 := ValidateInt(params, "uid", 0, 200, 10) 24 | equal(t, 123, uid1) 25 | equal(t, nil, err1) 26 | uid2, err2 := ValidateInt(params, "uids", 0, 200, 10) 27 | equal(t, 10, uid2) 28 | equal(t, nil, err2) 29 | uid3, err3 := ValidateInt(params, "uids", 0, 10, 10) 30 | equal(t, 10, uid3) 31 | equal(t, nil, err3) 32 | uid4, err4 := ValidateInt(params, "uid", 0, 10) 33 | fmt.Println(uid4) 34 | equal(t, "uid is too big (maximum is 10)", err4.Error()) 35 | str, err5 := ValidateString(params, "nickname", 0, 20, "default") 36 | equal(t, "fengmoti", str) 37 | equal(t, "uid is too big (maximum is 10)", err5.Error()) 38 | } 39 | 40 | // Expected to be equal. 41 | func equal(t *testing.T, expected, actual interface{}) { 42 | if !reflect.DeepEqual(expected, actual) { 43 | t.Errorf("Expected %v (type %v) - Got %v (type %v)", expected, reflect.TypeOf(expected), actual, reflect.TypeOf(actual)) 44 | } 45 | } 46 | --------------------------------------------------------------------------------