feat: 数据判断功能库

main
熊二 1 year ago
parent bb44a78885
commit 32a7f7d6fc
  1. 607
      pkg/is/is.go
  2. 79
      pkg/is/regexes.go
  3. 211
      pkg/is/util.go

@ -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})|(&gt)|(&lt)|(&quot)|(&amp)+[;]?`
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…
Cancel
Save