package string_utils import ( "encoding/json" "fmt" "regexp" "strconv" "strings" "unicode" "golang.org/x/text/runes" "golang.org/x/text/transform" "golang.org/x/text/unicode/norm" ) // ReplaceNonSpacingMarks removes diacritics e.g. êžů becomes ezu func ReplaceNonSpacingMarks(str string) string { t := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC) // Mn: non-spacing marks result, _, _ := transform.String(t, str) return result } func RemoveAllWhiteSpaces(s string) string { return strings.ReplaceAll(strings.ReplaceAll(s, " ", ""), "\t", "") } func ReplaceCaseInsensitive(string, toReplace, replaceWith string) string { regex := regexp.MustCompile("(?i)" + strings.ToLower(toReplace)) return regex.ReplaceAllString(string, replaceWith) } // TrimQuotes - trims quotes from a string (ie: "foo" will return foo) func TrimQuotes(stringToTrim string) string { if len(stringToTrim) >= 2 { if stringToTrim[0] == '"' && stringToTrim[len(stringToTrim)-1] == '"' { return stringToTrim[1 : len(stringToTrim)-1] } } return stringToTrim } // IsNumericString returns true if the string can be converted to a float without error func IsNumericString(s string) bool { _, err := strconv.ParseFloat(s, 64) return err == nil } // StandardisePhoneNumber standardises phone numbers with +27 instead of 0 prefix func StandardisePhoneNumber(number string) string { // is the first rune/char of the string a 0 if []rune(number)[0] == []rune("0")[0] { // Add south african country code (hardcoded for now) number = "+27" + number[1:] } return number } func FormatPhoneNumber(phoneNumber string) string { if len(phoneNumber) > 7 { return phoneNumber } // Format as 076 453 2188 phoneNumber = insertInto(phoneNumber, 3, " ") phoneNumber = insertInto(phoneNumber, 7, " ") return phoneNumber } func insertInto(s string, index int, characters string) string { return s[:index] + characters + s[index:] } func IsAlphaNumeric(str string) bool { regex := regexp.MustCompile("^[a-zA-Z0-9]*$") return regex.MatchString(str) } func IsAlphaNumericOrDash(str string) bool { regex := regexp.MustCompile("^[a-zA-Z0-9-]*$") return regex.MatchString(str) } func Equal(a string, b string) bool { return strings.TrimSpace(strings.ToLower(a)) == strings.TrimSpace(strings.ToLower(b)) } func UnwrapString(s *string) string { if s == nil { return "" } return *s } // TrimP trims specified strings, replacing empty string with nil func TrimP(sp *string) *string { if sp == nil { return nil } s := strings.TrimSpace(*sp) if s == "" { return nil } return &s } // ConcatP concatenates all specified non-empty strings with ", " separators func ConcatP(args ...*string) string { s := "" for _, arg := range args { if arg != nil && *arg != "" { if s != "" { s += ", " } s += *arg } } return s } func ToJSONString(object interface{}) (string, error) { jsonBytes, err := json.Marshal(&object) if err != nil { return "", err } return string(jsonBytes), nil } func Int64ToString(number int64) string { return strconv.FormatInt(number, 10) } func IntToString(number int) string { return strconv.Itoa(number) } func StringToInt(stringValue string) (int, error) { return strconv.Atoi(stringValue) } func Int64SliceToString(numbers []int64) string { numString := fmt.Sprint(numbers) numString = strings.Join(strings.Split(numString, " "), ",") return numString } func StringToInt64(stringValue string) (int64, error) { number, err := strconv.ParseInt(stringValue, 10, 64) return number, err } func StringToFloat64(stringValue string) (float64, error) { number, err := strconv.ParseFloat(stringValue, 64) if err != nil { return 0, err } return number, nil } func Float64ToString(number float64, precision int) string { return strconv.FormatFloat(number, 'f', precision, 64) } func Float64ToStringWithPrec(number float64, prec int) string { return strconv.FormatFloat(number, 'f', prec, 64) } func ValidateStringAsInt64(stringValue string) error { _, err := StringToInt64(stringValue) return err } func PtoString(stringPointer *string) string { if stringPointer == nil { return "" } return *stringPointer } func IsEmpty(sp *string) bool { if sp == nil { return true } return len(*sp) == 0 } // StringTrimQuotes - trims quotes from a string (ie: "foo" will return foo) func StringTrimQuotes(stringToTrim string) string { if len(stringToTrim) >= 2 { if stringToTrim[0] == '"' && stringToTrim[len(stringToTrim)-1] == '"' { return stringToTrim[1 : len(stringToTrim)-1] } } return stringToTrim } func KeyToHumanReadable(s string) string { s = strings.TrimSpace(s) re := regexp.MustCompile("(_|-)") s = re.ReplaceAllString(s, " ") return sentenceCase(string(s)) } func sentenceCase(str string) string { for i, v := range str { return string(unicode.ToUpper(v)) + str[i+1:] } return "" } // RemoveUrlScheme Removes http:// or https:// from a URL func RemoveUrlScheme(str string) string { newStr := strings.Replace(str, "http://", "", 1) newStr = strings.Replace(str, "https://", "", 1) return newStr } // EscapeOpenSearchSearchString See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#_reserved_characters func EscapeOpenSearchSearchString(str string) string { searchString := str // Reserved characters // NOTE: first char must be "\" to prevent replacing it again after replacing other chars with "\\" reservedCharacters := []string{"\\", "+", "-", "=", "&&", "||", "!", "(", ")", "{", "}", "[", "]", "^", "\"", "~", "*", "?", ":", "/"} // Remove "<" and ">" strings.ReplaceAll(searchString, "<", "") strings.ReplaceAll(searchString, ">", "") // Escape the reserved characters with double backslashes ("\\") for _, char := range reservedCharacters { if strings.Contains(searchString, char) { re := regexp.MustCompile(char) searchString = re.ReplaceAllString(searchString, "\\"+char) } } return searchString }