diff --git a/prost-reflect/src/dynamic/fields.rs b/prost-reflect/src/dynamic/fields.rs index 0ce39e05..0ae3c6b9 100644 --- a/prost-reflect/src/dynamic/fields.rs +++ b/prost-reflect/src/dynamic/fields.rs @@ -175,7 +175,6 @@ impl DynamicMessageFieldSet { }) } - #[cfg(feature = "serde")] pub(crate) fn iter_include_default<'a>( &'a self, message: &'a MessageDescriptor, diff --git a/prost-reflect/src/dynamic/mod.rs b/prost-reflect/src/dynamic/mod.rs index 0dee59c6..4fec99cb 100644 --- a/prost-reflect/src/dynamic/mod.rs +++ b/prost-reflect/src/dynamic/mod.rs @@ -1200,3 +1200,19 @@ fn type_sizes() { assert_eq!(std::mem::size_of::(), 40); assert_eq!(std::mem::size_of::(), 56); } + +pub(crate) enum Either { + Left(L), + Right(R), +} + +impl> Iterator for Either { + type Item = L::Item; + + fn next(&mut self) -> Option { + match self { + Either::Left(left) => left.next(), + Either::Right(right) => right.next(), + } + } +} diff --git a/prost-reflect/src/dynamic/serde/ser/mod.rs b/prost-reflect/src/dynamic/serde/ser/mod.rs index c31380b2..8aba6891 100644 --- a/prost-reflect/src/dynamic/serde/ser/mod.rs +++ b/prost-reflect/src/dynamic/serde/ser/mod.rs @@ -54,62 +54,38 @@ fn serialize_dynamic_message_fields( where S: SerializeMap, { - if options.skip_default_fields { - for field in value.fields.iter(&value.desc) { - let (name, value, ref kind) = match field { - ValueAndDescriptor::Field(value, ref field_desc) => { - let name = if options.use_proto_field_name { - field_desc.name() - } else { - field_desc.json_name() - }; - (name, value, field_desc.kind()) - } - ValueAndDescriptor::Extension(value, ref extension_desc) => { - (extension_desc.json_name(), value, extension_desc.kind()) - } - ValueAndDescriptor::Unknown(_) => continue, - }; - - map.serialize_entry( - name, - &SerializeWrapper { - value: &ValueAndKind { - value: value.as_ref(), - kind, - }, - options, - }, - )?; - } + let fields = if options.skip_default_fields { + crate::dynamic::Either::Left(value.fields.iter(&value.desc)) } else { - for field in value.fields.iter_include_default(&value.desc) { - let (name, value, ref kind) = match field { - ValueAndDescriptor::Field(value, ref field_desc) => { - let name = if options.use_proto_field_name { - field_desc.name() - } else { - field_desc.json_name() - }; - (name, value, field_desc.kind()) - } - ValueAndDescriptor::Extension(value, ref extension_desc) => { - (extension_desc.json_name(), value, extension_desc.kind()) - } - ValueAndDescriptor::Unknown(_) => continue, - }; + crate::dynamic::Either::Right(value.fields.iter_include_default(&value.desc)) + }; - map.serialize_entry( - name, - &SerializeWrapper { - value: &ValueAndKind { - value: value.as_ref(), - kind, - }, - options, + for field in fields { + let (name, value, ref kind) = match field { + ValueAndDescriptor::Field(value, ref field_desc) => { + let name = if options.use_proto_field_name { + field_desc.name() + } else { + field_desc.json_name() + }; + (name, value, field_desc.kind()) + } + ValueAndDescriptor::Extension(value, ref extension_desc) => { + (extension_desc.json_name(), value, extension_desc.kind()) + } + ValueAndDescriptor::Unknown(_) => continue, + }; + + map.serialize_entry( + name, + &SerializeWrapper { + value: &ValueAndKind { + value: value.as_ref(), + kind, }, - )?; - } + options, + }, + )?; } Ok(()) diff --git a/prost-reflect/src/dynamic/text_format/format.rs b/prost-reflect/src/dynamic/text_format/format.rs index 35757e78..3b479ba0 100644 --- a/prost-reflect/src/dynamic/text_format/format.rs +++ b/prost-reflect/src/dynamic/text_format/format.rs @@ -42,7 +42,12 @@ where } } - let fields = message.fields.iter(&message.desc); + let fields = if self.options.skip_default_fields { + crate::dynamic::Either::Left(message.fields.iter(&message.desc)) + } else { + crate::dynamic::Either::Right(message.fields.iter_include_default(&message.desc)) + }; + if self.options.skip_unknown_fields { self.fmt_delimited( fields.filter(|f| !matches!(f, ValueAndDescriptor::Unknown(..))), @@ -85,7 +90,15 @@ where write!(self.f, "{}", value) } Value::Message(message) => { - if message.fields.iter(&message.desc).all(|f| { + let mut fields = if self.options.skip_default_fields { + crate::dynamic::Either::Left(message.fields.iter(&message.desc)) + } else { + crate::dynamic::Either::Right( + message.fields.iter_include_default(&message.desc), + ) + }; + + if fields.all(|f| { self.options.skip_unknown_fields && matches!(f, ValueAndDescriptor::Unknown(..)) }) { self.f.write_str("{}") @@ -309,6 +322,7 @@ fn as_any(message: &DynamicMessage) -> Option<(String, DynamicMessage)> { #[cfg(feature = "text-format")] mod tests { use super::*; + use crate::ReflectMessage; fn fmt_unknown(value: &UnknownFieldSet) -> String { let mut string = String::new(); @@ -418,4 +432,16 @@ mod tests { }"# ); } + + #[test] + fn fmt_include_default() { + let timestamp: prost_types::Timestamp = Default::default(); + let message = timestamp.transcode_to_dynamic(); + + let mut string = String::new(); + Writer::new(FormatOptions::new().skip_default_fields(false), &mut string) + .fmt_message(&message) + .unwrap(); + assert_eq!(string, "seconds:0,nanos:0"); + } } diff --git a/prost-reflect/src/dynamic/text_format/mod.rs b/prost-reflect/src/dynamic/text_format/mod.rs index e69f992b..982aeae0 100644 --- a/prost-reflect/src/dynamic/text_format/mod.rs +++ b/prost-reflect/src/dynamic/text_format/mod.rs @@ -18,6 +18,7 @@ pub struct FormatOptions { pretty: bool, skip_unknown_fields: bool, expand_any: bool, + skip_default_fields: bool, } #[cfg(feature = "text-format")] @@ -145,6 +146,18 @@ impl FormatOptions { self } + /// Whether to skip fields which have their default value. + /// + /// If `true`, any fields for which [`has_field`][DynamicMessage::has_field] returns `false` will + /// not be included. If `false`, they will be included with their default value. + /// + /// The default value is `true`. + #[cfg(feature = "text-format")] + pub fn skip_default_fields(mut self, yes: bool) -> Self { + self.skip_default_fields = yes; + self + } + /// Whether to use the expanded form of the `google.protobuf.Any` type. /// /// If set to `true`, `Any` fields will use an expanded form: @@ -193,6 +206,7 @@ impl Default for FormatOptions { pretty: false, skip_unknown_fields: true, expand_any: true, + skip_default_fields: true, } } }