Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add skip_default_fields option to text_format #131

Merged
merged 1 commit into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion prost-reflect/src/dynamic/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ impl DynamicMessageFieldSet {
})
}

#[cfg(feature = "serde")]
pub(crate) fn iter_include_default<'a>(
&'a self,
message: &'a MessageDescriptor,
Expand Down
16 changes: 16 additions & 0 deletions prost-reflect/src/dynamic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1200,3 +1200,19 @@ fn type_sizes() {
assert_eq!(std::mem::size_of::<DynamicMessage>(), 40);
assert_eq!(std::mem::size_of::<Value>(), 56);
}

pub(crate) enum Either<L, R> {
Left(L),
Right(R),
}

impl<L: Iterator, R: Iterator<Item = L::Item>> Iterator for Either<L, R> {
type Item = L::Item;

fn next(&mut self) -> Option<Self::Item> {
match self {
Either::Left(left) => left.next(),
Either::Right(right) => right.next(),
}
}
}
82 changes: 29 additions & 53 deletions prost-reflect/src/dynamic/serde/ser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,62 +54,38 @@
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,

Check warning on line 76 in prost-reflect/src/dynamic/serde/ser/mod.rs

View check run for this annotation

Codecov / codecov/patch

prost-reflect/src/dynamic/serde/ser/mod.rs#L76

Added line #L76 was not covered by tests
};

map.serialize_entry(
name,

Check warning on line 80 in prost-reflect/src/dynamic/serde/ser/mod.rs

View check run for this annotation

Codecov / codecov/patch

prost-reflect/src/dynamic/serde/ser/mod.rs#L80

Added line #L80 was not covered by tests
&SerializeWrapper {
value: &ValueAndKind {
value: value.as_ref(),
kind,

Check warning on line 84 in prost-reflect/src/dynamic/serde/ser/mod.rs

View check run for this annotation

Codecov / codecov/patch

prost-reflect/src/dynamic/serde/ser/mod.rs#L84

Added line #L84 was not covered by tests
},
)?;
}
options,

Check warning on line 86 in prost-reflect/src/dynamic/serde/ser/mod.rs

View check run for this annotation

Codecov / codecov/patch

prost-reflect/src/dynamic/serde/ser/mod.rs#L86

Added line #L86 was not covered by tests
},
)?;
}

Ok(())
Expand Down
30 changes: 28 additions & 2 deletions prost-reflect/src/dynamic/text_format/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@
}
}

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(..))),
Expand Down Expand Up @@ -85,7 +90,15 @@
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),

Check warning on line 97 in prost-reflect/src/dynamic/text_format/format.rs

View check run for this annotation

Codecov / codecov/patch

prost-reflect/src/dynamic/text_format/format.rs#L97

Added line #L97 was not covered by tests
)
};

if fields.all(|f| {
self.options.skip_unknown_fields && matches!(f, ValueAndDescriptor::Unknown(..))
}) {
self.f.write_str("{}")
Expand Down Expand Up @@ -309,6 +322,7 @@
#[cfg(feature = "text-format")]
mod tests {
use super::*;
use crate::ReflectMessage;

fn fmt_unknown(value: &UnknownFieldSet) -> String {
let mut string = String::new();
Expand Down Expand Up @@ -418,4 +432,16 @@
}"#
);
}

#[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");
}
}
14 changes: 14 additions & 0 deletions prost-reflect/src/dynamic/text_format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub struct FormatOptions {
pretty: bool,
skip_unknown_fields: bool,
expand_any: bool,
skip_default_fields: bool,
}

#[cfg(feature = "text-format")]
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -193,6 +206,7 @@ impl Default for FormatOptions {
pretty: false,
skip_unknown_fields: true,
expand_any: true,
skip_default_fields: true,
}
}
}