Add FormatSlice function

Signed-off-by: Babak K. Shandiz <babak.k.shandiz@gmail.com>
This commit is contained in:
Babak K. Shandiz 2024-05-06 17:54:21 +01:00
parent 4896546432
commit 1e88ec55fc
No known key found for this signature in database
GPG key ID: 44950AED81AD710F
2 changed files with 155 additions and 0 deletions

View file

@ -2,8 +2,10 @@ package text
import (
"fmt"
"math"
"net/url"
"regexp"
"slices"
"strings"
"time"
@ -81,3 +83,64 @@ func RemoveDiacritics(value string) string {
func PadRight(maxWidth int, s string) string {
return text.PadRight(maxWidth, s)
}
// FormatSlice concatenates elements of the given string slice into a
// well-formatted, possibly multiline, string with specific line length limit.
// Elements can be optionally surrounded by custom strings (e.g., quotes or
// brackets). If the lineLength argument is non-positive, no line length limit
// will be applied.
func FormatSlice(values []string, lineLength uint, indent uint, prependWith string, appendWith string, sort bool) string {
if lineLength <= 0 {
lineLength = math.MaxInt
}
sortedValues := values
if sort {
sortedValues = slices.Clone(values)
slices.Sort(sortedValues)
}
pre := strings.Repeat(" ", int(indent))
if len(sortedValues) == 0 {
return pre
} else if len(sortedValues) == 1 {
return pre + sortedValues[0]
}
builder := strings.Builder{}
currentLineLength := 0
sep := ","
ws := " "
for i := 0; i < len(sortedValues); i++ {
v := prependWith + sortedValues[i] + appendWith
isLast := i == -1+len(sortedValues)
if currentLineLength == 0 {
builder.WriteString(pre)
builder.WriteString(v)
currentLineLength += len(v)
if !isLast {
builder.WriteString(sep)
currentLineLength += len(sep)
}
} else {
if !isLast && currentLineLength+len(ws)+len(v)+len(sep) > int(lineLength) ||
isLast && currentLineLength+len(ws)+len(v) > int(lineLength) {
currentLineLength = 0
builder.WriteString("\n")
i--
continue
}
builder.WriteString(ws)
builder.WriteString(v)
currentLineLength += len(ws) + len(v)
if !isLast {
builder.WriteString(sep)
currentLineLength += len(sep)
}
}
}
return builder.String()
}

View file

@ -54,3 +54,95 @@ func TestFuzzyAgoAbbr(t *testing.T) {
assert.Equal(t, expected, fuzzy)
}
}
func TestFormatSliceDoc(t *testing.T) {
tests := []struct {
name string
values []string
indent uint
lineLength uint
prependWith string
appendWith string
sort bool
wants string
}{
{
name: "empty",
lineLength: 10,
values: []string{},
wants: "",
},
{
name: "empty with indent",
lineLength: 10,
indent: 2,
values: []string{},
wants: " ",
},
{
name: "single",
lineLength: 10,
values: []string{"foo"},
wants: "foo",
},
{
name: "single with indent",
lineLength: 10,
indent: 2,
values: []string{"foo"},
wants: " foo",
},
{
name: "long single with indent",
lineLength: 10,
indent: 2,
values: []string{"some-long-value"},
wants: " some-long-value",
},
{
name: "exact line length",
lineLength: 4,
values: []string{"a", "b"},
wants: "a, b",
},
{
name: "values longer than line length",
lineLength: 4,
values: []string{"long-value", "long-value"},
wants: "long-value,\nlong-value",
},
{
name: "zero line length (no wrapping expected)",
lineLength: 0,
values: []string{"foo", "bar"},
wants: "foo, bar",
},
{
name: "simple",
lineLength: 10,
values: []string{"foo", "bar", "baz", "foo", "bar", "baz"},
wants: "foo, bar,\nbaz, foo,\nbar, baz",
},
{
name: "simple, surrounded",
lineLength: 13,
prependWith: "<",
appendWith: ">",
values: []string{"foo", "bar", "baz", "foo", "bar", "baz"},
wants: "<foo>, <bar>,\n<baz>, <foo>,\n<bar>, <baz>",
},
{
name: "sort",
lineLength: 99,
sort: true,
values: []string{"c", "b", "a"},
wants: "a, b, c",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.wants, FormatSlice(tt.values, tt.lineLength, tt.indent, tt.prependWith, tt.appendWith, tt.sort))
})
}
}