Skip to content

Commit 17a0fef

Browse files
plusvicstepancheg
authored andcommitted
fix: allow commas and semicolons between fields in constant messages
According to the Protobuf Specification the fields in a constant message can be separated by spaces, commas or semicolons. All the following variants are accepted by the official compiler`protoc`. ``` {foo: 1,bar: 2,baz: 3,} {foo: 1;bar: 2;baz: 3;} {foo: 1 bar: 2 baz: 3} {foo: 1,bar: 2;baz: 3} {foo: 1,bar: 2 baz: 3} ```
1 parent 46e7f0d commit 17a0fef

File tree

2 files changed

+43
-4
lines changed

2 files changed

+43
-4
lines changed

protobuf-parse/src/pure/parser.rs

+35
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,18 @@ impl<'a> Parser<'a> {
377377
while !self.tokenizer.lookahead_is_symbol('}')? {
378378
let n = self.next_message_constant_field_name()?;
379379
let v = self.next_field_value()?;
380+
381+
// Consume the comma or semicolon if present. Commas and semicolons
382+
// between message fields are optional, all these are valid:
383+
//
384+
// {foo: 1,bar: 2,baz: 3,}
385+
// {foo: 1;bar: 2;baz: 3;}
386+
// {foo: 1 bar: 2 baz: 3}
387+
// {foo: 1,bar: 2;baz: 3}
388+
// {foo: 1,bar: 2 baz: 3}
389+
//
390+
self.tokenizer.next_symbol_if_in(&[',', ';'])?;
391+
380392
r.fields.insert(n, v);
381393
}
382394
self.tokenizer
@@ -1336,6 +1348,29 @@ mod test {
13361348
assert_eq!("10", mess.t.options[0].value.format());
13371349
}
13381350

1351+
#[test]
1352+
fn test_field_options() {
1353+
let msg = r#" (my_opt).my_field = {foo: 1 bar: 2} "#;
1354+
let opt = parse(msg, |p| p.next_field_option());
1355+
assert_eq!(r#"{ foo: 1 bar: 2 }"#, opt.value.format());
1356+
1357+
let msg = r#" (my_opt).my_field = {foo: 1; bar:2;} "#;
1358+
let opt = parse(msg, |p| p.next_field_option());
1359+
assert_eq!(r#"{ foo: 1 bar: 2 }"#, opt.value.format());
1360+
1361+
let msg = r#" (my_opt).my_field = {foo: 1, bar: 2} "#;
1362+
let opt = parse(msg, |p| p.next_field_option());
1363+
assert_eq!(r#"{ foo: 1 bar: 2 }"#, opt.value.format());
1364+
1365+
let msg = r#" (my_opt).my_field = "foo" "#;
1366+
let opt = parse(msg, |p| p.next_field_option());
1367+
assert_eq!(r#""foo""#, opt.value.format());
1368+
1369+
let msg = r#" (my_opt) = { my_field: "foo"} "#;
1370+
let opt = parse(msg, |p| p.next_field_option());
1371+
assert_eq!(r#"{ my_field: "foo" }"#, opt.value.format());
1372+
}
1373+
13391374
#[test]
13401375
fn test_message() {
13411376
let msg = r#"message ReferenceData

protobuf-support/src/lexer/tokenizer.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,15 @@ impl<'a> Tokenizer<'a> {
194194
Ok(())
195195
}
196196

197-
pub fn next_symbol_if_eq(&mut self, symbol: char) -> TokenizerResult<bool> {
198-
Ok(self.next_token_if(|token| match token {
199-
&Token::Symbol(c) if c == symbol => true,
197+
pub fn next_symbol_if_in(&mut self, symbols: &[char]) -> TokenizerResult<bool> {
198+
self.next_token_if(|token| match token {
199+
Token::Symbol(c) if symbols.contains(c) => true,
200200
_ => false,
201-
})? != None)
201+
}).map(|token| token.is_some())
202+
}
203+
204+
pub fn next_symbol_if_eq(&mut self, symbol: char) -> TokenizerResult<bool> {
205+
self.next_symbol_if_in(&[symbol])
202206
}
203207

204208
pub fn next_symbol_expect_eq(

0 commit comments

Comments
 (0)