优化文件结构、测试文件
This commit is contained in:
parent
c66b0080af
commit
1b6950b8d9
273
helper/func.go
273
helper/func.go
@ -1,15 +1,10 @@
|
|||||||
package helper
|
package helper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dlclark/regexp2"
|
|
||||||
"io"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type IntNumber interface {
|
type IntNumber interface {
|
||||||
@ -40,224 +35,6 @@ func StructColumn[T any, M any](arr []M, field string) (r []T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func RangeSlice[T IntNumber](start, end, step T) []T {
|
|
||||||
if step == 0 {
|
|
||||||
panic("step can't be 0")
|
|
||||||
}
|
|
||||||
l := int((end-start+1)/step + 1)
|
|
||||||
if l < 0 {
|
|
||||||
l = 0 - l
|
|
||||||
}
|
|
||||||
r := make([]T, 0, l)
|
|
||||||
for i := start; ; {
|
|
||||||
r = append(r, i)
|
|
||||||
i = i + step
|
|
||||||
if (step > 0 && i > end) || (step < 0 && i < end) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func StrJoin(s ...string) (str string) {
|
|
||||||
if len(s) == 1 {
|
|
||||||
return s[0]
|
|
||||||
} else if len(s) > 1 {
|
|
||||||
b := strings.Builder{}
|
|
||||||
for _, s2 := range s {
|
|
||||||
b.WriteString(s2)
|
|
||||||
}
|
|
||||||
str = b.String()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func SlicePagination[T any](arr []T, page, pageSize int) []T {
|
|
||||||
start := (page - 1) * pageSize
|
|
||||||
l := len(arr)
|
|
||||||
if start > l {
|
|
||||||
start = l
|
|
||||||
}
|
|
||||||
end := page * pageSize
|
|
||||||
if l < end {
|
|
||||||
end = l
|
|
||||||
}
|
|
||||||
return arr[start:end]
|
|
||||||
}
|
|
||||||
|
|
||||||
func StringMd5(str string) string {
|
|
||||||
h := md5.New()
|
|
||||||
_, err := io.WriteString(h, str)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%x", h.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
var allHtmlTag = regexp.MustCompile("</?.*>")
|
|
||||||
|
|
||||||
func StripTags(str, allowable string) string {
|
|
||||||
html := ""
|
|
||||||
if allowable == "" {
|
|
||||||
return allHtmlTag.ReplaceAllString(str, "")
|
|
||||||
}
|
|
||||||
r := strings.Split(allowable, ">")
|
|
||||||
re := ""
|
|
||||||
for _, reg := range r {
|
|
||||||
if reg == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tag := strings.TrimLeft(reg, "<")
|
|
||||||
ree := fmt.Sprintf(`%s|\/%s`, tag, tag)
|
|
||||||
re = fmt.Sprintf("%s|%s", re, ree)
|
|
||||||
}
|
|
||||||
ree := strings.Trim(re, "|")
|
|
||||||
reg := fmt.Sprintf("<(?!%s).*?>", ree)
|
|
||||||
compile, err := regexp2.Compile(reg, regexp2.IgnoreCase)
|
|
||||||
if err != nil {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
html, err = compile.Replace(str, "", 0, -1)
|
|
||||||
if err != nil {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
return html
|
|
||||||
}
|
|
||||||
|
|
||||||
var tag = regexp.MustCompile(`<(.*?)>`)
|
|
||||||
|
|
||||||
func StripTagsX(str, allowable string) string {
|
|
||||||
if allowable == "" {
|
|
||||||
return allHtmlTag.ReplaceAllString(str, "")
|
|
||||||
}
|
|
||||||
tags := tag.ReplaceAllString(allowable, "$1|")
|
|
||||||
or := strings.TrimRight(tags, "|")
|
|
||||||
reg := fmt.Sprintf(`<(/?(%s).*?)>`, or)
|
|
||||||
regx := fmt.Sprintf(`\{\[(/?(%s).*?)\]\}`, or)
|
|
||||||
cp, err := regexp.Compile(reg)
|
|
||||||
if err != nil {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
rep := cp.ReplaceAllString(str, "{[$1]}")
|
|
||||||
tmp := tag.ReplaceAllString(rep, "")
|
|
||||||
rex, err := regexp.Compile(regx)
|
|
||||||
if err != nil {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
html := rex.ReplaceAllString(tmp, "<$1>")
|
|
||||||
return html
|
|
||||||
}
|
|
||||||
|
|
||||||
var tagx = regexp.MustCompile(`(</?[a-z0-9]+?)( |>)`)
|
|
||||||
var selfCloseTags = map[string]string{"area": "", "base": "", "basefont": "", "br": "", "col": "", "command": "", "embed": "", "frame": "", "hr": "", "img": "", "input": "", "isindex": "", "link": "", "meta": "", "param": "", "source": "", "track": "", "wbr": ""}
|
|
||||||
|
|
||||||
func CloseHtmlTag(str string) string {
|
|
||||||
tags := tag.FindAllString(str, -1)
|
|
||||||
if len(tags) < 1 {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
var tagss = make([]string, 0, len(tags))
|
|
||||||
for _, s := range tags {
|
|
||||||
ss := strings.TrimSpace(tagx.FindString(s))
|
|
||||||
if ss[len(ss)-1] != '>' {
|
|
||||||
ss = fmt.Sprintf("%s>", ss)
|
|
||||||
if _, ok := selfCloseTags[ss]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tagss = append(tagss, ss)
|
|
||||||
}
|
|
||||||
r := SliceMap(SliceReverse(ClearClosedTag(tagss)), func(s string) string {
|
|
||||||
return fmt.Sprintf("</%s>", strings.Trim(s, "<>"))
|
|
||||||
})
|
|
||||||
return strings.Join(r, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func ClearClosedTag(s []string) []string {
|
|
||||||
i := 0
|
|
||||||
for {
|
|
||||||
if len(s[i:]) < 2 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
l := s[i]
|
|
||||||
r := fmt.Sprintf(`</%s>`, strings.Trim(l, "<>"))
|
|
||||||
if s[i+1] == r {
|
|
||||||
if len(s[i+1:]) > 1 {
|
|
||||||
ss := s[:i]
|
|
||||||
s = append(ss, s[i+2:]...)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
s = s[:i]
|
|
||||||
}
|
|
||||||
i = 0
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func SliceMap[T, R any](arr []T, fn func(T) R) []R {
|
|
||||||
r := make([]R, 0, len(arr))
|
|
||||||
for _, t := range arr {
|
|
||||||
r = append(r, fn(t))
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func SliceFilter[T any](arr []T, fn func(T) bool) []T {
|
|
||||||
var r []T
|
|
||||||
for _, t := range arr {
|
|
||||||
if fn(t) {
|
|
||||||
r = append(r, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func SliceReduce[T, R any](arr []T, fn func(T, R) R, r R) R {
|
|
||||||
for _, t := range arr {
|
|
||||||
r = fn(t, r)
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func SliceReverse[T any](arr []T) []T {
|
|
||||||
var r = make([]T, 0, len(arr))
|
|
||||||
for i := len(arr); i > 0; i-- {
|
|
||||||
r = append(r, arr[i-1])
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func SliceSelfReverse[T any](arr []T) []T {
|
|
||||||
l := len(arr)
|
|
||||||
half := l / 2
|
|
||||||
for i := 0; i < half; i++ {
|
|
||||||
arr[i], arr[l-i-1] = arr[l-i-1], arr[i]
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
func SimpleSliceToMap[K comparable, V any](arr []V, fn func(V) K) map[K]V {
|
|
||||||
return SliceToMap(arr, func(v V) (K, V) {
|
|
||||||
return fn(v), v
|
|
||||||
}, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SliceToMap[K comparable, V, T any](arr []V, fn func(V) (K, T), isCoverPrev bool) map[K]T {
|
|
||||||
m := make(map[K]T)
|
|
||||||
for _, v := range arr {
|
|
||||||
k, r := fn(v)
|
|
||||||
if !isCoverPrev {
|
|
||||||
if _, ok := m[k]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m[k] = r
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func RandNum[T IntNumber](start, end T) T {
|
func RandNum[T IntNumber](start, end T) T {
|
||||||
end++
|
end++
|
||||||
return T(rand.Int63n(int64(end-start))) + start
|
return T(rand.Int63n(int64(end-start))) + start
|
||||||
@ -317,56 +94,6 @@ func Sum[T IntNumber | ~float64 | ~float32](a ...T) T {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func SliceChunk[T any](arr []T, size int) [][]T {
|
|
||||||
var r [][]T
|
|
||||||
i := 0
|
|
||||||
for {
|
|
||||||
if len(arr) <= size+i {
|
|
||||||
r = append(r, arr[i:])
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r = append(r, arr[i:i+size])
|
|
||||||
i += size
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func NumberToString[T IntNumber | ~float64 | ~float32](n T) string {
|
func NumberToString[T IntNumber | ~float64 | ~float32](n T) string {
|
||||||
return fmt.Sprintf("%v", n)
|
return fmt.Sprintf("%v", n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Slice[T any](arr []T, offset, length int) (r []T) {
|
|
||||||
l := len(arr)
|
|
||||||
if length == 0 {
|
|
||||||
length = l - offset
|
|
||||||
}
|
|
||||||
if l > offset && l >= offset+length {
|
|
||||||
r = append(make([]T, 0, length), arr[offset:offset+length]...)
|
|
||||||
arr = append(arr[:offset], arr[offset+length:]...)
|
|
||||||
} else if l <= offset {
|
|
||||||
return
|
|
||||||
} else if l > offset && l < offset+length {
|
|
||||||
r = append(make([]T, 0, length), arr[offset:]...)
|
|
||||||
arr = arr[:offset]
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func Comb[T any](arr []T, m int) (r [][]T) {
|
|
||||||
if m == 1 {
|
|
||||||
for _, t := range arr {
|
|
||||||
r = append(r, []T{t})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
l := len(arr) - m
|
|
||||||
for i := 0; i <= l; i++ {
|
|
||||||
next := Slice(arr, i+1, 0)
|
|
||||||
nexRes := Comb(next, m-1)
|
|
||||||
for _, re := range nexRes {
|
|
||||||
t := append([]T{arr[i]}, re...)
|
|
||||||
r = append(r, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
@ -74,382 +74,6 @@ func TestStructColumn(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRangeSlice(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
start int
|
|
||||||
end int
|
|
||||||
step int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want []int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{
|
|
||||||
start: 1,
|
|
||||||
end: 5,
|
|
||||||
step: 1,
|
|
||||||
},
|
|
||||||
want: []int{1, 2, 3, 4, 5},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "t2",
|
|
||||||
args: args{
|
|
||||||
start: 0,
|
|
||||||
end: 5,
|
|
||||||
step: 2,
|
|
||||||
},
|
|
||||||
want: []int{0, 2, 4},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "t3",
|
|
||||||
args: args{
|
|
||||||
start: 1,
|
|
||||||
end: 11,
|
|
||||||
step: 3,
|
|
||||||
},
|
|
||||||
want: []int{1, 4, 7, 10},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "t4",
|
|
||||||
args: args{
|
|
||||||
start: 0,
|
|
||||||
end: -5,
|
|
||||||
step: -1,
|
|
||||||
},
|
|
||||||
want: []int{0, -1, -2, -3, -4, -5},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := RangeSlice(tt.args.start, tt.args.end, tt.args.step); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("RangeSlice() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStrJoin(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
s []string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantStr string
|
|
||||||
}{
|
|
||||||
{name: "t1", args: args{s: []string{"a", "b", "c"}}, wantStr: "abc"},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if gotStr := StrJoin(tt.args.s...); gotStr != tt.wantStr {
|
|
||||||
t.Errorf("StrJoin() = %v, want %v", gotStr, tt.wantStr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSlicePagination(t *testing.T) {
|
|
||||||
arr := RangeSlice[int](1, 10, 1)
|
|
||||||
type args struct {
|
|
||||||
arr []int
|
|
||||||
page int
|
|
||||||
pageSize int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want []int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{
|
|
||||||
arr: arr,
|
|
||||||
page: 1,
|
|
||||||
pageSize: 2,
|
|
||||||
},
|
|
||||||
want: RangeSlice[int](1, 2, 1),
|
|
||||||
}, {
|
|
||||||
name: "t2",
|
|
||||||
args: args{
|
|
||||||
arr: arr,
|
|
||||||
page: 2,
|
|
||||||
pageSize: 2,
|
|
||||||
},
|
|
||||||
want: RangeSlice[int](3, 4, 1),
|
|
||||||
}, {
|
|
||||||
name: "t3",
|
|
||||||
args: args{
|
|
||||||
arr: arr,
|
|
||||||
page: 4,
|
|
||||||
pageSize: 3,
|
|
||||||
},
|
|
||||||
want: []int{10},
|
|
||||||
}, {
|
|
||||||
name: "t4",
|
|
||||||
args: args{
|
|
||||||
arr: arr,
|
|
||||||
page: 5,
|
|
||||||
pageSize: 3,
|
|
||||||
},
|
|
||||||
want: []int{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := SlicePagination(tt.args.arr, tt.args.page, tt.args.pageSize); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("SlicePagination() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStripTags(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
str string
|
|
||||||
allowable string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{
|
|
||||||
str: "<p>ppppp<span>ffff</span></p><img />",
|
|
||||||
allowable: "<p><img>",
|
|
||||||
},
|
|
||||||
want: "<p>pppppffff</p><img />",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := StripTags(tt.args.str, tt.args.allowable); got != tt.want {
|
|
||||||
t.Errorf("StripTags() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStripTagsX(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
str string
|
|
||||||
allowable string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{
|
|
||||||
str: "<p>ppppp<span>ffff</span></p><img />",
|
|
||||||
allowable: "<p><img>",
|
|
||||||
},
|
|
||||||
want: "<p>pppppffff</p><img />",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := StripTagsX(tt.args.str, tt.args.allowable); got != tt.want {
|
|
||||||
t.Errorf("StripTagsX() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkStripTags(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
StripTags(`<p>ppppp<span>ffff</span></p><img />`, "<p><img>")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func BenchmarkStripTagsX(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
StripTagsX(`<p>ppppp<span>ffff</span></p><img />`, "<p><img>")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCloseHtmlTag(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
str string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{str: `<pre class="wp-block-preformatted">GRANT privileges ON databasename.tablename TO 'username'@'h...<p class="read-more"><a href="/p/305">继续阅读</a></p>`},
|
|
||||||
want: "</pre>",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "t2",
|
|
||||||
args: args{str: `<pre><div>`},
|
|
||||||
want: "</div></pre>",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := CloseHtmlTag(tt.args.str); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("CloseHtmlTag() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_clearTag(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
s []string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{s: []string{"<pre>", "<p>", "<span>", "</span>"}},
|
|
||||||
want: []string{"<pre>", "<p>"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "t2",
|
|
||||||
args: args{s: []string{"<pre>", "</pre>", "<div>", "<span>", "</span>"}},
|
|
||||||
want: []string{"<div>"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "t3",
|
|
||||||
args: args{s: []string{"<pre>", "</pre>"}},
|
|
||||||
want: []string{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "t4",
|
|
||||||
args: args{s: []string{"<pre>", "<p>"}},
|
|
||||||
want: []string{"<pre>", "<p>"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := ClearClosedTag(tt.args.s); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("ClearClosedTag() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSliceReduce(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
arr []int
|
|
||||||
fn func(int, int) int
|
|
||||||
r int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{arr: RangeSlice(1, 10, 1), fn: func(i int, i2 int) int {
|
|
||||||
return i + i2
|
|
||||||
}},
|
|
||||||
want: 55,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := SliceReduce(tt.args.arr, tt.args.fn, tt.args.r); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("SliceReduce() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSliceFilter(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
arr []int
|
|
||||||
fn func(int) bool
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want []int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{arr: RangeSlice(1, 10, 1), fn: func(i int) bool {
|
|
||||||
return i > 4
|
|
||||||
}},
|
|
||||||
want: RangeSlice(5, 10, 1),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := SliceFilter(tt.args.arr, tt.args.fn); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("SliceFilter() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSliceMap(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
arr []int8
|
|
||||||
fn func(int8) int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want []int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{
|
|
||||||
arr: RangeSlice[int8](1, 10, 1),
|
|
||||||
fn: func(i int8) int {
|
|
||||||
return int(i)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: RangeSlice(1, 10, 1),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := SliceMap(tt.args.arr, tt.args.fn); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("SliceMap() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSliceReverse(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
arr []int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want []int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{arr: RangeSlice(1, 10, 1)},
|
|
||||||
want: RangeSlice(10, 1, -1),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := SliceReverse(tt.args.arr); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("SliceReverse() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToInterface(t *testing.T) {
|
func TestToInterface(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
v int
|
v int
|
||||||
@ -474,111 +98,6 @@ func TestToInterface(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSliceSelfReverse(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
arr []int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want []int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{
|
|
||||||
arr: RangeSlice(1, 10, 1),
|
|
||||||
},
|
|
||||||
want: RangeSlice(10, 1, -1),
|
|
||||||
}, {
|
|
||||||
name: "t2",
|
|
||||||
args: args{
|
|
||||||
arr: RangeSlice(1, 9, 1),
|
|
||||||
},
|
|
||||||
want: RangeSlice(9, 1, -1),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := SliceSelfReverse(tt.args.arr); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("SliceSelfReverse() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSliceToMap(t *testing.T) {
|
|
||||||
type ss struct {
|
|
||||||
id int
|
|
||||||
v string
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
arr []ss
|
|
||||||
fn func(ss) (int, ss)
|
|
||||||
isCoverPrev bool
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want map[int]ss
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{
|
|
||||||
arr: []ss{{1, "k1"}, {2, "v2"}, {2, "v3"}},
|
|
||||||
fn: func(s ss) (int, ss) {
|
|
||||||
return s.id, s
|
|
||||||
},
|
|
||||||
isCoverPrev: true,
|
|
||||||
},
|
|
||||||
want: map[int]ss{1: {1, "k1"}, 2: {2, "v3"}},
|
|
||||||
}, {
|
|
||||||
name: "t2",
|
|
||||||
args: args{
|
|
||||||
arr: []ss{{1, "k1"}, {2, "v2"}, {2, "v3"}},
|
|
||||||
fn: func(s ss) (int, ss) {
|
|
||||||
return s.id, s
|
|
||||||
},
|
|
||||||
isCoverPrev: false,
|
|
||||||
},
|
|
||||||
want: map[int]ss{1: {1, "k1"}, 2: {2, "v2"}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := SliceToMap(tt.args.arr, tt.args.fn, tt.args.isCoverPrev); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("SliceToMap() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSimpleSliceToMap(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
arr []int
|
|
||||||
fn func(int) int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want map[int]int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{arr: []int{1, 2, 3}, fn: func(i int) int {
|
|
||||||
return i
|
|
||||||
}},
|
|
||||||
want: map[int]int{1: 1, 2: 2, 3: 3},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := SimpleSliceToMap(tt.args.arr, tt.args.fn); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("SimpleSliceToMap() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRandNum(t *testing.T) {
|
func TestRandNum(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
start int
|
start int
|
||||||
@ -709,42 +228,6 @@ func TestMax(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSliceChunk(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
arr []int
|
|
||||||
size int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want [][]int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{
|
|
||||||
arr: RangeSlice(1, 7, 1),
|
|
||||||
size: 2,
|
|
||||||
},
|
|
||||||
want: [][]int{{1, 2}, {3, 4}, {5, 6}, {7}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "t2",
|
|
||||||
args: args{
|
|
||||||
arr: RangeSlice(1, 8, 1),
|
|
||||||
size: 2,
|
|
||||||
},
|
|
||||||
want: [][]int{{1, 2}, {3, 4}, {5, 6}, {7, 8}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := SliceChunk(tt.args.arr, tt.args.size); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("SliceChunk() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSum(t *testing.T) {
|
func TestSum(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
a []int
|
a []int
|
||||||
@ -797,114 +280,3 @@ func TestNumberToString(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSlice(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
arr []int
|
|
||||||
offset int
|
|
||||||
length int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantR []int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{
|
|
||||||
arr: RangeSlice(1, 10, 1),
|
|
||||||
offset: 3,
|
|
||||||
length: 2,
|
|
||||||
},
|
|
||||||
wantR: RangeSlice(4, 5, 1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "t2",
|
|
||||||
args: args{
|
|
||||||
arr: RangeSlice(1, 10, 1),
|
|
||||||
offset: 3,
|
|
||||||
length: 0,
|
|
||||||
},
|
|
||||||
wantR: RangeSlice(4, 10, 1),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if gotR := Slice(tt.args.arr, tt.args.offset, tt.args.length); !reflect.DeepEqual(gotR, tt.wantR) {
|
|
||||||
t.Errorf("Slice() = %v, want %v", gotR, tt.wantR)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestComb(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
arr []int
|
|
||||||
m int
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantR [][]int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "t1",
|
|
||||||
args: args{
|
|
||||||
arr: RangeSlice(1, 5, 1),
|
|
||||||
m: 2,
|
|
||||||
},
|
|
||||||
wantR: [][]int{
|
|
||||||
{1, 2},
|
|
||||||
{1, 3},
|
|
||||||
{1, 4},
|
|
||||||
{1, 5},
|
|
||||||
{2, 3},
|
|
||||||
{2, 4},
|
|
||||||
{2, 5},
|
|
||||||
{3, 4},
|
|
||||||
{3, 5},
|
|
||||||
{4, 5},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "t2",
|
|
||||||
args: args{
|
|
||||||
arr: RangeSlice(1, 5, 1),
|
|
||||||
m: 3,
|
|
||||||
},
|
|
||||||
wantR: [][]int{
|
|
||||||
{1, 2, 3},
|
|
||||||
{1, 2, 4},
|
|
||||||
{1, 2, 5},
|
|
||||||
{1, 3, 4},
|
|
||||||
{1, 3, 5},
|
|
||||||
{1, 4, 5},
|
|
||||||
{2, 3, 4},
|
|
||||||
{2, 3, 5},
|
|
||||||
{2, 4, 5},
|
|
||||||
{3, 4, 5},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "t3",
|
|
||||||
args: args{
|
|
||||||
arr: RangeSlice(1, 5, 1),
|
|
||||||
m: 4,
|
|
||||||
},
|
|
||||||
wantR: [][]int{
|
|
||||||
{1, 2, 3, 4},
|
|
||||||
{1, 2, 3, 5},
|
|
||||||
{1, 2, 4, 5},
|
|
||||||
{1, 3, 4, 5},
|
|
||||||
{2, 3, 4, 5},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if gotR := Comb(tt.args.arr, tt.args.m); !reflect.DeepEqual(gotR, tt.wantR) {
|
|
||||||
t.Errorf("Comb() = %v, want %v", gotR, tt.wantR)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
105
helper/html.go
105
helper/html.go
@ -1,6 +1,9 @@
|
|||||||
package helper
|
package helper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/dlclark/regexp2"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -58,3 +61,105 @@ func htmlSpecialCharsDecode(text string, flags int) string {
|
|||||||
}
|
}
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var allHtmlTag = regexp.MustCompile("</?.*>")
|
||||||
|
|
||||||
|
func StripTags(str, allowable string) string {
|
||||||
|
html := ""
|
||||||
|
if allowable == "" {
|
||||||
|
return allHtmlTag.ReplaceAllString(str, "")
|
||||||
|
}
|
||||||
|
r := strings.Split(allowable, ">")
|
||||||
|
re := ""
|
||||||
|
for _, reg := range r {
|
||||||
|
if reg == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tag := strings.TrimLeft(reg, "<")
|
||||||
|
ree := fmt.Sprintf(`%s|\/%s`, tag, tag)
|
||||||
|
re = fmt.Sprintf("%s|%s", re, ree)
|
||||||
|
}
|
||||||
|
ree := strings.Trim(re, "|")
|
||||||
|
reg := fmt.Sprintf("<(?!%s).*?>", ree)
|
||||||
|
compile, err := regexp2.Compile(reg, regexp2.IgnoreCase)
|
||||||
|
if err != nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
html, err = compile.Replace(str, "", 0, -1)
|
||||||
|
if err != nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
|
||||||
|
var tag = regexp.MustCompile(`<(.*?)>`)
|
||||||
|
|
||||||
|
func StripTagsX(str, allowable string) string {
|
||||||
|
if allowable == "" {
|
||||||
|
return allHtmlTag.ReplaceAllString(str, "")
|
||||||
|
}
|
||||||
|
tags := tag.ReplaceAllString(allowable, "$1|")
|
||||||
|
or := strings.TrimRight(tags, "|")
|
||||||
|
reg := fmt.Sprintf(`<(/?(%s).*?)>`, or)
|
||||||
|
regx := fmt.Sprintf(`\{\[(/?(%s).*?)\]\}`, or)
|
||||||
|
cp, err := regexp.Compile(reg)
|
||||||
|
if err != nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
rep := cp.ReplaceAllString(str, "{[$1]}")
|
||||||
|
tmp := tag.ReplaceAllString(rep, "")
|
||||||
|
rex, err := regexp.Compile(regx)
|
||||||
|
if err != nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
html := rex.ReplaceAllString(tmp, "<$1>")
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
|
||||||
|
var tagx = regexp.MustCompile(`(</?[a-z0-9]+?)( |>)`)
|
||||||
|
var selfCloseTags = map[string]string{"area": "", "base": "", "basefont": "", "br": "", "col": "", "command": "", "embed": "", "frame": "", "hr": "", "img": "", "input": "", "isindex": "", "link": "", "meta": "", "param": "", "source": "", "track": "", "wbr": ""}
|
||||||
|
|
||||||
|
func CloseHtmlTag(str string) string {
|
||||||
|
tags := tag.FindAllString(str, -1)
|
||||||
|
if len(tags) < 1 {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
var tagss = make([]string, 0, len(tags))
|
||||||
|
for _, s := range tags {
|
||||||
|
ss := strings.TrimSpace(tagx.FindString(s))
|
||||||
|
if ss[len(ss)-1] != '>' {
|
||||||
|
ss = fmt.Sprintf("%s>", ss)
|
||||||
|
if _, ok := selfCloseTags[ss]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tagss = append(tagss, ss)
|
||||||
|
}
|
||||||
|
r := SliceMap(SliceReverse(ClearClosedTag(tagss)), func(s string) string {
|
||||||
|
return fmt.Sprintf("</%s>", strings.Trim(s, "<>"))
|
||||||
|
})
|
||||||
|
return strings.Join(r, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClearClosedTag(s []string) []string {
|
||||||
|
i := 0
|
||||||
|
for {
|
||||||
|
if len(s[i:]) < 2 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
l := s[i]
|
||||||
|
r := fmt.Sprintf(`</%s>`, strings.Trim(l, "<>"))
|
||||||
|
if s[i+1] == r {
|
||||||
|
if len(s[i+1:]) > 1 {
|
||||||
|
ss := s[:i]
|
||||||
|
s = append(ss, s[i+2:]...)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
s = s[:i]
|
||||||
|
}
|
||||||
|
i = 0
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package helper
|
package helper
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func Test_htmlSpecialChars(t *testing.T) {
|
func Test_htmlSpecialChars(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
@ -90,3 +93,138 @@ func Test_htmlSpecialCharsDecode(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStripTags(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
str string
|
||||||
|
allowable string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{
|
||||||
|
str: "<p>ppppp<span>ffff</span></p><img />",
|
||||||
|
allowable: "<p><img>",
|
||||||
|
},
|
||||||
|
want: "<p>pppppffff</p><img />",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := StripTags(tt.args.str, tt.args.allowable); got != tt.want {
|
||||||
|
t.Errorf("StripTags() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStripTagsX(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
str string
|
||||||
|
allowable string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{
|
||||||
|
str: "<p>ppppp<span>ffff</span></p><img />",
|
||||||
|
allowable: "<p><img>",
|
||||||
|
},
|
||||||
|
want: "<p>pppppffff</p><img />",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := StripTagsX(tt.args.str, tt.args.allowable); got != tt.want {
|
||||||
|
t.Errorf("StripTagsX() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkStripTags(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
StripTags(`<p>ppppp<span>ffff</span></p><img />`, "<p><img>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func BenchmarkStripTagsX(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
StripTagsX(`<p>ppppp<span>ffff</span></p><img />`, "<p><img>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloseHtmlTag(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
str string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{str: `<pre class="wp-block-preformatted">GRANT privileges ON databasename.tablename TO 'username'@'h...<p class="read-more"><a href="/p/305">继续阅读</a></p>`},
|
||||||
|
want: "</pre>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "t2",
|
||||||
|
args: args{str: `<pre><div>`},
|
||||||
|
want: "</div></pre>",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := CloseHtmlTag(tt.args.str); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("CloseHtmlTag() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_clearTag(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
s []string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{s: []string{"<pre>", "<p>", "<span>", "</span>"}},
|
||||||
|
want: []string{"<pre>", "<p>"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "t2",
|
||||||
|
args: args{s: []string{"<pre>", "</pre>", "<div>", "<span>", "</span>"}},
|
||||||
|
want: []string{"<div>"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "t3",
|
||||||
|
args: args{s: []string{"<pre>", "</pre>"}},
|
||||||
|
want: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "t4",
|
||||||
|
args: args{s: []string{"<pre>", "<p>"}},
|
||||||
|
want: []string{"<pre>", "<p>"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := ClearClosedTag(tt.args.s); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("ClearClosedTag() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
145
helper/slice.go
Normal file
145
helper/slice.go
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package helper
|
||||||
|
|
||||||
|
func SliceMap[T, R any](arr []T, fn func(T) R) []R {
|
||||||
|
r := make([]R, 0, len(arr))
|
||||||
|
for _, t := range arr {
|
||||||
|
r = append(r, fn(t))
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceFilter[T any](arr []T, fn func(T) bool) []T {
|
||||||
|
var r []T
|
||||||
|
for _, t := range arr {
|
||||||
|
if fn(t) {
|
||||||
|
r = append(r, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceReduce[T, R any](arr []T, fn func(T, R) R, r R) R {
|
||||||
|
for _, t := range arr {
|
||||||
|
r = fn(t, r)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceReverse[T any](arr []T) []T {
|
||||||
|
var r = make([]T, 0, len(arr))
|
||||||
|
for i := len(arr); i > 0; i-- {
|
||||||
|
r = append(r, arr[i-1])
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceSelfReverse[T any](arr []T) []T {
|
||||||
|
l := len(arr)
|
||||||
|
half := l / 2
|
||||||
|
for i := 0; i < half; i++ {
|
||||||
|
arr[i], arr[l-i-1] = arr[l-i-1], arr[i]
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
func SimpleSliceToMap[K comparable, V any](arr []V, fn func(V) K) map[K]V {
|
||||||
|
return SliceToMap(arr, func(v V) (K, V) {
|
||||||
|
return fn(v), v
|
||||||
|
}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceToMap[K comparable, V, T any](arr []V, fn func(V) (K, T), isCoverPrev bool) map[K]T {
|
||||||
|
m := make(map[K]T)
|
||||||
|
for _, v := range arr {
|
||||||
|
k, r := fn(v)
|
||||||
|
if !isCoverPrev {
|
||||||
|
if _, ok := m[k]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m[k] = r
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func RangeSlice[T IntNumber](start, end, step T) []T {
|
||||||
|
if step == 0 {
|
||||||
|
panic("step can't be 0")
|
||||||
|
}
|
||||||
|
l := int((end-start+1)/step + 1)
|
||||||
|
if l < 0 {
|
||||||
|
l = 0 - l
|
||||||
|
}
|
||||||
|
r := make([]T, 0, l)
|
||||||
|
for i := start; ; {
|
||||||
|
r = append(r, i)
|
||||||
|
i = i + step
|
||||||
|
if (step > 0 && i > end) || (step < 0 && i < end) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func SlicePagination[T any](arr []T, page, pageSize int) []T {
|
||||||
|
start := (page - 1) * pageSize
|
||||||
|
l := len(arr)
|
||||||
|
if start > l {
|
||||||
|
start = l
|
||||||
|
}
|
||||||
|
end := page * pageSize
|
||||||
|
if l < end {
|
||||||
|
end = l
|
||||||
|
}
|
||||||
|
return arr[start:end]
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceChunk[T any](arr []T, size int) [][]T {
|
||||||
|
var r [][]T
|
||||||
|
i := 0
|
||||||
|
for {
|
||||||
|
if len(arr) <= size+i {
|
||||||
|
r = append(r, arr[i:])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
r = append(r, arr[i:i+size])
|
||||||
|
i += size
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func Slice[T any](arr []T, offset, length int) (r []T) {
|
||||||
|
l := len(arr)
|
||||||
|
if length == 0 {
|
||||||
|
length = l - offset
|
||||||
|
}
|
||||||
|
if l > offset && l >= offset+length {
|
||||||
|
r = append(make([]T, 0, length), arr[offset:offset+length]...)
|
||||||
|
arr = append(arr[:offset], arr[offset+length:]...)
|
||||||
|
} else if l <= offset {
|
||||||
|
return
|
||||||
|
} else if l > offset && l < offset+length {
|
||||||
|
r = append(make([]T, 0, length), arr[offset:]...)
|
||||||
|
arr = arr[:offset]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Comb[T any](arr []T, m int) (r [][]T) {
|
||||||
|
if m == 1 {
|
||||||
|
for _, t := range arr {
|
||||||
|
r = append(r, []T{t})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l := len(arr) - m
|
||||||
|
for i := 0; i <= l; i++ {
|
||||||
|
next := Slice(arr, i+1, 0)
|
||||||
|
nexRes := Comb(next, m-1)
|
||||||
|
for _, re := range nexRes {
|
||||||
|
t := append([]T{arr[i]}, re...)
|
||||||
|
r = append(r, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
479
helper/slice_test.go
Normal file
479
helper/slice_test.go
Normal file
@ -0,0 +1,479 @@
|
|||||||
|
package helper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSlicePagination(t *testing.T) {
|
||||||
|
arr := RangeSlice[int](1, 10, 1)
|
||||||
|
type args struct {
|
||||||
|
arr []int
|
||||||
|
page int
|
||||||
|
pageSize int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{
|
||||||
|
arr: arr,
|
||||||
|
page: 1,
|
||||||
|
pageSize: 2,
|
||||||
|
},
|
||||||
|
want: RangeSlice[int](1, 2, 1),
|
||||||
|
}, {
|
||||||
|
name: "t2",
|
||||||
|
args: args{
|
||||||
|
arr: arr,
|
||||||
|
page: 2,
|
||||||
|
pageSize: 2,
|
||||||
|
},
|
||||||
|
want: RangeSlice[int](3, 4, 1),
|
||||||
|
}, {
|
||||||
|
name: "t3",
|
||||||
|
args: args{
|
||||||
|
arr: arr,
|
||||||
|
page: 4,
|
||||||
|
pageSize: 3,
|
||||||
|
},
|
||||||
|
want: []int{10},
|
||||||
|
}, {
|
||||||
|
name: "t4",
|
||||||
|
args: args{
|
||||||
|
arr: arr,
|
||||||
|
page: 5,
|
||||||
|
pageSize: 3,
|
||||||
|
},
|
||||||
|
want: []int{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := SlicePagination(tt.args.arr, tt.args.page, tt.args.pageSize); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("SlicePagination() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSliceReduce(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
arr []int
|
||||||
|
fn func(int, int) int
|
||||||
|
r int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{arr: RangeSlice(1, 10, 1), fn: func(i int, i2 int) int {
|
||||||
|
return i + i2
|
||||||
|
}},
|
||||||
|
want: 55,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := SliceReduce(tt.args.arr, tt.args.fn, tt.args.r); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("SliceReduce() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSliceFilter(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
arr []int
|
||||||
|
fn func(int) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{arr: RangeSlice(1, 10, 1), fn: func(i int) bool {
|
||||||
|
return i > 4
|
||||||
|
}},
|
||||||
|
want: RangeSlice(5, 10, 1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := SliceFilter(tt.args.arr, tt.args.fn); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("SliceFilter() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSliceMap(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
arr []int8
|
||||||
|
fn func(int8) int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{
|
||||||
|
arr: RangeSlice[int8](1, 10, 1),
|
||||||
|
fn: func(i int8) int {
|
||||||
|
return int(i)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: RangeSlice(1, 10, 1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := SliceMap(tt.args.arr, tt.args.fn); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("SliceMap() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSliceReverse(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
arr []int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{arr: RangeSlice(1, 10, 1)},
|
||||||
|
want: RangeSlice(10, 1, -1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := SliceReverse(tt.args.arr); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("SliceReverse() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRangeSlice(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
start int
|
||||||
|
end int
|
||||||
|
step int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{
|
||||||
|
start: 1,
|
||||||
|
end: 5,
|
||||||
|
step: 1,
|
||||||
|
},
|
||||||
|
want: []int{1, 2, 3, 4, 5},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "t2",
|
||||||
|
args: args{
|
||||||
|
start: 0,
|
||||||
|
end: 5,
|
||||||
|
step: 2,
|
||||||
|
},
|
||||||
|
want: []int{0, 2, 4},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "t3",
|
||||||
|
args: args{
|
||||||
|
start: 1,
|
||||||
|
end: 11,
|
||||||
|
step: 3,
|
||||||
|
},
|
||||||
|
want: []int{1, 4, 7, 10},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "t4",
|
||||||
|
args: args{
|
||||||
|
start: 0,
|
||||||
|
end: -5,
|
||||||
|
step: -1,
|
||||||
|
},
|
||||||
|
want: []int{0, -1, -2, -3, -4, -5},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := RangeSlice(tt.args.start, tt.args.end, tt.args.step); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("RangeSlice() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSlice(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
arr []int
|
||||||
|
offset int
|
||||||
|
length int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantR []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{
|
||||||
|
arr: RangeSlice(1, 10, 1),
|
||||||
|
offset: 3,
|
||||||
|
length: 2,
|
||||||
|
},
|
||||||
|
wantR: RangeSlice(4, 5, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "t2",
|
||||||
|
args: args{
|
||||||
|
arr: RangeSlice(1, 10, 1),
|
||||||
|
offset: 3,
|
||||||
|
length: 0,
|
||||||
|
},
|
||||||
|
wantR: RangeSlice(4, 10, 1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if gotR := Slice(tt.args.arr, tt.args.offset, tt.args.length); !reflect.DeepEqual(gotR, tt.wantR) {
|
||||||
|
t.Errorf("Slice() = %v, want %v", gotR, tt.wantR)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComb(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
arr []int
|
||||||
|
m int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantR [][]int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{
|
||||||
|
arr: RangeSlice(1, 5, 1),
|
||||||
|
m: 2,
|
||||||
|
},
|
||||||
|
wantR: [][]int{
|
||||||
|
{1, 2},
|
||||||
|
{1, 3},
|
||||||
|
{1, 4},
|
||||||
|
{1, 5},
|
||||||
|
{2, 3},
|
||||||
|
{2, 4},
|
||||||
|
{2, 5},
|
||||||
|
{3, 4},
|
||||||
|
{3, 5},
|
||||||
|
{4, 5},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "t2",
|
||||||
|
args: args{
|
||||||
|
arr: RangeSlice(1, 5, 1),
|
||||||
|
m: 3,
|
||||||
|
},
|
||||||
|
wantR: [][]int{
|
||||||
|
{1, 2, 3},
|
||||||
|
{1, 2, 4},
|
||||||
|
{1, 2, 5},
|
||||||
|
{1, 3, 4},
|
||||||
|
{1, 3, 5},
|
||||||
|
{1, 4, 5},
|
||||||
|
{2, 3, 4},
|
||||||
|
{2, 3, 5},
|
||||||
|
{2, 4, 5},
|
||||||
|
{3, 4, 5},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "t3",
|
||||||
|
args: args{
|
||||||
|
arr: RangeSlice(1, 5, 1),
|
||||||
|
m: 4,
|
||||||
|
},
|
||||||
|
wantR: [][]int{
|
||||||
|
{1, 2, 3, 4},
|
||||||
|
{1, 2, 3, 5},
|
||||||
|
{1, 2, 4, 5},
|
||||||
|
{1, 3, 4, 5},
|
||||||
|
{2, 3, 4, 5},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if gotR := Comb(tt.args.arr, tt.args.m); !reflect.DeepEqual(gotR, tt.wantR) {
|
||||||
|
t.Errorf("Comb() = %v, want %v", gotR, tt.wantR)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSliceChunk(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
arr []int
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want [][]int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{
|
||||||
|
arr: RangeSlice(1, 7, 1),
|
||||||
|
size: 2,
|
||||||
|
},
|
||||||
|
want: [][]int{{1, 2}, {3, 4}, {5, 6}, {7}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "t2",
|
||||||
|
args: args{
|
||||||
|
arr: RangeSlice(1, 8, 1),
|
||||||
|
size: 2,
|
||||||
|
},
|
||||||
|
want: [][]int{{1, 2}, {3, 4}, {5, 6}, {7, 8}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := SliceChunk(tt.args.arr, tt.args.size); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("SliceChunk() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSimpleSliceToMap(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
arr []int
|
||||||
|
fn func(int) int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want map[int]int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{arr: []int{1, 2, 3}, fn: func(i int) int {
|
||||||
|
return i
|
||||||
|
}},
|
||||||
|
want: map[int]int{1: 1, 2: 2, 3: 3},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := SimpleSliceToMap(tt.args.arr, tt.args.fn); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("SimpleSliceToMap() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSliceToMap(t *testing.T) {
|
||||||
|
type ss struct {
|
||||||
|
id int
|
||||||
|
v string
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
arr []ss
|
||||||
|
fn func(ss) (int, ss)
|
||||||
|
isCoverPrev bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want map[int]ss
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{
|
||||||
|
arr: []ss{{1, "k1"}, {2, "v2"}, {2, "v3"}},
|
||||||
|
fn: func(s ss) (int, ss) {
|
||||||
|
return s.id, s
|
||||||
|
},
|
||||||
|
isCoverPrev: true,
|
||||||
|
},
|
||||||
|
want: map[int]ss{1: {1, "k1"}, 2: {2, "v3"}},
|
||||||
|
}, {
|
||||||
|
name: "t2",
|
||||||
|
args: args{
|
||||||
|
arr: []ss{{1, "k1"}, {2, "v2"}, {2, "v3"}},
|
||||||
|
fn: func(s ss) (int, ss) {
|
||||||
|
return s.id, s
|
||||||
|
},
|
||||||
|
isCoverPrev: false,
|
||||||
|
},
|
||||||
|
want: map[int]ss{1: {1, "k1"}, 2: {2, "v2"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := SliceToMap(tt.args.arr, tt.args.fn, tt.args.isCoverPrev); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("SliceToMap() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSliceSelfReverse(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
arr []int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{
|
||||||
|
arr: RangeSlice(1, 10, 1),
|
||||||
|
},
|
||||||
|
want: RangeSlice(10, 1, -1),
|
||||||
|
}, {
|
||||||
|
name: "t2",
|
||||||
|
args: args{
|
||||||
|
arr: RangeSlice(1, 9, 1),
|
||||||
|
},
|
||||||
|
want: RangeSlice(9, 1, -1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := SliceSelfReverse(tt.args.arr); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("SliceSelfReverse() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
30
helper/strings.go
Normal file
30
helper/strings.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package helper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func StrJoin(s ...string) (str string) {
|
||||||
|
if len(s) == 1 {
|
||||||
|
return s[0]
|
||||||
|
} else if len(s) > 1 {
|
||||||
|
b := strings.Builder{}
|
||||||
|
for _, s2 := range s {
|
||||||
|
b.WriteString(s2)
|
||||||
|
}
|
||||||
|
str = b.String()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func StringMd5(str string) string {
|
||||||
|
h := md5.New()
|
||||||
|
_, err := io.WriteString(h, str)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%x", h.Sum(nil))
|
||||||
|
}
|
23
helper/strings_test.go
Normal file
23
helper/strings_test.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package helper
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestStrJoin(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
s []string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantStr string
|
||||||
|
}{
|
||||||
|
{name: "t1", args: args{s: []string{"a", "b", "c"}}, wantStr: "abc"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if gotStr := StrJoin(tt.args.s...); gotStr != tt.wantStr {
|
||||||
|
t.Errorf("StrJoin() = %v, want %v", gotStr, tt.wantStr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -2,9 +2,11 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"github/fthvgb1/wp-go/helper"
|
||||||
"github/fthvgb1/wp-go/internal/config"
|
"github/fthvgb1/wp-go/internal/config"
|
||||||
"github/fthvgb1/wp-go/internal/db"
|
"github/fthvgb1/wp-go/internal/db"
|
||||||
wp2 "github/fthvgb1/wp-go/internal/models"
|
"github/fthvgb1/wp-go/internal/models"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -34,7 +36,7 @@ func TestFind(t *testing.T) {
|
|||||||
in [][]any
|
in [][]any
|
||||||
}
|
}
|
||||||
type posts struct {
|
type posts struct {
|
||||||
wp2.Posts
|
models.Posts
|
||||||
N int `db:"n"`
|
N int `db:"n"`
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@ -105,7 +107,7 @@ func TestFind(t *testing.T) {
|
|||||||
in: nil,
|
in: nil,
|
||||||
},
|
},
|
||||||
wantR: func() []posts {
|
wantR: func() []posts {
|
||||||
r, err := Select[posts](ctx, "select post_status,count(*) n from "+wp2.Posts{}.Table()+" where ID<1000 group by post_status having n>1")
|
r, err := Select[posts](ctx, "select post_status,count(*) n from "+models.Posts{}.Table()+" where ID<1000 group by post_status having n>1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -150,10 +152,10 @@ func TestFind(t *testing.T) {
|
|||||||
group: "a.post_author",
|
group: "a.post_author",
|
||||||
order: SqlBuilder{{"n", "desc"}},
|
order: SqlBuilder{{"n", "desc"}},
|
||||||
join: SqlBuilder{
|
join: SqlBuilder{
|
||||||
{"a", "left join", wp2.Users{}.Table() + " b", "a.post_author=b.ID"},
|
{"a", "left join", models.Users{}.Table() + " b", "a.post_author=b.ID"},
|
||||||
{"left join", "wp_term_relationships c", "a.Id=c.object_id"},
|
{"left join", "wp_term_relationships c", "a.Id=c.object_id"},
|
||||||
{"left join", wp2.TermTaxonomy{}.Table() + " d", "c.term_taxonomy_id=d.term_taxonomy_id"},
|
{"left join", models.TermTaxonomy{}.Table() + " d", "c.term_taxonomy_id=d.term_taxonomy_id"},
|
||||||
{"left join", wp2.Terms{}.Table() + " e", "d.term_id=e.term_id"},
|
{"left join", models.Terms{}.Table() + " e", "d.term_id=e.term_id"},
|
||||||
},
|
},
|
||||||
having: SqlBuilder{{"n", ">", "0", "int"}},
|
having: SqlBuilder{{"n", ">", "0", "int"}},
|
||||||
limit: 10,
|
limit: 10,
|
||||||
@ -191,7 +193,7 @@ func TestFindOneById(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
want wp2.Posts
|
want models.Posts
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@ -199,10 +201,12 @@ func TestFindOneById(t *testing.T) {
|
|||||||
args: args{
|
args: args{
|
||||||
1,
|
1,
|
||||||
},
|
},
|
||||||
want: func() wp2.Posts {
|
want: func() models.Posts {
|
||||||
r, err := Get[wp2.Posts](ctx, "select * from "+wp2.Posts{}.Table()+" where ID=?", 1)
|
r, err := Get[models.Posts](ctx, "select * from "+models.Posts{}.Table()+" where ID=?", 1)
|
||||||
if err != nil {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
} else if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}(),
|
}(),
|
||||||
@ -211,7 +215,10 @@ func TestFindOneById(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got, err := FindOneById[wp2.Posts](ctx, tt.args.id)
|
got, err := FindOneById[models.Posts](ctx, tt.args.id)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("FindOneById() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("FindOneById() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
@ -233,7 +240,7 @@ func TestFirstOne(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
want wp2.Posts
|
want models.Posts
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@ -245,10 +252,12 @@ func TestFirstOne(t *testing.T) {
|
|||||||
in: nil,
|
in: nil,
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
want: func() wp2.Posts {
|
want: func() models.Posts {
|
||||||
r, err := Get[wp2.Posts](ctx, "select * from "+wp2.Posts{}.Table()+" where post_status='publish' order by ID desc limit 1")
|
r, err := Get[models.Posts](ctx, "select * from "+models.Posts{}.Table()+" where post_status='publish' order by ID desc limit 1")
|
||||||
if err != nil {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
} else if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}(),
|
}(),
|
||||||
@ -256,7 +265,7 @@ func TestFirstOne(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got, err := FirstOne[wp2.Posts](ctx, tt.args.where, tt.args.fields, tt.args.order, tt.args.in...)
|
got, err := FirstOne[models.Posts](ctx, tt.args.where, tt.args.fields, tt.args.order, tt.args.in...)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("FirstOne() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("FirstOne() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
@ -268,33 +277,6 @@ func TestFirstOne(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
sql string
|
|
||||||
params []any
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantR wp2.Posts
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
gotR, err := Get[wp2.Posts](ctx, tt.args.sql, tt.args.params...)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(gotR, tt.wantR) {
|
|
||||||
t.Errorf("Get() gotR = %v, want %v", gotR, tt.wantR)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLastOne(t *testing.T) {
|
func TestLastOne(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
where ParseWhere
|
where ParseWhere
|
||||||
@ -304,7 +286,7 @@ func TestLastOne(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
want wp2.Posts
|
want models.Posts
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@ -316,8 +298,8 @@ func TestLastOne(t *testing.T) {
|
|||||||
fields: "*",
|
fields: "*",
|
||||||
in: nil,
|
in: nil,
|
||||||
},
|
},
|
||||||
want: func() wp2.Posts {
|
want: func() models.Posts {
|
||||||
r, err := Get[wp2.Posts](ctx, "select * from "+wp2.Posts{}.Table()+" where post_status='publish' order by "+wp2.Posts{}.PrimaryKey()+" desc limit 1")
|
r, err := Get[models.Posts](ctx, "select * from "+models.Posts{}.Table()+" where post_status='publish' order by "+models.Posts{}.PrimaryKey()+" desc limit 1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -327,7 +309,7 @@ func TestLastOne(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got, err := LastOne[wp2.Posts](ctx, tt.args.where, tt.args.fields, tt.args.in...)
|
got, err := LastOne[models.Posts](ctx, tt.args.where, tt.args.fields, tt.args.in...)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("LastOne() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("LastOne() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
@ -339,33 +321,6 @@ func TestLastOne(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelect(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
sql string
|
|
||||||
params []any
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want []wp2.Posts
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, err := Select[wp2.Posts](ctx, tt.args.sql, tt.args.params...)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("Select() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("Select() got = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSimpleFind(t *testing.T) {
|
func TestSimpleFind(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
where ParseWhere
|
where ParseWhere
|
||||||
@ -375,14 +330,33 @@ func TestSimpleFind(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
want []wp2.Posts
|
want []models.Posts
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{},
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{
|
||||||
|
where: SqlBuilder{
|
||||||
|
{"ID", "in", ""},
|
||||||
|
},
|
||||||
|
fields: "*",
|
||||||
|
in: [][]any{{1, 2}},
|
||||||
|
},
|
||||||
|
want: func() (r []models.Posts) {
|
||||||
|
r, err := Select[models.Posts](ctx, "select * from "+models.Posts{}.Table()+" where ID in (?,?)", 1, 2)
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
panic(err)
|
||||||
|
} else if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}(),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got, err := SimpleFind[wp2.Posts](ctx, tt.args.where, tt.args.fields, tt.args.in...)
|
got, err := SimpleFind[models.Posts](ctx, tt.args.where, tt.args.fields, tt.args.in...)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("SimpleFind() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("SimpleFind() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
@ -409,15 +383,41 @@ func TestSimplePagination(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
wantR []wp2.Posts
|
wantR []models.Posts
|
||||||
wantTotal int
|
wantTotal int
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{},
|
{
|
||||||
|
name: "t1",
|
||||||
|
args: args{
|
||||||
|
where: SqlBuilder{
|
||||||
|
{"ID", "in", ""},
|
||||||
|
},
|
||||||
|
fields: "*",
|
||||||
|
group: "",
|
||||||
|
page: 1,
|
||||||
|
pageSize: 5,
|
||||||
|
order: nil,
|
||||||
|
join: nil,
|
||||||
|
having: nil,
|
||||||
|
in: [][]any{helper.SliceMap[int, any](helper.RangeSlice(431, 440, 1), helper.ToAny[int])},
|
||||||
|
},
|
||||||
|
wantR: func() (r []models.Posts) {
|
||||||
|
r, err := Select[models.Posts](ctx, "select * from "+models.Posts{}.Table()+" where ID in (?,?,?,?,?)", helper.SliceMap[int, any](helper.RangeSlice(431, 435, 1), helper.ToAny[int])...)
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
panic(err)
|
||||||
|
} else if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}(),
|
||||||
|
wantTotal: 10,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
gotR, gotTotal, err := SimplePagination[wp2.Posts](ctx, tt.args.where, tt.args.fields, tt.args.group, tt.args.page, tt.args.pageSize, tt.args.order, tt.args.join, tt.args.having, tt.args.in...)
|
gotR, gotTotal, err := SimplePagination[models.Posts](ctx, tt.args.where, tt.args.fields, tt.args.group, tt.args.page, tt.args.pageSize, tt.args.order, tt.args.join, tt.args.having, tt.args.in...)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("SimplePagination() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("SimplePagination() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user