Skip to content

Commit

Permalink
Allow env var split delimiter escaping (#129)
Browse files Browse the repository at this point in the history
* Allow env var split delimiter escaping

* Update options.go
  • Loading branch information
peterbourgon committed Feb 8, 2024
1 parent 29a1b5c commit a70a378
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 1 deletion.
6 changes: 6 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ func WithEnvVarPrefix(prefix string) Option {
// one time, with the value `a,b,c`. Providing WithEnvVarSplit with a comma
// delimiter would set `foo` multiple times, with the values `a`, `b`, and `c`.
//
// If an env var value contains the delimiter prefixed by a single backslash,
// that occurrence will be treated as a literal string, and not as a split
// point. For example, `FOO=a,b\,c` with a delimiter of `,` would yield values
// `a` and `b,c`. Or, `FOO=axxxb\xxxc` with a delimiter of `xxx` would yield
// values `a` and `bxxxc`.
//
// By default, no splitting of environment variable values occurs.
func WithEnvVarSplit(delimiter string) Option {
return func(pc *ParseContext) {
Expand Down
14 changes: 13 additions & 1 deletion parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func parse(fs Flags, args []string, options ...Option) error {
// The value may need to be split.
vals := []string{val}
if pc.envVarSplit != "" {
vals = strings.Split(val, pc.envVarSplit)
vals = splitEscape(val, pc.envVarSplit)
}

// Set the flag to the value(s).
Expand Down Expand Up @@ -291,6 +291,18 @@ func maybePrefix(key string, prefix string) string {
return key
}

func splitEscape(s string, separator string) []string {
escape := `\`
tokens := strings.Split(s, separator)
for i := len(tokens) - 2; i >= 0; i-- {
if strings.HasSuffix(tokens[i], escape) {
tokens[i] = tokens[i][:len(tokens[i])-len(escape)] + separator + tokens[i+1]
tokens = append(tokens[:i+1], tokens[i+2:]...)
}
}
return tokens
}

//
//
//
Expand Down
12 changes: 12 additions & 0 deletions parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ func TestParse(t *testing.T) {
Options: []ff.Option{ff.WithEnvVarPrefix("TEST_PARSE"), ff.WithEnvVarSplit(",")},
Want: fftest.Vars{S: " three ", X: []string{"one", " two", " three "}},
},
{
Name: "env var split escaping",
Environment: map[string]string{"TEST_PARSE_S": `a\,b`, "TEST_PARSE_X": `one,two\,three`},
Options: []ff.Option{ff.WithEnvVarPrefix("TEST_PARSE"), ff.WithEnvVarSplit(",")},
Want: fftest.Vars{S: `a,b`, X: []string{`one`, `two,three`}},
},
{
Name: "env var split escaping multichar",
Environment: map[string]string{"TEST_PARSE_S": `a\xxb`, "TEST_PARSE_X": `onexxtwo\xxthree`},
Options: []ff.Option{ff.WithEnvVarPrefix("TEST_PARSE"), ff.WithEnvVarSplit("xx")},
Want: fftest.Vars{S: `axxb`, X: []string{`one`, `twoxxthree`}},
},
}

testcases.Run(t)
Expand Down

0 comments on commit a70a378

Please sign in to comment.