Skip to content

Commit

Permalink
Add print_message_fields_in_index_order to text-format (#140)
Browse files Browse the repository at this point in the history
  • Loading branch information
bouk authored Jan 21, 2025
1 parent 80b9b1e commit 090d944
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 74 deletions.
6 changes: 6 additions & 0 deletions prost-reflect-tests/src/test.proto
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,9 @@ enum EnumWithAlias {
message MessageWithAliasedEnum {
EnumWithAlias aliased = 1;
}

message IndexOrder {
int32 a = 3;
int32 b = 2;
int32 c = 1;
}
21 changes: 19 additions & 2 deletions prost-reflect-tests/src/text_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use prost_reflect::{text_format::FormatOptions, DynamicMessage, ReflectMessage,

use crate::{
proto::{
contains_group, ComplexType, ContainsGroup, MessageWithAliasedEnum, Point, ScalarArrays,
Scalars, WellKnownTypes,
contains_group, ComplexType, ContainsGroup, IndexOrder, MessageWithAliasedEnum, Point,
ScalarArrays, Scalars, WellKnownTypes,
},
test_file_descriptor,
};
Expand Down Expand Up @@ -353,6 +353,23 @@ fn fmt_group() {
);
}

#[test]
fn fmt_index_order() {
let value = IndexOrder { a: 1, b: 2, c: 3 }.transcode_to_dynamic();
assert_eq!(
value.to_text_format_with_options(
&FormatOptions::new().print_message_fields_in_index_order(true)
),
"a:1,b:2,c:3"
);
assert_eq!(
value.to_text_format_with_options(
&FormatOptions::new().print_message_fields_in_index_order(false)
),
"c:3,b:2,a:1"
);
}

#[test]
fn parse_group() {
let value = ContainsGroup {
Expand Down
13 changes: 13 additions & 0 deletions prost-reflect/src/descriptor/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,19 @@ impl MessageDescriptor {
})
}

pub(crate) fn fields_in_index_order(
&self,
) -> impl ExactSizeIterator<Item = FieldDescriptor> + '_ {
self.inner()
.fields
.iter()
.enumerate()
.map(|(index, _)| FieldDescriptor {
message: self.clone(),
index: index as u32,
})
}

/// Gets an iterator yielding a [`OneofDescriptor`] for each oneof field defined in this message.
pub fn oneofs(&self) -> impl ExactSizeIterator<Item = OneofDescriptor> + '_ {
indices(&self.inner().oneofs).map(|index| OneofDescriptor {
Expand Down
96 changes: 43 additions & 53 deletions prost-reflect/src/dynamic/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use crate::{
ExtensionDescriptor, FieldDescriptor, Kind, MessageDescriptor, OneofDescriptor, Value,
};

use super::unknown::{UnknownField, UnknownFieldSet};
use super::{
unknown::{UnknownField, UnknownFieldSet},
Either,
};

pub(crate) trait FieldDescriptorLike: fmt::Debug {
#[cfg(feature = "text-format")]
Expand Down Expand Up @@ -143,68 +146,55 @@ impl DynamicMessageFieldSet {
}
}

/// Iterates over the fields in the message.
///
/// If `include_default` is `true`, fields with their default value will be included.
/// If `index_order` is `true`, fields will be iterated in the order they were defined in the source code. Otherwise, they will be iterated in field number order.
pub(crate) fn iter<'a>(
&'a self,
message: &'a MessageDescriptor,
include_default: bool,
index_order: bool,
) -> impl Iterator<Item = ValueAndDescriptor<'a>> + 'a {
self.fields
.iter()
.filter_map(move |(&number, value)| match value {
ValueOrUnknown::Value(value) => {
if let Some(field) = message.get_field(number) {
if field.has(value) {
Some(ValueAndDescriptor::Field(Cow::Borrowed(value), field))
} else {
None
}
} else if let Some(extension) = message.get_extension(number) {
if extension.has(value) {
Some(ValueAndDescriptor::Extension(
Cow::Borrowed(value),
extension,
))
} else {
None
}
} else {
panic!("no field found with number {}", number)
}
let field_descriptors = if index_order {
Either::Left(message.fields_in_index_order())
} else {
Either::Right(message.fields())
};

let fields = field_descriptors
.filter(move |f| {
if include_default {
!f.supports_presence() || self.has(f)
} else {
self.has(f)
}
ValueOrUnknown::Unknown(unknown) => Some(ValueAndDescriptor::Unknown(unknown)),
ValueOrUnknown::Taken => None,
})
}

pub(crate) fn iter_include_default<'a>(
&'a self,
message: &'a MessageDescriptor,
) -> impl Iterator<Item = ValueAndDescriptor<'a>> + 'a {
let fields = message
.fields()
.filter(move |f| !f.supports_presence() || self.has(f))
.map(move |f| ValueAndDescriptor::Field(self.get(&f), f));
let others = self
.fields
.iter()
.filter_map(move |(&number, value)| match value {
ValueOrUnknown::Value(value) => {
if let Some(extension) = message.get_extension(number) {
if extension.has(value) {
Some(ValueAndDescriptor::Extension(
Cow::Borrowed(value),
extension,
))
.map(|f| ValueAndDescriptor::Field(self.get(&f), f));

let extensions_unknowns =
self.fields
.iter()
.filter_map(move |(&number, value)| match value {
ValueOrUnknown::Value(value) => {
if let Some(extension) = message.get_extension(number) {
if extension.has(value) {
Some(ValueAndDescriptor::Extension(
Cow::Borrowed(value),
extension,
))
} else {
None
}
} else {
None
}
} else {
None
}
}
ValueOrUnknown::Unknown(unknown) => Some(ValueAndDescriptor::Unknown(unknown)),
ValueOrUnknown::Taken => None,
});
fields.chain(others)
ValueOrUnknown::Unknown(unknown) => Some(ValueAndDescriptor::Unknown(unknown)),
ValueOrUnknown::Taken => None,
});

fields.chain(extensions_unknowns)
}

pub(crate) fn iter_fields<'a>(
Expand Down
4 changes: 2 additions & 2 deletions prost-reflect/src/dynamic/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl Message for DynamicMessage {
where
Self: Sized,
{
for field in self.fields.iter(&self.desc) {
for field in self.fields.iter(&self.desc, false, false) {
match field {
ValueAndDescriptor::Field(value, field_desc) => {
value.encode_field(&field_desc, buf)
Expand Down Expand Up @@ -61,7 +61,7 @@ impl Message for DynamicMessage {

fn encoded_len(&self) -> usize {
let mut len = 0;
for field in self.fields.iter(&self.desc) {
for field in self.fields.iter(&self.desc, false, false) {
match field {
ValueAndDescriptor::Field(value, field_desc) => {
len += value.encoded_len(&field_desc);
Expand Down
8 changes: 3 additions & 5 deletions prost-reflect/src/dynamic/serde/ser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,9 @@ fn serialize_dynamic_message_fields<S>(
where
S: SerializeMap,
{
let fields = if options.skip_default_fields {
crate::dynamic::Either::Left(value.fields.iter(&value.desc))
} else {
crate::dynamic::Either::Right(value.fields.iter_include_default(&value.desc))
};
let fields = value
.fields
.iter(&value.desc, !options.skip_default_fields, false);

for field in fields {
let (name, value, ref kind) = match field {
Expand Down
22 changes: 10 additions & 12 deletions prost-reflect/src/dynamic/text_format/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ where
}
}

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))
};
let fields = message.fields.iter(
&message.desc,
!self.options.skip_default_fields,
self.options.print_message_fields_in_index_order,
);

if self.options.skip_unknown_fields {
self.fmt_delimited(
Expand Down Expand Up @@ -90,13 +90,11 @@ where
write!(self.f, "{}", value)
}
Value::Message(message) => {
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),
)
};
let mut fields = message.fields.iter(
&message.desc,
!self.options.skip_default_fields,
self.options.print_message_fields_in_index_order,
);

if fields.all(|f| {
self.options.skip_unknown_fields && matches!(f, ValueAndDescriptor::Unknown(..))
Expand Down
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 @@ -19,6 +19,7 @@ pub struct FormatOptions {
skip_unknown_fields: bool,
expand_any: bool,
skip_default_fields: bool,
print_message_fields_in_index_order: bool,
}

#[cfg(feature = "text-format")]
Expand Down Expand Up @@ -158,6 +159,18 @@ impl FormatOptions {
self
}

/// Whether to print message fields in the order they were defined in source code.
///
/// If set to `true`, message fields will be printed in the order they were defined in the source code.
/// Otherwise, they will be printed in field number order.
///
/// The default value is `false`.
#[cfg(feature = "text-format")]
pub fn print_message_fields_in_index_order(mut self, yes: bool) -> Self {
self.print_message_fields_in_index_order = 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 @@ -207,6 +220,7 @@ impl Default for FormatOptions {
skip_unknown_fields: true,
expand_any: true,
skip_default_fields: true,
print_message_fields_in_index_order: false,
}
}
}

0 comments on commit 090d944

Please sign in to comment.