Skip to content

Commit

Permalink
fix: allow commas and semicolons between fields in constant messages
Browse files Browse the repository at this point in the history
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}
```
  • Loading branch information
plusvic committed Sep 4, 2024
1 parent 0beff24 commit 9d32945
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 4 deletions.
35 changes: 35 additions & 0 deletions protobuf-parse/src/pure/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
12 changes: 8 additions & 4 deletions protobuf-support/src/lexer/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,15 @@ impl<'a> Tokenizer<'a> {
Ok(())
}

pub fn next_symbol_if_eq(&mut self, symbol: char) -> TokenizerResult<bool> {
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<bool> {
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<bool> {
self.next_symbol_if_in(&[symbol])
}

pub fn next_symbol_expect_eq(
Expand Down

0 comments on commit 9d32945

Please sign in to comment.