Skip to content

Commit 5e34763

Browse files
authored
Merge pull request #16 from jf-tech/fqdn_with_esc
Adding `BuildFQDNWithEsc` and `LastNameletOfFQDNWithEsc`
2 parents 69ef0ae + b7b96ed commit 5e34763

File tree

2 files changed

+102
-2
lines changed

2 files changed

+102
-2
lines changed

strs/strs.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,11 @@ func CopyStrPtr(sp *string) *string {
5454
const (
5555
// FQDNDelimiter is the default FQDN delimiter.
5656
FQDNDelimiter = "."
57+
// FQDNEsc is the default escape char for FQDN. Esc is used for escaping "." and itself.
58+
FQDNEsc = "%"
5759
)
5860

59-
// BuildFQDN builds an FQDN from a slice of namelet strings.
61+
// BuildFQDN builds an FQDN (dot delimited) from a slice of namelet strings.
6062
func BuildFQDN(namelets ...string) string {
6163
return BuildFQDN2(FQDNDelimiter, namelets...)
6264
}
@@ -66,9 +68,22 @@ func BuildFQDN2(delimiter string, namelets ...string) string {
6668
return strings.Join(namelets, delimiter)
6769
}
6870

71+
var fqdnEscReplacer = strings.NewReplacer(".", "%.", "%", "%%")
72+
73+
// BuildFQDNWithEsc builds an FQDN (dot delimited) from a slice of namelet strings with proper escaping.
74+
// e.g. If namelets are 'a.b', 'c%d', it will return 'a%.b.c%%d'.
75+
// Note this function isn't optimized for alloc/perf.
76+
func BuildFQDNWithEsc(namelets ...string) string {
77+
return strings.Join(
78+
NoErrMapSlice(namelets, func(s string) string {
79+
return fqdnEscReplacer.Replace(s)
80+
}),
81+
FQDNDelimiter)
82+
}
83+
6984
// LastNameletOfFQDN returns the last namelet of an FQDN delimited by default
7085
// delimiter. If there is no delimiter in the FQDN, then the FQDN itself is
71-
// // returned.
86+
// returned.
7287
func LastNameletOfFQDN(fqdn string) string {
7388
return LastNameletOfFQDN2(FQDNDelimiter, fqdn)
7489
}
@@ -84,6 +99,15 @@ func LastNameletOfFQDN2(delimiter, fqdn string) string {
8499
return fqdn[index+1:]
85100
}
86101

102+
// LastNameletOfFQDNWithEsc returns the last namelet of an FQDN delimited by default
103+
// delimiter, with escaping considered. If there is no delimiter in the FQDN, then the
104+
// FQDN itself is returned.
105+
// Note this function isn't optimized for alloc/perf.
106+
func LastNameletOfFQDNWithEsc(fqdn string) string {
107+
namelets := SplitWithEsc(fqdn, FQDNDelimiter, FQDNEsc)
108+
return Unescape(namelets[len(namelets)-1], FQDNEsc)
109+
}
110+
87111
// CopySlice copies a string slice. The returned slice is guaranteed to be a different
88112
// slice (thus the name Copy) so modifying the src from the caller side won't affect
89113
// the returned slice.

strs/strs_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,39 @@ func TestBuildFQDN(t *testing.T) {
109109
}
110110
}
111111

112+
func TestBuildFQDNWithEsc(t *testing.T) {
113+
for _, test := range []struct {
114+
name string
115+
namelets []string
116+
expected string
117+
}{
118+
{
119+
name: "nil",
120+
namelets: nil,
121+
expected: "",
122+
},
123+
{
124+
name: "empty",
125+
namelets: []string{},
126+
expected: "",
127+
},
128+
{
129+
name: "single with space, dot, and percent",
130+
namelets: []string{"o n%.e"},
131+
expected: "o n%%%.e",
132+
},
133+
{
134+
name: "multiple",
135+
namelets: []string{"o n%.e", ".", "%three%"},
136+
expected: "o n%%%.e.%..%%three%%",
137+
},
138+
} {
139+
t.Run(test.name, func(t *testing.T) {
140+
assert.Equal(t, test.expected, BuildFQDNWithEsc(test.namelets...))
141+
})
142+
}
143+
}
144+
112145
func TestLastNameletOfFQDN(t *testing.T) {
113146
for _, test := range []struct {
114147
name string
@@ -147,6 +180,49 @@ func TestLastNameletOfFQDN(t *testing.T) {
147180
}
148181
}
149182

183+
func TestLastNameletOfFQDNWithEsc(t *testing.T) {
184+
for _, test := range []struct {
185+
name string
186+
fqdn string
187+
expected string
188+
}{
189+
{
190+
name: "empty",
191+
fqdn: "",
192+
expected: "",
193+
},
194+
{
195+
name: "no delimiter, no esc",
196+
fqdn: "abc",
197+
expected: "abc",
198+
},
199+
{
200+
name: "no delimiter, with esc",
201+
fqdn: "%.a%b%%c",
202+
expected: ".ab%c",
203+
},
204+
{
205+
name: "delimiter at beginning",
206+
fqdn: ".a%.bc",
207+
expected: "a.bc",
208+
},
209+
{
210+
name: "delimiter at the end",
211+
fqdn: "a%%bc.",
212+
expected: "",
213+
},
214+
{
215+
name: "fqdn",
216+
fqdn: "o n%%%.e.%..%%three%%",
217+
expected: "%three%",
218+
},
219+
} {
220+
t.Run(test.name, func(t *testing.T) {
221+
assert.Equal(t, test.expected, LastNameletOfFQDNWithEsc(test.fqdn))
222+
})
223+
}
224+
}
225+
150226
func TestCopySlice(t *testing.T) {
151227
for _, test := range []struct {
152228
name string

0 commit comments

Comments
 (0)