You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
334 lines
7.5 KiB
Go
334 lines
7.5 KiB
Go
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</%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
|
|
}
|