diff --git a/Cargo.toml b/Cargo.toml index 11aa483..3d01084 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,9 +25,9 @@ itertools = "0.14" pbjson = { path = "pbjson", version = "0.8" } pbjson-build = { path = "pbjson-build", version = "0.8" } pbjson-types = { path = "pbjson-types", version = "0.8" } -prost = "0.14" -prost-build = "0.14" -prost-types = "0.14" +prost = { git = "https://github.com/blizzardfinnegan/prost.git", rev = "d20b25c7ad45182da894ed7a559f869360c4bc36" } +prost-build = { git = "https://github.com/blizzardfinnegan/prost.git", rev = "d20b25c7ad45182da894ed7a559f869360c4bc36" } +prost-types = { git = "https://github.com/blizzardfinnegan/prost.git", rev = "d20b25c7ad45182da894ed7a559f869360c4bc36" } rand = "0.9" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/pbjson-build/src/generator/message.rs b/pbjson-build/src/generator/message.rs index 0ecaa8d..36dbdc2 100644 --- a/pbjson-build/src/generator/message.rs +++ b/pbjson-build/src/generator/message.rs @@ -42,6 +42,7 @@ pub fn generate_message( btree_map_paths: &[String], emit_fields: bool, preserve_proto_field_names: bool, + unknown_field_storage_field: &Option, ) -> Result<()> { let rust_type = resolver.rust_type(&message.path); @@ -67,6 +68,7 @@ pub fn generate_message( writer, ignore_unknown_fields, btree_map_paths, + unknown_field_storage_field, )?; write_deserialize_end(0, writer)?; Ok(()) @@ -514,6 +516,7 @@ fn write_deserialize_message( writer: &mut W, ignore_unknown_fields: bool, btree_map_paths: &[String], + unknown_field_storage_field: &Option, ) -> Result<()> { write_deserialize_field_name(2, message, writer, ignore_unknown_fields)?; @@ -655,6 +658,15 @@ fn write_deserialize_message( )?; } + if let Some(unknown_field_storage_field) = unknown_field_storage_field { + writeln!( + writer, + "{indent}{unknown_field_field}: Default::default(),", + indent = Indent(indent + 3), + unknown_field_field = unknown_field_storage_field + )?; + } + writeln!(writer, "{}}})", Indent(indent + 2))?; writeln!(writer, "{}}}", Indent(indent + 1))?; writeln!(writer, "{}}}", Indent(indent))?; diff --git a/pbjson-build/src/lib.rs b/pbjson-build/src/lib.rs index 839506c..0374dc7 100644 --- a/pbjson-build/src/lib.rs +++ b/pbjson-build/src/lib.rs @@ -109,6 +109,7 @@ pub struct Builder { use_integers_for_enums: bool, ignore_unknown_enum_variants: bool, preserve_proto_field_names: bool, + unknown_fields_storage_field: Option, } impl Builder { @@ -207,6 +208,13 @@ impl Builder { self } + /// Store unknown fields in the specified field when constructing. + /// This field is used when using prost generated code with unknown_field support + pub fn unknown_fields_storage_field>(&mut self, field_name: S) -> &mut Self { + self.unknown_fields_storage_field = Some(field_name.into()); + self + } + /// Generates code for all registered types where `prefixes` contains a prefix of /// the fully-qualified path of the type pub fn build>(&mut self, prefixes: &[S]) -> Result<()> { @@ -295,6 +303,7 @@ impl Builder { &self.btree_map_paths, self.emit_fields, self.preserve_proto_field_names, + &self.unknown_fields_storage_field, )? } } diff --git a/pbjson-test/build.rs b/pbjson-test/build.rs index d9283bd..13fa26e 100644 --- a/pbjson-test/build.rs +++ b/pbjson-test/build.rs @@ -30,7 +30,8 @@ fn main() -> Result<()> { .extern_path(".google.protobuf", "::pbjson_types") .extern_path(".test.external", "crate") .bytes([".test"]) - .protoc_arg("--experimental_allow_proto3_optional"); + .protoc_arg("--experimental_allow_proto3_optional") + .include_unknown_fields(".", "_unknown_fields"); if cfg!(feature = "btree") { prost_config.btree_map([".test"]); @@ -68,6 +69,8 @@ fn main() -> Result<()> { builder.preserve_proto_field_names(); } + builder.unknown_fields_storage_field("_unknown_fields"); + builder.build(&[".test"])?; Ok(()) diff --git a/pbjson-test/src/lib.rs b/pbjson-test/src/lib.rs index 45dde55..799dab4 100644 --- a/pbjson-test/src/lib.rs +++ b/pbjson-test/src/lib.rs @@ -158,7 +158,9 @@ mod tests { #[test] #[cfg(not(feature = "ignore-unknown-fields"))] fn test_unknown_field_error() { - let message = Empty {}; + let message = Empty { + ..Default::default() + }; let encoded = serde_json::to_string(&message).unwrap(); let _decoded: Empty = serde_json::from_str(&encoded).unwrap(); @@ -179,13 +181,20 @@ mod tests { #[test] #[cfg(feature = "ignore-unknown-fields")] fn test_ignore_unknown_field() { - let message = Empty {}; + let message = Empty { + ..Default::default() + }; let encoded = serde_json::to_string(&message).unwrap(); let _decoded: Empty = serde_json::from_str(&encoded).unwrap(); let empty = serde_json::from_str::("{\n \"foo\": \"bar\"\n}").unwrap(); - assert_eq!(empty, Empty {}); + assert_eq!( + empty, + Empty { + ..Default::default() + } + ); } #[test] @@ -787,12 +796,17 @@ mod tests { fn test_escaped() -> Result<(), Box> { use super::test::escape::{Abstract, Target, Type}; - let r#type = Type { example: true }; + let r#type = Type { + example: true, + ..Default::default() + }; let r#abstract = Abstract { r#type: Some(r#type), + ..Default::default() }; let target = Target { r#abstract: Some(r#abstract), + ..Default::default() }; let encoded = serde_json::to_string(&target)?;