prost_reflect/dynamic/text_format/mod.rs
1mod format;
2#[cfg(feature = "text-format")]
3mod parse;
4
5#[cfg(feature = "text-format")]
6pub use self::parse::ParseError;
7#[cfg(feature = "text-format")]
8use crate::{DynamicMessage, MessageDescriptor};
9
10pub(super) use self::format::Writer;
11
12/// Options to control printing of the protobuf text format.
13///
14/// Used by [`DynamicMessage::to_text_format_with_options()`].
15#[derive(Debug, Clone)]
16#[cfg_attr(docsrs, doc(cfg(feature = "text-format")))]
17pub struct FormatOptions {
18 pretty: bool,
19 skip_unknown_fields: bool,
20 expand_any: bool,
21 skip_default_fields: bool,
22 print_message_fields_in_index_order: bool,
23}
24
25#[cfg(feature = "text-format")]
26impl DynamicMessage {
27 /// Parse a [`DynamicMessage`] from the given message encoded using the [text format](https://developers.google.com/protocol-buffers/docs/text-format-spec).
28 ///
29 /// # Examples
30 ///
31 /// ```
32 /// # use prost::Message;
33 /// # use prost_reflect::{DynamicMessage, DescriptorPool, Value};
34 /// # let pool = DescriptorPool::decode(include_bytes!("../../file_descriptor_set.bin").as_ref()).unwrap();
35 /// # let message_descriptor = pool.get_message_by_name("package.MyMessage").unwrap();
36 /// let dynamic_message = DynamicMessage::parse_text_format(message_descriptor, "foo: 150").unwrap();
37 /// assert_eq!(dynamic_message.get_field_by_name("foo").unwrap().as_ref(), &Value::I32(150));
38 /// ```
39 #[cfg_attr(docsrs, doc(cfg(feature = "text-format")))]
40 pub fn parse_text_format(desc: MessageDescriptor, input: &str) -> Result<Self, ParseError> {
41 let mut message = DynamicMessage::new(desc);
42 message.merge_text_format(input)?;
43 Ok(message)
44 }
45
46 /// Merges the given message encoded using the [text format](https://developers.google.com/protocol-buffers/docs/text-format-spec) into this message.
47 ///
48 /// # Examples
49 ///
50 /// ```
51 /// # use prost::Message;
52 /// # use prost_reflect::{DynamicMessage, DescriptorPool, Value};
53 /// # let pool = DescriptorPool::decode(include_bytes!("../../file_descriptor_set.bin").as_ref()).unwrap();
54 /// # let message_descriptor = pool.get_message_by_name("package.MyMessage").unwrap();
55 /// let mut dynamic_message = DynamicMessage::new(message_descriptor);
56 /// dynamic_message.merge_text_format("foo: 150").unwrap();
57 /// assert_eq!(dynamic_message.get_field_by_name("foo").unwrap().as_ref(), &Value::I32(150));
58 /// ```
59 #[cfg_attr(docsrs, doc(cfg(feature = "text-format")))]
60 pub fn merge_text_format(&mut self, input: &str) -> Result<(), ParseError> {
61 parse::Parser::new(input)
62 .parse_message(self)
63 .map_err(|kind| ParseError::new(kind, input))
64 }
65
66 /// Formats this dynamic message using the protobuf text format, with default options.
67 ///
68 /// # Examples
69 ///
70 /// ```
71 /// # use prost::Message;
72 /// # use prost_types::FileDescriptorSet;
73 /// # use prost_reflect::{DynamicMessage, DescriptorPool, Value, text_format::FormatOptions};
74 /// # let pool = DescriptorPool::decode(include_bytes!("../../file_descriptor_set.bin").as_ref()).unwrap();
75 /// # let message_descriptor = pool.get_message_by_name("package.MyMessage").unwrap();
76 /// let dynamic_message = DynamicMessage::decode(message_descriptor, b"\x08\x96\x01\x1a\x02\x10\x42".as_ref()).unwrap();
77 /// assert_eq!(dynamic_message.to_text_format(), "foo:150,nested{bar:66}");
78 /// ```
79 #[cfg_attr(docsrs, doc(cfg(feature = "text-format")))]
80 pub fn to_text_format(&self) -> String {
81 self.to_text_format_with_options(&FormatOptions::new())
82 }
83
84 /// Formats this dynamic message using the protobuf text format, with custom options.
85 ///
86 /// # Examples
87 ///
88 /// ```
89 /// # use prost::Message;
90 /// # use prost_types::FileDescriptorSet;
91 /// # use prost_reflect::{DynamicMessage, DescriptorPool, Value, text_format::FormatOptions};
92 /// # let pool = DescriptorPool::decode(include_bytes!("../../file_descriptor_set.bin").as_ref()).unwrap();
93 /// # let message_descriptor = pool.get_message_by_name("package.MyMessage").unwrap();
94 /// let dynamic_message = DynamicMessage::decode(message_descriptor, b"\x08\x96\x01\x1a\x02\x10\x42".as_ref()).unwrap();
95 /// let options = FormatOptions::new().pretty(true);
96 /// assert_eq!(dynamic_message.to_text_format_with_options(&options), "foo: 150\nnested {\n bar: 66\n}");
97 /// ```
98 #[cfg_attr(docsrs, doc(cfg(feature = "text-format")))]
99 pub fn to_text_format_with_options(&self, options: &FormatOptions) -> String {
100 let mut result = String::new();
101 format::Writer::new(options.clone(), &mut result)
102 .fmt_message(self)
103 .expect("writing to string cannot fail");
104 result
105 }
106}
107
108impl FormatOptions {
109 /// Creates new instance of [`FormatOptions`] with default options.
110 pub fn new() -> Self {
111 FormatOptions::default()
112 }
113
114 /// Whether to prettify the format output.
115 ///
116 /// If set to `true`, each field will be printed on a new line, and nested messages will be indented.
117 ///
118 /// The default value is `false`.
119 pub fn pretty(mut self, yes: bool) -> Self {
120 self.pretty = yes;
121 self
122 }
123
124 /// Whether to include unknown fields in the output.
125 ///
126 /// If set to `false`, unknown fields will be printed. The protobuf format does not include type information,
127 /// so the formatter will attempt to infer types.
128 ///
129 /// The default value is `true`.
130 ///
131 /// # Examples
132 ///
133 /// ```
134 /// # use prost::Message;
135 /// # use prost_types::FileDescriptorSet;
136 /// # use prost_reflect::{DynamicMessage, DescriptorPool, Value, text_format::FormatOptions};
137 /// # let pool = DescriptorPool::decode(include_bytes!("../../file_descriptor_set.bin").as_ref()).unwrap();
138 /// # let message_descriptor = pool.get_message_by_name("google.protobuf.Empty").unwrap();
139 /// let dynamic_message = DynamicMessage::decode(message_descriptor, b"\x08\x96\x01\x1a\x02\x10\x42".as_ref()).unwrap();
140 /// assert_eq!(dynamic_message.to_text_format(), "");
141 /// let options = FormatOptions::new().skip_unknown_fields(false);
142 /// assert_eq!(dynamic_message.to_text_format_with_options(&options), "1:150,3{2:66}");
143 /// ```
144 #[cfg(feature = "text-format")]
145 pub fn skip_unknown_fields(mut self, yes: bool) -> Self {
146 self.skip_unknown_fields = yes;
147 self
148 }
149
150 /// Whether to skip fields which have their default value.
151 ///
152 /// If `true`, any fields for which [`has_field`][DynamicMessage::has_field] returns `false` will
153 /// not be included. If `false`, they will be included with their default value.
154 ///
155 /// The default value is `true`.
156 #[cfg(feature = "text-format")]
157 pub fn skip_default_fields(mut self, yes: bool) -> Self {
158 self.skip_default_fields = yes;
159 self
160 }
161
162 /// Whether to print message fields in the order they were defined in source code.
163 ///
164 /// If set to `true`, message fields will be printed in the order they were defined in the source code.
165 /// Otherwise, they will be printed in field number order.
166 ///
167 /// The default value is `false`.
168 #[cfg(feature = "text-format")]
169 pub fn print_message_fields_in_index_order(mut self, yes: bool) -> Self {
170 self.print_message_fields_in_index_order = yes;
171 self
172 }
173
174 /// Whether to use the expanded form of the `google.protobuf.Any` type.
175 ///
176 /// If set to `true`, `Any` fields will use an expanded form:
177 ///
178 /// ```textproto
179 /// [type.googleapis.com/package.MyMessage] {
180 /// foo: 150
181 /// }
182 /// ```
183 ///
184 /// If set to `false`, the normal text format representation will be used:
185 ///
186 /// ```textproto
187 /// type_url: "type.googleapis.com/package.MyMessage"
188 /// value: "\x08\x96\x01"
189 /// ```
190 ///
191 /// The default value is `true`.
192 ///
193 /// # Examples
194 ///
195 /// ```
196 /// # use prost::Message;
197 /// # use prost_types::FileDescriptorSet;
198 /// # use prost_reflect::{DynamicMessage, DescriptorPool, Value, text_format::FormatOptions, bytes::Bytes};
199 /// # let pool = DescriptorPool::decode(include_bytes!("../../file_descriptor_set.bin").as_ref()).unwrap();
200 /// let message_descriptor = pool.get_message_by_name("google.protobuf.Any").unwrap();
201 /// let mut dynamic_message = DynamicMessage::new(message_descriptor);
202 /// dynamic_message.set_field_by_name("type_url", Value::String("type.googleapis.com/package.MyMessage".to_owned()));
203 /// dynamic_message.set_field_by_name("value", Value::Bytes(Bytes::from_static(b"\x08\x96\x01\x1a\x02\x10\x42".as_ref())));
204 ///
205 /// assert_eq!(dynamic_message.to_text_format(), "[type.googleapis.com/package.MyMessage]{foo:150,nested{bar:66}}");
206 /// let options = FormatOptions::new().expand_any(false);
207 /// assert_eq!(dynamic_message.to_text_format_with_options(&options), r#"type_url:"type.googleapis.com/package.MyMessage",value:"\010\226\001\032\002\020B""#);
208 /// ```
209 #[cfg(feature = "text-format")]
210 pub fn expand_any(mut self, yes: bool) -> Self {
211 self.expand_any = yes;
212 self
213 }
214}
215
216impl Default for FormatOptions {
217 fn default() -> Self {
218 FormatOptions {
219 pretty: false,
220 skip_unknown_fields: true,
221 expand_any: true,
222 skip_default_fields: true,
223 print_message_fields_in_index_order: false,
224 }
225 }
226}