Skip to content

Commit

Permalink
Proper camel case in JSON parser/printer
Browse files Browse the repository at this point in the history
As per protobuf JSON spec
  • Loading branch information
stepancheg committed Aug 8, 2018
1 parent c2402d5 commit fe9ca9c
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 42 deletions.
89 changes: 52 additions & 37 deletions protobuf-test/src/common/v2/test_fmt_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,204 +16,219 @@ fn empty() {
fn test_bool() {
let mut m = TestTypes::new();
m.set_bool_singular(true);
test_json_print_parse_message("{bool_singular: true}", &m);
test_json_print_parse_message("{boolSingular: true}", &m);

let mut m = TestTypes::new();
m.set_bool_repeated(vec![true, false, false]);
test_json_print_parse_message("{bool_repeated: [true, false, false]}", &m);
test_json_print_parse_message("{boolRepeated: [true, false, false]}", &m);
}

#[test]
fn test_float() {
let mut m = TestTypes::new();
m.set_float_singular(10.0);
test_json_print_parse_message("{float_singular: 10.0}", &m);
test_json_print_parse_message("{floatSingular: 10.0}", &m);

let mut m = TestTypes::new();
m.float_repeated.push(11.0);
m.float_repeated.push(-12.5);
m.float_repeated.push(f32::NAN);
m.float_repeated.push(f32::NEG_INFINITY);
test_json_print_parse_message("{float_repeated: [11.0, -12.5, \"NaN\", \"-Infinity\"]}", &m);
test_json_print_parse_message("{floatRepeated: [11.0, -12.5, \"NaN\", \"-Infinity\"]}", &m);
}

#[test]
fn test_double() {
let mut m = TestTypes::new();
m.set_double_singular(12.0);
test_json_print_parse_message("{double_singular: 12.0}", &m);
test_json_parse_message("{double_singular: \"12.0\"}", &m);
test_json_print_parse_message("{doubleSingular: 12.0}", &m);
test_json_parse_message("{doubleSingular: \"12.0\"}", &m);

let mut m = TestTypes::new();
m.double_repeated.push(13.0);
m.double_repeated.push(f64::INFINITY);
test_json_print_parse_message("{double_repeated: [13.0, \"Infinity\"]}", &m);
test_json_print_parse_message("{doubleRepeated: [13.0, \"Infinity\"]}", &m);
}

#[test]
fn test_int32() {
let mut m = TestTypes::new();
m.set_int32_singular(1234);
test_json_print_parse_message("{int32_singular: 1234}", &m);
test_json_print_parse_message("{int32Singular: 1234}", &m);

let mut m = TestTypes::new();
m.int32_repeated.push(10);
m.int32_repeated.push(-20);
test_json_print_parse_message("{int32_repeated: [10, -20]}", &m);
test_json_print_parse_message("{int32Repeated: [10, -20]}", &m);
}

#[test]
fn test_int64() {
let mut m = TestTypes::new();
m.set_int64_singular(1234567890123456789);
test_json_print_parse_message("{int64_singular: \"1234567890123456789\"}", &m);
test_json_print_parse_message("{int64Singular: \"1234567890123456789\"}", &m);

let mut m = TestTypes::new();
m.int64_repeated.push(2345678901234567890);
m.int64_repeated.push(-2345678901234567890);
test_json_print_parse_message(
"{int64_repeated: [\"2345678901234567890\", \"-2345678901234567890\"]}", &m);
"{int64Repeated: [\"2345678901234567890\", \"-2345678901234567890\"]}", &m);
}

#[test]
fn test_sint32() {
let mut m = TestTypes::new();
m.set_sint32_singular(1234);
test_json_print_parse_message("{sint32_singular: 1234}", &m);
test_json_print_parse_message("{sint32Singular: 1234}", &m);

let mut m = TestTypes::new();
m.sint32_repeated.push(10);
m.sint32_repeated.push(-20);
test_json_print_parse_message("{sint32_repeated: [10, -20]}", &m);
test_json_print_parse_message("{sint32Repeated: [10, -20]}", &m);
}

#[test]
fn test_sint64() {
let mut m = TestTypes::new();
m.set_sint64_singular(1234567890123456789);
test_json_print_parse_message("{sint64_singular: \"1234567890123456789\"}", &m);
test_json_print_parse_message("{sint64Singular: \"1234567890123456789\"}", &m);

let mut m = TestTypes::new();
m.sint64_repeated.push(2345678901234567890);
m.sint64_repeated.push(-2345678901234567890);
test_json_print_parse_message(
"{sint64_repeated: [\"2345678901234567890\", \"-2345678901234567890\"]}", &m);
"{sint64Repeated: [\"2345678901234567890\", \"-2345678901234567890\"]}", &m);
}

#[test]
fn test_sfixed32() {
let mut m = TestTypes::new();
m.set_sfixed32_singular(1234);
test_json_print_parse_message("{sfixed32_singular: 1234}", &m);
test_json_print_parse_message("{sfixed32Singular: 1234}", &m);

let mut m = TestTypes::new();
m.sfixed32_repeated.push(10);
m.sfixed32_repeated.push(-20);
test_json_print_parse_message("{sfixed32_repeated: [10, -20]}", &m);
test_json_print_parse_message("{sfixed32Repeated: [10, -20]}", &m);
}

#[test]
fn test_sfixed64() {
let mut m = TestTypes::new();
m.set_sfixed64_singular(1234567890123456789);
test_json_print_parse_message("{sfixed64_singular: \"1234567890123456789\"}", &m);
test_json_print_parse_message("{sfixed64Singular: \"1234567890123456789\"}", &m);

let mut m = TestTypes::new();
m.sfixed64_repeated.push(2345678901234567890);
m.sfixed64_repeated.push(-2345678901234567890);
test_json_print_parse_message(
"{sfixed64_repeated: [\"2345678901234567890\", \"-2345678901234567890\"]}", &m);
"{sfixed64Repeated: [\"2345678901234567890\", \"-2345678901234567890\"]}", &m);
}

#[test]
fn test_uint32() {
let mut m = TestTypes::new();
m.set_uint32_singular(1234);
test_json_print_parse_message("{uint32_singular: 1234}", &m);
test_json_print_parse_message("{uint32Singular: 1234}", &m);

let mut m = TestTypes::new();
m.uint32_repeated.push(10);
m.uint32_repeated.push(20300);
test_json_print_parse_message("{uint32_repeated: [10, 20300]}", &m);
test_json_print_parse_message("{uint32Repeated: [10, 20300]}", &m);
}

#[test]
fn test_uint64() {
let mut m = TestTypes::new();
m.set_uint64_singular(1234567890123456789);
test_json_print_parse_message(
"{uint64_singular: \"1234567890123456789\"}", &m);
"{uint64Singular: \"1234567890123456789\"}", &m);

let mut m = TestTypes::new();
m.uint64_repeated.push(2345678901234567890);
m.uint64_repeated.push(1345678901234567890);
test_json_print_parse_message(
"{uint64_repeated: [\"2345678901234567890\", \"1345678901234567890\"]}", &m);
"{uint64Repeated: [\"2345678901234567890\", \"1345678901234567890\"]}", &m);
}

#[test]
fn test_fixed32() {
let mut m = TestTypes::new();
m.set_fixed32_singular(1234);
test_json_print_parse_message("{fixed32_singular: 1234}", &m);
test_json_print_parse_message("{fixed32Singular: 1234}", &m);

let mut m = TestTypes::new();
m.fixed32_repeated.push(10);
m.fixed32_repeated.push(20300);
test_json_print_parse_message("{fixed32_repeated: [10, 20300]}", &m);
test_json_print_parse_message("{fixed32Repeated: [10, 20300]}", &m);
}

#[test]
fn test_fixed64() {
let mut m = TestTypes::new();
m.set_fixed64_singular(1234567890123456789);
test_json_print_parse_message(
"{fixed64_singular: \"1234567890123456789\"}", &m);
"{fixed64Singular: \"1234567890123456789\"}", &m);
test_json_parse_message(
"{fixed64_singular: 1234567890123456789}", &m);
"{fixed64Singular: 1234567890123456789}", &m);

let mut m = TestTypes::new();
m.fixed64_repeated.push(2345678901234567890);
m.fixed64_repeated.push(1345678901234567890);
test_json_print_parse_message(
"{fixed64_repeated: [\"2345678901234567890\", \"1345678901234567890\"]}", &m);
"{fixed64Repeated: [\"2345678901234567890\", \"1345678901234567890\"]}", &m);
test_json_parse_message(
"{fixed64_repeated: [\"2345678901234567890\", 1345678901234567890]}", &m);
"{fixed64Repeated: [\"2345678901234567890\", 1345678901234567890]}", &m);
}

#[test]
fn test_string() {
let mut m = TestTypes::new();
m.set_string_singular("ab".to_owned());
test_json_print_parse_message("{string_singular: \"ab\"}", &m);
test_json_print_parse_message("{stringSingular: \"ab\"}", &m);

let mut m = TestTypes::new();
m.set_string_repeated(vec!["".to_owned(), "\0".to_owned(), "A\nB".to_owned()].into());
test_json_print_parse_message("{string_repeated: [\"\", \"\\u0000\", \"A\\nB\"]}", &m);
test_json_print_parse_message("{stringRepeated: [\"\", \"\\u0000\", \"A\\nB\"]}", &m);
}

#[test]
fn test_bytes() {
let mut m = TestTypes::new();
m.set_bytes_singular(b"ab".to_vec());
test_json_print_parse_message("{bytes_singular: \"YWI=\"}", &m);
test_json_print_parse_message("{bytesSingular: \"YWI=\"}", &m);

let mut m = TestTypes::new();
m.set_bytes_repeated(vec![b"".to_vec(), b"\0".to_vec(), b"A\nB".to_vec()].into());
test_json_print_parse_message("{bytes_repeated: [\"\", \"AA==\", \"QQpC\"]}", &m);
test_json_print_parse_message("{bytesRepeated: [\"\", \"AA==\", \"QQpC\"]}", &m);
}

#[test]
fn test_enum() {
let mut m = TestTypes::new();
m.set_test_enum_singular(TestEnum::DARK);
test_json_print_parse_message("{test_enum_singular: \"DARK\"}", &m);
test_json_parse_message("{test_enum_singular: 10}", &m);
test_json_print_parse_message("{testEnumSingular: \"DARK\"}", &m);
test_json_parse_message("{testEnumSingular: 10}", &m);

let mut m = TestTypes::new();
m.set_test_enum_repeated(vec![TestEnum::DARK, TestEnum::LIGHT]);
test_json_print_parse_message("{test_enum_repeated: [\"DARK\", \"LIGHT\"]}", &m);
test_json_parse_message("{test_enum_repeated: [\"DARK\", 20]}", &m);
test_json_print_parse_message("{testEnumRepeated: [\"DARK\", \"LIGHT\"]}", &m);
test_json_parse_message("{testEnumRepeated: [\"DARK\", 20]}", &m);
}

/// Proto3 JSON parsers are required to accept both the converted `lowerCamelCase` name
/// and the proto field name.
#[test]
fn test_accepts_both_json_and_original() {
let mut m = TestTypes::new();
m.set_bool_singular(true);
test_json_parse_message("{boolSingular: true}", &m);
test_json_parse_message("{bool_singular: true}", &m);

let mut m = TestTypes::new();
m.set_bool_repeated(vec![true, false, false]);
test_json_parse_message("{boolRepeated: [true, false, false]}", &m);
test_json_parse_message("{bool_repeated: [true, false, false]}", &m);
}

#[test]
Expand Down
19 changes: 19 additions & 0 deletions protobuf/src/json/json_name.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/// Implementation must match exactly
/// `ToJsonName()` function in C++ `descriptor.cc`.
pub(crate) fn json_name(input: &str) -> String {
let mut capitalize_next = false;
let mut result = String::with_capacity(input.len());

for c in input.chars() {
if c == '_' {
capitalize_next = true;
} else if capitalize_next {
result.extend(c.to_uppercase());
capitalize_next = false;
} else {
result.push(c);
}
}

result
}
2 changes: 2 additions & 0 deletions protobuf/src/json/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ mod print;
mod parse;
mod float;
mod base64;
mod json_name;

pub use self::print::print_to_string;
pub use self::parse::merge_from_str;
pub(crate) use self::json_name::json_name;
4 changes: 3 additions & 1 deletion protobuf/src/json/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,9 @@ impl<'a> Parser<'a> {
first = false;

let field_name = self.tokenizer.next_ident()?;
let field = match descriptor.field_by_name(&field_name) {
// Proto3 JSON parsers are required to accept both
// the converted `lowerCamelCase` name and the proto field name.
let field = match descriptor.field_by_name_or_json_name(&field_name) {
Some(field) => field,
// TODO: option to skip unknown types
None => return Err(ParseError::UnknownFieldName(field_name)),
Expand Down
7 changes: 5 additions & 2 deletions protobuf/src/reflect/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use reflect::repeated::ReflectRepeatedRef;
use reflect::repeated::ReflectRepeatedMut;
use reflect::map::ReflectMapRef;
use reflect::map::ReflectMapMut;
use json::json_name;


/// Reference to a value stored in a field, optional, repeated or map.
Expand Down Expand Up @@ -66,6 +67,7 @@ fn _assert_sync<'a>() {
pub struct FieldDescriptor {
proto: &'static FieldDescriptorProto,
accessor: FieldAccessor,
json_name: String,
}

impl FieldDescriptor {
Expand All @@ -77,6 +79,8 @@ impl FieldDescriptor {
FieldDescriptor {
proto,
accessor,
// probably could be lazy-init
json_name: json_name(proto.get_name()),
}
}

Expand All @@ -89,8 +93,7 @@ impl FieldDescriptor {
}

pub fn json_name(&self) -> &str {
// TODO: Message field names are mapped to lowerCamelCase and become JSON object keys.
self.name()
&self.json_name
}

pub fn is_repeated(&self) -> bool {
Expand Down
18 changes: 16 additions & 2 deletions protobuf/src/reflect/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub struct MessageDescriptor {
fields: Vec<FieldDescriptor>,

index_by_name: HashMap<String, usize>,
index_by_name_or_json_name: HashMap<String, usize>,
index_by_number: HashMap<u32, usize>,
}

Expand All @@ -75,10 +76,16 @@ impl MessageDescriptor {
}

let mut index_by_name = HashMap::new();
let mut index_by_name_or_json_name = HashMap::new();
let mut index_by_number = HashMap::new();
for (i, f) in proto.message.get_field().iter().enumerate() {
index_by_number.insert(f.get_number() as u32, i);
index_by_name.insert(f.get_name().to_string(), i);
assert!(index_by_number.insert(f.get_number() as u32, i).is_none());
assert!(index_by_name.insert(f.get_name().to_owned(), i).is_none());
assert!(index_by_name_or_json_name.insert(f.get_name().to_owned(), i).is_none());
if f.get_json_name() != f.get_name() {
let json_name = f.get_json_name().to_owned();
assert!(index_by_name_or_json_name.insert(json_name, i).is_none());
}
}

let mut full_name = file_descriptor_proto.get_package().to_string();
Expand All @@ -99,6 +106,7 @@ impl MessageDescriptor {
})
.collect(),
index_by_name,
index_by_name_or_json_name,
index_by_number,
file_descriptor_proto,
}
Expand Down Expand Up @@ -166,6 +174,12 @@ impl MessageDescriptor {
Some(&self.fields[index])
}

/// Find message field by field name or field JSON name
pub fn field_by_name_or_json_name<'a>(&'a self, name: &str) -> Option<&'a FieldDescriptor> {
let &index = self.index_by_name_or_json_name.get(name)?;
Some(&self.fields[index])
}

/// Find message field by field name
pub fn field_by_number<'a>(&'a self, number: u32) -> Option<&'a FieldDescriptor> {
let &index = self.index_by_number.get(&number)?;
Expand Down

0 comments on commit fe9ca9c

Please sign in to comment.