switch to go vendoring

This commit is contained in:
Michael Barz
2023-04-19 20:24:34 +02:00
parent 632fa05ef9
commit afc6ed1e41
8527 changed files with 3004916 additions and 2 deletions
+192
View File
@@ -0,0 +1,192 @@
# String Util
This is a go string operate util package.
- Github: https://github.com/gookit/goutil/strutil
- GoDoc: https://godoc.org/github.com/gookit/goutil/strutil
## Install
```bash
go get github.com/gookit/goutil/strutil
```
## Usage
```go
ss := strutil.ToArray("a,b,c", ",")
// Output: []string{"a", "b", "c"}
ints, err := strutil.ToIntSlice("1,2,3")
// Output: []int{1, 2, 3}
```
## Functions
```go
func AddSlashes(s string) string
func AnyToString(val interface{}, defaultAsErr bool) (str string, err error)
func B32Decode(str string) string
func B32Encode(str string) string
func B64Decode(str string) string
func B64Encode(str string) string
func Base64(str string) string
func Bool(s string) (bool, error)
func Byte2str(b []byte) string
func Byte2string(b []byte) string
func BytePos(s string, bt byte) int
func Camel(s string, sep ...string) string
func CamelCase(s string, sep ...string) string
func Compare(s1, s2, op string) bool
func Cut(s, sep string) (before string, after string, found bool)
func EscapeHTML(s string) string
func EscapeJS(s string) string
func FilterEmail(s string) string
func GenMd5(src interface{}) string
func HasAllSubs(s string, subs []string) bool
func HasOnePrefix(s string, prefixes []string) bool
func HasOneSub(s string, subs []string) bool
func HasPrefix(s string, prefix string) bool
func HasSuffix(s string, suffix string) bool
func Implode(sep string, ss ...string) string
func Indent(s, prefix string) string
func IndentBytes(b, prefix []byte) []byte
func Int(s string) (int, error)
func Int64(s string) int64
func Int64OrErr(s string) (int64, error)
func Int64OrPanic(s string) int64
func IntOrPanic(s string) int
func Ints(s string, sep ...string) []int
func IsAlphaNum(c uint8) bool
func IsAlphabet(char uint8) bool
func IsBlank(s string) bool
func IsBlankBytes(bs []byte) bool
func IsEmpty(s string) bool
func IsEndOf(s, suffix string) bool
func IsNotBlank(s string) bool
func IsNumChar(c byte) bool
func IsNumeric(s string) bool
func IsSpace(c byte) bool
func IsSpaceRune(r rune) bool
func IsStartOf(s, prefix string) bool
func IsStartsOf(s string, prefixes []string) bool
func IsSymbol(r rune) bool
func IsValidUtf8(s string) bool
func IsVersion(s string) bool
func Join(sep string, ss ...string) string
func JoinList(sep string, ss []string) string
func LTrim(s string, cutSet ...string) string
func Lower(s string) string
func LowerFirst(s string) string
func Lowercase(s string) string
func Ltrim(s string, cutSet ...string) string
func MD5(src interface{}) string
func Md5(src interface{}) string
func MicroTimeHexID() string
func MicroTimeID() string
func MustBool(s string) bool
func MustCut(s, sep string) (before string, after string)
func MustInt(s string) int
func MustInt64(s string) int64
func MustString(in interface{}) string
func MustToTime(s string, layouts ...string) time.Time
func NoCaseEq(s, t string) bool
func PadLeft(s, pad string, length int) string
func PadRight(s, pad string, length int) string
func Padding(s, pad string, length int, pos uint8) string
func PrettyJSON(v interface{}) (string, error)
func QuietBool(s string) bool
func QuietInt(s string) int
func QuietInt64(s string) int64
func QuietString(in interface{}) string
func Quote(s string) string
func RTrim(s string, cutSet ...string) string
func RandomBytes(length int) ([]byte, error)
func RandomChars(ln int) string
func RandomCharsV2(ln int) string
func RandomCharsV3(ln int) string
func RandomString(length int) (string, error)
func RenderTemplate(input string, data interface{}, fns template.FuncMap, isFile ...bool) string
func RenderText(input string, data interface{}, fns template.FuncMap, isFile ...bool) string
func Repeat(s string, times int) string
func RepeatBytes(char byte, times int) (chars []byte)
func RepeatRune(char rune, times int) (chars []rune)
func Replaces(str string, pairs map[string]string) string
func Rtrim(s string, cutSet ...string) string
func RuneCount(s string) int
func RuneIsLower(c rune) bool
func RuneIsUpper(c rune) bool
func RuneIsWord(c rune) bool
func RunePos(s string, ru rune) int
func RuneWidth(r rune) int
func Similarity(s, t string, rate float32) (float32, bool)
func SnakeCase(s string, sep ...string) string
func Split(s, sep string) (ss []string)
func SplitInlineComment(val string) (string, string)
func SplitN(s, sep string, n int) (ss []string)
func SplitNTrimmed(s, sep string, n int) (ss []string)
func SplitNValid(s, sep string, n int) (ss []string)
func SplitTrimmed(s, sep string) (ss []string)
func SplitValid(s, sep string) (ss []string)
func StrPos(s, sub string) int
func String(val interface{}) (string, error)
func StringOrErr(val interface{}) (string, error)
func Strings(s string, sep ...string) []string
func StripSlashes(s string) string
func Substr(s string, pos, length int) string
func TextSplit(s string, w int) []string
func TextTruncate(s string, w int, tail string) string
func TextWidth(s string) int
func TextWrap(s string, w int) string
func Title(s string) string
func ToArray(s string, sep ...string) []string
func ToBool(s string) (bool, error)
func ToBytes(s string) (b []byte)
func ToDuration(s string) (time.Duration, error)
func ToInt(s string) (int, error)
func ToInt64(s string) (int64, error)
func ToIntSlice(s string, sep ...string) (ints []int, err error)
func ToInts(s string, sep ...string) ([]int, error)
func ToSlice(s string, sep ...string) []string
func ToString(val interface{}) (string, error)
func ToStrings(s string, sep ...string) []string
func ToTime(s string, layouts ...string) (t time.Time, err error)
func Trim(s string, cutSet ...string) string
func TrimCut(s, sep string) (string, string)
func TrimLeft(s string, cutSet ...string) string
func TrimRight(s string, cutSet ...string) string
func URLDecode(s string) string
func URLEncode(s string) string
func Unquote(s string) string
func Upper(s string) string
func UpperFirst(s string) string
func UpperWord(s string) string
func Uppercase(s string) string
func Utf8Len(s string) int
func Utf8Split(s string, w int) []string
func Utf8Truncate(s string, w int, tail string) string
func Utf8Width(s string) (size int)
func Utf8len(s string) int
func VersionCompare(v1, v2, op string) bool
func WidthWrap(s string, w int) string
func WrapTag(s, tag string) string
```
## Code Check & Testing
```bash
gofmt -w -l ./
golint ./...
```
**Testing**:
```shell
go test -v ./strutil/...
```
**Test limit by regexp**:
```shell
go test -v -run ^TestSetByKeys ./strutil/...
```
+128
View File
@@ -0,0 +1,128 @@
package strutil
import (
"bytes"
"fmt"
"strings"
)
// Buffer wrap and extends the bytes.Buffer
type Buffer struct {
bytes.Buffer
}
// NewBuffer instance
func NewBuffer() *Buffer {
return &Buffer{}
}
// WriteAny type value to buffer
func (b *Buffer) WriteAny(vs ...any) {
for _, v := range vs {
_, _ = b.Buffer.WriteString(fmt.Sprint(v))
}
}
// QuietWriteByte to buffer
func (b *Buffer) QuietWriteByte(c byte) {
_ = b.WriteByte(c)
}
// QuietWritef write message to buffer
func (b *Buffer) QuietWritef(tpl string, vs ...any) {
_, _ = b.WriteString(fmt.Sprintf(tpl, vs...))
}
// Writeln write message to buffer with newline
func (b *Buffer) Writeln(ss ...string) {
b.QuietWriteln(ss...)
}
// QuietWriteln write message to buffer with newline
func (b *Buffer) QuietWriteln(ss ...string) {
_, _ = b.WriteString(strings.Join(ss, ""))
_ = b.WriteByte('\n')
}
// QuietWriteString to buffer
func (b *Buffer) QuietWriteString(ss ...string) {
_, _ = b.WriteString(strings.Join(ss, ""))
}
// MustWriteString to buffer
func (b *Buffer) MustWriteString(ss ...string) {
_, err := b.WriteString(strings.Join(ss, ""))
if err != nil {
panic(err)
}
}
// ResetAndGet buffer string.
func (b *Buffer) ResetAndGet() string {
s := b.String()
b.Reset()
return s
}
// ByteChanPool struct
//
// Usage:
//
// bp := strutil.NewByteChanPool(500, 1024, 1024)
// buf:=bp.Get()
// defer bp.Put(buf)
// // use buf do something ...
//
// refer https://www.flysnow.org/2020/08/21/golang-chan-byte-pool.html
// from https://github.com/minio/minio/blob/master/internal/bpool/bpool.go
type ByteChanPool struct {
c chan []byte
w int
wcap int
}
// NewByteChanPool instance
func NewByteChanPool(maxSize int, width int, capWidth int) *ByteChanPool {
return &ByteChanPool{
c: make(chan []byte, maxSize),
w: width,
wcap: capWidth,
}
}
// Get gets a []byte from the BytePool, or creates a new one if none are
// available in the pool.
func (bp *ByteChanPool) Get() (b []byte) {
select {
case b = <-bp.c:
// reuse existing buffer
default:
// create new buffer
if bp.wcap > 0 {
b = make([]byte, bp.w, bp.wcap)
} else {
b = make([]byte, bp.w)
}
}
return
}
// Put returns the given Buffer to the BytePool.
func (bp *ByteChanPool) Put(b []byte) {
select {
case bp.c <- b:
// buffer went back into pool
default:
// buffer didn't go back into pool, just discard
}
}
// Width returns the width of the byte arrays in this pool.
func (bp *ByteChanPool) Width() (n int) {
return bp.w
}
// WidthCap returns the cap width of the byte arrays in this pool.
func (bp *ByteChanPool) WidthCap() (n int) {
return bp.wcap
}
+175
View File
@@ -0,0 +1,175 @@
package strutil
import (
"regexp"
"strings"
"unicode"
"unicode/utf8"
)
// Equal check, alias of strings.EqualFold
var Equal = strings.EqualFold
// NoCaseEq check two strings is equals and case-insensitivity
func NoCaseEq(s, t string) bool {
return strings.EqualFold(s, t)
}
// IsNumChar returns true if the given character is a numeric, otherwise false.
func IsNumChar(c byte) bool {
return c >= '0' && c <= '9'
}
var numReg = regexp.MustCompile(`^\d+$`)
// IsNumeric returns true if the given string is a numeric, otherwise false.
func IsNumeric(s string) bool {
return numReg.MatchString(s)
}
// IsAlphabet char
func IsAlphabet(char uint8) bool {
// A 65 -> Z 90
if char >= 'A' && char <= 'Z' {
return true
}
// a 97 -> z 122
if char >= 'a' && char <= 'z' {
return true
}
return false
}
// IsAlphaNum reports whether the byte is an ASCII letter, number, or underscore
func IsAlphaNum(c uint8) bool {
return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
}
// StrPos alias of the strings.Index
func StrPos(s, sub string) int {
return strings.Index(s, sub)
}
// BytePos alias of the strings.IndexByte
func BytePos(s string, bt byte) int {
return strings.IndexByte(s, bt)
}
// HasOneSub substr in the given string.
func HasOneSub(s string, subs []string) bool {
for _, sub := range subs {
if strings.Contains(s, sub) {
return true
}
}
return false
}
// HasAllSubs all substr in the given string.
func HasAllSubs(s string, subs []string) bool {
for _, sub := range subs {
if !strings.Contains(s, sub) {
return false
}
}
return true
}
// IsStartsOf alias of the HasOnePrefix
func IsStartsOf(s string, prefixes []string) bool {
return HasOnePrefix(s, prefixes)
}
// HasOnePrefix the string start withs one of the subs
func HasOnePrefix(s string, prefixes []string) bool {
for _, prefix := range prefixes {
if strings.HasPrefix(s, prefix) {
return true
}
}
return false
}
// HasPrefix substr in the given string.
func HasPrefix(s string, prefix string) bool { return strings.HasPrefix(s, prefix) }
// IsStartOf alias of the strings.HasPrefix
func IsStartOf(s, prefix string) bool { return strings.HasPrefix(s, prefix) }
// HasSuffix substr in the given string.
func HasSuffix(s string, suffix string) bool { return strings.HasSuffix(s, suffix) }
// IsEndOf alias of the strings.HasSuffix
func IsEndOf(s, suffix string) bool { return strings.HasSuffix(s, suffix) }
// IsValidUtf8 valid utf8 string check
func IsValidUtf8(s string) bool { return utf8.ValidString(s) }
// ----- refer from github.com/yuin/goldmark/util
// refer from github.com/yuin/goldmark/util
var spaceTable = [256]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
// IsSpace returns true if the given character is a space, otherwise false.
func IsSpace(c byte) bool {
return spaceTable[c] == 1
}
// IsEmpty returns true if the given string is empty.
func IsEmpty(s string) bool { return len(s) == 0 }
// IsBlank returns true if the given string is all space characters.
func IsBlank(s string) bool {
return IsBlankBytes([]byte(s))
}
// IsNotBlank returns true if the given string is not blank.
func IsNotBlank(s string) bool {
return !IsBlankBytes([]byte(s))
}
// IsBlankBytes returns true if the given []byte is all space characters.
func IsBlankBytes(bs []byte) bool {
for _, b := range bs {
if !IsSpace(b) {
return false
}
}
return true
}
// IsSymbol reports whether the rune is a symbolic character.
func IsSymbol(r rune) bool {
return unicode.IsSymbol(r)
}
var verRegex = regexp.MustCompile(`^[0-9][\d.]+(-\w+)?$`)
// IsVersion number. eg: 1.2.0
func IsVersion(s string) bool {
return verRegex.MatchString(s)
}
// Compare for two string.
func Compare(s1, s2, op string) bool {
return VersionCompare(s1, s2, op)
}
// VersionCompare for two version string.
func VersionCompare(v1, v2, op string) bool {
switch op {
case ">", "gt":
return v1 > v2
case "<", "lt":
return v1 < v2
case ">=", "gte":
return v1 >= v2
case "<=", "lte":
return v1 <= v2
case "!=", "ne", "neq":
return v1 != v2
default: // eq
return v1 == v2
}
}
+420
View File
@@ -0,0 +1,420 @@
package strutil
import (
"encoding/json"
"errors"
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
"time"
"unsafe"
"github.com/gookit/goutil/comdef"
"github.com/gookit/goutil/internal/comfunc"
"github.com/gookit/goutil/mathutil"
)
var (
ErrDateLayout = errors.New("invalid date layout string")
ErrInvalidParam = errors.New("invalid input parameter")
// some regex for convert string.
toSnakeReg = regexp.MustCompile("[A-Z][a-z]")
toCamelRegs = map[string]*regexp.Regexp{
" ": regexp.MustCompile(" +[a-zA-Z]"),
"-": regexp.MustCompile("-+[a-zA-Z]"),
"_": regexp.MustCompile("_+[a-zA-Z]"),
}
)
// Internal func refers:
// strconv.QuoteRune()
// strconv.QuoteToASCII()
// strconv.AppendQuote()
// strconv.AppendQuoteRune()
// Quote alias of strings.Quote
func Quote(s string) string { return strconv.Quote(s) }
// Unquote remove start and end quotes by single-quote or double-quote
func Unquote(s string) string {
ln := len(s)
if ln < 2 {
return s
}
qs, qe := s[0], s[ln-1]
var valid bool
if qs == '"' && qe == '"' {
valid = true
} else if qs == '\'' && qe == '\'' {
valid = true
}
if valid {
s = s[1 : ln-1] // exclude quotes
}
// strconv.Unquote cannot unquote single-quote
// if ns, err := strconv.Unquote(s); err == nil {
// return ns
// }
return s
}
// Join alias of strings.Join
func Join(sep string, ss ...string) string { return strings.Join(ss, sep) }
// JoinList alias of strings.Join
func JoinList(sep string, ss []string) string { return strings.Join(ss, sep) }
// Implode alias of strings.Join
func Implode(sep string, ss ...string) string { return strings.Join(ss, sep) }
/*************************************************************
* convert value to string
*************************************************************/
// String convert val to string
func String(val any) (string, error) {
return AnyToString(val, true)
}
// QuietString convert value to string, will ignore error
func QuietString(in any) string {
val, _ := AnyToString(in, false)
return val
}
// MustString convert value to string, TODO will panic on error
func MustString(in any) string {
val, _ := AnyToString(in, false)
return val
}
// StringOrErr convert value to string, return error on failed
func StringOrErr(val any) (string, error) {
return AnyToString(val, true)
}
// ToString convert value to string
func ToString(val any) (string, error) {
return AnyToString(val, true)
}
// AnyToString convert value to string.
//
// if defaultAsErr:
//
// False will use fmt.Sprint convert complex type
// True will return error on fail.
func AnyToString(val any, defaultAsErr bool) (str string, err error) {
if val == nil {
return
}
switch value := val.(type) {
case int:
str = strconv.Itoa(value)
case int8:
str = strconv.Itoa(int(value))
case int16:
str = strconv.Itoa(int(value))
case int32: // same as `rune`
str = strconv.Itoa(int(value))
case int64:
str = strconv.FormatInt(value, 10)
case uint:
str = strconv.FormatUint(uint64(value), 10)
case uint8:
str = strconv.FormatUint(uint64(value), 10)
case uint16:
str = strconv.FormatUint(uint64(value), 10)
case uint32:
str = strconv.FormatUint(uint64(value), 10)
case uint64:
str = strconv.FormatUint(value, 10)
case float32:
str = strconv.FormatFloat(float64(value), 'f', -1, 32)
case float64:
str = strconv.FormatFloat(value, 'f', -1, 64)
case bool:
str = strconv.FormatBool(value)
case string:
str = value
case []byte:
str = string(value)
case time.Duration:
str = value.String()
case json.Number:
str = value.String()
default:
if defaultAsErr {
err = comdef.ErrConvType
} else {
str = fmt.Sprint(value)
}
}
return
}
/*************************************************************
* convert string value to byte
* refer from https://github.com/valyala/fastjson/blob/master/util.go
*************************************************************/
// Byte2str convert bytes to string
func Byte2str(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
// Byte2string convert bytes to string
func Byte2string(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
// ToBytes convert string to bytes
func ToBytes(s string) (b []byte) {
strh := (*reflect.StringHeader)(unsafe.Pointer(&s))
sh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
sh.Data = strh.Data
sh.Len = strh.Len
sh.Cap = strh.Len
return b
}
/*************************************************************
* convert string value to bool
*************************************************************/
// ToBool convert string to bool
func ToBool(s string) (bool, error) {
return comfunc.StrToBool(s)
}
// QuietBool convert to bool, will ignore error
func QuietBool(s string) bool {
val, _ := comfunc.StrToBool(strings.TrimSpace(s))
return val
}
// MustBool convert, will panic on error
func MustBool(s string) bool {
val, _ := comfunc.StrToBool(strings.TrimSpace(s))
return val
}
// Bool parse string to bool. like strconv.ParseBool()
func Bool(s string) (bool, error) {
return comfunc.StrToBool(s)
}
/*************************************************************
* convert string value to int, float
*************************************************************/
// Int convert string to int, alias of ToInt()
func Int(s string) (int, error) {
return strconv.Atoi(strings.TrimSpace(s))
}
// ToInt convert string to int, return error on fail
func ToInt(s string) (int, error) {
return strconv.Atoi(strings.TrimSpace(s))
}
// QuietInt convert string to int, will ignore error
func QuietInt(s string) int {
val, _ := ToInt(s)
return val
}
// MustInt convert string to int, will panic on error
func MustInt(s string) int {
val, _ := ToInt(s)
return val
}
// IntOrPanic convert value to int, will panic on error
func IntOrPanic(s string) int {
val, err := ToInt(s)
if err != nil {
panic(err)
}
return val
}
// Int64 convert string to int, will ignore error
func Int64(s string) int64 {
val, _ := Int64OrErr(s)
return val
}
// QuietInt64 convert string to int, will ignore error
func QuietInt64(s string) int64 {
val, _ := Int64OrErr(s)
return val
}
// ToInt64 convert string to int, return error on fail
func ToInt64(s string) (int64, error) {
return strconv.ParseInt(s, 10, 0)
}
// Int64OrErr convert string to int, return error on fail
func Int64OrErr(s string) (int64, error) {
return strconv.ParseInt(s, 10, 0)
}
// MustInt64 convert value to int, will panic on error
func MustInt64(s string) int64 {
return Int64OrPanic(s)
}
// Int64OrPanic convert value to int, will panic on error
func Int64OrPanic(s string) int64 {
val, err := strconv.ParseInt(s, 10, 0)
if err != nil {
panic(err)
}
return val
}
/*************************************************************
* convert string value to int/string slice, time.Time
*************************************************************/
// Ints alias of the ToIntSlice(). default sep is comma(,)
func Ints(s string, sep ...string) []int {
ints, _ := ToIntSlice(s, sep...)
return ints
}
// ToInts alias of the ToIntSlice(). default sep is comma(,)
func ToInts(s string, sep ...string) ([]int, error) { return ToIntSlice(s, sep...) }
// ToIntSlice split string to slice and convert item to int.
//
// Default sep is comma(,)
func ToIntSlice(s string, sep ...string) (ints []int, err error) {
ss := ToSlice(s, sep...)
for _, item := range ss {
iVal, err := mathutil.ToInt(item)
if err != nil {
return []int{}, err
}
ints = append(ints, iVal)
}
return
}
// ToArray alias of the ToSlice()
func ToArray(s string, sep ...string) []string { return ToSlice(s, sep...) }
// Strings alias of the ToSlice()
func Strings(s string, sep ...string) []string { return ToSlice(s, sep...) }
// ToStrings alias of the ToSlice()
func ToStrings(s string, sep ...string) []string { return ToSlice(s, sep...) }
// ToSlice split string to array.
func ToSlice(s string, sep ...string) []string {
if len(sep) > 0 {
return Split(s, sep[0])
}
return Split(s, ",")
}
// ToOSArgs split string to string[](such as os.Args)
// func ToOSArgs(s string) []string {
// return cliutil.StringToOSArgs(s) // error: import cycle not allowed
// }
// MustToTime convert date string to time.Time
func MustToTime(s string, layouts ...string) time.Time {
t, err := ToTime(s, layouts...)
if err != nil {
panic(err)
}
return t
}
// auto match use some commonly layouts.
// key is layout length.
var layoutMap = map[int][]string{
6: {"200601", "060102", time.Kitchen},
8: {"20060102"},
10: {"2006-01-02"},
13: {"2006-01-02 15"},
15: {time.Stamp},
16: {"2006-01-02 15:04"},
19: {"2006-01-02 15:04:05", time.RFC822, time.StampMilli},
20: {"2006-01-02 15:04:05Z"},
21: {time.RFC822Z},
22: {time.StampMicro},
24: {time.ANSIC},
25: {time.RFC3339, time.StampNano},
// 26: {time.Layout}, // must go >= 1.19
28: {time.UnixDate},
29: {time.RFC1123},
30: {time.RFC850},
31: {time.RFC1123Z},
35: {time.RFC3339Nano},
}
// ToTime convert date string to time.Time
func ToTime(s string, layouts ...string) (t time.Time, err error) {
// custom layout
if len(layouts) > 0 {
if len(layouts[0]) > 0 {
return time.Parse(layouts[0], s)
}
err = ErrDateLayout
return
}
// auto match use some commonly layouts.
strLn := len(s)
maybeLayouts, ok := layoutMap[strLn]
if !ok {
err = ErrInvalidParam
return
}
var hasAlphaT bool
if pos := strings.IndexByte(s, 'T'); pos > 0 && pos < 12 {
hasAlphaT = true
}
hasSlashR := strings.IndexByte(s, '/') > 0
for _, layout := range maybeLayouts {
// date string has "T". eg: "2006-01-02T15:04:05"
if hasAlphaT {
layout = strings.Replace(layout, " ", "T", 1)
}
// date string has "/". eg: "2006/01/02 15:04:05"
if hasSlashR {
layout = strings.Replace(layout, "-", "/", -1)
}
t, err = time.Parse(layout, s)
if err == nil {
return
}
}
// t, err = time.ParseInLocation(layout, s, time.Local)
return
}
// ToDuration parses a duration string. such as "300ms", "-1.5h" or "2h45m".
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
func ToDuration(s string) (time.Duration, error) {
return time.ParseDuration(s)
}
+202
View File
@@ -0,0 +1,202 @@
package strutil
import (
"bytes"
"crypto/md5"
"encoding/base32"
"encoding/base64"
"encoding/hex"
"fmt"
"net/url"
"strings"
"text/template"
)
//
// -------------------- escape --------------------
//
// EscapeJS escape javascript string
func EscapeJS(s string) string {
return template.JSEscapeString(s)
}
// EscapeHTML escape html string
func EscapeHTML(s string) string {
return template.HTMLEscapeString(s)
}
// AddSlashes add slashes for the string.
func AddSlashes(s string) string {
if ln := len(s); ln == 0 {
return ""
}
var buf bytes.Buffer
for _, char := range s {
switch char {
case '\'', '"', '\\':
buf.WriteRune('\\')
}
buf.WriteRune(char)
}
return buf.String()
}
// StripSlashes strip slashes for the string.
func StripSlashes(s string) string {
ln := len(s)
if ln == 0 {
return ""
}
var skip bool
var buf bytes.Buffer
for i, char := range s {
if skip {
skip = false
} else if char == '\\' {
if i+1 < ln && s[i+1] == '\\' {
skip = true
}
continue
}
buf.WriteRune(char)
}
return buf.String()
}
//
// -------------------- encode --------------------
//
// Md5 Generate a 32-bit md5 string
func Md5(src any) string {
return hex.EncodeToString(Md5Bytes(src))
}
// MD5 Generate a 32-bit md5 string
func MD5(src any) string { return Md5(src) }
// GenMd5 Generate a 32-bit md5 string
func GenMd5(src any) string { return Md5(src) }
// Md5Bytes Generate a 32-bit md5 bytes
func Md5Bytes(src any) []byte {
h := md5.New()
if s, ok := src.(string); ok {
h.Write([]byte(s))
} else {
h.Write([]byte(fmt.Sprint(src)))
}
return h.Sum(nil)
}
// URLEncode encode url string.
func URLEncode(s string) string {
if pos := strings.IndexRune(s, '?'); pos > -1 { // escape query data
return s[0:pos+1] + url.QueryEscape(s[pos+1:])
}
return s
}
// URLDecode decode url string.
func URLDecode(s string) string {
if pos := strings.IndexRune(s, '?'); pos > -1 { // un-escape query data
qy, err := url.QueryUnescape(s[pos+1:])
if err == nil {
return s[0:pos+1] + qy
}
}
return s
}
//
// -------------------- base encode --------------------
//
// B32Encode base32 encode
func B32Encode(str string) string {
return base32.StdEncoding.EncodeToString([]byte(str))
}
// B32Decode base32 decode
func B32Decode(str string) string {
dec, _ := base32.StdEncoding.DecodeString(str)
return string(dec)
}
// B64Encode base64 encode
func B64Encode(str string) string {
return base64.StdEncoding.EncodeToString([]byte(str))
}
// B64EncodeBytes base64 encode
func B64EncodeBytes(src []byte) []byte {
buf := make([]byte, base64.StdEncoding.EncodedLen(len(src)))
base64.StdEncoding.Encode(buf, src)
return buf
}
// B64Decode base64 decode
func B64Decode(str string) string {
dec, _ := base64.StdEncoding.DecodeString(str)
return string(dec)
}
// B64DecodeBytes base64 decode
func B64DecodeBytes(str string) []byte {
dbuf := make([]byte, base64.StdEncoding.DecodedLen(len(str)))
n, _ := base64.StdEncoding.Decode(dbuf, []byte(str))
return dbuf[:n]
}
// BaseEncoder interface
type BaseEncoder interface {
Encode(dst []byte, src []byte)
EncodeToString(src []byte) string
Decode(dst []byte, src []byte) (n int, err error)
DecodeString(s string) ([]byte, error)
}
// BaseType for base encoding
type BaseType uint8
// types for base encoding
const (
BaseTypeStd BaseType = iota
BaseTypeHex
BaseTypeURL
BaseTypeRawStd
BaseTypeRawURL
)
// Encoding instance
func Encoding(base int, typ BaseType) BaseEncoder {
if base == 32 {
switch typ {
case BaseTypeHex:
return base32.HexEncoding
default:
return base32.StdEncoding
}
}
// base 64
switch typ {
case BaseTypeURL:
return base64.URLEncoding
case BaseTypeRawURL:
return base64.RawURLEncoding
case BaseTypeRawStd:
return base64.RawStdEncoding
default:
return base64.StdEncoding
}
}
+69
View File
@@ -0,0 +1,69 @@
package strutil
import "strings"
/*************************************************************
* String filtering
*************************************************************/
// Trim string. if cutSet is empty, will trim SPACE.
func Trim(s string, cutSet ...string) string {
if ln := len(cutSet); ln > 0 && cutSet[0] != "" {
if ln == 1 {
return strings.Trim(s, cutSet[0])
}
return strings.Trim(s, strings.Join(cutSet, ""))
}
return strings.TrimSpace(s)
}
// Ltrim alias of TrimLeft
func Ltrim(s string, cutSet ...string) string { return TrimLeft(s, cutSet...) }
// LTrim alias of TrimLeft
func LTrim(s string, cutSet ...string) string { return TrimLeft(s, cutSet...) }
// TrimLeft char in the string. if cutSet is empty, will trim SPACE.
func TrimLeft(s string, cutSet ...string) string {
if ln := len(cutSet); ln > 0 && cutSet[0] != "" {
if ln == 1 {
return strings.TrimLeft(s, cutSet[0])
}
return strings.TrimLeft(s, strings.Join(cutSet, ""))
}
return strings.TrimLeft(s, " ")
}
// Rtrim alias of TrimRight
func Rtrim(s string, cutSet ...string) string { return TrimRight(s, cutSet...) }
// RTrim alias of TrimRight
func RTrim(s string, cutSet ...string) string { return TrimRight(s, cutSet...) }
// TrimRight char in the string. if cutSet is empty, will trim SPACE.
func TrimRight(s string, cutSet ...string) string {
if ln := len(cutSet); ln > 0 && cutSet[0] != "" {
if ln == 1 {
return strings.TrimRight(s, cutSet[0])
}
return strings.TrimRight(s, strings.Join(cutSet, ""))
}
return strings.TrimRight(s, " ")
}
// FilterEmail filter email, clear invalid chars.
func FilterEmail(s string) string {
s = strings.TrimSpace(s)
i := strings.LastIndex(s, "@")
if i == -1 {
return s
}
// According to rfc5321, "The local-part of a mailbox MUST BE treated as case sensitive"
return s[0:i] + "@" + strings.ToLower(s[i+1:])
}
+186
View File
@@ -0,0 +1,186 @@
package strutil
import (
"regexp"
"strings"
"unicode"
)
/*************************************************************
* change string case
*************************************************************/
// methods aliases
var (
UpWords = UpperWord
LoFirst = LowerFirst
UpFirst = UpperFirst
Snake = SnakeCase
)
// Title alias of the strings.ToTitle()
func Title(s string) string { return strings.ToTitle(s) }
// Lower alias of the strings.ToLower()
func Lower(s string) string { return strings.ToLower(s) }
// Lowercase alias of the strings.ToLower()
func Lowercase(s string) string { return strings.ToLower(s) }
// Upper alias of the strings.ToUpper()
func Upper(s string) string { return strings.ToUpper(s) }
// Uppercase alias of the strings.ToUpper()
func Uppercase(s string) string { return strings.ToUpper(s) }
// UpperWord Change the first character of each word to uppercase
func UpperWord(s string) string {
if len(s) == 0 {
return s
}
if len(s) == 1 {
return strings.ToUpper(s)
}
inWord := true
buf := make([]byte, 0, len(s))
i := 0
rs := []rune(s)
if RuneIsLower(rs[i]) {
buf = append(buf, []byte(string(unicode.ToUpper(rs[i])))...)
} else {
buf = append(buf, []byte(string(rs[i]))...)
}
for j := i + 1; j < len(rs); j++ {
if !RuneIsWord(rs[i]) && RuneIsWord(rs[j]) {
inWord = false
}
if RuneIsLower(rs[j]) && !inWord {
buf = append(buf, []byte(string(unicode.ToUpper(rs[j])))...)
inWord = true
} else {
buf = append(buf, []byte(string(rs[j]))...)
}
if RuneIsWord(rs[j]) {
inWord = true
}
i++
}
return string(buf)
}
// LowerFirst lower first char
func LowerFirst(s string) string {
if len(s) == 0 {
return s
}
rs := []rune(s)
f := rs[0]
if 'A' <= f && f <= 'Z' {
return string(unicode.ToLower(f)) + string(rs[1:])
}
return s
}
// UpperFirst upper first char
func UpperFirst(s string) string {
if len(s) == 0 {
return s
}
rs := []rune(s)
f := rs[0]
if 'a' <= f && f <= 'z' {
return string(unicode.ToUpper(f)) + string(rs[1:])
}
return s
}
// SnakeCase convert. eg "RangePrice" -> "range_price"
func SnakeCase(s string, sep ...string) string {
sepChar := "_"
if len(sep) > 0 {
sepChar = sep[0]
}
str := toSnakeReg.ReplaceAllStringFunc(s, func(s string) string {
return sepChar + LowerFirst(s)
})
return strings.TrimLeft(str, sepChar)
}
// Camel alias of the CamelCase
func Camel(s string, sep ...string) string { return CamelCase(s, sep...) }
// CamelCase convert string to camel case.
//
// Support:
//
// "range_price" -> "rangePrice"
// "range price" -> "rangePrice"
// "range-price" -> "rangePrice"
func CamelCase(s string, sep ...string) string {
sepChar := "_"
if len(sep) > 0 {
sepChar = sep[0]
}
// Not contains sep char
if !strings.Contains(s, sepChar) {
return s
}
// Get regexp instance
rgx, ok := toCamelRegs[sepChar]
if !ok {
rgx = regexp.MustCompile(regexp.QuoteMeta(sepChar) + "+[a-zA-Z]")
}
return rgx.ReplaceAllStringFunc(s, func(s string) string {
s = strings.TrimLeft(s, sepChar)
return UpperFirst(s)
})
}
//
// Indent format multi line text
// from package: github.com/kr/text
//
// Indent inserts prefix at the beginning of each non-empty line of s. The
// end-of-line marker is NL.
func Indent(s, prefix string) string {
return string(IndentBytes([]byte(s), []byte(prefix)))
}
// IndentBytes inserts prefix at the beginning of each non-empty line of b.
// The end-of-line marker is NL.
func IndentBytes(b, prefix []byte) []byte {
if len(b) == 0 {
return b
}
bol := true
res := make([]byte, 0, len(b)+len(prefix)*4)
for _, c := range b {
if bol && c != '\n' {
res = append(res, prefix...)
}
res = append(res, c)
bol = c == '\n'
}
return res
}
+35
View File
@@ -0,0 +1,35 @@
package strutil
import (
"strconv"
"time"
"github.com/gookit/goutil/mathutil"
)
// global id:
//
// https://github.com/rs/xid
// https://github.com/satori/go.uuid
var (
DefMinInt = 1000
DefMaxInt = 9999
)
// MicroTimeID generate.
// return like: 16074145697981929446(len: 20)
func MicroTimeID() string {
ms := time.Now().UnixNano() / 1000
ri := mathutil.RandomInt(DefMinInt, DefMaxInt)
return strconv.FormatInt(ms, 10) + strconv.FormatInt(int64(ri), 10)
}
// MicroTimeHexID generate.
// return like: 5b5f0588af1761ad3(len: 16-17)
func MicroTimeHexID() string {
ms := time.Now().UnixNano() / 1000
ri := mathutil.RandomInt(DefMinInt, DefMaxInt)
return strconv.FormatInt(ms, 16) + strconv.FormatInt(int64(ri), 16)
}
+168
View File
@@ -0,0 +1,168 @@
package strutil
import (
"fmt"
"strings"
)
// PosFlag type
type PosFlag uint8
// Position for padding/resize string
const (
PosLeft PosFlag = iota
PosRight
PosMiddle
)
/*************************************************************
* String padding operation
*************************************************************/
// Padding a string.
func Padding(s, pad string, length int, pos PosFlag) string {
diff := len(s) - length
if diff >= 0 { // do not need padding.
return s
}
if pad == "" || pad == " " {
mark := ""
if pos == PosRight { // to right
mark = "-"
}
// padding left: "%7s", padding right: "%-7s"
tpl := fmt.Sprintf("%s%d", mark, length)
return fmt.Sprintf(`%`+tpl+`s`, s)
}
if pos == PosRight { // to right
return s + Repeat(pad, -diff)
}
return Repeat(pad, -diff) + s
}
// PadLeft a string.
func PadLeft(s, pad string, length int) string {
return Padding(s, pad, length, PosLeft)
}
// PadRight a string.
func PadRight(s, pad string, length int) string {
return Padding(s, pad, length, PosRight)
}
// Resize a string by given length and align settings. padding space.
func Resize(s string, length int, align PosFlag) string {
diff := len(s) - length
if diff >= 0 { // do not need padding.
return s
}
if align == PosMiddle {
strLn := len(s)
padLn := (length - strLn) / 2
padStr := string(make([]byte, padLn))
if diff := length - padLn*2; diff > 0 {
s += " "
}
return padStr + s + padStr
}
return Padding(s, " ", length, align)
}
// PadChars padding a rune/byte to want length and with position flag
func PadChars[T byte | rune](cs []T, pad T, length int, pos PosFlag) []T {
ln := len(cs)
if ln >= length {
ns := make([]T, length)
copy(ns, cs[:length])
return ns
}
idx := length - ln
ns := make([]T, length)
ps := RepeatChars(pad, idx)
if pos == PosRight {
copy(ns, cs)
copy(ns[idx:], ps)
} else { // to left
copy(ns[:idx], ps)
copy(ns[idx:], cs)
}
return ns
}
// PadBytes padding a byte to want length and with position flag
func PadBytes(bs []byte, pad byte, length int, pos PosFlag) []byte {
return PadChars(bs, pad, length, pos)
}
// PadBytesLeft a byte to want length
func PadBytesLeft(bs []byte, pad byte, length int) []byte {
return PadChars(bs, pad, length, PosLeft)
}
// PadBytesRight a byte to want length
func PadBytesRight(bs []byte, pad byte, length int) []byte {
return PadChars(bs, pad, length, PosRight)
}
// PadRunes padding a rune to want length and with position flag
func PadRunes(rs []rune, pad rune, length int, pos PosFlag) []rune {
return PadChars(rs, pad, length, pos)
}
// PadRunesLeft a rune to want length
func PadRunesLeft(rs []rune, pad rune, length int) []rune {
return PadChars(rs, pad, length, PosLeft)
}
// PadRunesRight a rune to want length
func PadRunesRight(rs []rune, pad rune, length int) []rune {
return PadChars(rs, pad, length, PosRight)
}
/*************************************************************
* String repeat operation
*************************************************************/
// Repeat a string
func Repeat(s string, times int) string {
if times <= 0 {
return ""
}
if times == 1 {
return s
}
ss := make([]string, 0, times)
for i := 0; i < times; i++ {
ss = append(ss, s)
}
return strings.Join(ss, "")
}
// RepeatRune repeat a rune char.
func RepeatRune(char rune, times int) []rune {
return RepeatChars(char, times)
}
// RepeatBytes repeat a byte char.
func RepeatBytes(char byte, times int) []byte {
return RepeatChars(char, times)
}
// RepeatChars repeat a byte char.
func RepeatChars[T byte | rune](char T, times int) []T {
chars := make([]T, 0, times)
for i := 0; i < times; i++ {
chars = append(chars, char)
}
return chars
}
+83
View File
@@ -0,0 +1,83 @@
package strutil
import (
"crypto/rand"
"encoding/base64"
mathRand "math/rand"
"time"
)
// some consts string chars
const (
Numbers = "0123456789"
AlphaBet = "abcdefghijklmnopqrstuvwxyz"
AlphaBet1 = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
AlphaNum = "abcdefghijklmnopqrstuvwxyz0123456789"
AlphaNum2 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
AlphaNum3 = "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
)
// RandomChars generate give length random chars at `a-z`
func RandomChars(ln int) string {
cs := make([]byte, ln)
for i := 0; i < ln; i++ {
// 1607400451937462000
mathRand.Seed(time.Now().UnixNano())
idx := mathRand.Intn(25) // 0 - 25
cs[i] = AlphaBet[idx]
}
return string(cs)
}
// RandomCharsV2 generate give length random chars in `0-9a-z`
func RandomCharsV2(ln int) string {
cs := make([]byte, ln)
for i := 0; i < ln; i++ {
// 1607400451937462000
mathRand.Seed(time.Now().UnixNano())
idx := mathRand.Intn(35) // 0 - 35
cs[i] = AlphaNum[idx]
}
return string(cs)
}
// RandomCharsV3 generate give length random chars in `0-9a-zA-Z`
func RandomCharsV3(ln int) string {
cs := make([]byte, ln)
for i := 0; i < ln; i++ {
// 1607400451937462000
mathRand.Seed(time.Now().UnixNano())
idx := mathRand.Intn(61) // 0 - 61
cs[i] = AlphaNum2[idx]
}
return string(cs)
}
// RandomBytes generate
func RandomBytes(length int) ([]byte, error) {
b := make([]byte, length)
_, err := rand.Read(b)
// Note that err == nil only if we read len(b) bytes.
if err != nil {
return nil, err
}
return b, nil
}
// RandomString generate.
// Example:
//
// // this will give us a 44 byte, base64 encoded output
// token, err := RandomString(32)
// if err != nil {
// // Serve an appropriately vague error to the
// // user, but log the details internally.
// }
func RandomString(length int) (string, error) {
b, err := RandomBytes(length)
return base64.URLEncoding.EncodeToString(b), err
}
+227
View File
@@ -0,0 +1,227 @@
package strutil
import (
"strings"
"unicode"
"unicode/utf8"
"golang.org/x/text/width"
)
// RuneIsWord char: a-zA-Z
func RuneIsWord(c rune) bool {
return RuneIsLower(c) || RuneIsUpper(c)
}
// RuneIsLower char
func RuneIsLower(c rune) bool {
return 'a' <= c && c <= 'z'
}
// RuneIsUpper char
func RuneIsUpper(c rune) bool {
return 'A' <= c && c <= 'Z'
}
// RunePos alias of the strings.IndexRune
func RunePos(s string, ru rune) int { return strings.IndexRune(s, ru) }
// IsSpaceRune returns true if the given rune is a space, otherwise false.
func IsSpaceRune(r rune) bool {
return r <= 256 && IsSpace(byte(r)) || unicode.IsSpace(r)
}
// Utf8Len count of the string
func Utf8Len(s string) int { return utf8.RuneCountInString(s) }
// Utf8len of the string
func Utf8len(s string) int { return utf8.RuneCountInString(s) }
// RuneCount of the string
func RuneCount(s string) int { return len([]rune(s)) }
// RuneWidth of the rune.
//
// Example:
//
// RuneWidth('你') // 2
// RuneWidth('a') // 1
// RuneWidth('\n') // 0
func RuneWidth(r rune) int {
p := width.LookupRune(r)
k := p.Kind()
// eg: "\n"
if k == width.Neutral {
return 0
}
if k == width.EastAsianFullwidth || k == width.EastAsianWide || k == width.EastAsianAmbiguous {
return 2
}
return 1
}
// TextWidth utf8 string width. alias of RunesWidth()
func TextWidth(s string) int { return Utf8Width(s) }
// Utf8Width utf8 string width. alias of RunesWidth
func Utf8Width(s string) int { return RunesWidth([]rune(s)) }
// RunesWidth utf8 runes string width.
//
// Examples:
//
// str := "hi,你好"
//
// strutil.Utf8Width(str) => 7
// len(str) => 9
// len([]rune(str)) = utf8.RuneCountInString(s) => 5
func RunesWidth(rs []rune) (w int) {
if len(rs) == 0 {
return
}
for _, runeVal := range rs {
w += RuneWidth(runeVal)
}
return w
}
// TextTruncate alias of the Utf8Truncate()
func TextTruncate(s string, w int, tail string) string { return Utf8Truncate(s, w, tail) }
// Utf8Truncate a string with given width.
func Utf8Truncate(s string, w int, tail string) string {
if sw := Utf8Width(s); sw <= w {
return s
}
i := 0
r := []rune(s)
w -= TextWidth(tail)
tmpW := 0
for ; i < len(r); i++ {
cw := RuneWidth(r[i])
if tmpW+cw > w {
break
}
tmpW += cw
}
return string(r[0:i]) + tail
}
// TextSplit alias of the Utf8Split()
func TextSplit(s string, w int) []string { return Utf8Split(s, w) }
// Utf8Split split a string by width.
func Utf8Split(s string, w int) []string {
sw := Utf8Width(s)
if sw <= w {
return []string{s}
}
tmpW := 0
tmpS := ""
ss := make([]string, 0, sw/w+1)
for _, r := range s {
rw := RuneWidth(r)
if tmpW+rw == w {
tmpS += string(r)
ss = append(ss, tmpS)
tmpW, tmpS = 0, "" // reset
continue
}
if tmpW+rw > w {
ss = append(ss, tmpS)
// append to next line.
tmpW, tmpS = rw, string(r)
continue
}
tmpW += rw
tmpS += string(r)
}
if tmpW > 0 {
ss = append(ss, tmpS)
}
return ss
}
// TextWrap a string by "\n"
func TextWrap(s string, w int) string { return WidthWrap(s, w) }
// WidthWrap a string by "\n"
func WidthWrap(s string, w int) string {
tmpW := 0
out := ""
for _, r := range s {
cw := RuneWidth(r)
if r == '\n' {
out += string(r)
tmpW = 0
continue
}
if tmpW+cw > w {
out += "\n"
tmpW = 0
out += string(r)
tmpW += cw
continue
}
out += string(r)
tmpW += cw
}
return out
}
// WordWrap text string and limit width.
func WordWrap(s string, w int) string {
tmpW := 0
out := ""
for _, sub := range strings.Split(s, " ") {
cw := TextWidth(sub)
if tmpW+cw > w {
if tmpW != 0 {
out += "\n"
}
tmpW = 0
out += sub
tmpW += cw
continue
}
out += sub
tmpW += cw
}
return out
}
// Runes data slice
type Runes []rune
// Padding a rune to want length and with position
func (rs Runes) Padding(pad rune, length int, pos PosFlag) []rune {
return PadChars(rs, pad, length, pos)
}
// PadLeft a rune to want length
func (rs Runes) PadLeft(pad rune, length int) []rune {
return rs.Padding(pad, length, PosLeft)
}
// PadRight a rune to want length
func (rs Runes) PadRight(pad rune, length int) []rune {
return rs.Padding(pad, length, PosRight)
}
+77
View File
@@ -0,0 +1,77 @@
package strutil
// SimilarComparator definition
// links:
//
// https://github.com/mkideal/cli/blob/master/fuzzy.go
type SimilarComparator struct {
src, dst string
}
// NewComparator create
func NewComparator(src, dst string) *SimilarComparator {
return &SimilarComparator{src, dst}
}
// Similar by minDifferRate
// Usage:
//
// c := NewComparator("hello", "he")
// rate, ok :c.Similar(0.3)
func (c *SimilarComparator) Similar(minDifferRate float32) (float32, bool) {
dist := c.editDistance([]byte(c.src), []byte(c.dst))
differRate := dist / float32(max(len(c.src), len(c.dst))+4)
return differRate, differRate >= minDifferRate
}
func (c *SimilarComparator) editDistance(s, t []byte) float32 {
var (
m = len(s)
n = len(t)
d = make([][]float32, m+1)
)
for i := 0; i < m+1; i++ {
d[i] = make([]float32, n+1)
d[i][0] = float32(i)
}
for j := 0; j < n+1; j++ {
d[0][j] = float32(j)
}
for j := 1; j < n+1; j++ {
for i := 1; i < m+1; i++ {
if s[i-1] == t[j-1] {
d[i][j] = d[i-1][j-1]
} else {
d[i][j] = min(d[i-1][j]+1, min(d[i][j-1]+1, d[i-1][j-1]+1))
}
}
}
return d[m][n]
}
func min(x, y float32) float32 {
if x < y {
return x
}
return y
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
// Similarity calc for two string.
// Usage:
//
// rate, ok := Similarity("hello", "he")
func Similarity(s, t string, rate float32) (float32, bool) {
return NewComparator(s, t).Similar(rate)
}
+126
View File
@@ -0,0 +1,126 @@
package strutil
import "strings"
/*************************************************************
* String split operation
*************************************************************/
// Cut same of the strings.Cut
func Cut(s, sep string) (before string, after string, found bool) {
if i := strings.Index(s, sep); i >= 0 {
return s[:i], s[i+len(sep):], true
}
return s, "", false
}
// MustCut always returns two substring.
func MustCut(s, sep string) (before string, after string) {
before, after, _ = Cut(s, sep)
return
}
// TrimCut always returns two substring and trim space for items.
func TrimCut(s, sep string) (string, string) {
before, after, _ := Cut(s, sep)
return strings.TrimSpace(before), strings.TrimSpace(after)
}
// SplitValid string to slice. will trim each item and filter empty string node.
func SplitValid(s, sep string) (ss []string) { return Split(s, sep) }
// Split string to slice. will trim each item and filter empty string node.
func Split(s, sep string) (ss []string) {
if s = strings.TrimSpace(s); s == "" {
return
}
for _, val := range strings.Split(s, sep) {
if val = strings.TrimSpace(val); val != "" {
ss = append(ss, val)
}
}
return
}
// SplitNValid string to slice. will filter empty string node.
func SplitNValid(s, sep string, n int) (ss []string) { return SplitN(s, sep, n) }
// SplitN string to slice. will filter empty string node.
func SplitN(s, sep string, n int) (ss []string) {
if s = strings.TrimSpace(s); s == "" {
return
}
rawList := strings.Split(s, sep)
for i, val := range rawList {
if val = strings.TrimSpace(val); val != "" {
if len(ss) == n-1 {
ss = append(ss, strings.TrimSpace(strings.Join(rawList[i:], sep)))
break
}
ss = append(ss, val)
}
}
return
}
// SplitTrimmed split string to slice.
// will trim space for each node, but not filter empty
func SplitTrimmed(s, sep string) (ss []string) {
if s = strings.TrimSpace(s); s == "" {
return
}
for _, val := range strings.Split(s, sep) {
ss = append(ss, strings.TrimSpace(val))
}
return
}
// SplitNTrimmed split string to slice.
// will trim space for each node, but not filter empty
func SplitNTrimmed(s, sep string, n int) (ss []string) {
if s = strings.TrimSpace(s); s == "" {
return
}
for _, val := range strings.SplitN(s, sep, n) {
ss = append(ss, strings.TrimSpace(val))
}
return
}
// Substr for a string.
// if length <= 0, return pos to end.
func Substr(s string, pos, length int) string {
runes := []rune(s)
strLn := len(runes)
// pos is too large
if pos >= strLn {
return ""
}
stopIdx := pos + length
if length == 0 || stopIdx > strLn {
stopIdx = strLn
} else if length < 0 {
stopIdx = strLn + length
}
return string(runes[pos:stopIdx])
}
// SplitInlineComment for a text string.
func SplitInlineComment(val string) (string, string) {
if pos := strings.IndexByte(val, '#'); pos > -1 {
return strings.TrimRight(val[0:pos], " "), val[pos:]
}
if pos := strings.Index(val, "//"); pos > -1 {
return strings.TrimRight(val[0:pos], " "), val[pos:]
}
return val, ""
}
+90
View File
@@ -0,0 +1,90 @@
// Package strutil provide some string,char,byte util functions
package strutil
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"text/template"
)
// 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 {
ss := make([]string, len(pairs)*2)
for old, newVal := range pairs {
ss = append(ss, old, newVal)
}
return strings.NewReplacer(ss...).Replace(str)
}
// 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)
}
+85
View File
@@ -0,0 +1,85 @@
package strutil
import "strings"
// Value string
type Value string
// StrVal string. alias of Value
type StrVal = Value
// Set value
func (s *Value) Set(val string) error {
*s = Value(val)
return nil
}
// IsEmpty check
func (s *Value) IsEmpty() bool {
return string(*s) == ""
}
// IsStartWith prefix
func (s *Value) IsStartWith(sub string) bool {
return strings.HasPrefix(string(*s), sub)
}
// HasPrefix prefix
func (s *Value) HasPrefix(sub string) bool {
return strings.HasPrefix(string(*s), sub)
}
// IsEndWith suffix
func (s *Value) IsEndWith(sub string) bool {
return strings.HasSuffix(string(*s), sub)
}
// HasSuffix suffix
func (s *Value) HasSuffix(sub string) bool {
return strings.HasSuffix(string(*s), sub)
}
// Bytes string to bytes
func (s *Value) Bytes() []byte {
return []byte(*s)
}
// Val string
func (s *Value) Val() string {
return string(*s)
}
// Int convert
func (s *Value) Int() int {
return QuietInt(string(*s))
}
// Int64 convert
func (s *Value) Int64() int64 {
return QuietInt64(string(*s))
}
// Bool convert
func (s *Value) Bool() bool {
return QuietBool(string(*s))
}
// Value string
func (s *Value) String() string {
return string(*s)
}
// Split string
func (s *Value) Split(sep string) []string {
return strings.Split(string(*s), sep)
}
// SplitN string
func (s *Value) SplitN(sep string, n int) []string {
return strings.SplitN(string(*s), sep, n)
}
// TrimSpace string and return new
func (s *Value) TrimSpace() Value {
return Value(strings.TrimSpace(string(*s)))
}