parent
bb44a78885
commit
32a7f7d6fc
@ -0,0 +1,607 @@ |
||||
package is |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"net" |
||||
"net/url" |
||||
"os" |
||||
"reflect" |
||||
"strconv" |
||||
"strings" |
||||
"time" |
||||
) |
||||
|
||||
// Email 验证给出的字符串是不是有效的邮箱地址
|
||||
func Email(str string) bool { |
||||
return emailRegex.MatchString(str) |
||||
} |
||||
|
||||
// E164 判断给出的字符串是否符合 e.164 规范的手机号码
|
||||
func E164(str string) bool { |
||||
return e164Regex.MatchString(str) |
||||
} |
||||
|
||||
// PhoneNumber 判断给出的字符串是否符合中国大陆规范的手机号码
|
||||
func PhoneNumber(str string) bool { |
||||
return phoneNumberRegex.MatchString(str) |
||||
} |
||||
|
||||
// Semver 判断给出的字符串是否符合语义化版本号规范
|
||||
func Semver(str string) bool { |
||||
return semverRegex.MatchString(str) |
||||
} |
||||
|
||||
// Base64 判断给出的字符串是否为base64数据
|
||||
func Base64(str string) bool { |
||||
return base64Regex.MatchString(str) |
||||
} |
||||
|
||||
// URL 判断给出的字符串是否为有效的URL
|
||||
func URL(s string) bool { |
||||
var i int |
||||
// checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195
|
||||
// emulate browser and strip the '#' suffix prior to validation. see issue-#237
|
||||
if i = strings.Index(s, "#"); i > -1 { |
||||
s = s[:i] |
||||
} |
||||
if len(s) == 0 { |
||||
return false |
||||
} |
||||
u, err := url.ParseRequestURI(s) |
||||
if err != nil || u.Scheme == "" { |
||||
return false |
||||
} |
||||
return true |
||||
} |
||||
|
||||
// Base64URL 判断给出的字符串是否为有效且安全的 base64URL
|
||||
func Base64URL(str string) bool { |
||||
return base64URLRegex.MatchString(str) |
||||
} |
||||
|
||||
// JWT is the validation function for validating if the current field's value is a valid JWT string.
|
||||
func JWT(str string) bool { |
||||
return jWTRegex.MatchString(str) |
||||
} |
||||
|
||||
// UUID5 is the validation function for validating if the field's value is a valid v5 UUID.
|
||||
func UUID5(str string) bool { |
||||
return uUID5Regex.MatchString(str) |
||||
} |
||||
|
||||
// UUID4 is the validation function for validating if the field's value is a valid v4 UUID.
|
||||
func UUID4(str string) bool { |
||||
return uUID4Regex.MatchString(str) |
||||
} |
||||
|
||||
// UUID3 is the validation function for validating if the field's value is a valid v3 UUID.
|
||||
func UUID3(str string) bool { |
||||
return uUID3Regex.MatchString(str) |
||||
} |
||||
|
||||
// UUID is the validation function for validating if the field's value is a valid UUID of any version.
|
||||
func UUID(str string) bool { |
||||
return uUIDRegex.MatchString(str) |
||||
} |
||||
|
||||
// ULID is the validation function for validating if the field's value is a valid ULID.
|
||||
func ULID(str string) bool { |
||||
return uLIDRegex.MatchString(str) |
||||
} |
||||
|
||||
// MD4 is the validation function for validating if the field's value is a valid MD4.
|
||||
func MD4(str string) bool { |
||||
return md4Regex.MatchString(str) |
||||
} |
||||
|
||||
// MD5 is the validation function for validating if the field's value is a valid MD5.
|
||||
func MD5(str string) bool { |
||||
return md5Regex.MatchString(str) |
||||
} |
||||
|
||||
// SHA256 is the validation function for validating if the field's value is a valid SHA256.
|
||||
func SHA256(str string) bool { |
||||
return sha256Regex.MatchString(str) |
||||
} |
||||
|
||||
// SHA384 is the validation function for validating if the field's value is a valid SHA384.
|
||||
func SHA384(str string) bool { |
||||
return sha384Regex.MatchString(str) |
||||
} |
||||
|
||||
// SHA512 is the validation function for validating if the field's value is a valid SHA512.
|
||||
func SHA512(str string) bool { |
||||
return sha512Regex.MatchString(str) |
||||
} |
||||
|
||||
// ASCII is the validation function for validating if the field's value is a valid ASCII character.
|
||||
func ASCII(str string) bool { |
||||
return aSCIIRegex.MatchString(str) |
||||
} |
||||
|
||||
// Alpha is the validation function for validating if the current field's value is a valid alpha value.
|
||||
func Alpha(str string) bool { |
||||
return alphaRegex.MatchString(str) |
||||
} |
||||
|
||||
// Alphanumeric is the validation function for validating if the current field's value is a valid alphanumeric value.
|
||||
func Alphanumeric(str string) bool { |
||||
return alphaNumericRegex.MatchString(str) |
||||
} |
||||
|
||||
// AlphaUnicode is the validation function for validating if the current field's value is a valid alpha unicode value.
|
||||
func AlphaUnicode(str string) bool { |
||||
return alphaUnicodeRegex.MatchString(str) |
||||
} |
||||
|
||||
// AlphanumericUnicode is the validation function for validating if the current field's value is a valid alphanumeric unicode value.
|
||||
func AlphanumericUnicode(str string) bool { |
||||
return alphaUnicodeNumericRegex.MatchString(str) |
||||
} |
||||
|
||||
// Numeric is the validation function for validating if the current field's value is a valid numeric value.
|
||||
func Numeric[T any](t T) bool { |
||||
ctx := reflect.ValueOf(t) |
||||
switch ctx.Kind() { |
||||
case 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: |
||||
return true |
||||
default: |
||||
return numericRegex.MatchString(ctx.String()) |
||||
} |
||||
} |
||||
|
||||
// Number is the validation function for validating if the current field's value is a valid number.
|
||||
func Number[T any](t T) bool { |
||||
rv := reflect.ValueOf(t) |
||||
switch rv.Kind() { |
||||
case 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: |
||||
return true |
||||
default: |
||||
return numberRegex.MatchString(rv.String()) |
||||
} |
||||
} |
||||
|
||||
// Boolean is the validation function for validating if the current field's value can be safely converted to a boolean.
|
||||
func Boolean[T any](t T) bool { |
||||
ref := reflect.ValueOf(t) |
||||
switch ref.Kind() { |
||||
case reflect.String: |
||||
switch ref.String() { |
||||
case "1", "yes", "YES", "Yes", "on", "ON", "On", "true", "TRUE", "True", |
||||
"0", "no", "NO", "No", "", "off", "OFF", "Off", "false", "FALSE", "False": |
||||
return true |
||||
default: |
||||
return false |
||||
} |
||||
case reflect.Int, reflect.Int32, reflect.Int64: |
||||
n := ref.Int() |
||||
return n == 0 || n == 1 |
||||
case reflect.Uint, reflect.Uint32, reflect.Uint64: |
||||
n := ref.Uint() |
||||
return n == 0 || n == 1 |
||||
case reflect.Bool: |
||||
return ref.Bool() |
||||
} |
||||
return false |
||||
} |
||||
|
||||
// Default is the opposite of required aka HasValue
|
||||
func Default(val any) bool { |
||||
return !HasValue(val) |
||||
} |
||||
|
||||
// HasValue is the validation function for validating if the current field's value is not the default static value.
|
||||
func HasValue(val any) bool { |
||||
rv := reflect.ValueOf(val) |
||||
switch rv.Kind() { |
||||
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: |
||||
return !rv.IsNil() |
||||
default: |
||||
return rv.IsValid() && rv.Interface() != reflect.Zero(rv.Type()).Interface() |
||||
} |
||||
} |
||||
|
||||
// Hexadecimal is the validation function for validating if the current field's value is a valid hexadecimal.
|
||||
func Hexadecimal(str string) bool { |
||||
return hexadecimalRegex.MatchString(str) |
||||
} |
||||
|
||||
// HEXColor is the validation function for validating if the current field's value is a valid HEX color.
|
||||
func HEXColor(str string) bool { |
||||
return hexColorRegex.MatchString(str) |
||||
} |
||||
|
||||
// RGB is the validation function for validating if the current field's value is a valid RGB color.
|
||||
func RGB(str string) bool { |
||||
return rgbRegex.MatchString(str) |
||||
} |
||||
|
||||
// RGBA is the validation function for validating if the current field's value is a valid RGBA color.
|
||||
func RGBA(str string) bool { |
||||
return rgbaRegex.MatchString(str) |
||||
} |
||||
|
||||
// HSL is the validation function for validating if the current field's value is a valid HSL color.
|
||||
func HSL(str string) bool { |
||||
return hslRegex.MatchString(str) |
||||
} |
||||
|
||||
// HSLA is the validation function for validating if the current field's value is a valid HSLA color.
|
||||
func HSLA(str string) bool { |
||||
return hslaRegex.MatchString(str) |
||||
} |
||||
|
||||
func Color(str string) bool { |
||||
return HEXColor(str) || HSLA(str) || HSL(str) || RGB(str) || RGBA(str) |
||||
} |
||||
|
||||
// Latitude is the validation function for validating if the field's value is a valid latitude coordinate.
|
||||
func Latitude[T any](t T) bool { |
||||
ref := reflect.ValueOf(t) |
||||
var v string |
||||
switch ref.Kind() { |
||||
case reflect.String: |
||||
v = ref.String() |
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
||||
v = strconv.FormatInt(ref.Int(), 10) |
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
||||
v = strconv.FormatUint(ref.Uint(), 10) |
||||
case reflect.Float32: |
||||
v = strconv.FormatFloat(ref.Float(), 'f', -1, 32) |
||||
case reflect.Float64: |
||||
v = strconv.FormatFloat(ref.Float(), 'f', -1, 64) |
||||
default: |
||||
//fmt.Errorf("bad ref type %T", ref.Interface())
|
||||
return false |
||||
} |
||||
return latitudeRegex.MatchString(v) |
||||
} |
||||
|
||||
// Longitude is the validation function for validating if the field's value is a valid longitude coordinate.
|
||||
func Longitude[T any](t T) bool { |
||||
ref := reflect.ValueOf(t) |
||||
var v string |
||||
switch ref.Kind() { |
||||
case reflect.String: |
||||
v = ref.String() |
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
||||
v = strconv.FormatInt(ref.Int(), 10) |
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
||||
v = strconv.FormatUint(ref.Uint(), 10) |
||||
case reflect.Float32: |
||||
v = strconv.FormatFloat(ref.Float(), 'f', -1, 32) |
||||
case reflect.Float64: |
||||
v = strconv.FormatFloat(ref.Float(), 'f', -1, 64) |
||||
default: |
||||
//fmt.Errorf("bad field type %T", ref.Interface())
|
||||
return false |
||||
} |
||||
return longitudeRegex.MatchString(v) |
||||
} |
||||
|
||||
// JSON is the validation function for validating if the current field's value is a valid json string.
|
||||
func JSON[T any](t T) bool { |
||||
rv := reflect.ValueOf(t) |
||||
if rv.Type() == bytesType { |
||||
return json.Valid(rv.Bytes()) |
||||
} |
||||
if rv.Kind() == reflect.String { |
||||
return json.Valid([]byte(rv.String())) |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func Datetime(str, layout string) bool { |
||||
_, err := time.Parse(layout, str) |
||||
return err == nil |
||||
} |
||||
|
||||
// Timezone is the validation function for validating if the current field's value is a valid time zone string.
|
||||
func Timezone(str string) bool { |
||||
// empty value is converted to UTC by time.LoadLocation but disallow it as it is not a valid time zone name
|
||||
if str == "" { |
||||
return false |
||||
} |
||||
|
||||
// Local value is converted to the current system time zone by time.LoadLocation but disallow it as it is not a valid time zone name
|
||||
if strings.ToLower(str) == "local" { |
||||
return false |
||||
} |
||||
|
||||
_, err := time.LoadLocation(str) |
||||
return err == nil |
||||
} |
||||
|
||||
// IPv4 is the validation function for validating if a value is a valid v4 IP address.
|
||||
func IPv4(str string) bool { |
||||
ip := net.ParseIP(str) |
||||
return ip != nil && ip.To4() != nil |
||||
} |
||||
|
||||
// IPv6 is the validation function for validating if the field's value is a valid v6 IP address.
|
||||
func IPv6(str string) bool { |
||||
ip := net.ParseIP(str) |
||||
return ip != nil && ip.To4() == nil |
||||
} |
||||
|
||||
// IP is the validation function for validating if the field's value is a valid v4 or v6 IP address.
|
||||
func IP(str string) bool { |
||||
ip := net.ParseIP(str) |
||||
return ip != nil |
||||
} |
||||
|
||||
// MAC is the validation function for validating if the field's value is a valid MAC address.
|
||||
func MAC(str string) bool { |
||||
_, err := net.ParseMAC(str) |
||||
return err == nil |
||||
} |
||||
|
||||
// Lowercase is the validation function for validating if the current field's value is a lowercase string.
|
||||
func Lowercase(str string) bool { |
||||
if str == "" { |
||||
return false |
||||
} |
||||
return str == strings.ToLower(str) |
||||
} |
||||
|
||||
// Uppercase is the validation function for validating if the current field's value is an uppercase string.
|
||||
func Uppercase(str string) bool { |
||||
if str == "" { |
||||
return false |
||||
} |
||||
return str == strings.ToUpper(str) |
||||
} |
||||
|
||||
// Empty checks if a value is empty or not.
|
||||
// A value is considered empty if
|
||||
// - integer, float: zero
|
||||
// - bool: false
|
||||
// - string, array: len() == 0
|
||||
// - slice, map: nil or len() == 0
|
||||
// - interface, pointer: nil or the referenced value is empty
|
||||
func Empty[T any](t T) bool { |
||||
rv := reflect.ValueOf(t) |
||||
switch rv.Kind() { |
||||
case reflect.String, reflect.Array, reflect.Map, reflect.Slice: |
||||
return rv.Len() == 0 |
||||
case reflect.Bool: |
||||
return !rv.Bool() |
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
||||
return rv.Int() == 0 |
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: |
||||
return rv.Uint() == 0 |
||||
case reflect.Float32, reflect.Float64: |
||||
return rv.Float() == 0 |
||||
case reflect.Invalid: |
||||
return true |
||||
case reflect.Interface, reflect.Ptr: |
||||
if rv.IsNil() { |
||||
return true |
||||
} |
||||
return Empty(rv.Elem().Interface()) |
||||
case reflect.Struct: |
||||
v, ok := rv.Interface().(time.Time) |
||||
if ok && v.IsZero() { |
||||
return true |
||||
} |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func NotEmpty[T any](t T) bool { |
||||
return !Empty(t) |
||||
} |
||||
|
||||
func URLEncoded(str string) bool { |
||||
return uRLEncodedRegex.MatchString(str) |
||||
} |
||||
|
||||
func HTMLEncoded(str string) bool { |
||||
return hTMLEncodedRegex.MatchString(str) |
||||
} |
||||
|
||||
func HTML(str string) bool { |
||||
return hTMLRegex.MatchString(str) |
||||
} |
||||
|
||||
// File is the validation function for validating if the current field's value is a valid file path.
|
||||
func File(val any) bool { |
||||
field := reflect.ValueOf(val) |
||||
switch field.Kind() { |
||||
case reflect.String: |
||||
fileInfo, err := os.Stat(field.String()) |
||||
if err != nil { |
||||
return false |
||||
} |
||||
return !fileInfo.IsDir() |
||||
} |
||||
//fmt.Errorf("bad value type %T", field.Interface())
|
||||
return false |
||||
} |
||||
|
||||
// Dir is the validation function for validating if the current field's value is a valid directory.
|
||||
func Dir(val any) bool { |
||||
field := reflect.ValueOf(val) |
||||
if field.Kind() == reflect.String { |
||||
fileInfo, err := os.Stat(field.String()) |
||||
if err != nil { |
||||
return false |
||||
} |
||||
return fileInfo.IsDir() |
||||
} |
||||
//fmt.Errorf("bad field type %T", field.Interface())
|
||||
return false |
||||
} |
||||
|
||||
func OneOf(val any, vals []any) bool { |
||||
//rv := reflect.ValueOf(val)
|
||||
//
|
||||
//var v string
|
||||
//switch rv.Kind() {
|
||||
//case reflect.String:
|
||||
// v = rv.String()
|
||||
//case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
// v = strconv.FormatInt(rv.Int(), 10)
|
||||
//case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
// v = strconv.FormatUint(rv.Uint(), 10)
|
||||
//default:
|
||||
// //fmt.Errorf("Bad rv type %T", rv.Interface())
|
||||
// return false
|
||||
//}
|
||||
//for i := 0; i < len(vals); i++ {
|
||||
// if vals[i] == v {
|
||||
// return true
|
||||
// }
|
||||
//}
|
||||
if len(vals) == 0 { |
||||
return false |
||||
} |
||||
for _, a := range vals { |
||||
if Equal(val, a) { |
||||
return true |
||||
} |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func Length(val any, length int, op string) bool { |
||||
if n := calcLength(val); n == -1 { |
||||
return false |
||||
} else { |
||||
return Compare(n, length, op) |
||||
} |
||||
} |
||||
|
||||
func LengthBetween(val any, min, max int) bool { |
||||
if !Compare(min, max, "<") { |
||||
panic(ErrBadType) |
||||
} else if n, err := getLength(val, false); err != nil { |
||||
return false |
||||
} else { |
||||
return Compare(n, min, ">=") && Compare(n, max, "<=") |
||||
} |
||||
} |
||||
|
||||
// Compare intX,floatX value by given op. returns `srcVal op(=,!=,<,<=,>,>=) dstVal`
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// compare(2, 3, ">") // false
|
||||
// compare(2, 1.3, ">") // true
|
||||
// compare(2.2, 1.3, ">") // true
|
||||
// compare(2.1, 2, ">") // true
|
||||
func Compare(srcVal, dstVal any, op string) bool { |
||||
srv := reflect.ValueOf(srcVal) |
||||
|
||||
switch srv.Kind() { |
||||
case reflect.Struct: |
||||
if srv.Type().ConvertibleTo(timeType) { |
||||
drv := reflect.ValueOf(dstVal) |
||||
if drv.Type().ConvertibleTo(timeType) { |
||||
at := srv.Convert(timeType).Interface().(time.Time) |
||||
bt := drv.Convert(timeType).Interface().(time.Time) |
||||
return compTime(at, bt, op) |
||||
} |
||||
} |
||||
case reflect.Bool: |
||||
drv := reflect.ValueOf(dstVal) |
||||
switch drv.Kind() { |
||||
case reflect.Bool: |
||||
return compBool(srv.Bool(), drv.Bool(), op) |
||||
case reflect.String: |
||||
if bl, err := strconv.ParseBool(drv.String()); err == nil { |
||||
return compBool(srv.Bool(), bl, op) |
||||
} |
||||
} |
||||
default: |
||||
if srcStr, ok := srcVal.(string); ok { |
||||
if dstStr, ok2 := dstVal.(string); ok2 { |
||||
return compString(srcStr, dstStr, op) |
||||
} |
||||
break |
||||
} |
||||
|
||||
// float
|
||||
if srcFlt, ok := srcVal.(float64); ok { |
||||
if dstFlt, err := toFloat(dstVal); err == nil { |
||||
return compNum(srcFlt, dstFlt, op) |
||||
} |
||||
break |
||||
} |
||||
|
||||
if srcFlt, ok := srcVal.(float32); ok { |
||||
if dstFlt, err := toFloat(dstVal); err == nil { |
||||
return compNum(float64(srcFlt), dstFlt, op) |
||||
} |
||||
break |
||||
} |
||||
|
||||
// as int64
|
||||
if srcInt, err := toInt64(srcVal); err != nil { |
||||
break |
||||
} else if dstInt, ex := toInt64(dstVal); ex != nil { |
||||
break |
||||
} else { |
||||
return compNum(srcInt, dstInt, op) |
||||
} |
||||
} |
||||
|
||||
switch op { |
||||
case "=": |
||||
return srcVal == dstVal |
||||
case "!=": |
||||
return srcVal != dstVal |
||||
default: |
||||
//ErrBadType
|
||||
return false |
||||
} |
||||
} |
||||
|
||||
// GreaterThan is the validation function for validating if the current field's value is greater than the param's value.
|
||||
func GreaterThan(a, b any) bool { |
||||
return Compare(a, b, ">") |
||||
} |
||||
|
||||
// GreaterEqualThan is the validation function for validating if the current field's value is greater than or equal to the param's value.
|
||||
func GreaterEqualThan(a, b any) bool { |
||||
return Compare(a, b, ">=") |
||||
} |
||||
|
||||
// LessThan is the validation function for validating if the current field's value is less than the param's value.
|
||||
func LessThan(a, b any) bool { |
||||
return Compare(a, b, "<") |
||||
} |
||||
|
||||
// LessEqualThan is the validation function for validating if the current field's value is less than or equal to the param's value.
|
||||
func LessEqualThan(a, b any) bool { |
||||
return Compare(a, b, "<=") |
||||
} |
||||
|
||||
// Equal is the validation function for validating if the current field's value is equal to the param's value.
|
||||
func Equal(a, b any) bool { |
||||
return Compare(a, b, "=") |
||||
} |
||||
|
||||
func NotEqual(a, b any) bool { |
||||
return Compare(a, b, "!=") |
||||
} |
||||
|
||||
func Between(val, min, max any) bool { |
||||
if !Compare(min, max, ">") { |
||||
panic(ErrBadRange) |
||||
} |
||||
|
||||
return Compare(val, min, ">=") && Compare(val, max, "<=") |
||||
} |
||||
|
||||
func NotBetween(val, min, max any) bool { |
||||
if !Compare(min, max, ">") { |
||||
panic(ErrBadRange) |
||||
} |
||||
|
||||
return Compare(val, min, "<") || Compare(val, max, ">") |
||||
} |
@ -0,0 +1,79 @@ |
||||
package is |
||||
|
||||
import "regexp" |
||||
|
||||
const ( |
||||
alphaRegexString = "^[a-zA-Z]+$" |
||||
alphaNumericRegexString = "^[a-zA-Z0-9]+$" |
||||
alphaUnicodeRegexString = "^[\\p{L}]+$" |
||||
alphaUnicodeNumericRegexString = "^[\\p{L}\\p{N}]+$" |
||||
numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$" |
||||
numberRegexString = "^[0-9]+$" |
||||
hexadecimalRegexString = "^(0[xX])?[0-9a-fA-F]+$" |
||||
hexColorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$" |
||||
rgbRegexString = "^rgb\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*\\)$" |
||||
rgbaRegexString = "^rgba\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" |
||||
hslRegexString = "^hsl\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*\\)$" |
||||
hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" |
||||
emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" |
||||
e164RegexString = "^\\+[1-9]?[0-9]{7,14}$" |
||||
phoneNumberRegexString = "^(\\+?86)?1[0-9]{10}$" |
||||
base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" |
||||
base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$" |
||||
uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$" |
||||
uUID4RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" |
||||
uUID5RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" |
||||
uUIDRegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" |
||||
uLIDRegexString = "^[A-HJKMNP-TV-Z0-9]{26}$" |
||||
md4RegexString = "^[0-9a-f]{32}$" |
||||
md5RegexString = "^[0-9a-f]{32}$" |
||||
sha256RegexString = "^[0-9a-f]{64}$" |
||||
sha384RegexString = "^[0-9a-f]{96}$" |
||||
sha512RegexString = "^[0-9a-f]{128}$" |
||||
aSCIIRegexString = "^[\x00-\x7F]*$" |
||||
latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" |
||||
longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" |
||||
uRLEncodedRegexString = `^(?:[^%]|%[0-9A-Fa-f]{2})*$` |
||||
hTMLEncodedRegexString = `&#[x]?([0-9a-fA-F]{2})|(>)|(<)|(")|(&)+[;]?` |
||||
hTMLRegexString = `<[/]?([a-zA-Z]+).*?>` |
||||
jWTRegexString = "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]*$" |
||||
semverRegexString = `^(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-]+)*))?$` // numbered capture groups https://semver.org/
|
||||
) |
||||
|
||||
var ( |
||||
alphaRegex = regexp.MustCompile(alphaRegexString) |
||||
alphaNumericRegex = regexp.MustCompile(alphaNumericRegexString) |
||||
alphaUnicodeRegex = regexp.MustCompile(alphaUnicodeRegexString) |
||||
alphaUnicodeNumericRegex = regexp.MustCompile(alphaUnicodeNumericRegexString) |
||||
numericRegex = regexp.MustCompile(numericRegexString) |
||||
numberRegex = regexp.MustCompile(numberRegexString) |
||||
hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString) |
||||
hexColorRegex = regexp.MustCompile(hexColorRegexString) |
||||
rgbRegex = regexp.MustCompile(rgbRegexString) |
||||
rgbaRegex = regexp.MustCompile(rgbaRegexString) |
||||
hslRegex = regexp.MustCompile(hslRegexString) |
||||
hslaRegex = regexp.MustCompile(hslaRegexString) |
||||
e164Regex = regexp.MustCompile(e164RegexString) |
||||
phoneNumberRegex = regexp.MustCompile(phoneNumberRegexString) |
||||
emailRegex = regexp.MustCompile(emailRegexString) |
||||
base64Regex = regexp.MustCompile(base64RegexString) |
||||
base64URLRegex = regexp.MustCompile(base64URLRegexString) |
||||
uUID3Regex = regexp.MustCompile(uUID3RegexString) |
||||
uUID4Regex = regexp.MustCompile(uUID4RegexString) |
||||
uUID5Regex = regexp.MustCompile(uUID5RegexString) |
||||
uUIDRegex = regexp.MustCompile(uUIDRegexString) |
||||
uLIDRegex = regexp.MustCompile(uLIDRegexString) |
||||
md4Regex = regexp.MustCompile(md4RegexString) |
||||
md5Regex = regexp.MustCompile(md5RegexString) |
||||
sha256Regex = regexp.MustCompile(sha256RegexString) |
||||
sha384Regex = regexp.MustCompile(sha384RegexString) |
||||
sha512Regex = regexp.MustCompile(sha512RegexString) |
||||
aSCIIRegex = regexp.MustCompile(aSCIIRegexString) |
||||
latitudeRegex = regexp.MustCompile(latitudeRegexString) |
||||
longitudeRegex = regexp.MustCompile(longitudeRegexString) |
||||
uRLEncodedRegex = regexp.MustCompile(uRLEncodedRegexString) |
||||
hTMLEncodedRegex = regexp.MustCompile(hTMLEncodedRegexString) |
||||
hTMLRegex = regexp.MustCompile(hTMLRegexString) |
||||
jWTRegex = regexp.MustCompile(jWTRegexString) |
||||
semverRegex = regexp.MustCompile(semverRegexString) |
||||
) |
@ -0,0 +1,211 @@ |
||||
package is |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
"fmt" |
||||
"reflect" |
||||
"strconv" |
||||
"strings" |
||||
"time" |
||||
"unicode/utf8" |
||||
) |
||||
|
||||
var ( |
||||
ErrBadType = errors.New("bad value type") |
||||
ErrBadRange = errors.New("bad value range") |
||||
|
||||
timeType = reflect.TypeOf(time.Time{}) |
||||
//nilType = reflect.TypeOf([]byte(nil))
|
||||
) |
||||
|
||||
// convert value to float64, return error on failed
|
||||
func toFloat(in any) (f64 float64, err error) { |
||||
switch tVal := in.(type) { |
||||
case nil: |
||||
f64 = 0 |
||||
case string: |
||||
f64, err = strconv.ParseFloat(strings.TrimSpace(tVal), 64) |
||||
case int: |
||||
f64 = float64(tVal) |
||||
case int8: |
||||
f64 = float64(tVal) |
||||
case int16: |
||||
f64 = float64(tVal) |
||||
case int32: |
||||
f64 = float64(tVal) |
||||
case int64: |
||||
f64 = float64(tVal) |
||||
case uint: |
||||
f64 = float64(tVal) |
||||
case uint8: |
||||
f64 = float64(tVal) |
||||
case uint16: |
||||
f64 = float64(tVal) |
||||
case uint32: |
||||
f64 = float64(tVal) |
||||
case uint64: |
||||
f64 = float64(tVal) |
||||
case float32: |
||||
f64 = float64(tVal) |
||||
case float64: |
||||
f64 = tVal |
||||
case time.Duration: |
||||
f64 = float64(tVal) |
||||
case json.Number: |
||||
f64, err = tVal.Float64() |
||||
default: |
||||
err = ErrBadType |
||||
} |
||||
return |
||||
} |
||||
|
||||
// convert string to int64, return error on failed
|
||||
func toInt64(in any) (i64 int64, err error) { |
||||
switch tVal := in.(type) { |
||||
case nil: |
||||
i64 = 0 |
||||
case string: |
||||
i64, err = strconv.ParseInt(strings.TrimSpace(tVal), 10, 0) |
||||
case int: |
||||
i64 = int64(tVal) |
||||
case int8: |
||||
i64 = int64(tVal) |
||||
case int16: |
||||
i64 = int64(tVal) |
||||
case int32: |
||||
i64 = int64(tVal) |
||||
case int64: |
||||
i64 = tVal |
||||
case uint: |
||||
i64 = int64(tVal) |
||||
case uint8: |
||||
i64 = int64(tVal) |
||||
case uint16: |
||||
i64 = int64(tVal) |
||||
case uint32: |
||||
i64 = int64(tVal) |
||||
case uint64: |
||||
i64 = int64(tVal) |
||||
case float32: |
||||
i64 = int64(tVal) |
||||
case float64: |
||||
i64 = int64(tVal) |
||||
case time.Duration: |
||||
i64 = int64(tVal) |
||||
case json.Number: |
||||
i64, err = tVal.Int64() |
||||
default: |
||||
err = ErrBadType |
||||
} |
||||
return |
||||
} |
||||
|
||||
// compString compare string, returns the first op second
|
||||
func compString(first, second, op string) bool { |
||||
rs := strings.Compare(first, second) |
||||
if rs < 0 { |
||||
return op == "<" || op == "<=" |
||||
} else if rs > 0 { |
||||
return op == ">" || op == ">=" |
||||
} else { |
||||
return op == ">=" || op == "<=" || op == "=" |
||||
} |
||||
} |
||||
|
||||
func compTime(first, dstTime time.Time, op string) (ok bool) { |
||||
switch op { |
||||
case "<": |
||||
return first.Before(dstTime) |
||||
case "<=": |
||||
return first.Before(dstTime) || first.Equal(dstTime) |
||||
case ">": |
||||
return first.After(dstTime) |
||||
case ">=": |
||||
return first.After(dstTime) || first.Equal(dstTime) |
||||
case "=": |
||||
return first.Equal(dstTime) |
||||
case "!=": |
||||
return !first.Equal(dstTime) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func compNum[T int64 | float64 | uint64](first, second T, op string) bool { |
||||
switch op { |
||||
case "<": |
||||
return first < second |
||||
case "<=": |
||||
return first <= second |
||||
case ">": |
||||
return first > second |
||||
case ">=": |
||||
return first >= second |
||||
case "=": |
||||
return first == second |
||||
case "!=": |
||||
return first != second |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func compBool(first, second bool, op string) bool { |
||||
return compNum(boo2int(first), boo2int(second), op) |
||||
} |
||||
|
||||
func boo2int(a bool) int64 { |
||||
if a { |
||||
return 1 |
||||
} else { |
||||
return 0 |
||||
} |
||||
} |
||||
|
||||
// get reflect value length
|
||||
func calcLength(val any) int { |
||||
v := reflect.Indirect(reflect.ValueOf(val)) |
||||
|
||||
// (u)int use width.
|
||||
switch v.Kind() { |
||||
case reflect.String: |
||||
return len([]rune(v.String())) |
||||
case reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: |
||||
return v.Len() |
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: |
||||
return len(strconv.FormatInt(int64(v.Uint()), 10)) |
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
||||
return len(strconv.FormatInt(v.Int(), 10)) |
||||
case reflect.Float32, reflect.Float64: |
||||
return len(fmt.Sprint(v.Interface())) |
||||
} |
||||
|
||||
// cannot get length
|
||||
return -1 |
||||
} |
||||
|
||||
func getLength(a any, rune bool) (int, error) { |
||||
field := reflect.ValueOf(a) |
||||
|
||||
//if !field.IsValid() || field.IsNil() {
|
||||
// return 0, nil
|
||||
//}
|
||||
|
||||
switch field.Kind() { |
||||
case reflect.String: |
||||
if rune { |
||||
return utf8.RuneCountInString(field.String()), nil |
||||
} |
||||
return field.Len(), nil |
||||
|
||||
case reflect.Slice, reflect.Map, reflect.Array: |
||||
return field.Len(), nil |
||||
|
||||
case reflect.Ptr: |
||||
if field.Type().Elem().Kind() == reflect.Array { |
||||
// 类型声明中的长度
|
||||
return field.Type().Elem().Len(), nil |
||||
} |
||||
} |
||||
|
||||
return 0, ErrBadType |
||||
} |
Loading…
Reference in new issue