package nstr import ( "encoding/json" "net" "net/url" "reflect" "regexp" "strconv" "strings" "unicode" ) var ( alphaMatcher = regexp.MustCompile(`^[a-zA-Z]+$`) letterRegexMatcher = regexp.MustCompile(`[a-zA-Z]`) intStrMatcher = regexp.MustCompile(`^[\+-]?\d+$`) urlMatcher = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`) dnsMatcher = regexp.MustCompile(`^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$`) emailMatcher = regexp.MustCompile(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`) chineseMobileMatcher = regexp.MustCompile(`^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$`) chineseIdMatcher = regexp.MustCompile(`^[1-9]\d{5}(18|19|20|21|22)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$`) chineseMatcher = regexp.MustCompile("[\u4e00-\u9fa5]") chinesePhoneMatcher = regexp.MustCompile(`\d{3}-\d{8}|\d{4}-\d{7}|\d{4}-\d{8}`) creditCardMatcher = regexp.MustCompile(`^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$`) base64Matcher = regexp.MustCompile(`^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$`) ) // IsString check if the value data type is string or not. func IsString(v any) bool { if v == nil { return false } switch v.(type) { case string: return true default: return false } } // IsAlpha checks if the string contains only letters (a-zA-Z). func IsAlpha(str string) bool { return alphaMatcher.MatchString(str) } // IsAllUpper check if the string is all upper case letters A-Z. func IsAllUpper(str string) bool { for _, r := range str { if !unicode.IsUpper(r) { return false } } return str != "" } // IsAllLower check if the string is all lower case letters a-z. func IsAllLower(str string) bool { for _, r := range str { if !unicode.IsLower(r) { return false } } return str != "" } // ContainUpper check if the string contain at least one upper case letter A-Z. func ContainUpper(str string) bool { for _, r := range str { if unicode.IsUpper(r) && unicode.IsLetter(r) { return true } } return false } // ContainLower check if the string contain at least one lower case letter a-z. func ContainLower(str string) bool { for _, r := range str { if unicode.IsLower(r) && unicode.IsLetter(r) { return true } } return false } // ContainLetter check if the string contain at least one letter. func ContainLetter(str string) bool { return letterRegexMatcher.MatchString(str) } // IsJSON checks if the string is valid JSON. func IsJSON(str string) bool { return json.Valid([]byte(str)) } // IsNumberStr check if the string can convert to a number. func IsNumberStr(s string) bool { return IsIntStr(s) || IsFloatStr(s) } // IsFloatStr check if the string can convert to a float. func IsFloatStr(str string) bool { _, e := strconv.ParseFloat(str, 64) return e == nil } // IsIntStr check if the string can convert to a integer. func IsIntStr(str string) bool { return intStrMatcher.MatchString(str) } // IsIp check if the string is an ip address. func IsIp(ipstr string) bool { ip := net.ParseIP(ipstr) return ip != nil } // IsIpV4 check if the string is a ipv4 address. func IsIpV4(ipstr string) bool { ip := net.ParseIP(ipstr) if ip == nil { return false } return strings.Contains(ipstr, ".") } // IsIpV6 check if the string is a ipv6 address. func IsIpV6(ipstr string) bool { ip := net.ParseIP(ipstr) if ip == nil { return false } return strings.Contains(ipstr, ":") } // IsPort check if the string is a valid net port. func IsPort(str string) bool { if i, err := strconv.ParseInt(str, 10, 64); err == nil && i > 0 && i < 65536 { return true } return false } // IsUrl check if the string is url. func IsUrl(str string) bool { if str == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") { return false } u, err := url.Parse(str) if err != nil { return false } if strings.HasPrefix(u.Host, ".") { return false } if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) { return false } return urlMatcher.MatchString(str) } // IsDns check if the string is dns. func IsDns(dns string) bool { return dnsMatcher.MatchString(dns) } // IsEmail check if the string is a email address. func IsEmail(email string) bool { return emailMatcher.MatchString(email) } // IsChineseMobile check if the string is chinese mobile number. func IsChineseMobile(mobileNum string) bool { return chineseMobileMatcher.MatchString(mobileNum) } // IsChineseIdNum check if the string is chinese id card. func IsChineseIdNum(id string) bool { return chineseIdMatcher.MatchString(id) } // ContainChinese check if the string contain mandarin chinese. func ContainChinese(s string) bool { return chineseMatcher.MatchString(s) } // IsChinesePhone check if the string is chinese phone number. // Valid chinese phone is xxx-xxxxxxxx or xxxx-xxxxxxx. func IsChinesePhone(phone string) bool { return chinesePhoneMatcher.MatchString(phone) } // IsCreditCard check if the string is credit card. func IsCreditCard(creditCart string) bool { return creditCardMatcher.MatchString(creditCart) } // IsBase64 check if the string is base64 string. func IsBase64(base64 string) bool { return base64Matcher.MatchString(base64) } // IsEmptyString check if the string is empty. func IsEmptyString(str string) bool { return len(str) == 0 } // IsRegexMatch check if the string match the regexp. func IsRegexMatch(str, regex string) bool { reg := regexp.MustCompile(regex) return reg.MatchString(str) } // IsStrongPassword check if the string is strong password, if len(password) is less than the length param, return false // Strong password: alpha(lower+upper) + number + special chars(!@#$%^&*()?><). func IsStrongPassword(password string, length int) bool { if len(password) < length { return false } var num, lower, upper, special bool for _, r := range password { switch { case unicode.IsDigit(r): num = true case unicode.IsUpper(r): upper = true case unicode.IsLower(r): lower = true case unicode.IsSymbol(r), unicode.IsPunct(r): special = true } } return num && lower && upper && special } // IsWeakPassword check if the string is weak password // only letter or only number or letter + number. func IsWeakPassword(password string) bool { var num, letter, special bool for _, r := range password { switch { case unicode.IsDigit(r): num = true case unicode.IsLetter(r): letter = true case unicode.IsSymbol(r), unicode.IsPunct(r): special = true } } return (num || letter) && !special } // IsZeroValue checks if value is a zero value. func IsZeroValue(value any) bool { if value == nil { return true } rv := reflect.ValueOf(value) if rv.Kind() == reflect.Ptr { rv = rv.Elem() } if !rv.IsValid() { return true } switch rv.Kind() { case reflect.String: 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.Ptr, reflect.Chan, reflect.Func, reflect.Interface, reflect.Slice, reflect.Map: return rv.IsNil() } return reflect.DeepEqual(rv.Interface(), reflect.Zero(rv.Type()).Interface()) } // ----- refer from github.com/yuin/goldmark/util // refer from github.com/yuin/goldmark/util var spaceTable = [256]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} // IsSpace returns true if the given character is a space, otherwise false. func IsSpace(c byte) bool { return spaceTable[c] == 1 } // IsEmpty returns true if the given string is empty. func IsEmpty(s string) bool { return len(s) == 0 } // IsBlank returns true if the given string is all space characters. func IsBlank(s string) bool { return IsBlankBytes([]byte(s)) } // IsNotBlank returns true if the given string is not blank. func IsNotBlank(s string) bool { return !IsBlankBytes([]byte(s)) } // IsBlankBytes returns true if the given []byte is all space characters. func IsBlankBytes(bs []byte) bool { for _, b := range bs { if !IsSpace(b) { return false } } return true } // IsSymbol reports whether the rune is a symbolic character. func IsSymbol(r rune) bool { return unicode.IsSymbol(r) } // HasEmpty value for input strings func HasEmpty(ss ...string) bool { for _, s := range ss { if s == "" { return true } } return false } // IsAllEmpty for input strings func IsAllEmpty(ss ...string) bool { for _, s := range ss { if s != "" { return false } } return true } // ContainsByte in given string. func ContainsByte(s string, c byte) bool { return strings.IndexByte(s, c) >= 0 } // ContainsOne substr(s) in the given string. alias of HasOneSub() func ContainsOne(s string, subs []string) bool { return HasOneSub(s, subs) } // HasOneSub substr(s) in the given string. func HasOneSub(s string, subs []string) bool { for _, sub := range subs { if strings.Contains(s, sub) { return true } } return false } // ContainsAll substr(s) in the given string. alias of HasAllSubs() func ContainsAll(s string, subs []string) bool { return HasAllSubs(s, subs) } // HasAllSubs all substr in the given string. func HasAllSubs(s string, subs []string) bool { for _, sub := range subs { if !strings.Contains(s, sub) { return false } } return true } // IsStartsOf alias of the HasOnePrefix func IsStartsOf(s string, prefixes []string) bool { return HasOnePrefix(s, prefixes) } // HasOnePrefix the string start withs one of the subs func HasOnePrefix(s string, prefixes []string) bool { for _, prefix := range prefixes { if strings.HasPrefix(s, prefix) { return true } } return false } // HasPrefix substr in the given string. func HasPrefix(s string, prefix string) bool { return strings.HasPrefix(s, prefix) } // IsStartOf alias of the strings.HasPrefix func IsStartOf(s, prefix string) bool { return strings.HasPrefix(s, prefix) } // HasSuffix substr in the given string. func HasSuffix(s string, suffix string) bool { return strings.HasSuffix(s, suffix) } // IsEndOf alias of the strings.HasSuffix func IsEndOf(s, suffix string) bool { return strings.HasSuffix(s, suffix) } // HasOneSuffix the string end withs one of the subs func HasOneSuffix(s string, suffixes []string) bool { for _, suffix := range suffixes { if strings.HasSuffix(s, suffix) { return true } } return false }