From b523d6246b04ee898337f1d3702f39982326cfcc Mon Sep 17 00:00:00 2001 From: Aidan Jensen Date: Fri, 14 Nov 2025 00:20:32 -0800 Subject: [PATCH 1/4] Support unknownfields field Signed-off-by: Aidan Jensen --- pbjson-build/src/generator/message.rs | 12 ++++++++++++ pbjson-build/src/lib.rs | 11 +++++++++++ 2 files changed, 23 insertions(+) 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..30a36e1 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_field_storage_field: Option, } impl Builder { @@ -207,6 +208,15 @@ 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_field_storage_field>( + &mut self, field_name: S, + ) -> &mut Self { + self.unknown_field_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 +305,7 @@ impl Builder { &self.btree_map_paths, self.emit_fields, self.preserve_proto_field_names, + &self.unknown_field_storage_field, )? } } From d3e93ca7a991e157a967fe7e07566a6a8389f076 Mon Sep 17 00:00:00 2001 From: Aidan Jensen Date: Thu, 20 Nov 2025 09:23:40 -0800 Subject: [PATCH 2/4] update tests and use upstream branch Signed-off-by: Aidan Jensen --- Cargo.toml | 6 +++--- pbjson-build/src/lib.rs | 10 ++++------ pbjson-test/build.rs | 5 ++++- pbjson-test/src/lib.rs | 11 +++++++++-- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 11aa483..03367c0 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 = "8579f8b" } +prost-build = { git = "https://github.com/blizzardfinnegan/prost.git", rev = "8579f8b" } +prost-types = { git = "https://github.com/blizzardfinnegan/prost.git", rev = "8579f8b" } rand = "0.9" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/pbjson-build/src/lib.rs b/pbjson-build/src/lib.rs index 30a36e1..0374dc7 100644 --- a/pbjson-build/src/lib.rs +++ b/pbjson-build/src/lib.rs @@ -109,7 +109,7 @@ pub struct Builder { use_integers_for_enums: bool, ignore_unknown_enum_variants: bool, preserve_proto_field_names: bool, - unknown_field_storage_field: Option, + unknown_fields_storage_field: Option, } impl Builder { @@ -210,10 +210,8 @@ impl Builder { /// 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_field_storage_field>( - &mut self, field_name: S, - ) -> &mut Self { - self.unknown_field_storage_field = Some(field_name.into()); + pub fn unknown_fields_storage_field>(&mut self, field_name: S) -> &mut Self { + self.unknown_fields_storage_field = Some(field_name.into()); self } @@ -305,7 +303,7 @@ impl Builder { &self.btree_map_paths, self.emit_fields, self.preserve_proto_field_names, - &self.unknown_field_storage_field, + &self.unknown_fields_storage_field, )? } } diff --git a/pbjson-test/build.rs b/pbjson-test/build.rs index d9283bd..12b9255 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(".", Some("__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..75ad2f6 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(); @@ -787,12 +789,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)?; From 1a05be59efbb6de44b1e468a0fce48c5eb299582 Mon Sep 17 00:00:00 2001 From: Aidan Jensen Date: Wed, 3 Dec 2025 12:42:48 -0800 Subject: [PATCH 3/4] Update upstream Signed-off-by: Aidan Jensen --- pbjson-test/build.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pbjson-test/build.rs b/pbjson-test/build.rs index 12b9255..13fa26e 100644 --- a/pbjson-test/build.rs +++ b/pbjson-test/build.rs @@ -31,7 +31,7 @@ fn main() -> Result<()> { .extern_path(".test.external", "crate") .bytes([".test"]) .protoc_arg("--experimental_allow_proto3_optional") - .include_unknown_fields(".", Some("__unknown_fields")); + .include_unknown_fields(".", "_unknown_fields"); if cfg!(feature = "btree") { prost_config.btree_map([".test"]); @@ -69,7 +69,7 @@ fn main() -> Result<()> { builder.preserve_proto_field_names(); } - builder.unknown_fields_storage_field("__unknown_fields"); + builder.unknown_fields_storage_field("_unknown_fields"); builder.build(&[".test"])?; From cc199fa6bf6f6afa285e14b0579866e05262f0d3 Mon Sep 17 00:00:00 2001 From: Aidan Jensen Date: Fri, 5 Dec 2025 14:24:22 -0800 Subject: [PATCH 4/4] fix tests, update prost Signed-off-by: Aidan Jensen --- Cargo.toml | 6 +++--- pbjson-test/src/lib.rs | 11 +++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 03367c0..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 = { git = "https://github.com/blizzardfinnegan/prost.git", rev = "8579f8b" } -prost-build = { git = "https://github.com/blizzardfinnegan/prost.git", rev = "8579f8b" } -prost-types = { git = "https://github.com/blizzardfinnegan/prost.git", rev = "8579f8b" } +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-test/src/lib.rs b/pbjson-test/src/lib.rs index 75ad2f6..799dab4 100644 --- a/pbjson-test/src/lib.rs +++ b/pbjson-test/src/lib.rs @@ -181,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]