Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions envsubst_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package envsubst

import (
"io/ioutil"
"os"
"testing"
)
Expand All @@ -23,8 +22,9 @@ func TestIntegration(t *testing.T) {
t.Error("Expect bytes integration test to pass")
}
bytes, err = ReadFile("testdata/file.tmpl")
fexpected, err := ioutil.ReadFile("testdata/file.out")
fexpected, err := os.ReadFile("testdata/file.out")
if string(bytes) != string(fexpected) || err != nil {
t.Logf("output:\n%s\nexpected:\n%s\n", string(bytes), string(fexpected))
t.Error("Expect ReadFile integration test to pass")
}
}
33 changes: 21 additions & 12 deletions parse/lex.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,16 @@ type stateFn func(*lexer) stateFn

// lexer holds the state of the scanner
type lexer struct {
input string // the string being lexed
state stateFn // the next lexing function to enter
pos Pos // current position in the input
start Pos // start position of this item
width Pos // width of last rune read from input
lastPos Pos // position of most recent item returned by nextItem
items chan item // channel of lexed items
subsDepth int // depth of substitution
noDigit bool // if the lexer skips variables that start with a digit
input string // the string being lexed
state stateFn // the next lexing function to enter
pos Pos // current position in the input
start Pos // start position of this item
width Pos // width of last rune read from input
lastPos Pos // position of most recent item returned by nextItem
items chan item // channel of lexed items
subsDepth int // depth of substitution
braceDepth int // depth of braces within default value
noDigit bool // if the lexer skips variables that start with a digit
}

// next returns the next rune in the input.
Expand Down Expand Up @@ -242,10 +243,18 @@ func lexSubstitutionOperator(l *lexer) stateFn {
// lexSubstitution scans the elements inside substitution delimiters.
func lexSubstitution(l *lexer) stateFn {
switch r := l.next(); {
case r == '{':
l.braceDepth++
l.emit(itemText)
case r == '}':
l.subsDepth--
l.emit(itemRightDelim)
return lexText
if l.braceDepth > 0 {
l.braceDepth--
l.emit(itemText)
} else {
l.subsDepth--
l.emit(itemRightDelim)
return lexText
}
case r == eof || isEndOfLine(r):
return l.errorf("closing brace expected")
case isAlphaNumeric(r) && strings.HasPrefix(l.input[l.lastPos:], "${"):
Expand Down
81 changes: 81 additions & 0 deletions parse/lex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,87 @@ var lexTests = []lexTest{
{itemText, 10, "ABC}"},
tEOF,
}},
{"braces in default", "${BAR:-{}}", []item{
tLeft,
{itemVariable, 0, "BAR"},
tColDash,
{itemText, 0, "{"},
{itemText, 0, "}"},
tRight,
tEOF,
}},
{"nested braces in default", "${ENV:-{\"key\":\"value\"}}", []item{
tLeft,
{itemVariable, 0, "ENV"},
tColDash,
{itemText, 0, "{"},
{itemText, 0, "\""},
{itemText, 0, "k"},
{itemText, 0, "e"},
{itemText, 0, "y"},
{itemText, 0, "\""},
{itemText, 0, ":"},
{itemText, 0, "\""},
{itemText, 0, "v"},
{itemText, 0, "a"},
{itemText, 0, "l"},
{itemText, 0, "u"},
{itemText, 0, "e"},
{itemText, 0, "\""},
{itemText, 0, "}"},
tRight,
tEOF,
}},
{"multi-level nested braces in default", "${ENV:-{\"key\":{\"subkey\":\"subval\"},\"key2\":\"value2\"}}", []item{
tLeft,
{itemVariable, 0, "ENV"},
tColDash,
{itemText, 0, "{"},
{itemText, 0, "\""},
{itemText, 0, "k"},
{itemText, 0, "e"},
{itemText, 0, "y"},
{itemText, 0, "\""},
{itemText, 0, ":"},
{itemText, 0, "{"},
{itemText, 0, "\""},
{itemText, 0, "s"},
{itemText, 0, "u"},
{itemText, 0, "b"},
{itemText, 0, "k"},
{itemText, 0, "e"},
{itemText, 0, "y"},
{itemText, 0, "\""},
{itemText, 0, ":"},
{itemText, 0, "\""},
{itemText, 0, "s"},
{itemText, 0, "u"},
{itemText, 0, "b"},
{itemText, 0, "v"},
{itemText, 0, "a"},
{itemText, 0, "l"},
{itemText, 0, "\""},
{itemText, 0, "}"},
{itemText, 0, ","},
{itemText, 0, "\""},
{itemText, 0, "k"},
{itemText, 0, "e"},
{itemText, 0, "y"},
{itemText, 0, "2"},
{itemText, 0, "\""},
{itemText, 0, ":"},
{itemText, 0, "\""},
{itemText, 0, "v"},
{itemText, 0, "a"},
{itemText, 0, "l"},
{itemText, 0, "u"},
{itemText, 0, "e"},
{itemText, 0, "2"},
{itemText, 0, "\""},
{itemText, 0, "}"},
tRight,
tEOF,
}},
}

func TestLex(t *testing.T) {
Expand Down
2 changes: 2 additions & 0 deletions testdata/file.out
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
foo: bar
baz: baz
env: dev
curly_bar: bar
curly: {"key":{"subkey":"subval"},"key2":"value2"}
uri: http://bar.com/foo
host: localhost
2 changes: 2 additions & 0 deletions testdata/file.tmpl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
foo: $BAR
baz: ${FOO:=baz}
env: ${ENV:-dev}
curly_bar: ${BAR:-{}}
curly: ${ENV:-{"key":{"subkey":"subval"},"key2":"value2"}}
uri: http://${BAR:=$BAZ}.com/foo
host: ${BAR:+localhost}