diff --git a/readme.md b/readme.md index 36d540c..2877ea1 100644 --- a/readme.md +++ b/readme.md @@ -6,3 +6,9 @@ usage: ```bash go get git.sre.ink/go/gtool ``` + + +*** +https://github.com/duke-git/lancet + +lancet(柳叶刀)是一个全面、高效、可复用的go语言工具函数库。 lancet受到了java apache common包和lodash.js的启发。 \ No newline at end of file diff --git a/str/assertion.go b/str/assertion.go new file mode 100644 index 0000000..fcb50c3 --- /dev/null +++ b/str/assertion.go @@ -0,0 +1,14 @@ +package str + +// IsString check if the value data type is string or not. +func (sst *Str) IsString(v any) bool { + if v == nil { + return false + } + switch v.(type) { + case string: + return true + default: + return false + } +} diff --git a/str/assertion_test.go b/str/assertion_test.go new file mode 100644 index 0000000..b71e17b --- /dev/null +++ b/str/assertion_test.go @@ -0,0 +1,17 @@ +package str + +import ( + "fmt" + "testing" +) + +func TestStr_IsString(t *testing.T) { + fmt.Println(StrTool.IsString("hello world!")) + fmt.Println(StrTool.IsString(123)) + fmt.Println(StrTool.IsString(true)) + fmt.Println(StrTool.IsString(nil)) + fmt.Println(StrTool.IsString([]string{"hello", "world"})) + fmt.Println(StrTool.IsString(map[string]string{"hello": "world"})) + fmt.Println(StrTool.IsString(struct{}{})) + +} diff --git a/str/covert.go b/str/covert.go new file mode 100644 index 0000000..973eb20 --- /dev/null +++ b/str/covert.go @@ -0,0 +1,246 @@ +package str + +import ( + "regexp" + "strings" + "unicode" + "unicode/utf8" +) + +// Capitalize converts the first character of a string to upper case and the remaining to lower case. +func (sst *Str) Capitalize(s string) string { + if len(s) == 0 { + return "" + } + + out := make([]rune, len(s)) + for i, v := range s { + if i == 0 { + out[i] = unicode.ToUpper(v) + } else { + out[i] = unicode.ToLower(v) + } + } + + return string(out) +} + +// CamelCase covert string to camelCase string. +func (sst *Str) CamelCase(s string) string { + if len(s) == 0 { + return "" + } + + res := "" + blankSpace := " " + regex, _ := regexp.Compile("[-_&]+") + ss := regex.ReplaceAllString(s, blankSpace) + for i, v := range strings.Split(ss, blankSpace) { + vv := []rune(v) + if i == 0 { + if vv[i] >= 65 && vv[i] <= 96 { + vv[0] += 32 + } + res += string(vv) + } else { + res += sst.Capitalize(v) + } + } + + return res +} + +// UpperFirst converts the first character of string to upper case. +func (sst *Str) 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 (sst *Str) LowerFirst(s string) string { + if len(s) == 0 { + return "" + } + + r, size := utf8.DecodeRuneInString(s) + r = unicode.ToLower(r) + + return string(r) + s[size:] +} + +// PadEnd pads string on the right side if it's shorter than size. +// Padding characters are truncated if they exceed size. +func (sst *Str) PadEnd(source string, size int, padStr string) string { + len1 := len(source) + len2 := len(padStr) + + if len1 >= size { + return source + } + + fill := "" + if len2 >= size-len1 { + fill = padStr[0 : size-len1] + } else { + fill = strings.Repeat(padStr, size-len1) + } + return source + fill[0:size-len1] +} + +// PadStart pads string on the left side if it's shorter than size. +// Padding characters are truncated if they exceed size. +func (sst *Str) PadStart(source string, size int, padStr string) string { + len1 := len(source) + len2 := len(padStr) + + if len1 >= size { + return source + } + + fill := "" + if len2 >= size-len1 { + fill = padStr[0 : size-len1] + } else { + fill = strings.Repeat(padStr, size-len1) + } + return fill[0:size-len1] + source +} + +// KebabCase covert string to kebab-case +func (sst *Str) KebabCase(s string) string { + if len(s) == 0 { + return "" + } + + regex := regexp.MustCompile(`[\W|_]+`) + blankSpace := " " + match := regex.ReplaceAllString(s, blankSpace) + rs := strings.Split(match, blankSpace) + + var res []string + for _, v := range rs { + splitWords := splitWordsToLower(v) + if len(splitWords) > 0 { + res = append(res, splitWords...) + } + } + + return strings.Join(res, "-") +} + +// SnakeCase covert string to snake_case +func (sst *Str) SnakeCase(s string) string { + if len(s) == 0 { + return "" + } + + regex := regexp.MustCompile(`[\W|_]+`) + blankSpace := " " + match := regex.ReplaceAllString(s, blankSpace) + rs := strings.Split(match, blankSpace) + + var res []string + for _, v := range rs { + splitWords := splitWordsToLower(v) + if len(splitWords) > 0 { + res = append(res, splitWords...) + } + } + + return strings.Join(res, "_") +} + +// ReverseStr return string whose char order is reversed to the given string +func (sst *Str) ReverseStr(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 another string. +func (sst *Str) 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 anther string. will change str value +func (sst *Str) 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 +} + +// SplitEx split a given string whether the result contains empty string +func (sst *Str) SplitEx(s, sep string, removeEmptyString bool) []string { + if sep == "" { + return []string{} + } + + n := strings.Count(s, sep) + 1 + a := make([]string, n) + n-- + i := 0 + sepSave := 0 + ignore := false + + for i < n { + m := strings.Index(s, sep) + if m < 0 { + break + } + ignore = false + if removeEmptyString { + if s[:m+sepSave] == "" { + ignore = true + } + } + if !ignore { + a[i] = s[:m+sepSave] + s = s[m+len(sep):] + i++ + } else { + s = s[m+len(sep):] + } + } + + var ret []string + if removeEmptyString { + if s != "" { + a[i] = s + ret = a[:i+1] + } else { + ret = a[:i] + } + } else { + a[i] = s + ret = a[:i+1] + } + + return ret +} diff --git a/str/covert_test.go b/str/covert_test.go new file mode 100644 index 0000000..eaa836c --- /dev/null +++ b/str/covert_test.go @@ -0,0 +1,74 @@ +package str + +import ( + "fmt" + "testing" +) + +func TestStr_Capitalize(t *testing.T) { + fmt.Println(StrTool.Capitalize("hello world!")) + //Hello world! + fmt.Println(StrTool.CamelCase("hello world!")) + //helloWorld! + fmt.Println(StrTool.CamelCase("test_ok")) + //testOk +} + +func TestStr_UpperFirst(t *testing.T) { + fmt.Println(StrTool.UpperFirst("hello world!")) + //Hello world! + fmt.Println(StrTool.UpperFirst("test_ok")) + //Test ok + fmt.Println(StrTool.LowerFirst("Hello world!")) + //hello world! +} + +func TestStr_PadEnd(t *testing.T) { + fmt.Println(StrTool.PadEnd("hello world!", 100, "*")) + fmt.Println(StrTool.PadEnd("hello world!", 100, "-")) + fmt.Println(StrTool.PadEnd("hello world!", 100, " ")) + //hello world!**************************************************************************************** + //hello world!---------------------------------------------------------------------------------------- + //hello world! +} +func TestStr_PadStart(t *testing.T) { + fmt.Println(StrTool.PadStart("hello world!", 100, "*")) + fmt.Println(StrTool.PadStart("hello world!", 100, "-")) + fmt.Println(StrTool.PadStart("hello world!", 100, " ")) + + //****************************************************************************************hello world! + //----------------------------------------------------------------------------------------hello world! + // hello world! +} + +func TestStr_KebabCase(t *testing.T) { + fmt.Println(StrTool.KebabCase("hello world!")) + //hello-world + fmt.Println(StrTool.KebabCase("test_ok")) + //test-ok +} +func TestStr_SnakeCase(t *testing.T) { + fmt.Println(StrTool.SnakeCase("hello world!")) + //hello_world + fmt.Println(StrTool.SnakeCase("test_ok")) + //test_ok +} + +func TestStr_Wrap(t *testing.T) { + fmt.Println(StrTool.Wrap("hello world!", "100")) + //100hello world!100 + +} +func TestStr_Unwrap(t *testing.T) { + fmt.Println(StrTool.Unwrap("100hello world!100", "100")) + //hello world! +} +func TestStr_ReverseStr(t *testing.T) { + fmt.Println(StrTool.ReverseStr("hello world!")) + //!dlrow olleh +} + +func TestStr_SplitEx(t *testing.T) { + fmt.Println(StrTool.SplitEx("hello world!", "world", true)) + fmt.Println(StrTool.SplitEx("hello world!", "world", false)) +} diff --git a/str/str.go b/str/hex.go similarity index 100% rename from str/str.go rename to str/hex.go diff --git a/str/str_test.go b/str/hex_test.go similarity index 100% rename from str/str_test.go rename to str/hex_test.go diff --git a/str/internal.go b/str/internal.go new file mode 100644 index 0000000..267a88d --- /dev/null +++ b/str/internal.go @@ -0,0 +1,40 @@ +package str + +import "strings" + +// splitWordsToLower split a string into worlds by uppercase char +func splitWordsToLower(s string) []string { + var res []string + + upperIndexes := upperIndex(s) + l := len(upperIndexes) + if upperIndexes == nil || l == 0 { + if s != "" { + res = append(res, s) + } + return res + } + for i := 0; i < l; i++ { + if i < l-1 { + res = append(res, strings.ToLower(s[upperIndexes[i]:upperIndexes[i+1]])) + } else { + res = append(res, strings.ToLower(s[upperIndexes[i]:])) + } + } + return res +} + +// upperIndex get a int slice which elements are all the uppercase char index of a string +func upperIndex(s string) []int { + var res []int + for i := 0; i < len(s); i++ { + if 64 < s[i] && s[i] < 91 { + res = append(res, i) + } + } + if len(s) > 0 && res != nil && res[0] != 0 { + res = append([]int{0}, res...) + } + + return res +} diff --git a/str/internal_test.go b/str/internal_test.go new file mode 100644 index 0000000..72818c4 --- /dev/null +++ b/str/internal_test.go @@ -0,0 +1,11 @@ +package str + +import ( + "fmt" + "testing" +) + +func TestInternal(tt *testing.T) { + fmt.Println(splitWordsToLower("Hello World,goodbye world!")) + fmt.Println(upperIndex("Hello World,goodbye world!")) +}