223 lines
4.6 KiB
Go
223 lines
4.6 KiB
Go
|
// string
|
||
|
package utils
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
func PadStringRight(s string, pad string, size int, suffix string) (paddedString string) {
|
||
|
if len(pad) == 0 {
|
||
|
pad = " "
|
||
|
}
|
||
|
space := size - len(s)
|
||
|
if space > 0 {
|
||
|
paddedString = s + strings.Repeat(pad, space)
|
||
|
} else {
|
||
|
paddedString = s
|
||
|
}
|
||
|
paddedString += ": %v"
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func UnescapeSpecialChars(source string, charSet, specialChars string) string {
|
||
|
var buf strings.Builder
|
||
|
var escape bool
|
||
|
for _, c := range source {
|
||
|
if c == '\\' {
|
||
|
if escape {
|
||
|
buf.WriteByte('\\')
|
||
|
escape = false
|
||
|
} else {
|
||
|
escape = true
|
||
|
}
|
||
|
} else {
|
||
|
if escape {
|
||
|
if index := strings.IndexRune(charSet, c); index >= 0 {
|
||
|
if index < len(specialChars) {
|
||
|
buf.WriteByte(specialChars[index])
|
||
|
}
|
||
|
}
|
||
|
escape = false
|
||
|
} else {
|
||
|
buf.WriteRune(c)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return buf.String()
|
||
|
}
|
||
|
|
||
|
func UnescapeBlanks(source string) string {
|
||
|
return UnescapeSpecialChars(source, "nrt", "\n\r\t")
|
||
|
}
|
||
|
|
||
|
func EscapeSpecialChars(source string, charSet string) string {
|
||
|
var buf strings.Builder
|
||
|
for _, c := range source {
|
||
|
if strings.IndexRune(charSet, c) >= 0 {
|
||
|
buf.WriteByte(92) // 92 = backslash
|
||
|
}
|
||
|
buf.WriteRune(c)
|
||
|
}
|
||
|
return buf.String()
|
||
|
}
|
||
|
|
||
|
func EscapeShellSpecialChars(source string) string {
|
||
|
return EscapeSpecialChars(source, "!`$\\()#~&*;")
|
||
|
}
|
||
|
|
||
|
func SplitPair(source, sep string) (left, right string) {
|
||
|
parts := strings.SplitN(source, sep, 2)
|
||
|
left = strings.TrimSpace(parts[0])
|
||
|
if len(parts) > 1 {
|
||
|
right = strings.TrimSpace(parts[1])
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func StartsWith(s string, aliases ...string) bool {
|
||
|
startsWith := false
|
||
|
for _, alias := range aliases {
|
||
|
if startsWith = strings.HasPrefix(alias, s); startsWith {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
return startsWith
|
||
|
}
|
||
|
|
||
|
func JoinStringMap(m map[string]string, kvSep, itemSep string) string {
|
||
|
var sb strings.Builder
|
||
|
for key, value := range m {
|
||
|
if sb.Len() > 0 {
|
||
|
sb.WriteString(itemSep)
|
||
|
}
|
||
|
sb.WriteString(key)
|
||
|
sb.WriteString(kvSep)
|
||
|
sb.WriteString(value)
|
||
|
}
|
||
|
return sb.String()
|
||
|
}
|
||
|
|
||
|
func JoinMap(m map[string]any, kvSep, itemSep string) string {
|
||
|
var sb strings.Builder
|
||
|
for key, value := range m {
|
||
|
if sb.Len() > 0 {
|
||
|
sb.WriteString(itemSep)
|
||
|
}
|
||
|
sb.WriteString(key)
|
||
|
sb.WriteString(kvSep)
|
||
|
if s, ok := value.(fmt.Stringer); ok {
|
||
|
sb.WriteString(s.String())
|
||
|
} else {
|
||
|
sb.WriteString(fmt.Sprintf("%v", value))
|
||
|
}
|
||
|
}
|
||
|
return sb.String()
|
||
|
}
|
||
|
|
||
|
func SplitByRune(s string, sep rune, maxParts int) (parts []string) {
|
||
|
return ScopedSplitByRune(s, sep, `'"`, `'"`, maxParts)
|
||
|
// var capacity int = 0
|
||
|
// var sb strings.Builder
|
||
|
// if maxParts < 0 {
|
||
|
// for _, b := range s {
|
||
|
// if b == sep {
|
||
|
// capacity++
|
||
|
// }
|
||
|
// }
|
||
|
// capacity++
|
||
|
// } else {
|
||
|
// capacity = max(1, maxParts)
|
||
|
// }
|
||
|
// parts = make([]string, 0, capacity)
|
||
|
|
||
|
// count := 0
|
||
|
// quote := rune(0)
|
||
|
// for i, r := range s {
|
||
|
// if r == sep && quote == rune(0) {
|
||
|
// if len(parts) < capacity {
|
||
|
// count++
|
||
|
// if count == maxParts {
|
||
|
// sb.WriteString(s[i:])
|
||
|
// }
|
||
|
// parts = append(parts, sb.String())
|
||
|
// sb.Reset()
|
||
|
// if count == maxParts {
|
||
|
// break
|
||
|
// }
|
||
|
// }
|
||
|
// } else if r == '\'' || r == '"' {
|
||
|
// if quote == r {
|
||
|
// quote = rune(0)
|
||
|
// } else {
|
||
|
// quote = r
|
||
|
// }
|
||
|
// sb.WriteRune(r)
|
||
|
// } else {
|
||
|
// sb.WriteRune(r)
|
||
|
// }
|
||
|
// }
|
||
|
// if maxParts < 0 {
|
||
|
// parts = append(parts, sb.String())
|
||
|
// } else {
|
||
|
// for ; count < maxParts; count++ {
|
||
|
// parts = append(parts, sb.String())
|
||
|
// sb.Reset()
|
||
|
// }
|
||
|
// }
|
||
|
// return parts
|
||
|
}
|
||
|
|
||
|
func ScopedSplitByRune(s string, sep rune, scopeOpeners, scopeClosers string, maxParts int) (parts []string) {
|
||
|
var capacity int = 0
|
||
|
var sb strings.Builder
|
||
|
|
||
|
if len(scopeClosers) != len(scopeOpeners) {
|
||
|
panic("scope openers and closers must have the same length")
|
||
|
}
|
||
|
|
||
|
if maxParts < 0 {
|
||
|
capacity = strings.Count(s, string(sep)) + 1
|
||
|
} else {
|
||
|
capacity = max(1, maxParts)
|
||
|
}
|
||
|
|
||
|
parts = make([]string, 0, capacity)
|
||
|
|
||
|
count := 0
|
||
|
scopeIndex := -1
|
||
|
for i, r := range s {
|
||
|
if r == sep && scopeIndex < 0 {
|
||
|
if len(parts) < capacity {
|
||
|
count++
|
||
|
if count == maxParts {
|
||
|
sb.WriteString(s[i:])
|
||
|
}
|
||
|
parts = append(parts, sb.String())
|
||
|
sb.Reset()
|
||
|
if count == maxParts {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
if scopeIndex < 0 {
|
||
|
if quoteIndex := strings.IndexRune(scopeOpeners, r); quoteIndex >= 0 {
|
||
|
scopeIndex = quoteIndex
|
||
|
}
|
||
|
} else if quoteIndex := strings.IndexRune(scopeClosers, r); quoteIndex >= 0 {
|
||
|
scopeIndex = -1
|
||
|
}
|
||
|
sb.WriteRune(r)
|
||
|
}
|
||
|
}
|
||
|
if maxParts < 0 {
|
||
|
parts = append(parts, sb.String())
|
||
|
} else {
|
||
|
for ; count < maxParts; count++ {
|
||
|
parts = append(parts, sb.String())
|
||
|
sb.Reset()
|
||
|
}
|
||
|
}
|
||
|
return parts
|
||
|
}
|