mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-17 03:02:28 +08:00
Compare commits
6 Commits
v2.1.19
...
52ea64bc33
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52ea64bc33 | ||
|
|
14bc08c6d6 | ||
|
|
47bdd6718a | ||
|
|
18d27604e6 | ||
|
|
66bd339e3a | ||
|
|
04abb7a3ea |
2
go.mod
2
go.mod
@@ -4,5 +4,5 @@ go 1.18
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a
|
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a
|
||||||
golang.org/x/text v0.5.0
|
golang.org/x/text v0.9.0
|
||||||
)
|
)
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -2,3 +2,5 @@ golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLU
|
|||||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
|||||||
@@ -319,27 +319,17 @@ func GroupWith[T any, U comparable](slice []T, iteratee func(item T) U) map[U][]
|
|||||||
// Find iterates over elements of slice, returning the first one that passes a truth test on predicate function.
|
// Find iterates over elements of slice, returning the first one that passes a truth test on predicate function.
|
||||||
// If return T is nil then no items matched the predicate func.
|
// If return T is nil then no items matched the predicate func.
|
||||||
// Play: https://go.dev/play/p/CBKeBoHVLgq
|
// Play: https://go.dev/play/p/CBKeBoHVLgq
|
||||||
|
// Deprecated
|
||||||
func Find[T any](slice []T, predicate func(index int, item T) bool) (*T, bool) {
|
func Find[T any](slice []T, predicate func(index int, item T) bool) (*T, bool) {
|
||||||
index := -1
|
v, ok := FindBy(slice, predicate)
|
||||||
|
return &v, ok
|
||||||
for i, v := range slice {
|
|
||||||
if predicate(i, v) {
|
|
||||||
index = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if index == -1 {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return &slice[index], true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindLast iterates over elements of slice from end to begin,
|
// FindLast iterates over elements of slice from end to begin,
|
||||||
// return the first one that passes a truth test on predicate function.
|
// return the first one that passes a truth test on predicate function.
|
||||||
// If return T is nil then no items matched the predicate func.
|
// If return T is nil then no items matched the predicate func.
|
||||||
// Play: https://go.dev/play/p/FFDPV_j7URd
|
// Play: https://go.dev/play/p/FFDPV_j7URd
|
||||||
|
// Deprecated
|
||||||
func FindLast[T any](slice []T, predicate func(index int, item T) bool) (*T, bool) {
|
func FindLast[T any](slice []T, predicate func(index int, item T) bool) (*T, bool) {
|
||||||
index := -1
|
index := -1
|
||||||
|
|
||||||
@@ -357,6 +347,46 @@ func FindLast[T any](slice []T, predicate func(index int, item T) bool) (*T, boo
|
|||||||
return &slice[index], true
|
return &slice[index], true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindBy iterates over elements of slice, returning the first one that passes a truth test on predicate function.
|
||||||
|
// If return T is nil or zero value then no items matched the predicate func.
|
||||||
|
// In contrast to Find or FindLast, its return value no longer requires dereferencing
|
||||||
|
func FindBy[T any](slice []T, predicate func(index int, item T) bool) (v T, ok bool) {
|
||||||
|
index := -1
|
||||||
|
|
||||||
|
for i, v := range slice {
|
||||||
|
if predicate(i, v) {
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if index == -1 {
|
||||||
|
return v, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return slice[index], true
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLastBy iterates over elements of slice, returning the last one that passes a truth test on predicate function.
|
||||||
|
// If return T is nil or zero value then no items matched the predicate func.
|
||||||
|
// In contrast to Find or FindLast, its return value no longer requires dereferencing
|
||||||
|
func FindLastBy[T any](slice []T, predicate func(index int, item T) bool) (v T, ok bool) {
|
||||||
|
index := -1
|
||||||
|
|
||||||
|
for i := len(slice) - 1; i >= 0; i-- {
|
||||||
|
if predicate(i, slice[i]) {
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if index == -1 {
|
||||||
|
return v, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return slice[index], true
|
||||||
|
}
|
||||||
|
|
||||||
// Flatten flattens slice with one level.
|
// Flatten flattens slice with one level.
|
||||||
// Play: https://go.dev/play/p/hYa3cBEevtm
|
// Play: https://go.dev/play/p/hYa3cBEevtm
|
||||||
func Flatten(slice any) any {
|
func Flatten(slice any) any {
|
||||||
|
|||||||
@@ -341,6 +341,40 @@ func ExampleFindLast() {
|
|||||||
// true
|
// true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleFindBy() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
isEven := func(i, num int) bool {
|
||||||
|
return num%2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
result, ok := FindBy(nums, isEven)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
fmt.Println(ok)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 2
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFindLastBy() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
isEven := func(i, num int) bool {
|
||||||
|
return num%2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
result, ok := FindLastBy(nums, isEven)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
fmt.Println(ok)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 4
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleFlatten() {
|
func ExampleFlatten() {
|
||||||
arrs := [][][]string{{{"a", "b"}}, {{"c", "d"}}}
|
arrs := [][][]string{{{"a", "b"}}, {{"c", "d"}}}
|
||||||
|
|
||||||
|
|||||||
@@ -249,6 +249,44 @@ func TestFind(t *testing.T) {
|
|||||||
assert.Equal(2, *res)
|
assert.Equal(2, *res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFindBy(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestFindBy")
|
||||||
|
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
even := func(i, num int) bool {
|
||||||
|
return num%2 == 0
|
||||||
|
}
|
||||||
|
res, ok := FindBy(nums, even)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("found nothing")
|
||||||
|
}
|
||||||
|
assert.Equal(2, res)
|
||||||
|
|
||||||
|
res, ok = FindBy(nums, func(_ int, v int) bool {
|
||||||
|
return v == 6
|
||||||
|
})
|
||||||
|
assert.Equal(res == 0 && ok == false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindLastBy(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestFindBy")
|
||||||
|
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
even := func(i, num int) bool {
|
||||||
|
return num%2 == 0
|
||||||
|
}
|
||||||
|
res, ok := FindLastBy(nums, even)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("found nothing")
|
||||||
|
}
|
||||||
|
assert.Equal(4, res)
|
||||||
|
|
||||||
|
res, ok = FindLastBy(nums, func(_ int, v int) bool {
|
||||||
|
return v == 6
|
||||||
|
})
|
||||||
|
assert.Equal(res == 0 && ok == false, true)
|
||||||
|
}
|
||||||
|
|
||||||
func TestFindLast(t *testing.T) {
|
func TestFindLast(t *testing.T) {
|
||||||
nums := []int{1, 2, 3, 4, 5}
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
even := func(i, num int) bool {
|
even := func(i, num int) bool {
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
package strutil
|
package strutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CamelCase coverts string to camelCase string. Non letters and numbers will be ignored.
|
// CamelCase coverts string to camelCase string. Non letters and numbers will be ignored.
|
||||||
@@ -373,3 +375,67 @@ func RemoveNonPrintable(str string) string {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringToBytes converts a string to byte slice without a memory allocation
|
||||||
|
func StringToBytes(str string) (b []byte) {
|
||||||
|
sh := *(*reflect.StringHeader)(unsafe.Pointer(&str))
|
||||||
|
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||||
|
bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesToString converts a byte slice to string without a memory allocation
|
||||||
|
func BytesToString(bytes []byte) string {
|
||||||
|
return *(*string)(unsafe.Pointer(&bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBlank checks if a string is whitespace, empty
|
||||||
|
func IsBlank(str string) bool {
|
||||||
|
if len(str) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// memory copies will occur here, but UTF8 will be compatible
|
||||||
|
runes := []rune(str)
|
||||||
|
for _, r := range runes {
|
||||||
|
if !unicode.IsSpace(r) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPrefixAny check if a string starts with any of an array of specified strings
|
||||||
|
func HasPrefixAny(str string, prefixes []string) bool {
|
||||||
|
if len(str) == 0 || len(prefixes) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, prefix := range prefixes {
|
||||||
|
if strings.HasPrefix(str, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasSuffixAny check if a string ends with any of an array of specified strings
|
||||||
|
func HasSuffixAny(str string, suffixes []string) bool {
|
||||||
|
if len(str) == 0 || len(suffixes) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, suffix := range suffixes {
|
||||||
|
if strings.HasSuffix(str, suffix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexOffset returns the index of the first instance of substr in s after offsetting the string by `idxFrom`,
|
||||||
|
// or -1 if substr is not present in s.
|
||||||
|
func IndexOffset(str string, substr string, idxFrom int) int {
|
||||||
|
if idxFrom > len(str)-1 || idxFrom < 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Index(str[idxFrom:], substr) + idxFrom
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package strutil
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleAfter() {
|
func ExampleAfter() {
|
||||||
@@ -449,3 +450,81 @@ func ExampleRemoveNonPrintable() {
|
|||||||
// hello world
|
// hello world
|
||||||
// 你好😄
|
// 你好😄
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleStringToBytes() {
|
||||||
|
result1 := StringToBytes("abc")
|
||||||
|
result2 := reflect.DeepEqual(result1, []byte{'a', 'b', 'c'})
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
// Output:
|
||||||
|
// [97 98 99]
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleBytesToString() {
|
||||||
|
bytes := []byte{'a', 'b', 'c'}
|
||||||
|
result := BytesToString(bytes)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// abc
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleIsBlank() {
|
||||||
|
result1 := IsBlank("")
|
||||||
|
result2 := IsBlank("\t\v\f\n")
|
||||||
|
result3 := IsBlank(" 中文")
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleHasPrefixAny() {
|
||||||
|
result1 := HasPrefixAny("foo bar", []string{"fo", "xyz", "hello"})
|
||||||
|
result2 := HasPrefixAny("foo bar", []string{"oom", "world"})
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleHasSuffixAny() {
|
||||||
|
result1 := HasSuffixAny("foo bar", []string{"bar", "xyz", "hello"})
|
||||||
|
result2 := HasSuffixAny("foo bar", []string{"oom", "world"})
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleIndexOffset() {
|
||||||
|
str := "foo bar hello world"
|
||||||
|
|
||||||
|
result1 := IndexOffset(str, "o", 5)
|
||||||
|
result2 := IndexOffset(str, "o", 0)
|
||||||
|
result3 := IndexOffset(str, "d", len(str)-1)
|
||||||
|
result4 := IndexOffset(str, "d", len(str))
|
||||||
|
result5 := IndexOffset(str, "f", -1)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
fmt.Println(result4)
|
||||||
|
fmt.Println(result5)
|
||||||
|
// Output:
|
||||||
|
// 12
|
||||||
|
// 1
|
||||||
|
// 18
|
||||||
|
// -1
|
||||||
|
// -1
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package strutil
|
package strutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
@@ -349,3 +350,52 @@ func TestRemoveNonPrintable(t *testing.T) {
|
|||||||
assert.Equal("hello world", RemoveNonPrintable("hello\u00a0 \u200bworld\n"))
|
assert.Equal("hello world", RemoveNonPrintable("hello\u00a0 \u200bworld\n"))
|
||||||
assert.Equal("你好😄", RemoveNonPrintable("你好😄"))
|
assert.Equal("你好😄", RemoveNonPrintable("你好😄"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStringToBytes(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestStringToBytes")
|
||||||
|
str := "abc"
|
||||||
|
bytes := StringToBytes(str)
|
||||||
|
assert.Equal(reflect.DeepEqual(bytes, []byte{'a', 'b', 'c'}), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBytesToString(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestBytesToString")
|
||||||
|
bytes := []byte{'a', 'b', 'c'}
|
||||||
|
str := BytesToString(bytes)
|
||||||
|
assert.Equal(str == "abc", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsBlank(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestIsBlank")
|
||||||
|
assert.Equal(IsBlank(""), true)
|
||||||
|
assert.Equal(IsBlank("\t\v\f\n"), true)
|
||||||
|
assert.Equal(IsBlank(" 中文"), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefixAny(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestHasPrefixAny")
|
||||||
|
str := "foo bar"
|
||||||
|
prefixes := []string{"fo", "xyz", "hello"}
|
||||||
|
notMatches := []string{"oom", "world"}
|
||||||
|
assert.Equal(HasPrefixAny(str, prefixes), true)
|
||||||
|
assert.Equal(HasPrefixAny(str, notMatches), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasSuffixAny(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestHasSuffixAny")
|
||||||
|
str := "foo bar"
|
||||||
|
suffixes := []string{"bar", "xyz", "hello"}
|
||||||
|
notMatches := []string{"oom", "world"}
|
||||||
|
assert.Equal(HasSuffixAny(str, suffixes), true)
|
||||||
|
assert.Equal(HasSuffixAny(str, notMatches), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIndexOffset(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestIndexOffset")
|
||||||
|
str := "foo bar hello world"
|
||||||
|
assert.Equal(IndexOffset(str, "o", 5), 12)
|
||||||
|
assert.Equal(IndexOffset(str, "o", 0), 1)
|
||||||
|
assert.Equal(IndexOffset(str, "d", len(str)-1), len(str)-1)
|
||||||
|
assert.Equal(IndexOffset(str, "d", len(str)), -1)
|
||||||
|
assert.Equal(IndexOffset(str, "f", -1), -1)
|
||||||
|
}
|
||||||
|
|||||||
26
system/os_darwin.go
Normal file
26
system/os_darwin.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
//go:build darwin
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func WithForeground() Option {
|
||||||
|
return func(c *exec.Cmd) {
|
||||||
|
if c.SysProcAttr == nil {
|
||||||
|
c.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
Foreground: true,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.SysProcAttr.Foreground = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithWinHide() Option {
|
||||||
|
return func(c *exec.Cmd) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build linux
|
||||||
|
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -16,3 +18,9 @@ func WithForeground() Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithWinHide() Option {
|
||||||
|
return func(c *exec.Cmd) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
Reference in New Issue
Block a user