internal change PiperOrigin-RevId: 998993134
diff --git a/rust/cpp_kernel/reflection.cc b/rust/cpp_kernel/reflection.cc index 48c4649..9ce5e3a 100644 --- a/rust/cpp_kernel/reflection.cc +++ b/rust/cpp_kernel/reflection.cc
@@ -10,12 +10,15 @@ // Converts a message to its TextFormat representation. google::protobuf::rust::RustStringRawParts proto2_rust_Message_print_to_text_format( - const google::protobuf::MessageLite* m) { + const google::protobuf::MessageLite* m, bool single_line, bool hide_unknown_fields) { const auto* msg = google::protobuf::DynamicCastMessage<google::protobuf::Message>(m); // This should always hold because the Rust API requires `WithReflection`. ABSL_CHECK(msg != nullptr) << "Message does not support reflection."; + google::protobuf::TextFormat::Printer printer; + printer.SetSingleLineMode(single_line); + printer.SetHideUnknownFields(hide_unknown_fields); std::string text; - ABSL_CHECK(google::protobuf::TextFormat::PrintToString(*msg, &text)); + ABSL_CHECK(printer.PrintToString(*msg, &text)); return google::protobuf::rust::RustStringRawParts(text); }
diff --git a/rust/cpp_kernel/reflection.rs b/rust/cpp_kernel/reflection.rs index 15197be..6ee19ac 100644 --- a/rust/cpp_kernel/reflection.rs +++ b/rust/cpp_kernel/reflection.rs
@@ -21,27 +21,33 @@ /// From reflection.cc /// /// # Safety - /// - `m` is a legally dereferenceable MessageLite* that supports - /// reflection. - fn proto2_rust_Message_print_to_text_format(m: RawMessage) -> RustStringRawParts; + /// - `m` is a legally dereferenceable MessageLite* that supports reflection. + fn proto2_rust_Message_print_to_text_format( + m: RawMessage, + single_line: bool, + hide_unknown_fields: bool, + ) -> RustStringRawParts; /// From reflection.cc /// /// # Safety - /// - `m` is a legally dereferenceable and mutable MessageLite* that - /// supports reflection. - /// - `input` is a valid `PtrAndLen` pointing to readable memory for its - /// length. + /// - `m` is a legally dereferenceable and mutable MessageLite* that supports reflection. + /// - `input` is a valid `PtrAndLen` pointing to readable memory for its length. fn proto2_rust_Message_parse_from_text_format(m: RawMessage, input: PtrAndLen) -> bool; } -/// Returns the protobuf TextFormat representation of `msg`. +/// Returns the protobuf TextFormat representation of `msg` with custom options. #[cfg(not(lite_runtime))] -pub fn print_to_text_format<T: crate::WithReflection>(msg: &T) -> String { +pub fn print_to_text_format<T: crate::WithReflection>( + msg: &T, + single_line: bool, + hide_unknown_fields: bool, +) -> String { let raw = msg.as_view().get_raw_message(Private); // SAFETY: `raw` points to a live message, and `WithReflection` guarantees it // supports reflection. - unsafe { proto2_rust_Message_print_to_text_format(raw) }.into() + unsafe { proto2_rust_Message_print_to_text_format(raw, single_line, hide_unknown_fields) } + .into() } /// Parses a message from its protobuf TextFormat representation. @@ -50,8 +56,8 @@ let mut msg = T::default(); let raw = msg.get_raw_message_mut(Private); // SAFETY: - // - `raw` points to a live, mutable message, and `WithReflection` - // guarantees it supports reflection. + // - `raw` points to a live, mutable message, and `WithReflection` guarantees it supports + // reflection. // - `input` is converted from a valid `&str` slice. unsafe { proto2_rust_Message_parse_from_text_format(raw, input.as_bytes().into()) } .then_some(msg)
diff --git a/rust/test/shared/reflection_test.rs b/rust/test/shared/reflection_test.rs new file mode 100644 index 0000000..b921230 --- /dev/null +++ b/rust/test/shared/reflection_test.rs
@@ -0,0 +1,6 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2026 Google LLC. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd
diff --git a/rust/test/upb/reflection_test.rs b/rust/test/upb/reflection_test.rs deleted file mode 100644 index ec70403..0000000 --- a/rust/test/upb/reflection_test.rs +++ /dev/null
@@ -1,22 +0,0 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2026 Google LLC. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file or at -// https://developers.google.com/open-source/licenses/bsd - -#[cfg(test)] -mod tests { - use googletest::prelude::*; - use unittest_rust_proto::TestAllTypes; - - #[gtest] - fn test_print_to_text_format() { - let mut msg = TestAllTypes::new(); - msg.set_optional_int32(42); - msg.set_optional_string("super secret"); - - let text = protobuf::text_format::print(&msg); - expect_eq!(text, "optional_int32: 42\noptional_string: \"super secret\"\n"); - } -}
diff --git a/rust/upb/lib.rs b/rust/upb/lib.rs index 023bac7..fbf494f 100644 --- a/rust/upb/lib.rs +++ b/rust/upb/lib.rs
@@ -14,7 +14,7 @@ pub use associated_mini_table::{AssociatedMiniTable, AssociatedMiniTableEnum}; mod text; -pub use text::debug_string; +pub use text::{debug_string, TextEncodeOptions}; mod message; pub use message::MessagePtr;
diff --git a/rust/upb/reflection.rs b/rust/upb/reflection.rs index 5cb397f..c5c7783 100644 --- a/rust/upb/reflection.rs +++ b/rust/upb/reflection.rs
@@ -23,7 +23,10 @@ use reflection::message_def::RawMessageDef; use reflection::upb_TextEncode; -use upb::{Arena, MessagePtr, RawMiniTable, RawMiniTableEnum, RawMiniTableExtension, StringView}; +use upb::{ + Arena, MessagePtr, RawMiniTable, RawMiniTableEnum, RawMiniTableExtension, StringView, + TextEncodeOptions, +}; pub use reflection::def_pool::upb_DefPool_Init; pub use reflection::def_pool::RawDefPoolInit as DefPoolInitPtr; @@ -125,11 +128,12 @@ msg: MessagePtr<T>, def: MessageDef<'pool>, ext_pool: Option<&DefPool>, - options: u32, + options: TextEncodeOptions, ) -> String { let msg = msg.raw(); let def_raw = def.raw(); let ext_pool_raw = ext_pool.map(|p| p.raw()); + let options = options.to_bits(); // Only find out the length first to then allocate a buffer of the minimum size // needed. @@ -138,9 +142,8 @@ // - `def` is valid per safety requirements of this function. // - `ext_pool` is valid per safety requirements of this function. // - `buf` is null and `size` is 0. - let min_len = unsafe { - upb_TextEncode(msg, def_raw, ext_pool_raw, options as i32, core::ptr::null_mut(), 0) - }; + let min_len = + unsafe { upb_TextEncode(msg, def_raw, ext_pool_raw, options, core::ptr::null_mut(), 0) }; assert!(min_len < isize::MAX as usize); // +1 for the trailing NULL @@ -149,9 +152,8 @@ // SAFETY: // - `msg`, `def`, `ext_pool` are valid per safety requirements of this function. // - `buf` is legally writable for `buf.len()` bytes. - let written_len = unsafe { - upb_TextEncode(msg, def_raw, ext_pool_raw, options as i32, buf.as_mut_ptr(), buf.len()) - }; + let written_len = + unsafe { upb_TextEncode(msg, def_raw, ext_pool_raw, options, buf.as_mut_ptr(), buf.len()) }; assert_eq!(min_len, written_len); // Drop the trailing NULL written by `upb_TextEncode`.
diff --git a/rust/upb/text.rs b/rust/upb/text.rs index df319c2..92ac377 100644 --- a/rust/upb/text.rs +++ b/rust/upb/text.rs
@@ -5,9 +5,34 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -use super::sys::text::text::upb_DebugString; +use super::sys::text::text::{text_encode_options, upb_DebugString}; use super::{AssociatedMiniTable, MessagePtr}; +/// Options for upb's text encoders. +/// This is only used for `text_encode` for now, but it might be used for `debug_string` in the +/// future. That's why it must be in the non-reflection crate. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct TextEncodeOptions { + /// Print everything on a single line. + pub single_line: bool, + /// Do not print unknown fields. + pub skip_unknown: bool, +} + +impl TextEncodeOptions { + /// The bitmask that upb's text encoders expect. + pub fn to_bits(self) -> i32 { + let mut bits = 0; + if self.single_line { + bits |= text_encode_options::SINGLE_LINE; + } + if self.skip_unknown { + bits |= text_encode_options::SKIP_UNKNOWN; + } + bits + } +} + /// Returns a string of field number to value entries of a message. /// /// # Safety
diff --git a/rust/upb_kernel/reflection.rs b/rust/upb_kernel/reflection.rs index 2191d9f..7e83b96 100644 --- a/rust/upb_kernel/reflection.rs +++ b/rust/upb_kernel/reflection.rs
@@ -11,6 +11,7 @@ //! the message's `MessageDef` in the process-wide `DefPool` (see `def_pool.rs`), then hands both //! the message and the def to upb. +use super::upb::TextEncodeOptions; use super::{message_def, upb_reflection, UpbGetMessagePtr, UpbWithReflection}; use crate::__internal::Private; use crate::WithReflection; @@ -22,15 +23,19 @@ pub trait KernelWithReflection: UpbWithReflection + UpbGetMessagePtr<Msg = Self> {} impl<T: UpbWithReflection + UpbGetMessagePtr<Msg = T>> KernelWithReflection for T {} -/// Returns the protobuf TextFormat representation of `msg`. -pub fn print_to_text_format<T: WithReflection>(msg: &T) -> String { +/// Returns the protobuf TextFormat representation of `msg` with custom options. +pub fn print_to_text_format<T: WithReflection>( + msg: &T, + single_line: bool, + hide_unknown_fields: bool, +) -> String { let ptr = msg.get_ptr(Private); let def = message_def::<T>(); + let options = TextEncodeOptions { single_line, skip_unknown: hide_unknown_fields }; // SAFETY: // - `ptr` points to a live message that `msg` keeps alive for the duration of the call. // - `def` describes `T`, and `WithReflection` guarantees `kernelWithReflection`, which // guarantees `UpbGetMessagePtr<Msg = T>, therefore `ptr` is a `MessagePtr<T>`. // TODO: Pass the global DefPool as `ext_pool` so that extensions are printed. - // TODO: Support configuring options. - unsafe { upb_reflection::text_encode(ptr, def, None, 0) } + unsafe { upb_reflection::text_encode(ptr, def, None, options) } }