ADDED:
* `stringsx` package
** `stringsx.Indent()`, to indent/prefix multiline strings
** `stringsx.Redact()`, to mask strings
** `stringsx.TrimLines()`, like strings.TrimSpace() but multiline
** `stringsx.TrimSpaceLeft()`, like strings.TrimSpace() but only to the
    left of a string.
** `stringsx.TrimSpaceRight()`, like strings.TrimSpace() but only to the
    right of a string.
This commit is contained in:
brent saner
2025-11-14 01:02:59 -05:00
parent e101758187
commit b1d8ea34a6
7 changed files with 724 additions and 0 deletions

451
stringsx/funcs_test.go Normal file
View File

@@ -0,0 +1,451 @@
package stringsx
import (
"testing"
)
type (
testIndentSet struct {
name string
orig string
indent string
lvl uint
ws bool
empty bool
tgt string
}
testRedactSet struct {
name string
orig string
leading uint
trailing uint
tgt string
newline bool
mask string // defaults to DefMaskStr.
}
testTrimLinesSet struct {
name string
orig string
left bool
right bool
tgt string
}
testTrimSet struct {
name string
orig string
tgt string
}
)
func TestIndent(t *testing.T) {
var out string
var tests []testIndentSet = []testIndentSet{
testIndentSet{
name: "standard, no trailing newline",
orig: "foo\nbar\nbaz",
indent: "",
lvl: 1,
ws: false,
empty: false,
tgt: "\tfoo\n\tbar\n\tbaz",
},
testIndentSet{
name: "standard, trailing newline",
orig: "foo\nbar\nbaz\n",
indent: "",
lvl: 1,
ws: false,
empty: false,
tgt: "\tfoo\n\tbar\n\tbaz\n",
},
testIndentSet{
name: "standard, trailing newline with empty",
orig: "foo\nbar\nbaz\n",
indent: "",
lvl: 1,
ws: false,
empty: true,
tgt: "\tfoo\n\tbar\n\tbaz\n\t",
},
testIndentSet{
name: "standard, trailing newline with ws",
orig: "foo\nbar\nbaz\n",
indent: "",
lvl: 1,
ws: true,
empty: false,
tgt: "\tfoo\n\tbar\n\tbaz\n",
},
testIndentSet{
name: "standard, trailing newline with ws and empty",
orig: "foo\nbar\nbaz\n",
indent: "",
lvl: 1,
ws: true,
empty: true,
tgt: "\tfoo\n\tbar\n\tbaz\n\t",
},
testIndentSet{
name: "standard, trailing ws newline with empty",
orig: "foo\nbar\nbaz\n ",
indent: "",
lvl: 1,
ws: false,
empty: true,
tgt: "\tfoo\n\tbar\n\tbaz\n ",
},
testIndentSet{
name: "standard, trailing ws newline with ws",
orig: "foo\nbar\nbaz\n ",
indent: "",
lvl: 1,
ws: true,
empty: false,
tgt: "\tfoo\n\tbar\n\tbaz\n\t ",
},
testIndentSet{
name: "standard, trailing ws newline with ws and empty",
orig: "foo\nbar\nbaz\n \n",
indent: "",
lvl: 1,
ws: true,
empty: true,
tgt: "\tfoo\n\tbar\n\tbaz\n\t \n\t",
},
testIndentSet{
name: "comment",
orig: "foo\nbar\nbaz",
indent: "# ",
lvl: 1,
ws: false,
empty: false,
tgt: "# foo\n# bar\n# baz",
},
}
for idx, ts := range tests {
out = Indent(ts.orig, ts.indent, ts.lvl, ts.ws, ts.empty)
if out == ts.tgt {
t.Logf("[%d] OK (%s): %#v: got %#v", idx, ts.name, ts.orig, out)
} else {
t.Errorf(
"[%d] FAIL (%s): %#v (len %d):\n"+
"\t\t\texpected (len %d): %#v\n"+
"\t\t\tgot (len %d): %#v\n"+
"\t\t%#v",
idx, ts.name, ts.orig, len(ts.orig),
len(ts.tgt), ts.tgt,
len(out), out,
ts,
)
}
}
}
func TestRedact(t *testing.T) {
var out string
var tests []testRedactSet = []testRedactSet{
testRedactSet{
name: "empty in, empty out",
orig: "",
leading: 0,
trailing: 0,
tgt: "",
},
testRedactSet{
name: "standard",
orig: "password",
leading: 0,
trailing: 0,
tgt: "************************",
},
testRedactSet{
name: "standard with newline",
orig: "pass\nword",
leading: 0,
trailing: 0,
tgt: "************\n************",
newline: true,
},
testRedactSet{
name: "standard with Windows newline",
orig: "pass\r\nword",
leading: 0,
trailing: 0,
tgt: "************\r\n************",
newline: true,
},
testRedactSet{
name: "standard with newline without newlines",
orig: "pass\nword",
leading: 0,
trailing: 0,
tgt: "***************************",
},
testRedactSet{
name: "single leading",
orig: "password",
leading: 1,
trailing: 0,
tgt: "p*********************",
},
testRedactSet{
name: "single trailing",
orig: "password",
leading: 0,
trailing: 1,
tgt: "*********************d",
},
testRedactSet{
name: "three leading",
orig: "password",
leading: 3,
trailing: 0,
tgt: "pas***************",
},
testRedactSet{
name: "three trailing",
orig: "password",
leading: 0,
trailing: 3,
tgt: "***************ord",
},
testRedactSet{
name: "three leading and trailing",
orig: "password",
leading: 3,
trailing: 3,
tgt: "pas******ord",
},
testRedactSet{
name: "unmask overflow leading",
orig: "password",
leading: 5,
trailing: 4,
tgt: "************************",
},
testRedactSet{
name: "unmask overflow trailing",
orig: "password",
leading: 4,
trailing: 5,
tgt: "************************",
},
testRedactSet{
name: "single mask",
orig: "password",
leading: 0,
trailing: 0,
tgt: "********",
mask: "*",
},
testRedactSet{
name: "standard trailing newline with newlines",
orig: "password\n",
leading: 0,
trailing: 0,
tgt: "************************\n",
newline: true,
},
testRedactSet{
name: "standard trailing newline without newlines",
orig: "password\n",
leading: 0,
trailing: 0,
tgt: "***************************",
},
}
for idx, ts := range tests {
out = Redact(ts.orig, ts.mask, ts.leading, ts.trailing, ts.newline)
if out == ts.tgt {
t.Logf("[%d] OK (%s): %#v: got %#v", idx, ts.name, ts.orig, out)
} else {
t.Errorf(
"[%d] FAIL (%s): %#v (len %d):\n"+
"\t\t\texpected (len %d): %#v\n"+
"\t\t\tgot (len %d): %#v\n"+
"\t\t%#v",
idx, ts.name, ts.orig, len(ts.orig),
len(ts.tgt), ts.tgt,
len(out), out,
ts,
)
}
}
}
func TestTrimLines(t *testing.T) {
var out string
var tests []testTrimLinesSet = []testTrimLinesSet{
testTrimLinesSet{
name: "none",
orig: " foo \n bar \n baz ",
left: false,
right: false,
tgt: " foo \n bar \n baz ",
},
testTrimLinesSet{
name: "standard",
orig: " foo \n bar \n baz ",
left: true,
right: true,
tgt: "foo\nbar\nbaz",
},
testTrimLinesSet{
name: "left only",
orig: " foo \n bar \n baz ",
left: true,
right: false,
tgt: "foo \nbar \nbaz ",
},
testTrimLinesSet{
name: "right only",
orig: " foo \n bar \n baz ",
left: false,
right: true,
tgt: " foo\n bar\n baz",
},
testTrimLinesSet{
name: "standard, trailing newline",
orig: " foo \n bar \n baz \n",
left: true,
right: true,
tgt: "foo\nbar\nbaz\n",
},
testTrimLinesSet{
name: "left only, trailing newline",
orig: " foo \n bar \n baz \n",
left: true,
right: false,
tgt: "foo \nbar \nbaz \n",
},
testTrimLinesSet{
name: "right only, trailing newline",
orig: " foo \n bar \n baz \n",
left: false,
right: true,
tgt: " foo\n bar\n baz\n",
},
// Since there's no "non-space" boundary, both of these condition tests do the same thing.
testTrimLinesSet{
name: "left only, trailing newline and ws",
orig: " foo \n bar \n baz \n ",
left: true,
right: false,
tgt: "foo \nbar \nbaz \n",
},
testTrimLinesSet{
name: "right only, trailing newline and ws",
orig: " foo \n bar \n baz \n ",
left: false,
right: true,
tgt: " foo\n bar\n baz\n",
},
}
for idx, ts := range tests {
out = TrimLines(ts.orig, ts.left, ts.right)
if out == ts.tgt {
t.Logf("[%d] OK (%s): %#v: got %#v", idx, ts.name, ts.orig, out)
} else {
t.Errorf(
"[%d] FAIL (%s): %#v (len %d):\n"+
"\t\t\texpected (len %d): %#v\n"+
"\t\t\tgot (len %d): %#v\n"+
"\t\t%#v",
idx, ts.name, ts.orig, len(ts.orig),
len(ts.tgt), ts.tgt,
len(out), out,
ts,
)
}
}
}
func TestTrimSpaceLeft(t *testing.T) {
var out string
var tests []testTrimSet = []testTrimSet{
testTrimSet{
name: "standard",
orig: " foo ",
tgt: "foo ",
},
testTrimSet{
name: "tabs",
orig: "\t\tfoo\t\t",
tgt: "foo\t\t",
},
testTrimSet{
name: "newlines",
orig: "\n\nfoo\n\n",
tgt: "foo\n\n",
},
}
for idx, ts := range tests {
out = TrimSpaceLeft(ts.orig)
if out == ts.tgt {
t.Logf("[%d] OK (%s): %#v: got %#v", idx, ts.name, ts.orig, out)
} else {
t.Errorf(
"[%d] FAIL (%s): %#v (len %d):\n"+
"\t\t\texpected (len %d): %#v\n"+
"\t\t\tgot (len %d): %#v\n"+
"\t\t%#v",
idx, ts.name, ts.orig, len(ts.orig),
len(ts.tgt), ts.tgt,
len(out), out,
ts,
)
}
}
}
func TestTrimSpaceRight(t *testing.T) {
var out string
var tests []testTrimSet = []testTrimSet{
testTrimSet{
name: "standard",
orig: " foo ",
tgt: " foo",
},
testTrimSet{
name: "tabs",
orig: "\t\tfoo\t\t",
tgt: "\t\tfoo",
},
testTrimSet{
name: "newlines",
orig: "\n\nfoo\n\n",
tgt: "\n\nfoo",
},
}
for idx, ts := range tests {
out = TrimSpaceRight(ts.orig)
if out == ts.tgt {
t.Logf("[%d] OK (%s): %#v: got %#v", idx, ts.name, ts.orig, out)
} else {
t.Errorf(
"[%d] FAIL (%s): %#v (len %d):\n"+
"\t\t\texpected (len %d): %#v\n"+
"\t\t\tgot (len %d): %#v\n"+
"\t\t%#v",
idx, ts.name, ts.orig, len(ts.orig),
len(ts.tgt), ts.tgt,
len(out), out,
ts,
)
}
}
}