Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
f30de13c34
|
@ -0,0 +1,321 @@
|
|||
package stream
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewString 创建字符串流
|
||||
func NewString[S ~string](s S) *String[S] {
|
||||
return &String[S]{s}
|
||||
}
|
||||
|
||||
// String 字符串流
|
||||
type String[S ~string] struct {
|
||||
str S
|
||||
}
|
||||
|
||||
// Elem 返回字符串
|
||||
func (s *String[S]) Elem() S {
|
||||
return s.str
|
||||
}
|
||||
|
||||
// String 返回字符串
|
||||
func (s *String[S]) String() string {
|
||||
return string(s.str)
|
||||
}
|
||||
|
||||
// Index 返回字符串指定位置的字符,当索引超出范围时将会触发 panic
|
||||
func (s *String[S]) Index(i int) *String[S] {
|
||||
return NewString(S(s.str[i]))
|
||||
}
|
||||
|
||||
// Range 返回字符串指定范围的字符
|
||||
func (s *String[S]) Range(start, end int) *String[S] {
|
||||
return NewString(s.str[start:end])
|
||||
}
|
||||
|
||||
// TrimSpace 返回去除字符串首尾空白字符的字符串
|
||||
func (s *String[S]) TrimSpace() *String[S] {
|
||||
return NewString(S(strings.TrimSpace(string(s.str))))
|
||||
}
|
||||
|
||||
// Trim 返回去除字符串首尾指定字符的字符串
|
||||
func (s *String[S]) Trim(cs string) *String[S] {
|
||||
return NewString(S(strings.Trim(string(s.str), cs)))
|
||||
}
|
||||
|
||||
// TrimPrefix 返回去除字符串前缀的字符串
|
||||
func (s *String[S]) TrimPrefix(prefix string) *String[S] {
|
||||
return NewString(S(strings.TrimPrefix(string(s.str), prefix)))
|
||||
}
|
||||
|
||||
// TrimSuffix 返回去除字符串后缀的字符串
|
||||
func (s *String[S]) TrimSuffix(suffix string) *String[S] {
|
||||
return NewString(S(strings.TrimSuffix(string(s.str), suffix)))
|
||||
}
|
||||
|
||||
// ToUpper 返回字符串的大写形式
|
||||
func (s *String[S]) ToUpper() *String[S] {
|
||||
return NewString(S(strings.ToUpper(string(s.str))))
|
||||
}
|
||||
|
||||
// ToLower 返回字符串的小写形式
|
||||
func (s *String[S]) ToLower() *String[S] {
|
||||
return NewString(S(strings.ToLower(string(s.str))))
|
||||
}
|
||||
|
||||
// Equal 返回字符串是否相等
|
||||
func (s *String[S]) Equal(ss S) bool {
|
||||
return s.str == ss
|
||||
}
|
||||
|
||||
// HasPrefix 返回字符串是否包含指定前缀
|
||||
func (s *String[S]) HasPrefix(prefix S) bool {
|
||||
return strings.HasPrefix(string(s.str), string(prefix))
|
||||
}
|
||||
|
||||
// HasSuffix 返回字符串是否包含指定后缀
|
||||
func (s *String[S]) HasSuffix(suffix S) bool {
|
||||
return strings.HasSuffix(string(s.str), string(suffix))
|
||||
}
|
||||
|
||||
// Len 返回字符串长度
|
||||
func (s *String[S]) Len() int {
|
||||
return len(s.str)
|
||||
}
|
||||
|
||||
// Contains 返回字符串是否包含指定子串
|
||||
func (s *String[S]) Contains(sub S) bool {
|
||||
return strings.Contains(string(s.str), string(sub))
|
||||
}
|
||||
|
||||
// Count 返回字符串包含指定子串的次数
|
||||
func (s *String[S]) Count(sub S) int {
|
||||
return strings.Count(string(s.str), string(sub))
|
||||
}
|
||||
|
||||
// Repeat 返回重复 count 次的字符串
|
||||
func (s *String[S]) Repeat(count int) *String[S] {
|
||||
return NewString(S(strings.Repeat(string(s.str), count)))
|
||||
}
|
||||
|
||||
// Replace 返回替换指定子串后的字符串
|
||||
func (s *String[S]) Replace(old, new S, n int) *String[S] {
|
||||
return NewString(S(strings.Replace(string(s.str), string(old), string(new), n)))
|
||||
}
|
||||
|
||||
// ReplaceAll 返回替换所有指定子串后的字符串
|
||||
func (s *String[S]) ReplaceAll(old, new S) *String[S] {
|
||||
return NewString(S(strings.ReplaceAll(string(s.str), string(old), string(new))))
|
||||
}
|
||||
|
||||
// Append 返回追加指定字符串后的字符串
|
||||
func (s *String[S]) Append(ss S) *String[S] {
|
||||
return NewString(S(string(s.str) + string(ss)))
|
||||
}
|
||||
|
||||
// Prepend 返回追加指定字符串后的字符串,追加的字符串在前
|
||||
func (s *String[S]) Prepend(ss S) *String[S] {
|
||||
return NewString(S(string(ss) + string(s.str)))
|
||||
}
|
||||
|
||||
// Clear 返回清空字符串后的字符串
|
||||
func (s *String[S]) Clear() *String[S] {
|
||||
return NewString(S(""))
|
||||
}
|
||||
|
||||
// Reverse 返回反转字符串后的字符串
|
||||
func (s *String[S]) Reverse() *String[S] {
|
||||
var str = []rune(string(s.str))
|
||||
for i, j := 0, len(str)-1; i < j; i, j = i+1, j-1 {
|
||||
str[i], str[j] = str[j], str[i]
|
||||
}
|
||||
return NewString(S(string(str)))
|
||||
}
|
||||
|
||||
// Queto 返回带引号的字符串
|
||||
func (s *String[S]) Queto() *String[S] {
|
||||
return NewString(S(strconv.Quote(string(s.str))))
|
||||
}
|
||||
|
||||
// QuetoToASCII 返回带引号的字符串
|
||||
func (s *String[S]) QuetoToASCII() *String[S] {
|
||||
return NewString(S(strconv.QuoteToASCII(string(s.str))))
|
||||
}
|
||||
|
||||
// FirstUpper 返回首字母大写的字符串
|
||||
func (s *String[S]) FirstUpper() *String[S] {
|
||||
var upperStr string
|
||||
vv := []rune(string(s.str))
|
||||
for i := 0; i < len(vv); i++ {
|
||||
if i == 0 {
|
||||
if vv[i] >= 97 && vv[i] <= 122 {
|
||||
vv[i] -= 32
|
||||
upperStr += string(vv[i])
|
||||
} else {
|
||||
return s
|
||||
}
|
||||
} else {
|
||||
upperStr += string(vv[i])
|
||||
}
|
||||
}
|
||||
return NewString(S(upperStr))
|
||||
}
|
||||
|
||||
// FirstLower 返回首字母小写的字符串
|
||||
func (s *String[S]) FirstLower() *String[S] {
|
||||
var lowerStr string
|
||||
vv := []rune(string(s.str))
|
||||
for i := 0; i < len(vv); i++ {
|
||||
if i == 0 {
|
||||
if vv[i] >= 65 && vv[i] <= 90 {
|
||||
vv[i] += 32
|
||||
lowerStr += string(vv[i])
|
||||
} else {
|
||||
return s
|
||||
}
|
||||
} else {
|
||||
lowerStr += string(vv[i])
|
||||
}
|
||||
}
|
||||
return NewString(S(lowerStr))
|
||||
}
|
||||
|
||||
// SnakeCase 返回蛇形命名的字符串
|
||||
func (s *String[S]) SnakeCase() *String[S] {
|
||||
var str = string(s.str)
|
||||
var result string
|
||||
for i, v := range str {
|
||||
if v >= 65 && v <= 90 {
|
||||
if i != 0 {
|
||||
result += "_"
|
||||
}
|
||||
result += string(v + 32)
|
||||
} else {
|
||||
result += string(v)
|
||||
}
|
||||
}
|
||||
return NewString(S(result))
|
||||
}
|
||||
|
||||
// CamelCase 返回驼峰命名的字符串
|
||||
func (s *String[S]) CamelCase() *String[S] {
|
||||
var str = string(s.str)
|
||||
var result string
|
||||
var upper = false
|
||||
for _, v := range str {
|
||||
if v == 95 {
|
||||
upper = true
|
||||
} else {
|
||||
if upper {
|
||||
result += string(v - 32)
|
||||
upper = false
|
||||
} else {
|
||||
result += string(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return NewString(S(result))
|
||||
}
|
||||
|
||||
// KebabCase 返回短横线命名的字符串
|
||||
func (s *String[S]) KebabCase() *String[S] {
|
||||
var str = string(s.str)
|
||||
var result string
|
||||
for i, v := range str {
|
||||
if v >= 65 && v <= 90 {
|
||||
if i != 0 {
|
||||
result += "-"
|
||||
}
|
||||
result += string(v + 32)
|
||||
} else {
|
||||
result += string(v)
|
||||
}
|
||||
}
|
||||
return NewString(S(result))
|
||||
}
|
||||
|
||||
// TitleCase 返回标题命名的字符串
|
||||
func (s *String[S]) TitleCase() *String[S] {
|
||||
var str = string(s.str)
|
||||
var result string
|
||||
var upper = true
|
||||
for _, v := range str {
|
||||
if v == 95 || v == 45 || v == 32 {
|
||||
upper = true
|
||||
} else {
|
||||
if upper {
|
||||
result += string(v - 32)
|
||||
upper = false
|
||||
} else {
|
||||
result += string(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return NewString(S(result))
|
||||
}
|
||||
|
||||
// Bytes 返回字符串的字节数组
|
||||
func (s *String[S]) Bytes() []byte {
|
||||
return []byte(s.str)
|
||||
}
|
||||
|
||||
// Runes 返回字符串的字符数组
|
||||
func (s *String[S]) Runes() []rune {
|
||||
return []rune(s.str)
|
||||
}
|
||||
|
||||
// Default 当字符串为空时设置默认值
|
||||
func (s *String[S]) Default(def S) *String[S] {
|
||||
if s.str == "" {
|
||||
return NewString(def)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Handle 处理字符串
|
||||
func (s *String[S]) Handle(f func(S)) *String[S] {
|
||||
f(s.str)
|
||||
return s
|
||||
}
|
||||
|
||||
// Update 更新字符串
|
||||
func (s *String[S]) Update(f func(S) S) *String[S] {
|
||||
s.str = f(s.str)
|
||||
return s
|
||||
}
|
||||
|
||||
// Split 返回字符串切片
|
||||
func (s *String[S]) Split(sep string) Strings[S] {
|
||||
slice := strings.Split(string(s.str), sep)
|
||||
rep := make([]S, len(slice))
|
||||
for i, v := range slice {
|
||||
rep[i] = S(v)
|
||||
}
|
||||
return NewStrings(rep...)
|
||||
}
|
||||
|
||||
// SplitN 返回字符串切片
|
||||
func (s *String[S]) SplitN(sep string, n int) Strings[S] {
|
||||
slice := strings.SplitN(string(s.str), sep, n)
|
||||
rep := make([]S, len(slice))
|
||||
for i, v := range slice {
|
||||
rep[i] = S(v)
|
||||
}
|
||||
return NewStrings(rep...)
|
||||
}
|
||||
|
||||
// Batched 将字符串按照指定长度分组,最后一组可能小于指定长度
|
||||
func (s *String[S]) Batched(size int) Strings[S] {
|
||||
var str = string(s.str)
|
||||
var result = make([]S, 0, len(str)/size+1)
|
||||
for len(str) >= size {
|
||||
result = append(result, S(str[:size]))
|
||||
str = str[size:]
|
||||
}
|
||||
if len(str) > 0 {
|
||||
result = append(result, S(str))
|
||||
}
|
||||
return NewStrings(result...)
|
||||
}
|
|
@ -0,0 +1,824 @@
|
|||
package stream_test
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/stream"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewString(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", want: "hello"},
|
||||
{name: "case2", in: "world", want: "world"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in)
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s) = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_String(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", want: "hello"},
|
||||
{name: "case2", in: "world", want: "world"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).String()
|
||||
if got != c.want {
|
||||
t.Fatalf("String(%s).String() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Index(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
i int
|
||||
want string
|
||||
shouldPanic bool
|
||||
}{
|
||||
{name: "case1", in: "hello", i: 0, want: "h", shouldPanic: false},
|
||||
{name: "case2", in: "world", i: 2, want: "r", shouldPanic: false},
|
||||
{name: "case3", in: "world", i: 5, want: "", shouldPanic: true},
|
||||
{name: "case4", in: "world", i: -1, want: "", shouldPanic: true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); (r != nil) != c.shouldPanic {
|
||||
t.Fatalf("NewString(%s).Index(%d) should panic", c.in, c.i)
|
||||
}
|
||||
}()
|
||||
got := stream.NewString(c.in).Index(c.i)
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).Index(%d) = %s; want %s", c.in, c.i, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Range(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
start int
|
||||
end int
|
||||
want string
|
||||
shouldPanic bool
|
||||
}{
|
||||
{name: "case1", in: "hello", start: 0, end: 2, want: "he", shouldPanic: false},
|
||||
{name: "case2", in: "world", start: 2, end: 5, want: "rld", shouldPanic: false},
|
||||
{name: "case3", in: "world", start: 5, end: 6, want: "", shouldPanic: true},
|
||||
{name: "case4", in: "world", start: -1, end: 6, want: "", shouldPanic: true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); (r != nil) != c.shouldPanic {
|
||||
t.Fatalf("NewString(%s).Range(%d, %d) should panic", c.in, c.start, c.end)
|
||||
}
|
||||
}()
|
||||
got := stream.NewString(c.in).Range(c.start, c.end)
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).Range(%d, %d) = %s; want %s", c.in, c.start, c.end, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_TrimSpace(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: " hello ", want: "hello"},
|
||||
{name: "case2", in: " world ", want: "world"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).TrimSpace()
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).TrimSpace() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Trim(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
cs string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", cs: "h", want: "ello"},
|
||||
{name: "case2", in: "world", cs: "d", want: "worl"},
|
||||
{name: "none", in: "world", cs: "", want: "world"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Trim(c.cs)
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).Trim(%s) = %s; want %s", c.in, c.cs, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_TrimPrefix(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
prefix string
|
||||
want string
|
||||
isEqual bool
|
||||
}{
|
||||
{name: "case1", in: "hello", prefix: "h", want: "ello", isEqual: false},
|
||||
{name: "case2", in: "world", prefix: "w", want: "orld", isEqual: false},
|
||||
{name: "none", in: "world", prefix: "x", want: "world", isEqual: true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).TrimPrefix(c.prefix)
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).TrimPrefix(%s) = %s; want %s", c.in, c.prefix, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_TrimSuffix(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
suffix string
|
||||
want string
|
||||
isEqual bool
|
||||
}{
|
||||
{name: "case1", in: "hello", suffix: "o", want: "hell", isEqual: false},
|
||||
{name: "case2", in: "world", suffix: "d", want: "worl", isEqual: false},
|
||||
{name: "none", in: "world", suffix: "x", want: "world", isEqual: true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).TrimSuffix(c.suffix)
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).TrimSuffix(%s) = %s; want %s", c.in, c.suffix, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_ToUpper(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", want: "HELLO"},
|
||||
{name: "case2", in: "world", want: "WORLD"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).ToUpper()
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).ToUpper() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_ToLower(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "HELLO", want: "hello"},
|
||||
{name: "case2", in: "WORLD", want: "world"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).ToLower()
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).ToLower() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Equal(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
ss string
|
||||
want bool
|
||||
}{
|
||||
{name: "case1", in: "hello", ss: "hello", want: true},
|
||||
{name: "case2", in: "world", ss: "world", want: true},
|
||||
{name: "case3", in: "world", ss: "worldx", want: false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Equal(c.ss)
|
||||
if got != c.want {
|
||||
t.Fatalf("NewString(%s).Equal(%s) = %t; want %t", c.in, c.ss, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_HasPrefix(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
prefix string
|
||||
want bool
|
||||
}{
|
||||
{name: "case1", in: "hello", prefix: "h", want: true},
|
||||
{name: "case2", in: "world", prefix: "w", want: true},
|
||||
{name: "case3", in: "world", prefix: "x", want: false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).HasPrefix(c.prefix)
|
||||
if got != c.want {
|
||||
t.Fatalf("NewString(%s).HasPrefix(%s) = %t; want %t", c.in, c.prefix, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Len(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want int
|
||||
}{
|
||||
{name: "case1", in: "hello", want: 5},
|
||||
{name: "case2", in: "world", want: 5},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Len()
|
||||
if got != c.want {
|
||||
t.Fatalf("NewString(%s).Len() = %d; want %d", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Contains(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
ss string
|
||||
want bool
|
||||
}{
|
||||
{name: "case1", in: "hello", ss: "he", want: true},
|
||||
{name: "case2", in: "world", ss: "or", want: true},
|
||||
{name: "case3", in: "world", ss: "x", want: false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Contains(c.ss)
|
||||
if got != c.want {
|
||||
t.Fatalf("NewString(%s).Contains(%s) = %t; want %t", c.in, c.ss, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Count(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
ss string
|
||||
want int
|
||||
}{
|
||||
{name: "case1", in: "hello", ss: "l", want: 2},
|
||||
{name: "case2", in: "world", ss: "o", want: 1},
|
||||
{name: "case3", in: "world", ss: "x", want: 0},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Count(c.ss)
|
||||
if got != c.want {
|
||||
t.Fatalf("NewString(%s).Count(%s) = %d; want %d", c.in, c.ss, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Repeat(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
count int
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", count: 2, want: "hellohello"},
|
||||
{name: "case2", in: "world", count: 3, want: "worldworldworld"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Repeat(c.count)
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).Repeat(%d) = %s; want %s", c.in, c.count, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Replace(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
old string
|
||||
new string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", old: "l", new: "x", want: "hexxo"},
|
||||
{name: "case2", in: "world", old: "o", new: "x", want: "wxrld"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Replace(c.old, c.new, -1)
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).Replace(%s, %s) = %s; want %s", c.in, c.old, c.new, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_ReplaceAll(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
old string
|
||||
new string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", old: "l", new: "x", want: "hexxo"},
|
||||
{name: "case2", in: "world", old: "o", new: "x", want: "wxrld"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).ReplaceAll(c.old, c.new)
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).ReplaceAll(%s, %s) = %s; want %s", c.in, c.old, c.new, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Append(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
ss string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", ss: " world", want: "hello world"},
|
||||
{name: "case2", in: "world", ss: " hello", want: "world hello"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Append(c.ss)
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).Append(%s) = %s; want %s", c.in, c.ss, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Prepend(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
ss string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", ss: "world ", want: "world hello"},
|
||||
{name: "case2", in: "world", ss: "hello ", want: "hello world"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Prepend(c.ss)
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).Prepend(%s) = %s; want %s", c.in, c.ss, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Clear(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", want: ""},
|
||||
{name: "case2", in: "world", want: ""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Clear()
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).Clear() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Reverse(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", want: "olleh"},
|
||||
{name: "case2", in: "world", want: "dlrow"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Reverse()
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).Reverse() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Queto(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", want: "\"hello\""},
|
||||
{name: "case2", in: "world", want: "\"world\""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Queto()
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).Queto() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_QuetoToASCII(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", want: "\"hello\""},
|
||||
{name: "case2", in: "world", want: "\"world\""},
|
||||
{name: "case3", in: "你好", want: "\"\\u4f60\\u597d\""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).QuetoToASCII()
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).QuetoToASCII() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_FirstUpper(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", want: "Hello"},
|
||||
{name: "case2", in: "world", want: "World"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).FirstUpper()
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).FirstUpper() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_FirstLower(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "Hello", want: "hello"},
|
||||
{name: "case2", in: "World", want: "world"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).FirstLower()
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).FirstLower() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_SnakeCase(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "HelloWorld", want: "hello_world"},
|
||||
{name: "case2", in: "HelloWorldHello", want: "hello_world_hello"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).SnakeCase()
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).SnakeCase() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_CamelCase(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello_world", want: "helloWorld"},
|
||||
{name: "case2", in: "hello_world_hello", want: "helloWorldHello"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).CamelCase()
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).CamelCase() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_KebabCase(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "HelloWorld", want: "hello-world"},
|
||||
{name: "case2", in: "HelloWorldHello", want: "hello-world-hello"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).KebabCase()
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).KebabCase() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_TitleCase(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello_world", want: "HelloWorld"},
|
||||
{name: "case2", in: "hello_world_hello", want: "HelloWorldHello"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).TitleCase()
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).TitleCase() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Bytes(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", want: "hello"},
|
||||
{name: "case2", in: "world", want: "world"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Bytes()
|
||||
if string(got) != c.want {
|
||||
t.Fatalf("NewString(%s).Bytes() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Runes(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", want: "hello"},
|
||||
{name: "case2", in: "world", want: "world"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Runes()
|
||||
if string(got) != c.want {
|
||||
t.Fatalf("NewString(%s).Runes() = %v; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Default(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "", want: "default"},
|
||||
{name: "case2", in: "world", want: "world"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Default("default")
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).Default() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Handle(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", want: "hello"},
|
||||
{name: "case2", in: "world", want: "world"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var w string
|
||||
got := stream.NewString(c.in).Handle(func(s string) {
|
||||
w = s
|
||||
})
|
||||
if w != c.want {
|
||||
t.Fatalf("NewString(%s).Handle() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Update(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "case1", in: "hello", want: "HELLO"},
|
||||
{name: "case2", in: "world", want: "WORLD"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Update(func(s string) string {
|
||||
return stream.NewString(s).ToUpper().String()
|
||||
})
|
||||
if got.String() != c.want {
|
||||
t.Fatalf("NewString(%s).Update() = %s; want %s", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Split(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
sep string
|
||||
want []string
|
||||
}{
|
||||
{name: "case1", in: "hello world", sep: " ", want: []string{"hello", "world"}},
|
||||
{name: "case2", in: "hello,world", sep: ",", want: []string{"hello", "world"}},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Split(c.sep)
|
||||
for i, v := range got {
|
||||
if v != c.want[i] {
|
||||
t.Fatalf("NewString(%s).Split(%s) = %v; want %v", c.in, c.sep, got, c.want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_SplitN(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
sep string
|
||||
n int
|
||||
want []string
|
||||
}{
|
||||
{name: "case1", in: "hello world", sep: " ", n: 2, want: []string{"hello", "world"}},
|
||||
{name: "case2", in: "hello,world", sep: ",", n: 2, want: []string{"hello", "world"}},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).SplitN(c.sep, c.n)
|
||||
for i, v := range got {
|
||||
if v != c.want[i] {
|
||||
t.Fatalf("NewString(%s).SplitN(%s, %d) = %v; want %v", c.in, c.sep, c.n, got, c.want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestString_Batched(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
in string
|
||||
size int
|
||||
want []string
|
||||
}{
|
||||
{name: "case1", in: "hello world", size: 5, want: []string{"hello", " worl", "d"}},
|
||||
{name: "case2", in: "hello,world", size: 5, want: []string{"hello", ",worl", "d"}},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got := stream.NewString(c.in).Batched(c.size)
|
||||
for i, v := range got {
|
||||
if v != c.want[i] {
|
||||
t.Fatalf("NewString(%s).Batched(%d) = %v; want %v", c.in, c.size, got, c.want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
package stream
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// NewStrings 创建字符串切片
|
||||
func NewStrings[S ~string](s ...S) Strings[S] {
|
||||
var slice = make(Strings[S], len(s))
|
||||
for i, v := range s {
|
||||
slice[i] = v
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
// Strings 字符串切片
|
||||
type Strings[S ~string] []S
|
||||
|
||||
// Len 返回切片长度
|
||||
func (s *Strings[S]) Len() int {
|
||||
return len(*s)
|
||||
}
|
||||
|
||||
// Append 添加字符串
|
||||
func (s *Strings[S]) Append(ss ...S) *Strings[S] {
|
||||
*s = append(*s, NewStrings(ss...)...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Clear 清空切片
|
||||
func (s *Strings[S]) Clear() *Strings[S] {
|
||||
*s = make(Strings[S], 0)
|
||||
return s
|
||||
}
|
||||
|
||||
// Copy 复制切片
|
||||
func (s *Strings[S]) Copy() *Strings[S] {
|
||||
ss := make(Strings[S], len(*s))
|
||||
copy(ss, *s)
|
||||
return &ss
|
||||
}
|
||||
|
||||
// Range 返回指定范围的切片
|
||||
func (s *Strings[S]) Range(start, end int) *Strings[S] {
|
||||
*s = (*s)[start:end]
|
||||
return s
|
||||
}
|
||||
|
||||
// First 返回第一个元素
|
||||
func (s *Strings[S]) First() *String[S] {
|
||||
return NewString((*s)[0])
|
||||
}
|
||||
|
||||
// Last 返回最后一个元素
|
||||
func (s *Strings[S]) Last() *String[S] {
|
||||
return NewString((*s)[len(*s)-1])
|
||||
}
|
||||
|
||||
// Index 返回指定的元素
|
||||
func (s *Strings[S]) Index(i int) *String[S] {
|
||||
return NewString((*s)[i])
|
||||
}
|
||||
|
||||
// Reverse 反转切片
|
||||
func (s *Strings[S]) Reverse() *Strings[S] {
|
||||
for i, j := 0, len(*s)-1; i < j; i, j = i+1, j-1 {
|
||||
(*s)[i], (*s)[j] = (*s)[j], (*s)[i]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Desc 降序排序
|
||||
func (s *Strings[S]) Desc() *Strings[S] {
|
||||
sort.Slice(*s, func(i, j int) bool {
|
||||
return (*s)[i] > (*s)[j]
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
// Asc 升序排序
|
||||
func (s *Strings[S]) Asc() *Strings[S] {
|
||||
sort.Slice(*s, func(i, j int) bool {
|
||||
return (*s)[i] < (*s)[j]
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
// Sort 自定义排序
|
||||
func (s *Strings[S]) Sort(f func(int, int) bool) *Strings[S] {
|
||||
sort.Slice(*s, func(i, j int) bool {
|
||||
return f(i, j)
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
// Unique 去重
|
||||
func (s *Strings[S]) Unique() *Strings[S] {
|
||||
m := map[S]struct{}{}
|
||||
for _, v := range *s {
|
||||
m[v] = struct{}{}
|
||||
}
|
||||
*s = make(Strings[S], 0, len(m))
|
||||
for k := range m {
|
||||
*s = append(*s, k)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Delete 删除指定位置的字符串
|
||||
func (s *Strings[S]) Delete(i int) *Strings[S] {
|
||||
*s = append((*s)[:i], (*s)[i+1:]...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Each 遍历切片
|
||||
func (s *Strings[S]) Each(f func(int, S) bool) *Strings[S] {
|
||||
for i, v := range *s {
|
||||
if !f(i, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package stream_test
|
Loading…
Reference in New Issue