diff --git a/protobuf-parse/src/pure/parser.rs b/protobuf-parse/src/pure/parser.rs index a4f16e87a..a61a110b1 100644 --- a/protobuf-parse/src/pure/parser.rs +++ b/protobuf-parse/src/pure/parser.rs @@ -377,6 +377,18 @@ impl<'a> Parser<'a> { while !self.tokenizer.lookahead_is_symbol('}')? { let n = self.next_message_constant_field_name()?; let v = self.next_field_value()?; + + // Consume the comma or semicolon if present. Commas and semicolons + // between message fields are optional, all these are valid: + // + // {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} + // + self.tokenizer.next_symbol_if_in(&[',', ';'])?; + r.fields.insert(n, v); } self.tokenizer @@ -1336,6 +1348,29 @@ mod test { assert_eq!("10", mess.t.options[0].value.format()); } + #[test] + fn test_field_options() { + let msg = r#" (my_opt).my_field = {foo: 1 bar: 2} "#; + let opt = parse(msg, |p| p.next_field_option()); + assert_eq!(r#"{ foo: 1 bar: 2 }"#, opt.value.format()); + + let msg = r#" (my_opt).my_field = {foo: 1; bar:2;} "#; + let opt = parse(msg, |p| p.next_field_option()); + assert_eq!(r#"{ foo: 1 bar: 2 }"#, opt.value.format()); + + let msg = r#" (my_opt).my_field = {foo: 1, bar: 2} "#; + let opt = parse(msg, |p| p.next_field_option()); + assert_eq!(r#"{ foo: 1 bar: 2 }"#, opt.value.format()); + + let msg = r#" (my_opt).my_field = "foo" "#; + let opt = parse(msg, |p| p.next_field_option()); + assert_eq!(r#""foo""#, opt.value.format()); + + let msg = r#" (my_opt) = { my_field: "foo"} "#; + let opt = parse(msg, |p| p.next_field_option()); + assert_eq!(r#"{ my_field: "foo" }"#, opt.value.format()); + } + #[test] fn test_message() { let msg = r#"message ReferenceData diff --git a/protobuf-support/src/lexer/tokenizer.rs b/protobuf-support/src/lexer/tokenizer.rs index c5e84a0a9..94b641d3d 100644 --- a/protobuf-support/src/lexer/tokenizer.rs +++ b/protobuf-support/src/lexer/tokenizer.rs @@ -194,11 +194,15 @@ impl<'a> Tokenizer<'a> { Ok(()) } - pub fn next_symbol_if_eq(&mut self, symbol: char) -> TokenizerResult { - Ok(self.next_token_if(|token| match token { - &Token::Symbol(c) if c == symbol => true, + pub fn next_symbol_if_in(&mut self, symbols: &[char]) -> TokenizerResult { + self.next_token_if(|token| match token { + Token::Symbol(c) if symbols.contains(c) => true, _ => false, - })? != None) + }).map(|token| token.is_some()) + } + + pub fn next_symbol_if_eq(&mut self, symbol: char) -> TokenizerResult { + self.next_symbol_if_in(&[symbol]) } pub fn next_symbol_expect_eq(