Upstream gookit/goutil state of the touched files before 949a07a2cc

This commit is contained in:
upstream committed 2026-09-04 09:07:10 +02:00
1 parent d6c91f3cdd
commit b17a55589a
3 files changed
+382 -74

No files matched your search

+221
View File
@@ -0,0 +1,221 @@
package strutil
import (
"strings"
"github.com/gookit/goutil/comdef"
)
// PosFlag type
type PosFlag = comdef.Position
// Position for padding/resize string
const (
PosLeft PosFlag = iota
PosMiddle
PosRight
PosAuto
)
/*************************************************************
* String padding operation
*************************************************************/
// Padding Fill the string to the specified length.
//
// params:
// - s: 原始字符串
// - pad: 用于填充的字符或字符串
// - length: 目标长度
// - padPos: 填充位置标志(左填充或右填充)
func Padding(s, pad string, length int, padPos PosFlag) string {
return padding(s, pad, len(s), length, padPos)
}
// Utf8Padding Fill the string to the specified length. use utf8 width.
func Utf8Padding(s, pad string, wantLen int, padPos PosFlag) string {
return padding(s, pad, Utf8Width(s), wantLen, padPos)
}
func padding(s, pad string, sLen, wantLen int, padPos PosFlag) string {
diff := sLen - wantLen
if diff >= 0 { // do not need padding.
return s
}
// pad space.
if len(s) == sLen && (pad == "" || pad == " ") {
// Sprintf: 是按字数来填充的,不管中英文都是一个字符 - 有问题
if padPos == PosRight { // to right
return s + strings.Repeat(" ", -diff)
}
return strings.Repeat(" ", -diff) + s
}
// other character.
if padPos == 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)
}
// 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)
if pos == PosRight {
copy(ns, cs)
for i := ln; i < length; i++ {
ns[i] = pad
}
return ns
}
// to left
for i := 0; i < idx; i++ {
ns[i] = pad
}
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)
}
// Align a string by given length and align settings. alias of Resize
func Align(s string, length int, align comdef.Align) string {
return resize(s, len(s), length, align, false)
}
// Utf8Align a string by given length and align settings. alias of Utf8Resize
func Utf8Align(s string, length int, align comdef.Align) string {
return resize(s, Utf8Width(s), length, align, false)
}
// Resize a string by given length and align settings. use padding space.
// If len(s) > wantLen, will truncate it.
func Resize(s string, length int, align comdef.Align) string {
return resize(s, len(s), length, align, true)
}
// Utf8Resize a string by given length and align settings. use padding space.
// If width(s) > wantLen, will truncate it.
func Utf8Resize(s string, length int, align comdef.Align) string {
return resize(s, Utf8Width(s), length, align, true)
}
// resize a string by given length and align settings. use padding space.
func resize(s string, sLen, wantLen int, align comdef.Align, cutOverflow bool) string {
diff := sLen - wantLen
if diff >= 0 { // do not need padding.
// cutOverflow: truncate on sLen > wantLen
if cutOverflow && sLen > wantLen {
if len(s) == sLen {
return s[:wantLen]
}
return utf8Truncate(s, sLen, wantLen, "")
}
return s
}
if align == comdef.Center {
padLn := (wantLen - sLen) / 2
if diff = wantLen - padLn*2; diff > 0 {
s += " "
}
padStr := string(RepeatBytes(' ', padLn))
return padStr + s + padStr
}
padStr := string(RepeatBytes(' ', wantLen-sLen))
// tip: 左对齐 - 使用空白填充右边
if align == comdef.Left || align == comdef.PosAuto {
return s + padStr
}
return padStr + s
}
/*************************************************************
* String repeat operation
*************************************************************/
// Repeat a string by given times.
func Repeat(s string, times int) string {
if times < 1 {
return ""
}
if times == 1 {
return s
}
var sb strings.Builder
sb.Grow(len(s) * times)
for i := 0; i < times; i++ {
sb.WriteString(s)
}
return sb.String()
}
// 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 {
if times <= 0 {
return make([]T, 0)
}
chars := make([]T, times)
for i := 0; i < times; i++ {
chars[i] = char
}
return chars
}
+161
View File
@@ -0,0 +1,161 @@
package strutil_test
import (
"fmt"
"testing"
"github.com/gookit/goutil/comdef"
"github.com/gookit/goutil/strutil"
"github.com/gookit/goutil/x/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))
}
}
s := "hi,你好"
assert.Eq(t, "hi,你好0", strutil.Padding(s, "0", 10, strutil.PosRight))
assert.Eq(t, "hi,你好 ", strutil.Padding(s, " ", 10, strutil.PosRight))
assert.Eq(t, "hi,你好000", strutil.Utf8Padding(s, "0", 10, strutil.PosRight))
}
func TestResize(t *testing.T) {
tests := []struct {
want, give string
len int
align strutil.PosFlag
}{
{"ab ", "ab", 5, strutil.PosLeft},
{" ab", "ab", 5, strutil.PosRight},
{"ab012", "ab012", 5, strutil.PosLeft},
{"ab", "ab", 2, strutil.PosLeft},
}
for _, tt := range tests {
assert.Eq(t, tt.want, strutil.Resize(tt.give, tt.len, tt.align))
}
want := " title "
assert.Eq(t, want, strutil.Resize("title", 20, strutil.PosMiddle))
s := "hi,你好"
fmt.Println(len(s), strutil.Utf8Width(s))
assert.Eq(t, "hi,你好 ", strutil.Align(s, 10, strutil.PosLeft))
assert.Eq(t, "hi,你好 ", strutil.Resize(s, 10, strutil.PosLeft))
// utf8
assert.Eq(t, "hi,你好 ", strutil.Utf8Align(s, 10, strutil.PosAuto))
assert.Eq(t, "hi,你好 ", strutil.Utf8Resize(s, 10, comdef.PosAuto))
// long string
s1 := "hi,你好 some 内容太长"
assert.Eq(t, s1, strutil.Align(s1, 13, strutil.PosLeft))
assert.Eq(t, "hi,你好 some ", strutil.Resize(s1, 15, strutil.PosLeft))
assert.Eq(t, s1, strutil.Utf8Align(s1, 13, strutil.PosLeft))
assert.Eq(t, "hi,你好 some 内", strutil.Utf8Resize(s1, 15, comdef.Left))
}
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("A", 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))
assert.Eq(t, []byte{}, strutil.RepeatBytes('a', 0))
assert.Eq(t, []byte{}, strutil.RepeatBytes(' ', 0))
assert.Eq(t, []byte{}, strutil.RepeatBytes(' ', -2))
assert.Eq(t, []byte{' '}, strutil.RepeatBytes(' ', 1))
assert.Len(t, strutil.RepeatBytes(' ', 20), 20)
}
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))
}
}
func TestPadRunes(t *testing.T) {
assert.Eq(t, []rune("hi123aaa"), strutil.PadRunesRight([]rune("hi123"), 'a', 8))
assert.Eq(t, []rune("aaahi123"), strutil.PadRunesLeft([]rune("hi123"), 'a', 8))
assert.Eq(t, []rune("hi123aaa"), strutil.PadRunes([]rune("hi123"), 'a', 8, strutil.PosRight))
}
// runtime error: make slice: cap out of range #76
// https://github.com/gookit/goutil/issues/76
func TestIssues_76(t *testing.T) {
// TODO
}