utils/string.go

226 lines
4.6 KiB
Go
Raw Permalink Normal View History

// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
2024-02-16 15:51:28 +01:00
// 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
}