package util import ( "crypto/rand" "fmt" "math/big" "net/mail" "net/url" "strconv" "strings" "unicode/utf8" "github.com/google/uuid" "github.com/nyaruka/phonenumbers" "github.com/pkg/errors" ) // ConvertStringToInt32 converts a string to int32. func ConvertStringToInt32(src string) (int32, error) { parsed, err := strconv.ParseInt(src, 10, 32) if err != nil { return 0, err } return int32(parsed), nil } // HasPrefixes returns true if the string s has any of the given prefixes. func HasPrefixes(src string, prefixes ...string) bool { for _, prefix := range prefixes { if strings.HasPrefix(src, prefix) { return true } } return false } // ValidateEmail validates the email. func ValidateEmail(email string) bool { if _, err := mail.ParseAddress(email); err != nil { return false } return true } func GenUUID() string { return uuid.New().String() } var letters = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") // RandomString returns a random string with length n. func RandomString(n int) (string, error) { var sb strings.Builder sb.Grow(n) for i := 0; i < n; i++ { // The reason for using crypto/rand instead of math/rand is that // the former relies on hardware to generate random numbers and // thus has a stronger source of random numbers. randNum, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters)))) if err != nil { return "", err } if _, err := sb.WriteRune(letters[randNum.Uint64()]); err != nil { return "", err } } return sb.String(), nil } // ValidatePhone validates the phone number. func ValidatePhone(phone string) error { phoneNumber, err := phonenumbers.Parse(phone, "") if err != nil { return err } if !phonenumbers.IsValidNumber(phoneNumber) { return errors.New("invalid phone number") } return nil } // SanitizeUTF8String returns a copy of the string s with each run of invalid or unprintable UTF-8 byte sequences // replaced by its hexadecimal representation string. func SanitizeUTF8String(s string) string { var b strings.Builder for i, c := range s { if c != utf8.RuneError { continue } _, wid := utf8.DecodeRuneInString(s[i:]) if wid == 1 { b.Grow(len(s)) _, _ = b.WriteString(s[:i]) s = s[i:] break } } // Fast path for unchanged input if b.Cap() == 0 { // didn't call b.Grow above return s } for i := 0; i < len(s); { c := s[i] // U+0000-U+0019 are control characters if 0x20 <= c && c < utf8.RuneSelf { i++ _ = b.WriteByte(c) continue } _, wid := utf8.DecodeRuneInString(s[i:]) if wid == 1 { i++ _, _ = b.WriteString(fmt.Sprintf("\\x%02x", c)) continue } _, _ = b.WriteString(s[i : i+wid]) i += wid } return b.String() } // ReplaceString replaces all occurrences of old in slice with new. func ReplaceString(slice []string, old, new string) []string { for i, s := range slice { if s == old { slice[i] = new } } return slice } // TruncateString truncates the string to have a maximum length of `limit` characters. func TruncateString(str string, limit int) (string, bool) { chars := 0 // The string may contain unicode characters, so we iterate here. for i := range str { if chars >= limit { return str[:i], true } chars++ } return str, false } // TruncateStringWithDescription tries to truncate the string and append "..." if truncated. func TruncateStringWithDescription(str string) string { const limit = 450 if truncatedStr, truncated := TruncateString(str, limit); truncated { return fmt.Sprintf("%s...", truncatedStr) } return str } // ValidateURI validates the URI. func ValidateURI(uri string) bool { u, err := url.Parse(uri) return err == nil && u.Scheme != "" && u.Host != "" }