diff --git a/conf/lex.go b/conf/lex.go index d09bf445..3570057b 100644 --- a/conf/lex.go +++ b/conf/lex.go @@ -73,6 +73,9 @@ const ( sqStringStart = '\'' sqStringEnd = '\'' optValTerm = ';' + topOptStart = '{' + topOptValTerm = ',' + topOptTerm = '}' blockStart = '(' blockEnd = ')' ) @@ -234,6 +237,8 @@ func lexTop(lx *lexer) stateFn { } switch r { + case topOptStart: + return lexSkip(lx, lexTop) case commentHashStart: lx.push(lexTop) return lexCommentStart @@ -280,7 +285,7 @@ func lexTopValueEnd(lx *lexer) stateFn { fallthrough case isWhitespace(r): return lexTopValueEnd - case isNL(r) || r == eof || r == optValTerm: + case isNL(r) || r == eof || r == optValTerm || r == topOptValTerm || r == topOptTerm: lx.ignore() return lexTop } diff --git a/conf/lex_test.go b/conf/lex_test.go index 5742413d..bba9c0c1 100644 --- a/conf/lex_test.go +++ b/conf/lex_test.go @@ -1049,3 +1049,152 @@ func TestMapInclude(t *testing.T) { lx = lex("foo { include \"users.conf\"}") expect(t, lx, expectedItems) } + +func TestJSONCompat(t *testing.T) { + for _, test := range []struct { + name string + input string + expected []item + }{ + { + name: "should omit initial and final brackets at top level with a single item", + input: ` + { + "http_port": 8223 + } + `, + expected: []item{ + {itemKey, "http_port", 3}, + {itemInteger, "8223", 3}, + }, + }, + { + name: "should omit trailing commas at top level with two items", + input: ` + { + "http_port": 8223, + "port": 4223 + } + `, + expected: []item{ + {itemKey, "http_port", 3}, + {itemInteger, "8223", 3}, + {itemKey, "port", 4}, + {itemInteger, "4223", 4}, + }, + }, + { + name: "should omit trailing commas at top level with multiple items", + input: ` + { + "http_port": 8223, + "port": 4223, + "max_payload": "5MB", + "debug": true, + "max_control_line": 1024 + } + `, + expected: []item{ + {itemKey, "http_port", 3}, + {itemInteger, "8223", 3}, + {itemKey, "port", 4}, + {itemInteger, "4223", 4}, + {itemKey, "max_payload", 5}, + {itemString, "5MB", 5}, + {itemKey, "debug", 6}, + {itemBool, "true", 6}, + {itemKey, "max_control_line", 7}, + {itemInteger, "1024", 7}, + }, + }, + { + name: "should support JSON not prettified", + input: `{"http_port": 8224,"port": 4224} + `, + expected: []item{ + {itemKey, "http_port", 1}, + {itemInteger, "8224", 1}, + {itemKey, "port", 1}, + {itemInteger, "4224", 1}, + }, + }, + { + name: "should support JSON not prettified with final bracket after newline", + input: `{"http_port": 8225,"port": 4225 + } + `, + expected: []item{ + {itemKey, "http_port", 1}, + {itemInteger, "8225", 1}, + {itemKey, "port", 1}, + {itemInteger, "4225", 1}, + }, + }, + { + name: "should support uglified JSON with inner blocks", + input: `{"http_port": 8227,"port": 4227,"write_deadline": "1h","cluster": {"port": 6222,"routes": ["nats://127.0.0.1:4222","nats://127.0.0.1:4223","nats://127.0.0.1:4224"]}} + `, + expected: []item{ + {itemKey, "http_port", 1}, + {itemInteger, "8227", 1}, + {itemKey, "port", 1}, + {itemInteger, "4227", 1}, + {itemKey, "write_deadline", 1}, + {itemString, "1h", 1}, + {itemKey, "cluster", 1}, + {itemMapStart, "", 1}, + {itemKey, "port", 1}, + {itemInteger, "6222", 1}, + {itemKey, "routes", 1}, + {itemArrayStart, "", 1}, + {itemString, "nats://127.0.0.1:4222", 1}, + {itemString, "nats://127.0.0.1:4223", 1}, + {itemString, "nats://127.0.0.1:4224", 1}, + {itemArrayEnd, "", 1}, + {itemMapEnd, "", 1}, + }, + }, + { + name: "should support prettified JSON with inner blocks", + input: ` + { + "http_port": 8227, + "port": 4227, + "write_deadline": "1h", + "cluster": { + "port": 6222, + "routes": [ + "nats://127.0.0.1:4222", + "nats://127.0.0.1:4223", + "nats://127.0.0.1:4224" + ] + } + } + `, + expected: []item{ + {itemKey, "http_port", 3}, + {itemInteger, "8227", 3}, + {itemKey, "port", 4}, + {itemInteger, "4227", 4}, + {itemKey, "write_deadline", 5}, + {itemString, "1h", 5}, + {itemKey, "cluster", 6}, + {itemMapStart, "", 6}, + {itemKey, "port", 7}, + {itemInteger, "6222", 7}, + {itemKey, "routes", 8}, + {itemArrayStart, "", 8}, + {itemString, "nats://127.0.0.1:4222", 9}, + {itemString, "nats://127.0.0.1:4223", 10}, + {itemString, "nats://127.0.0.1:4224", 11}, + {itemArrayEnd, "", 12}, + {itemMapEnd, "", 13}, + }, + }, + } { + t.Run(test.name, func(t *testing.T) { + lx := lex(test.input) + expect(t, lx, test.expected) + }) + } +}