Skip to content

Commit

Permalink
fix: Resolve Envoy xDS proto issues (#103)
Browse files Browse the repository at this point in the history
* fix: Escape Self since it is a reserved keyword

* fix: Allow duplicate numbers in enums when aliases are enabled

* fix: Avoid field name collisions with the map ident
  • Loading branch information
sporkmonger authored Sep 17, 2023
1 parent fc231ac commit 688c8b4
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 28 deletions.
4 changes: 2 additions & 2 deletions pbjson-build/src/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use prost_types::{
FileDescriptorProto, FileDescriptorSet, MessageOptions, OneofDescriptorProto,
};

use crate::escape::escape_ident;
use crate::escape::{escape_ident, escape_type};

#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct Package {
Expand Down Expand Up @@ -75,7 +75,7 @@ impl TypeName {

pub fn to_upper_camel_case(&self) -> String {
use heck::ToUpperCamelCase;
self.0.to_upper_camel_case()
escape_type(self.0.to_upper_camel_case())
}
}

Expand Down
8 changes: 8 additions & 0 deletions pbjson-build/src/escape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,11 @@ pub fn escape_ident(mut ident: String) -> String {
};
ident
}

pub fn escape_type(mut ident: String) -> String {
// this keyword is not supported as a raw identifier and is therefore suffixed with an underscore.
if ident == "Self" {
ident += "_";
}
ident
}
5 changes: 5 additions & 0 deletions pbjson-build/src/generator/enumeration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use super::{
use crate::descriptor::{EnumDescriptor, TypePath};
use crate::generator::write_fields_array;
use crate::resolver::Resolver;
use std::collections::HashSet;
use std::io::{Result, Write};

pub fn generate_enum<W: Write>(
Expand All @@ -22,9 +23,13 @@ pub fn generate_enum<W: Write>(
) -> Result<()> {
let rust_type = resolver.rust_type(path);

let mut seen_numbers = HashSet::new();
let variants: Vec<_> = descriptor
.values
.iter()
// Skip duplicates if we've seen the number before
// Protobuf's `allow_alias` option permits duplicates if set
.filter(|variant| seen_numbers.insert(variant.number()))
.map(|variant| {
let variant_name = variant.name.clone().unwrap();
let variant_number = variant.number();
Expand Down
54 changes: 30 additions & 24 deletions pbjson-build/src/generator/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use super::{
Indent,
};
use crate::descriptor::TypePath;
use crate::escape::escape_type;
use crate::generator::write_fields_array;
use crate::resolver::Resolver;

Expand Down Expand Up @@ -522,7 +523,7 @@ fn write_deserialize_message<W: Write>(
{indent} formatter.write_str("struct {name}")
{indent} }}
{indent} fn visit_map<V>(self, mut map: V) -> std::result::Result<{rust_type}, V::Error>
{indent} fn visit_map<V>(self, mut map_: V) -> std::result::Result<{rust_type}, V::Error>
{indent} where
{indent} V: serde::de::MapAccess<'de>,
{indent} {{"#,
Expand Down Expand Up @@ -552,7 +553,7 @@ fn write_deserialize_message<W: Write>(
if !message.fields.is_empty() || !message.one_ofs.is_empty() {
writeln!(
writer,
"{}while let Some(k) = map.next_key()? {{",
"{}while let Some(k) = map_.next_key()? {{",
Indent(indent + 2)
)?;

Expand Down Expand Up @@ -587,7 +588,7 @@ fn write_deserialize_message<W: Write>(
)?;
writeln!(
writer,
"{}let _ = map.next_value::<serde::de::IgnoredAny>()?;",
"{}let _ = map_.next_value::<serde::de::IgnoredAny>()?;",
Indent(indent + 5),
)?;
writeln!(writer, "{}}}", Indent(indent + 4))?;
Expand All @@ -598,12 +599,12 @@ fn write_deserialize_message<W: Write>(
} else {
writeln!(
writer,
"{}while map.next_key::<GeneratedField>()?.is_some() {{",
"{}while map_.next_key::<GeneratedField>()?.is_some() {{",
Indent(indent + 2)
)?;
writeln!(
writer,
"{}let _ = map.next_value::<serde::de::IgnoredAny>()?;",
"{}let _ = map_.next_value::<serde::de::IgnoredAny>()?;",
Indent(indent + 3)
)?;
writeln!(writer, "{}}}", Indent(indent + 2))?;
Expand Down Expand Up @@ -725,15 +726,15 @@ fn write_deserialize_field_name<W: Write>(
Indent(indent + 5),
json_name,
proto_name,
type_name
escape_type(type_name.to_string())
)?;
} else {
writeln!(
writer,
"{}\"{}\" => Ok(GeneratedField::{}),",
Indent(indent + 5),
json_name,
type_name
escape_type(type_name.to_string())
)?;
}
}
Expand Down Expand Up @@ -789,7 +790,12 @@ fn write_fields_enum<'a, W: Write, I: Iterator<Item = &'a str>>(
)?;
writeln!(writer, "{}enum GeneratedField {{", Indent(indent))?;
for type_name in fields {
writeln!(writer, "{}{},", Indent(indent + 1), type_name)?;
writeln!(
writer,
"{}{},",
Indent(indent + 1),
escape_type(type_name.to_string())
)?;
}

if ignore_unknown_fields {
Expand Down Expand Up @@ -842,7 +848,7 @@ fn write_deserialize_field<W: Write>(
Some(deserializer) => {
write!(
writer,
"map.next_value::<::std::option::Option<{}>>()?.map(|x| {}::{}(x.0))",
"map_.next_value::<::std::option::Option<{}>>()?.map(|x| {}::{}(x.0))",
deserializer,
resolver.rust_type(&one_of.path),
field.rust_type_name()
Expand All @@ -851,7 +857,7 @@ fn write_deserialize_field<W: Write>(
None => {
write!(
writer,
"map.next_value::<::std::option::Option<_>>()?.map({}::{})",
"map_.next_value::<::std::option::Option<_>>()?.map({}::{})",
resolver.rust_type(&one_of.path),
field.rust_type_name()
)?;
Expand All @@ -860,15 +866,15 @@ fn write_deserialize_field<W: Write>(
FieldType::Enum(path) => {
write!(
writer,
"map.next_value::<::std::option::Option<{}>>()?.map(|x| {}::{}(x as i32))",
"map_.next_value::<::std::option::Option<{}>>()?.map(|x| {}::{}(x as i32))",
resolver.rust_type(path),
resolver.rust_type(&one_of.path),
field.rust_type_name()
)?;
}
FieldType::Message(_) => writeln!(
writer,
"map.next_value::<::std::option::Option<_>>()?.map({}::{})",
"map_.next_value::<::std::option::Option<_>>()?.map({}::{})",
resolver.rust_type(&one_of.path),
field.rust_type_name()
)?,
Expand All @@ -883,21 +889,21 @@ fn write_deserialize_field<W: Write>(
FieldModifier::Optional => {
write!(
writer,
"map.next_value::<::std::option::Option<{}>>()?.map(|x| x as i32)",
"map_.next_value::<::std::option::Option<{}>>()?.map(|x| x as i32)",
resolver.rust_type(path)
)?;
}
FieldModifier::Repeated => {
write!(
writer,
"Some(map.next_value::<Vec<{}>>()?.into_iter().map(|x| x as i32).collect())",
"Some(map_.next_value::<Vec<{}>>()?.into_iter().map(|x| x as i32).collect())",
resolver.rust_type(path)
)?;
}
_ => {
write!(
writer,
"Some(map.next_value::<{}>()? as i32)",
"Some(map_.next_value::<{}>()? as i32)",
resolver.rust_type(path)
)?;
}
Expand All @@ -908,12 +914,12 @@ fn write_deserialize_field<W: Write>(
match btree_map {
true => write!(
writer,
"{}map.next_value::<std::collections::BTreeMap<",
"{}map_.next_value::<std::collections::BTreeMap<",
Indent(indent + 2),
)?,
false => write!(
writer,
"{}map.next_value::<std::collections::HashMap<",
"{}map_.next_value::<std::collections::HashMap<",
Indent(indent + 2),
)?,
}
Expand Down Expand Up @@ -975,9 +981,9 @@ fn write_deserialize_field<W: Write>(
FieldType::Message(_) => match field.field_modifier {
FieldModifier::Repeated => {
// No explicit presence for repeated fields
write!(writer, "Some(map.next_value()?)")?;
write!(writer, "Some(map_.next_value()?)")?;
}
_ => write!(writer, "map.next_value()?")?,
_ => write!(writer, "map_.next_value()?")?,
},
},
}
Expand All @@ -1004,9 +1010,9 @@ fn write_encode_scalar_field<W: Write>(
None => {
return match field_modifier {
FieldModifier::Optional => {
write!(writer, "map.next_value()?")
write!(writer, "map_.next_value()?")
}
_ => write!(writer, "Some(map.next_value()?)"),
_ => write!(writer, "Some(map_.next_value()?)"),
};
}
};
Expand All @@ -1017,15 +1023,15 @@ fn write_encode_scalar_field<W: Write>(
FieldModifier::Optional => {
writeln!(
writer,
"{}map.next_value::<::std::option::Option<{}>>()?.map(|x| x.0)",
"{}map_.next_value::<::std::option::Option<{}>>()?.map(|x| x.0)",
Indent(indent + 1),
deserializer
)?;
}
FieldModifier::Repeated => {
writeln!(
writer,
"{}Some(map.next_value::<Vec<{}>>()?",
"{}Some(map_.next_value::<Vec<{}>>()?",
Indent(indent + 1),
deserializer
)?;
Expand All @@ -1038,7 +1044,7 @@ fn write_encode_scalar_field<W: Write>(
_ => {
writeln!(
writer,
"{}Some(map.next_value::<{}>()?.0)",
"{}Some(map_.next_value::<{}>()?.0)",
Indent(indent + 1),
deserializer
)?;
Expand Down
4 changes: 2 additions & 2 deletions pbjson-build/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use prost_types::{
};

use crate::descriptor::{Descriptor, DescriptorSet, MessageDescriptor, Syntax, TypeName, TypePath};
use crate::escape::escape_ident;
use crate::escape::{escape_ident, escape_type};

#[derive(Debug, Clone, Copy)]
pub enum ScalarType {
Expand Down Expand Up @@ -81,7 +81,7 @@ pub struct Field {
impl Field {
pub fn rust_type_name(&self) -> String {
use heck::ToUpperCamelCase;
self.name.to_upper_camel_case()
escape_type(self.name.to_upper_camel_case())
}

pub fn rust_field_name(&self) -> String {
Expand Down
1 change: 1 addition & 0 deletions pbjson-test/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ fn main() -> Result<()> {
root.join("syntax3.proto"),
root.join("common.proto"),
root.join("duplicate_name.proto"),
root.join("duplicate_number.proto"),
root.join("escape.proto"),
];

Expand Down
16 changes: 16 additions & 0 deletions pbjson-test/protos/duplicate_number.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
syntax = "proto3";

package test.duplicate_number;

message Compressor {
enum CompressionLevel {
option allow_alias = true;

DEFAULT = 0;
BEST_SPEED = 1;
COMPRESSION_LEVEL_1 = 1;
COMPRESSION_LEVEL_2 = 2;
COMPRESSION_LEVEL_3 = 3;
BEST_COMPRESSION = 3;
}
}
5 changes: 5 additions & 0 deletions pbjson-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ pub mod test {
include!(concat!(env!("OUT_DIR"), "/test.duplicate_name.serde.rs"));
}

pub mod duplicate_number {
include!(concat!(env!("OUT_DIR"), "/test.duplicate_number.rs"));
include!(concat!(env!("OUT_DIR"), "/test.duplicate_number.serde.rs"));
}

pub mod escape {
include!(concat!(
env!("OUT_DIR"),
Expand Down

0 comments on commit 688c8b4

Please sign in to comment.