diff --git a/group_test.go b/group_test.go new file mode 100644 index 000000000..94ade836a --- /dev/null +++ b/group_test.go @@ -0,0 +1,40 @@ +package goutil_test + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gookit/goutil" + "github.com/gookit/goutil/netutil/httpreq" + "github.com/gookit/goutil/testutil/assert" +) + +func TestNewErrGroup(t *testing.T) { + httpreq.ConfigStd(func(hc *http.Client) { + hc.Timeout = 3 * time.Second + }) + + eg := goutil.NewErrGroup() + eg.Add(func() error { + resp, err := httpreq.Get("https://httpbin.org/get", nil) + if err != nil { + return err + } + + fmt.Println(resp.Body) + return nil + }, func() error { + resp, err := httpreq.Post("https://httpbin.org/post", "hi", nil) + if err != nil { + return err + } + + fmt.Println(resp.Body) + return nil + }) + + err := eg.Wait() + assert.NoErr(t, err) +} diff --git a/strutil/padding.go b/strutil/padding.go new file mode 100644 index 000000000..d3687f9c3 --- /dev/null +++ b/strutil/padding.go @@ -0,0 +1,168 @@ +package strutil + +import ( + "fmt" + "strings" +) + +// PosFlag type +type PosFlag uint8 + +// Position for padding/resize string +const ( + PosLeft PosFlag = iota + PosRight + PosMiddle +) + +/************************************************************* + * String padding operation + *************************************************************/ + +// Padding a string. +func Padding(s, pad string, length int, pos PosFlag) string { + diff := len(s) - length + if diff >= 0 { // do not need padding. + return s + } + + if pad == "" || pad == " " { + mark := "" + if pos == PosRight { // to right + mark = "-" + } + + // padding left: "%7s", padding right: "%-7s" + tpl := fmt.Sprintf("%s%d", mark, length) + return fmt.Sprintf(`%`+tpl+`s`, s) + } + + if pos == PosRight { // to right + return s + Repeat(pad, -diff) + } + return Repeat(pad, -diff) + s +} + +// PadLeft a string. +func PadLeft(s, pad string, length int) string { + return Padding(s, pad, length, PosLeft) +} + +// PadRight a string. +func PadRight(s, pad string, length int) string { + return Padding(s, pad, length, PosRight) +} + +// Resize a string by given length and align settings. padding space. +func Resize(s string, length int, align PosFlag) string { + diff := len(s) - length + if diff >= 0 { // do not need padding. + return s + } + + if align == PosMiddle { + strLn := len(s) + padLn := (length - strLn) / 2 + padStr := string(make([]byte, padLn, padLn)) + + if diff := length - padLn*2; diff > 0 { + s += " " + } + return padStr + s + padStr + } + + return Padding(s, " ", length, align) +} + +// PadChars padding a rune/byte to want length and with position flag +func PadChars[T byte | rune](cs []T, pad T, length int, pos PosFlag) []T { + ln := len(cs) + if ln >= length { + ns := make([]T, length) + copy(ns, cs[:length]) + return ns + } + + idx := length - ln + ns := make([]T, length) + ps := RepeatChars(pad, idx) + if pos == PosRight { + copy(ns, cs) + copy(ns[idx:], ps) + } else { // to left + copy(ns[:idx], ps) + copy(ns[idx:], cs) + } + + return ns +} + +// PadBytes padding a byte to want length and with position flag +func PadBytes(bs []byte, pad byte, length int, pos PosFlag) []byte { + return PadChars(bs, pad, length, pos) +} + +// PadBytesLeft a byte to want length +func PadBytesLeft(bs []byte, pad byte, length int) []byte { + return PadChars(bs, pad, length, PosLeft) +} + +// PadBytesRight a byte to want length +func PadBytesRight(bs []byte, pad byte, length int) []byte { + return PadChars(bs, pad, length, PosRight) +} + +// PadRunes padding a rune to want length and with position flag +func PadRunes(rs []rune, pad rune, length int, pos PosFlag) []rune { + return PadChars(rs, pad, length, pos) +} + +// PadRunesLeft a rune to want length +func PadRunesLeft(rs []rune, pad rune, length int) []rune { + return PadChars(rs, pad, length, PosLeft) +} + +// PadRunesRight a rune to want length +func PadRunesRight(rs []rune, pad rune, length int) []rune { + return PadChars(rs, pad, length, PosRight) +} + +/************************************************************* + * String repeat operation + *************************************************************/ + +// Repeat a string +func Repeat(s string, times int) string { + if times <= 0 { + return "" + } + if times == 1 { + return s + } + + ss := make([]string, 0, times) + for i := 0; i < times; i++ { + ss = append(ss, s) + } + + return strings.Join(ss, "") +} + +// RepeatRune repeat a rune char. +func RepeatRune(char rune, times int) []rune { + return RepeatChars(char, times) +} + +// RepeatBytes repeat a byte char. +func RepeatBytes(char byte, times int) []byte { + return RepeatChars(char, times) +} + +// RepeatChars repeat a byte char. +func RepeatChars[T byte | rune](char T, times int) []T { + chars := make([]T, 0, times) + for i := 0; i < times; i++ { + chars = append(chars, char) + } + return chars +} diff --git a/strutil/padding_test.go b/strutil/padding_test.go new file mode 100644 index 000000000..3422fa783 --- /dev/null +++ b/strutil/padding_test.go @@ -0,0 +1,100 @@ +package strutil_test + +import ( + "testing" + + "github.com/gookit/goutil/strutil" + "github.com/gookit/goutil/testutil/assert" +) + +func TestPadding(t *testing.T) { + tests := []struct { + want, give, pad string + len int + pos strutil.PosFlag + }{ + {"ab000", "ab", "0", 5, strutil.PosRight}, + {"000ab", "ab", "0", 5, strutil.PosLeft}, + {"ab012", "ab012", "0", 4, strutil.PosLeft}, + {"ab ", "ab", "", 5, strutil.PosRight}, + {" ab", "ab", "", 5, strutil.PosLeft}, + } + + for _, tt := range tests { + assert.Eq(t, tt.want, strutil.Padding(tt.give, tt.pad, tt.len, tt.pos)) + + if tt.pos == strutil.PosRight { + assert.Eq(t, tt.want, strutil.PadRight(tt.give, tt.pad, tt.len)) + } else { + assert.Eq(t, tt.want, strutil.PadLeft(tt.give, tt.pad, tt.len)) + } + } +} + +func TestRepeat(t *testing.T) { + assert.Eq(t, "aaa", strutil.Repeat("a", 3)) + assert.Eq(t, "DD", strutil.Repeat("D", 2)) + assert.Eq(t, "D", strutil.Repeat("D", 1)) + assert.Eq(t, "", strutil.Repeat("0", 0)) + assert.Eq(t, "", strutil.Repeat("D", -3)) +} + +func TestRepeatRune(t *testing.T) { + tests := []struct { + want []rune + give rune + times int + }{ + {[]rune("bbb"), 'b', 3}, + {[]rune("..."), '.', 3}, + {[]rune(" "), ' ', 2}, + } + + for _, tt := range tests { + assert.Eq(t, tt.want, strutil.RepeatRune(tt.give, tt.times)) + } +} + +func TestRepeatBytes(t *testing.T) { + assert.Eq(t, []byte("aaa"), strutil.RepeatBytes('a', 3)) +} + +func TestPadChars(t *testing.T) { + tests := []struct { + wt []byte + ls []byte + pad byte + pln int + }{ + { + []byte("aaaabc"), []byte("abc"), 'a', 6, + }, + { + []byte("abc"), []byte("abcd"), 'a', 3, + }, + } + for _, item := range tests { + assert.Eq(t, item.wt, strutil.PadChars(item.ls, item.pad, item.pln, strutil.PosLeft)) + assert.Eq(t, item.wt, strutil.PadBytes(item.ls, item.pad, item.pln, strutil.PosLeft)) + assert.Eq(t, item.wt, strutil.PadBytesLeft(item.ls, item.pad, item.pln)) + } + + tests2 := []struct { + wt []byte + ls []byte + pad byte + pln int + }{ + { + []byte("abcaaa"), []byte("abc"), 'a', 6, + }, + { + []byte("abc"), []byte("abcd"), 'a', 3, + }, + } + for _, item := range tests2 { + assert.Eq(t, item.wt, strutil.PadChars(item.ls, item.pad, item.pln, strutil.PosRight)) + assert.Eq(t, item.wt, strutil.PadBytes(item.ls, item.pad, item.pln, strutil.PosRight)) + assert.Eq(t, item.wt, strutil.PadBytesRight(item.ls, item.pad, item.pln)) + } +} diff --git a/strutil/strutil.go b/strutil/strutil.go index 9d7f45d46..ea1f69d5d 100644 --- a/strutil/strutil.go +++ b/strutil/strutil.go @@ -9,114 +9,6 @@ import ( "text/template" ) -// PosFlag type -type PosFlag uint8 - -// Position for padding/resize string -const ( - PosLeft PosFlag = iota - PosRight - PosMiddle -) - -/************************************************************* - * String padding operation - *************************************************************/ - -// Padding a string. -func Padding(s, pad string, length int, pos PosFlag) string { - diff := len(s) - length - if diff >= 0 { // do not need padding. - return s - } - - if pad == "" || pad == " " { - mark := "" - if pos == PosRight { // to right - mark = "-" - } - - // padding left: "%7s", padding right: "%-7s" - tpl := fmt.Sprintf("%s%d", mark, length) - return fmt.Sprintf(`%`+tpl+`s`, s) - } - - if pos == PosRight { // to right - return s + Repeat(pad, -diff) - } - return Repeat(pad, -diff) + s -} - -// PadLeft a string. -func PadLeft(s, pad string, length int) string { - return Padding(s, pad, length, PosLeft) -} - -// PadRight a string. -func PadRight(s, pad string, length int) string { - return Padding(s, pad, length, PosRight) -} - -// Resize a string by given length and align settings. padding space. -func Resize(s string, length int, align PosFlag) string { - diff := len(s) - length - if diff >= 0 { // do not need padding. - return s - } - - if align == PosMiddle { - strLn := len(s) - padLn := (length - strLn) / 2 - padStr := string(make([]byte, padLn, padLn)) - - if diff := length - padLn*2; diff > 0 { - s += " " - } - return padStr + s + padStr - } - - return Padding(s, " ", length, align) -} - -/************************************************************* - * String repeat operation - *************************************************************/ - -// Repeat a string -func Repeat(s string, times int) string { - if times <= 0 { - return "" - } - if times == 1 { - return s - } - - ss := make([]string, 0, times) - for i := 0; i < times; i++ { - ss = append(ss, s) - } - - return strings.Join(ss, "") -} - -// RepeatRune repeat a rune char. -func RepeatRune(char rune, times int) []rune { - chars := make([]rune, 0, times) - for i := 0; i < times; i++ { - chars = append(chars, char) - } - return chars -} - -// RepeatBytes repeat a byte char. -func RepeatBytes(char byte, times int) []byte { - chars := make([]byte, 0, times) - for i := 0; i < times; i++ { - chars = append(chars, char) - } - return chars -} - // Replaces replace multi strings // // pairs: {old1: new1, old2: new2, ...} diff --git a/strutil/strutil_test.go b/strutil/strutil_test.go index 053a938f5..45f7d6661 100644 --- a/strutil/strutil_test.go +++ b/strutil/strutil_test.go @@ -13,34 +13,6 @@ func TestSimilarity(t *testing.T) { is.True(ok) } -func TestRepeat(t *testing.T) { - assert.Eq(t, "aaa", strutil.Repeat("a", 3)) - assert.Eq(t, "DD", strutil.Repeat("D", 2)) - assert.Eq(t, "D", strutil.Repeat("D", 1)) - assert.Eq(t, "", strutil.Repeat("0", 0)) - assert.Eq(t, "", strutil.Repeat("D", -3)) -} - -func TestRepeatRune(t *testing.T) { - tests := []struct { - want []rune - give rune - times int - }{ - {[]rune("bbb"), 'b', 3}, - {[]rune("..."), '.', 3}, - {[]rune(" "), ' ', 2}, - } - - for _, tt := range tests { - assert.Eq(t, tt.want, strutil.RepeatRune(tt.give, tt.times)) - } -} - -func TestRepeatBytes(t *testing.T) { - assert.Eq(t, []byte("aaa"), strutil.RepeatBytes('a', 3)) -} - func TestRenderTemplate(t *testing.T) { tpl := "hi, My name is {{ .name | upFirst }}, age is {{ .age }}" assert.Eq(t, "hi, My name is Inhere, age is 2000", strutil.RenderTemplate(tpl, map[string]any{ @@ -58,31 +30,8 @@ func TestReplaces(t *testing.T) { })) } -func TestPadding(t *testing.T) { - tests := []struct { - want, give, pad string - len int - pos strutil.PosFlag - }{ - {"ab000", "ab", "0", 5, strutil.PosRight}, - {"000ab", "ab", "0", 5, strutil.PosLeft}, - {"ab012", "ab012", "0", 4, strutil.PosLeft}, - {"ab ", "ab", "", 5, strutil.PosRight}, - {" ab", "ab", "", 5, strutil.PosLeft}, - } - - for _, tt := range tests { - assert.Eq(t, tt.want, strutil.Padding(tt.give, tt.pad, tt.len, tt.pos)) - - if tt.pos == strutil.PosRight { - assert.Eq(t, tt.want, strutil.PadRight(tt.give, tt.pad, tt.len)) - } else { - assert.Eq(t, tt.want, strutil.PadLeft(tt.give, tt.pad, tt.len)) - } - } -} - func TestWrapTag(t *testing.T) { + assert.Eq(t, "abc", strutil.WrapTag("abc", "info")) } func TestPrettyJSON(t *testing.T) {