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
diff --git a/envvar/.api b/envvar/.api
index 569d0b4..866ee03 100644
--- a/envvar/.api
+++ b/envvar/.api
@@ -1,16 +1,18 @@
-pkg envvar, func AppendUsingSeparator(string, string, string) string
+pkg envvar, func AppendUniqueToken(string, string, string) string
pkg envvar, func CopyMap(map[string]string) map[string]string
pkg envvar, func CopySlice([]string) []string
+pkg envvar, func FilterToken([]string, string) []string
pkg envvar, func JoinKeyValue(string, string) string
pkg envvar, func JoinTokens([]string, string) string
pkg envvar, func MapToSlice(map[string]string) []string
pkg envvar, func MergeMaps(...map[string]string) map[string]string
pkg envvar, func MergeSlices(...[]string) []string
-pkg envvar, func PrependUsingSeparator(string, string, string) string
+pkg envvar, func PrependUniqueToken(string, string, string) string
pkg envvar, func SliceToMap([]string) map[string]string
pkg envvar, func SortByKey([]string)
pkg envvar, func SplitKeyValue(string) (string, string)
pkg envvar, func SplitTokens(string, string) []string
+pkg envvar, func UniqueTokens([]string) []string
pkg envvar, func VarsFromMap(map[string]string) *Vars
pkg envvar, func VarsFromOS() *Vars
pkg envvar, func VarsFromSlice([]string) *Vars
diff --git a/envvar/envvar.go b/envvar/envvar.go
index 788f948..b8a9c3d 100644
--- a/envvar/envvar.go
+++ b/envvar/envvar.go
@@ -145,22 +145,51 @@
return value
}
-// PrependUsingSeparator prepends the parameter val to parameter existing using
-// separator to split the tokens in existing and to prepend val.
-// PrependUsingSeparator uses SplitTokens on the existing string
-// and hence filters out empty tokens, so "A::B:" becomes "A:B".
-func PrependUsingSeparator(val, existing, separator string) string {
- tmp := SplitTokens(existing, separator)
- return JoinTokens(append([]string{val}, tmp...), separator)
+// UniqueTokens returns a new slice containing tokens that are not empty or
+// duplicated, and in the same relative order as the original slice.
+func UniqueTokens(tokens []string) []string {
+ var unique []string
+ seen := make(map[string]bool)
+ for _, token := range tokens {
+ if token == "" || seen[token] {
+ continue
+ }
+ seen[token] = true
+ unique = append(unique, token)
+ }
+ return unique
}
-// AppendUsingSeparator appends the parameter val to parameter existing using
-// separator to split the tokens in existing string and to append val.
-// AppendUsingSeparator uses SplitTokens on the existing string
-// and hence filters out empty tokens, so "A::B:" becomes "A:B".
-func AppendUsingSeparator(val, existing, separator string) string {
- tmp := SplitTokens(existing, separator)
- return JoinTokens(append(tmp, val), separator)
+// FilterToken returns a new slice containing tokens that are not empty or match
+// the target, and in the same relative order as the original slice.
+func FilterToken(tokens []string, target string) []string {
+ var filtered []string
+ for _, token := range tokens {
+ if token == "" || token == target {
+ continue
+ }
+ filtered = append(filtered, token)
+ }
+ return filtered
+}
+
+// PrependUniqueToken prepends token to value, which is separated by separator,
+// removing all empty and duplicate tokens. Returns a string where token only
+// occurs once, and is first.
+func PrependUniqueToken(value, separator, token string) string {
+ result := SplitTokens(value, separator)
+ result = append([]string{token}, result...)
+ return JoinTokens(UniqueTokens(result), separator)
+}
+
+// AppendUniqueToken appends token to value, which is separated by separator,
+// and removes all empty and duplicate tokens. Returns a string where token
+// only occurs once, and is last.
+func AppendUniqueToken(value, separator, token string) string {
+ result := SplitTokens(value, separator)
+ result = FilterToken(result, token)
+ result = append(result, token)
+ return JoinTokens(UniqueTokens(result), separator)
}
// SortByKey sorts vars into ascending key order, where vars is expected to be
diff --git a/envvar/envvar_test.go b/envvar/envvar_test.go
index e2f9cc9..163cd34 100644
--- a/envvar/envvar_test.go
+++ b/envvar/envvar_test.go
@@ -161,36 +161,95 @@
}
}
-func TestAppendPrepend(t *testing.T) {
+func TestUniqueTokens(t *testing.T) {
tests := []struct {
- Sep, Value, Existing, Result string
+ Tokens, Want []string
}{
- {":", "Z", "", "Z"},
- {":", "", "Z", "Z"},
- {":", "", "", ""},
- {":", "X", ":A:B", "X:A:B"},
- {":", "Y", "A:B", "Y:A:B"},
- {":", "Z", "A:::B", "Z:A:B"},
- {":", "Z", "A:::B:", "Z:A:B"},
+ {nil, nil},
+ {[]string{""}, nil},
+ {[]string{"A"}, []string{"A"}},
+ {[]string{"A", "A"}, []string{"A"}},
+ {[]string{"A", "B"}, []string{"A", "B"}},
+ {[]string{"A", "B", "A", "B"}, []string{"A", "B"}},
}
- for i, test := range tests {
- if got, want := PrependUsingSeparator(test.Value, test.Existing, test.Sep), test.Result; got != want {
- t.Errorf("SplitTokens(%d) got %v, want %v", i, got, want)
+ for _, test := range tests {
+ if got, want := UniqueTokens(test.Tokens), test.Want; !reflect.DeepEqual(got, want) {
+ t.Errorf("UniqueTokens(%q) got %q, want %q", test.Tokens, got, want)
}
}
- tests = []struct {
- Sep, Value, Existing, Result string
+}
+
+func TestFilterToken(t *testing.T) {
+ tests := []struct {
+ Tokens []string
+ Target string
+ Want []string
}{
- {":", "Z", "", "Z"},
- {":", "", "Z", "Z"},
- {":", "", "", ""},
- {":", "X", ":A:B:", "A:B:X"},
- {":", "Y", "A:B", "A:B:Y"},
- {":", "Z", "A:::B", "A:B:Z"},
+ {nil, "", nil},
+ {nil, "A", nil},
+ {[]string{""}, "", nil},
+ {[]string{""}, "A", nil},
+ {[]string{"A"}, "", []string{"A"}},
+ {[]string{"A"}, "A", nil},
+ {[]string{"A"}, "B", []string{"A"}},
+ {[]string{"A", "A"}, "", []string{"A", "A"}},
+ {[]string{"A", "A"}, "A", nil},
+ {[]string{"A", "A"}, "B", []string{"A", "A"}},
+ {[]string{"A", "B"}, "", []string{"A", "B"}},
+ {[]string{"A", "B"}, "A", []string{"B"}},
+ {[]string{"A", "B"}, "B", []string{"A"}},
+ {[]string{"A", "B", "A", "B"}, "", []string{"A", "B", "A", "B"}},
+ {[]string{"A", "B", "A", "B"}, "A", []string{"B", "B"}},
+ {[]string{"A", "B", "A", "B"}, "B", []string{"A", "A"}},
}
- for i, test := range tests {
- if got, want := AppendUsingSeparator(test.Value, test.Existing, test.Sep), test.Result; got != want {
- t.Errorf("SplitTokens(%d) got %v, want %v", i, got, want)
+ for _, test := range tests {
+ if got, want := FilterToken(test.Tokens, test.Target), test.Want; !reflect.DeepEqual(got, want) {
+ t.Errorf("FilterToken(%q, %q) got %q, want %q", test.Tokens, test.Target, got, want)
+ }
+ }
+}
+
+func TestPrependUniqueToken(t *testing.T) {
+ tests := []struct {
+ Sep, Value, Token, Want string
+ }{
+ {":", "", "", ""},
+ {":", "", "Z", "Z"},
+ {":", "Z", "", "Z"},
+ {":", "Z", "Z", "Z"},
+ {":", ":Z:Z:", "Z", "Z"},
+ {":", ":A:B", "Z", "Z:A:B"},
+ {":", "A:B", "Z", "Z:A:B"},
+ {":", "A:::B", "Z", "Z:A:B"},
+ {":", "A:::B:", "Z", "Z:A:B"},
+ {":", "Z:A:Z:B:Z", "Z", "Z:A:B"},
+ {":", "Z:A:Z:B:Z:A:Z:B:Z", "Z", "Z:A:B"},
+ }
+ for _, test := range tests {
+ if got, want := PrependUniqueToken(test.Value, test.Sep, test.Token), test.Want; got != want {
+ t.Errorf("PrependUniqueToken(%q, %q, %q) got %v, want %v", test.Value, test.Sep, test.Token, got, want)
+ }
+ }
+}
+
+func TestAppendUniqueToken(t *testing.T) {
+ tests := []struct {
+ Sep, Value, Token, Want string
+ }{
+ {":", "", "", ""},
+ {":", "", "Z", "Z"},
+ {":", "Z", "", "Z"},
+ {":", "Z", "Z", "Z"},
+ {":", ":Z:Z:", "Z", "Z"},
+ {":", ":A:B:", "Z", "A:B:Z"},
+ {":", "A:B", "Z", "A:B:Z"},
+ {":", "A:::B", "Z", "A:B:Z"},
+ {":", "Z:A:Z:B:Z", "Z", "A:B:Z"},
+ {":", "Z:A:Z:B:Z:A:Z:B:Z", "Z", "A:B:Z"},
+ }
+ for _, test := range tests {
+ if got, want := AppendUniqueToken(test.Value, test.Sep, test.Token), test.Want; got != want {
+ t.Errorf("AppendUniqueToken(%q, %q, %q) got %v, want %v", test.Value, test.Sep, test.Token, got, want)
}
}
}