Skip to content

Commit d25b44b

Browse files
author
Todd Wang
committed
lib: Add helper functions to envvar to manipulate token lists.
Renames {Prepend,Append}UsingSeparator to {Prepend,Append}UniqueToken, and changes the semantics to also filter away duplicate tokens. The previous functions didn't seem to provide much value over manually calling {Split,Join}Tokens. The new functions provide a bit more functionalty wrapped into a one-line call. Also added UniqueTokens and FilterTokens, which help manipulate slices of tokens. MultiPart: 2/2 Change-Id: Ice2c126615af17ff43804c472826d738e5724608
1 parent 4dcb83a commit d25b44b

File tree

3 files changed

+128
-38
lines changed

3 files changed

+128
-38
lines changed

envvar/.api

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
pkg envvar, func AppendUsingSeparator(string, string, string) string
1+
pkg envvar, func AppendUniqueToken(string, string, string) string
22
pkg envvar, func CopyMap(map[string]string) map[string]string
33
pkg envvar, func CopySlice([]string) []string
4+
pkg envvar, func FilterToken([]string, string) []string
45
pkg envvar, func JoinKeyValue(string, string) string
56
pkg envvar, func JoinTokens([]string, string) string
67
pkg envvar, func MapToSlice(map[string]string) []string
78
pkg envvar, func MergeMaps(...map[string]string) map[string]string
89
pkg envvar, func MergeSlices(...[]string) []string
9-
pkg envvar, func PrependUsingSeparator(string, string, string) string
10+
pkg envvar, func PrependUniqueToken(string, string, string) string
1011
pkg envvar, func SliceToMap([]string) map[string]string
1112
pkg envvar, func SortByKey([]string)
1213
pkg envvar, func SplitKeyValue(string) (string, string)
1314
pkg envvar, func SplitTokens(string, string) []string
15+
pkg envvar, func UniqueTokens([]string) []string
1416
pkg envvar, func VarsFromMap(map[string]string) *Vars
1517
pkg envvar, func VarsFromOS() *Vars
1618
pkg envvar, func VarsFromSlice([]string) *Vars

envvar/envvar.go

+43-14
Original file line numberDiff line numberDiff line change
@@ -145,22 +145,51 @@ func JoinTokens(tokens []string, separator string) string {
145145
return value
146146
}
147147

148-
// PrependUsingSeparator prepends the parameter val to parameter existing using
149-
// separator to split the tokens in existing and to prepend val.
150-
// PrependUsingSeparator uses SplitTokens on the existing string
151-
// and hence filters out empty tokens, so "A::B:" becomes "A:B".
152-
func PrependUsingSeparator(val, existing, separator string) string {
153-
tmp := SplitTokens(existing, separator)
154-
return JoinTokens(append([]string{val}, tmp...), separator)
148+
// UniqueTokens returns a new slice containing tokens that are not empty or
149+
// duplicated, and in the same relative order as the original slice.
150+
func UniqueTokens(tokens []string) []string {
151+
var unique []string
152+
seen := make(map[string]bool)
153+
for _, token := range tokens {
154+
if token == "" || seen[token] {
155+
continue
156+
}
157+
seen[token] = true
158+
unique = append(unique, token)
159+
}
160+
return unique
161+
}
162+
163+
// FilterToken returns a new slice containing tokens that are not empty or match
164+
// the target, and in the same relative order as the original slice.
165+
func FilterToken(tokens []string, target string) []string {
166+
var filtered []string
167+
for _, token := range tokens {
168+
if token == "" || token == target {
169+
continue
170+
}
171+
filtered = append(filtered, token)
172+
}
173+
return filtered
174+
}
175+
176+
// PrependUniqueToken prepends token to value, which is separated by separator,
177+
// removing all empty and duplicate tokens. Returns a string where token only
178+
// occurs once, and is first.
179+
func PrependUniqueToken(value, separator, token string) string {
180+
result := SplitTokens(value, separator)
181+
result = append([]string{token}, result...)
182+
return JoinTokens(UniqueTokens(result), separator)
155183
}
156184

157-
// AppendUsingSeparator appends the parameter val to parameter existing using
158-
// separator to split the tokens in existing string and to append val.
159-
// AppendUsingSeparator uses SplitTokens on the existing string
160-
// and hence filters out empty tokens, so "A::B:" becomes "A:B".
161-
func AppendUsingSeparator(val, existing, separator string) string {
162-
tmp := SplitTokens(existing, separator)
163-
return JoinTokens(append(tmp, val), separator)
185+
// AppendUniqueToken appends token to value, which is separated by separator,
186+
// and removes all empty and duplicate tokens. Returns a string where token
187+
// only occurs once, and is last.
188+
func AppendUniqueToken(value, separator, token string) string {
189+
result := SplitTokens(value, separator)
190+
result = FilterToken(result, token)
191+
result = append(result, token)
192+
return JoinTokens(UniqueTokens(result), separator)
164193
}
165194

166195
// SortByKey sorts vars into ascending key order, where vars is expected to be

envvar/envvar_test.go

+81-22
Original file line numberDiff line numberDiff line change
@@ -161,36 +161,95 @@ func TestSplitJoinTokens(t *testing.T) {
161161
}
162162
}
163163

164-
func TestAppendPrepend(t *testing.T) {
164+
func TestUniqueTokens(t *testing.T) {
165165
tests := []struct {
166-
Sep, Value, Existing, Result string
166+
Tokens, Want []string
167167
}{
168-
{":", "Z", "", "Z"},
169-
{":", "", "Z", "Z"},
170-
{":", "", "", ""},
171-
{":", "X", ":A:B", "X:A:B"},
172-
{":", "Y", "A:B", "Y:A:B"},
173-
{":", "Z", "A:::B", "Z:A:B"},
174-
{":", "Z", "A:::B:", "Z:A:B"},
175-
}
176-
for i, test := range tests {
177-
if got, want := PrependUsingSeparator(test.Value, test.Existing, test.Sep), test.Result; got != want {
178-
t.Errorf("SplitTokens(%d) got %v, want %v", i, got, want)
168+
{nil, nil},
169+
{[]string{""}, nil},
170+
{[]string{"A"}, []string{"A"}},
171+
{[]string{"A", "A"}, []string{"A"}},
172+
{[]string{"A", "B"}, []string{"A", "B"}},
173+
{[]string{"A", "B", "A", "B"}, []string{"A", "B"}},
174+
}
175+
for _, test := range tests {
176+
if got, want := UniqueTokens(test.Tokens), test.Want; !reflect.DeepEqual(got, want) {
177+
t.Errorf("UniqueTokens(%q) got %q, want %q", test.Tokens, got, want)
179178
}
180179
}
181-
tests = []struct {
182-
Sep, Value, Existing, Result string
180+
}
181+
182+
func TestFilterToken(t *testing.T) {
183+
tests := []struct {
184+
Tokens []string
185+
Target string
186+
Want []string
183187
}{
184-
{":", "Z", "", "Z"},
188+
{nil, "", nil},
189+
{nil, "A", nil},
190+
{[]string{""}, "", nil},
191+
{[]string{""}, "A", nil},
192+
{[]string{"A"}, "", []string{"A"}},
193+
{[]string{"A"}, "A", nil},
194+
{[]string{"A"}, "B", []string{"A"}},
195+
{[]string{"A", "A"}, "", []string{"A", "A"}},
196+
{[]string{"A", "A"}, "A", nil},
197+
{[]string{"A", "A"}, "B", []string{"A", "A"}},
198+
{[]string{"A", "B"}, "", []string{"A", "B"}},
199+
{[]string{"A", "B"}, "A", []string{"B"}},
200+
{[]string{"A", "B"}, "B", []string{"A"}},
201+
{[]string{"A", "B", "A", "B"}, "", []string{"A", "B", "A", "B"}},
202+
{[]string{"A", "B", "A", "B"}, "A", []string{"B", "B"}},
203+
{[]string{"A", "B", "A", "B"}, "B", []string{"A", "A"}},
204+
}
205+
for _, test := range tests {
206+
if got, want := FilterToken(test.Tokens, test.Target), test.Want; !reflect.DeepEqual(got, want) {
207+
t.Errorf("FilterToken(%q, %q) got %q, want %q", test.Tokens, test.Target, got, want)
208+
}
209+
}
210+
}
211+
212+
func TestPrependUniqueToken(t *testing.T) {
213+
tests := []struct {
214+
Sep, Value, Token, Want string
215+
}{
216+
{":", "", "", ""},
185217
{":", "", "Z", "Z"},
218+
{":", "Z", "", "Z"},
219+
{":", "Z", "Z", "Z"},
220+
{":", ":Z:Z:", "Z", "Z"},
221+
{":", ":A:B", "Z", "Z:A:B"},
222+
{":", "A:B", "Z", "Z:A:B"},
223+
{":", "A:::B", "Z", "Z:A:B"},
224+
{":", "A:::B:", "Z", "Z:A:B"},
225+
{":", "Z:A:Z:B:Z", "Z", "Z:A:B"},
226+
{":", "Z:A:Z:B:Z:A:Z:B:Z", "Z", "Z:A:B"},
227+
}
228+
for _, test := range tests {
229+
if got, want := PrependUniqueToken(test.Value, test.Sep, test.Token), test.Want; got != want {
230+
t.Errorf("PrependUniqueToken(%q, %q, %q) got %v, want %v", test.Value, test.Sep, test.Token, got, want)
231+
}
232+
}
233+
}
234+
235+
func TestAppendUniqueToken(t *testing.T) {
236+
tests := []struct {
237+
Sep, Value, Token, Want string
238+
}{
186239
{":", "", "", ""},
187-
{":", "X", ":A:B:", "A:B:X"},
188-
{":", "Y", "A:B", "A:B:Y"},
189-
{":", "Z", "A:::B", "A:B:Z"},
240+
{":", "", "Z", "Z"},
241+
{":", "Z", "", "Z"},
242+
{":", "Z", "Z", "Z"},
243+
{":", ":Z:Z:", "Z", "Z"},
244+
{":", ":A:B:", "Z", "A:B:Z"},
245+
{":", "A:B", "Z", "A:B:Z"},
246+
{":", "A:::B", "Z", "A:B:Z"},
247+
{":", "Z:A:Z:B:Z", "Z", "A:B:Z"},
248+
{":", "Z:A:Z:B:Z:A:Z:B:Z", "Z", "A:B:Z"},
190249
}
191-
for i, test := range tests {
192-
if got, want := AppendUsingSeparator(test.Value, test.Existing, test.Sep), test.Result; got != want {
193-
t.Errorf("SplitTokens(%d) got %v, want %v", i, got, want)
250+
for _, test := range tests {
251+
if got, want := AppendUniqueToken(test.Value, test.Sep, test.Token), test.Want; got != want {
252+
t.Errorf("AppendUniqueToken(%q, %q, %q) got %v, want %v", test.Value, test.Sep, test.Token, got, want)
194253
}
195254
}
196255
}

0 commit comments

Comments
 (0)