package nstr import ( "bytes" "encoding/json" "fmt" "strings" "text/template" "unicode" "unicode/utf8" ) // OrCond return s1 on cond is True, OR return s2. // Like: cond ? s1 : s2 func OrCond(cond bool, s1, s2 string) string { if cond { return s1 } return s2 } // OrElse return s OR orVal(new-value) on s is empty func OrElse(s, orVal string) string { if s != "" { return s } return orVal } // OrHandle return fn(s) on s is not empty. func OrHandle(s string, fn func(s string) string) string { if s != "" { return fn(s) } return s } // Valid return first not empty element. func Valid(ss ...string) string { for _, s := range ss { if s != "" { return s } } return "" } // Replaces replace multi strings // // pairs: {old1: new1, old2: new2, ...} // // Can also use: // // strings.NewReplacer("old1", "new1", "old2", "new2").Replace(str) func Replaces(str string, pairs map[string]string) string { return NewReplacer(pairs).Replace(str) } // NewReplacer instance func NewReplacer(pairs map[string]string) *strings.Replacer { ss := make([]string, len(pairs)*2) for old, newVal := range pairs { ss = append(ss, old, newVal) } return strings.NewReplacer(ss...) } // PrettyJSON get pretty Json string // Deprecated: please use fmtutil.PrettyJSON() or jsonutil.Pretty() instead it func PrettyJSON(v any) (string, error) { out, err := json.MarshalIndent(v, "", " ") return string(out), err } // RenderTemplate render text template func RenderTemplate(input string, data any, fns template.FuncMap, isFile ...bool) string { return RenderText(input, data, fns, isFile...) } // RenderText render text template func RenderText(input string, data any, fns template.FuncMap, isFile ...bool) string { t := template.New("simple-text") t.Funcs(template.FuncMap{ // don't escape content "raw": func(s string) string { return s }, "trim": func(s string) string { return strings.TrimSpace(s) }, // join strings "join": func(ss []string, sep string) string { return strings.Join(ss, sep) }, // lower first char "lcFirst": func(s string) string { return LowerFirst(s) }, // upper first char "upFirst": func(s string) string { return UpperFirst(s) }, }) // add custom template functions if len(fns) > 0 { t.Funcs(fns) } if len(isFile) > 0 && isFile[0] { template.Must(t.ParseFiles(input)) } else { template.Must(t.Parse(input)) } // use buffer receive rendered content buf := new(bytes.Buffer) if err := t.Execute(buf, data); err != nil { panic(err) } return buf.String() } // WrapTag for given string. func WrapTag(s, tag string) string { if s == "" { return s } return fmt.Sprintf("<%s>%s", tag, s, tag) } // SplitSame split string into strings with the same type of unicode character // example: user-define -> [user,define] func SplitSame(s string, upperCase bool) []string { var runes [][]rune lastCharType := 0 charType := 0 // split into fields based on type of unicode character for _, r := range s { switch true { case RuneIsLower(r): charType = 1 case RuneIsUpper(r): charType = 2 case RuneIsDigit(r): charType = 3 default: charType = 4 } if charType == lastCharType { runes[len(runes)-1] = append(runes[len(runes)-1], r) } else { runes = append(runes, []rune{r}) } lastCharType = charType } for i := 0; i < len(runes)-1; i++ { if RuneIsUpper(runes[i][0]) && RuneIsLower(runes[i+1][0]) { runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...) runes[i] = runes[i][:len(runes[i])-1] } } // filter all none letters and none digit var result []string for _, rs := range runes { if len(rs) > 0 && (unicode.IsLetter(rs[0]) || RuneIsDigit(rs[0])) { if upperCase { result = append(result, string(RuneToUpperAll(rs))) } else { result = append(result, string(RuneToLowerAll(rs))) } } } return result } // CamelCase coverts string to camelCase string. Non letters and numbers will be ignored. func CamelCase(s string) string { var builder strings.Builder strs := SplitSame(s, false) for i, str := range strs { if i == 0 { builder.WriteString(strings.ToLower(str)) } else { builder.WriteString(Capitalize(str)) } } return builder.String() } // Capitalize converts the first character of a string to upper case and the remaining to lower case. func Capitalize(s string) string { result := make([]rune, len(s)) for i, v := range s { if i == 0 { result[i] = unicode.ToUpper(v) } else { result[i] = unicode.ToLower(v) } } return string(result) } // KebabCase coverts string to kebab-case, non letters and numbers will be ignored. func KebabCase(s string) string { result := SplitSame(s, false) return strings.Join(result, "-") } // UpperKebabCase coverts string to upper KEBAB-CASE, non letters and numbers will be ignored func UpperKebabCase(s string) string { result := SplitSame(s, true) return strings.Join(result, "-") } // SnakeCase coverts string to snake_case, non letters and numbers will be ignored func SnakeCase(s string) string { result := SplitSame(s, false) return strings.Join(result, "_") } // UpperSnakeCase coverts string to upper SNAKE_CASE, non letters and numbers will be ignored func UpperSnakeCase(s string) string { result := SplitSame(s, true) return strings.Join(result, "_") } // UpperFirst converts the first character of string to upper case. func UpperFirst(s string) string { if len(s) == 0 { return "" } r, size := utf8.DecodeRuneInString(s) r = unicode.ToUpper(r) return string(r) + s[size:] } // LowerFirst converts the first character of string to lower case. func LowerFirst(s string) string { if len(s) == 0 { return "" } r, size := utf8.DecodeRuneInString(s) r = unicode.ToLower(r) return string(r) + s[size:] } // Before returns the substring of the source string up to the first occurrence of the specified string. func Before(s, char string) string { if s == "" || char == "" { return s } i := strings.Index(s, char) return s[0:i] } // BeforeLast returns the substring of the source string up to the last occurrence of the specified string. func BeforeLast(s, char string) string { if s == "" || char == "" { return s } i := strings.LastIndex(s, char) return s[0:i] } // After returns the substring after the first occurrence of a specified string in the source string. func After(s, char string) string { if s == "" || char == "" { return s } i := strings.Index(s, char) return s[i+len(char):] } // AfterLast returns the substring after the last occurrence of a specified string in the source string. func AfterLast(s, char string) string { if s == "" || char == "" { return s } i := strings.LastIndex(s, char) return s[i+len(char):] } // Reverse returns string whose char order is reversed to the given string. func Reverse(s string) string { r := []rune(s) for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 { r[i], r[j] = r[j], r[i] } return string(r) } // Wrap a string with given string. func Wrap(str string, wrapWith string) string { if str == "" || wrapWith == "" { return str } var sb strings.Builder sb.WriteString(wrapWith) sb.WriteString(str) sb.WriteString(wrapWith) return sb.String() } // Unwrap a given string from another string. will change source string. func Unwrap(str string, wrapToken string) string { if str == "" || wrapToken == "" { return str } firstIndex := strings.Index(str, wrapToken) lastIndex := strings.LastIndex(str, wrapToken) if firstIndex == 0 && lastIndex > 0 && lastIndex <= len(str)-1 { if len(wrapToken) <= lastIndex { str = str[len(wrapToken):lastIndex] } } return str }