go项目脚手架
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sorbet/pkg/is/is.go

607 lines
17 KiB

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() == nilType {
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, ">")
}