Skip to content

Commit 44c09ab

Browse files
authored
chore: move string utils to central location (#334)
## Issue None ## Description Move string utils package to common location for reuse Signed-off-by: Artur Shad Nik <[email protected]>
1 parent de015d9 commit 44c09ab

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

internal/utils/strings/strings.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Package strings contains utility functions for working with strings.
2+
package strings
3+
4+
import "strings"
5+
6+
// DeDupeStrSlice deduplicates a slices of strings
7+
func DeDupeStrSlice(ss []string) []string {
8+
found := make(map[string]bool)
9+
l := []string{}
10+
for _, s := range ss {
11+
if _, ok := found[s]; !ok {
12+
found[s] = true
13+
l = append(l, s)
14+
}
15+
}
16+
return l
17+
}
18+
19+
// Sanitize sends a string to lowercase, trims whitespace, replaces all non alphanumeric characters with a dash,
20+
// removes consecutive dashes, and trims any leading or trailing dashes.
21+
func Sanitize(s string) string {
22+
s = strings.ToLower(s)
23+
s = strings.TrimSpace(s)
24+
s = strings.Map(func(r rune) rune {
25+
if r >= 'a' && r <= 'z' || r >= '0' && r <= '9' {
26+
return r
27+
}
28+
return '-'
29+
}, s)
30+
31+
// Remove consecutive dashes
32+
var b strings.Builder
33+
prevDash := false
34+
for _, r := range s {
35+
if r == '-' {
36+
if !prevDash {
37+
b.WriteRune(r)
38+
prevDash = true
39+
}
40+
} else {
41+
b.WriteRune(r)
42+
prevDash = false
43+
}
44+
}
45+
46+
return strings.Trim(b.String(), "-")
47+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package strings
2+
3+
import (
4+
"slices"
5+
"testing"
6+
)
7+
8+
func TestDeDupeStrSlice(t *testing.T) {
9+
tests := []struct {
10+
name string
11+
input []string
12+
expected []string
13+
}{
14+
{
15+
name: "Empty slice",
16+
input: []string{},
17+
expected: []string{},
18+
},
19+
{
20+
name: "Single element",
21+
input: []string{"foo"},
22+
expected: []string{"foo"},
23+
},
24+
{
25+
name: "Duplicate elements",
26+
input: []string{"foo", "foo"},
27+
expected: []string{"foo"},
28+
},
29+
{
30+
name: "Multiple elements",
31+
input: []string{"foo", "bar", "foo", "baz", "bar"},
32+
expected: []string{"foo", "bar", "baz"},
33+
},
34+
}
35+
36+
for _, tt := range tests {
37+
t.Run(tt.name, func(t *testing.T) {
38+
actual := DeDupeStrSlice(tt.input)
39+
if len(actual) != len(tt.expected) {
40+
t.Fatalf("expected %v, got %v", tt.expected, actual)
41+
}
42+
if !slices.Equal(actual, tt.expected) {
43+
t.Fatalf("expected %v, got %v", tt.expected, actual)
44+
}
45+
})
46+
}
47+
}
48+
49+
func TestSanitize(t *testing.T) {
50+
tests := []struct {
51+
name string
52+
input string
53+
expected string
54+
}{
55+
{
56+
name: "Empty string",
57+
input: "",
58+
expected: "",
59+
},
60+
{
61+
name: "Lowercase",
62+
input: "FOO",
63+
expected: "foo",
64+
},
65+
{
66+
name: "Trim whitespace",
67+
input: " foo ",
68+
expected: "foo",
69+
},
70+
{
71+
name: "Replace non-alphanumeric characters",
72+
input: "foo-bar_baz",
73+
expected: "foo-bar-baz",
74+
},
75+
{
76+
name: "Remove consecutive & leading/trailing dashes",
77+
input: "! Example---string with **multiple** non-alphanumeric---characters---! ",
78+
expected: "example-string-with-multiple-non-alphanumeric-characters",
79+
},
80+
}
81+
82+
for _, tt := range tests {
83+
t.Run(tt.name, func(t *testing.T) {
84+
actual := Sanitize(tt.input)
85+
if actual != tt.expected {
86+
t.Fatalf("expected %v, got %v", tt.expected, actual)
87+
}
88+
})
89+
}
90+
}

0 commit comments

Comments
 (0)