diff --git a/pbjson-build/src/escape.rs b/pbjson-build/src/escape.rs index 554ac85..6d0aab8 100644 --- a/pbjson-build/src/escape.rs +++ b/pbjson-build/src/escape.rs @@ -24,3 +24,12 @@ pub fn escape_ident(mut ident: String) -> String { }; ident } + +/// Converts a `snake_case` identifier to an `UpperCamel` case Rust type identifier. +pub fn escape_type(mut ident: String) -> String { + // Suffix an underscore for the `Self` Rust keyword as it is not allowed as raw identifier. + if ident == "Self" { + ident += "_"; + } + ident +} diff --git a/pbjson-build/src/generator/enumeration.rs b/pbjson-build/src/generator/enumeration.rs index faa8b78..8bd7a65 100644 --- a/pbjson-build/src/generator/enumeration.rs +++ b/pbjson-build/src/generator/enumeration.rs @@ -4,6 +4,9 @@ //! An enumeration should be decode-able from the full string variant name //! or its integer tag number, and should encode to the string representation +use std::collections::HashSet; +use std::io::{Result, Write}; + use super::{ write_deserialize_end, write_deserialize_start, write_serialize_end, write_serialize_start, Indent, @@ -11,7 +14,6 @@ use super::{ use crate::descriptor::{EnumDescriptor, TypePath}; use crate::generator::write_fields_array; use crate::resolver::Resolver; -use std::io::{Result, Write}; pub fn generate_enum( resolver: &Resolver<'_>, @@ -21,9 +23,16 @@ pub fn generate_enum( ) -> Result<()> { let rust_type = resolver.rust_type(path); + let mut numbers = HashSet::new(); + let variants: Vec<_> = descriptor .values .iter() + .filter(|variant| { + // Skip duplicate enum values. Protobuf allows this when the + // 'allow_alias' option is set. + numbers.insert(variant.number()) + }) .map(|variant| { let variant_name = variant.name.clone().unwrap(); let rust_variant = resolver.rust_variant(path, &variant_name); diff --git a/pbjson-build/src/generator/message.rs b/pbjson-build/src/generator/message.rs index 795018f..32c3b32 100644 --- a/pbjson-build/src/generator/message.rs +++ b/pbjson-build/src/generator/message.rs @@ -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; @@ -440,7 +441,7 @@ fn write_deserialize_message( {indent} formatter.write_str("struct {name}") {indent} }} -{indent} fn visit_map(self, mut map: V) -> Result<{rust_type}, V::Error> +{indent} fn visit_map(self, mut pbjson_map: V) -> Result<{rust_type}, V::Error> {indent} where {indent} V: serde::de::MapAccess<'de>, {indent} {{"#, @@ -470,7 +471,7 @@ fn write_deserialize_message( if !message.fields.is_empty() || !message.one_ofs.is_empty() { writeln!( writer, - "{}while let Some(k) = map.next_key()? {{", + "{}while let Some(k) = pbjson_map.next_key()? {{", Indent(indent + 2) )?; @@ -491,7 +492,7 @@ fn write_deserialize_message( } else { writeln!( writer, - "{}while map.next_key::()?.is_some() {{}}", + "{}while pbjson_map.next_key::()?.is_some() {{}}", Indent(indent + 2) )?; } @@ -591,7 +592,7 @@ fn write_deserialize_field_name( "{}\"{}\" => Ok(GeneratedField::{}),", Indent(indent + 5), json_name, - type_name + escape_type(type_name.to_string()), )?; } writeln!( @@ -631,7 +632,12 @@ fn write_fields_enum<'a, W: Write, I: Iterator>( )?; 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()) + )?; } writeln!(writer, "{}}}", Indent(indent)) } @@ -689,14 +695,14 @@ fn write_deserialize_field( FieldModifier::Repeated => { write!( writer, - "map.next_value::>()?.into_iter().map(|x| x as i32).collect()", + "pbjson_map.next_value::>()?.into_iter().map(|x| x as i32).collect()", resolver.rust_type(path) )?; } _ => { write!( writer, - "map.next_value::<{}>()? as i32", + "pbjson_map.next_value::<{}>()? as i32", resolver.rust_type(path) )?; } @@ -705,7 +711,7 @@ fn write_deserialize_field( writeln!(writer)?; write!( writer, - "{}map.next_value::( write!(writer, "{}", Indent(indent + 1))?; } _ => { - write!(writer, "map.next_value()?",)?; + write!(writer, "pbjson_map.next_value()?",)?; } }; @@ -785,7 +791,7 @@ fn write_encode_scalar_field( let deserializer = match scalar { ScalarType::Bytes => "BytesDeserialize", _ if scalar.is_numeric() => "NumberDeserialize", - _ => return write!(writer, "map.next_value()?",), + _ => return write!(writer, "pbjson_map.next_value()?",), }; writeln!(writer)?; @@ -794,7 +800,7 @@ fn write_encode_scalar_field( FieldModifier::Repeated => { writeln!( writer, - "{}map.next_value::>>()?", + "{}pbjson_map.next_value::>>()?", Indent(indent + 1), deserializer )?; @@ -807,7 +813,7 @@ fn write_encode_scalar_field( _ => { writeln!( writer, - "{}map.next_value::<::pbjson::private::{}<_>>()?.0", + "{}pbjson_map.next_value::<::pbjson::private::{}<_>>()?.0", Indent(indent + 1), deserializer )?; diff --git a/pbjson-build/src/message.rs b/pbjson-build/src/message.rs index b1f85ca..1980be3 100644 --- a/pbjson-build/src/message.rs +++ b/pbjson-build/src/message.rs @@ -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_type, escape_ident}; #[derive(Debug, Clone, Copy)] pub enum ScalarType { @@ -86,7 +86,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 { diff --git a/pbjson-build/src/resolver.rs b/pbjson-build/src/resolver.rs index 3a8e5d3..b5ef499 100644 --- a/pbjson-build/src/resolver.rs +++ b/pbjson-build/src/resolver.rs @@ -1,4 +1,5 @@ use crate::descriptor::{Package, TypePath}; +use crate::escape::{escape_type, escape_ident}; #[derive(Debug)] pub struct Resolver<'a> { @@ -57,7 +58,7 @@ impl<'a> Resolver<'a> { .path() .iter() .zip(path.path()) - .filter(|(a, b)| a == b) + .take_while(|(a, b)| a == b) .count(); let super_count = self.package.path().len() - shared_prefix; @@ -75,11 +76,11 @@ impl<'a> Resolver<'a> { while let Some(i) = iter.next() { match iter.peek() { Some(_) => { - ret.push_str(i.to_snake_case().as_str()); + ret.push_str(escape_ident(i.to_snake_case()).as_str()); ret.push_str("::"); } None => { - ret.push_str(i.to_upper_camel_case().as_str()); + ret.push_str(escape_type(i.to_upper_camel_case()).as_str()); } } } @@ -110,6 +111,20 @@ mod tests { use super::*; use crate::descriptor::TypeName; + #[test] + fn test_complicated_resolver() { + let resolver_package = Package::new("envoy.service.health.v3"); + let resolver = Resolver::new(&[], &resolver_package, false); + + let to_resolve = TypePath::new(Package::new("envoy.config.core.v3")) + .child(TypeName::new("HealthStatus")); + + assert_eq!( + "super::super::super::config::core::v3::HealthStatus", + resolver.rust_type(&to_resolve) + ); + } + #[test] fn test_resolver() { let resolver_package = Package::new("test.syntax3");