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}