ron/ser/
path_meta.rs

1//! Path-based metadata to serialize with a value.
2//!
3//! Path-based in this context means that the metadata is linked
4//! to the data in a relative and hierarchical fashion by tracking
5//! the current absolute path of the field being serialized.
6//!
7//! # Example
8//!
9//! ```
10//! # use ron::ser::{PrettyConfig, path_meta::Field};
11//!
12//! #[derive(serde::Serialize)]
13//! struct Creature {
14//!     seconds_since_existing: usize,
15//!     linked: Option<Box<Self>>,
16//! }
17//!
18//! let mut config = PrettyConfig::default();
19//!
20//! config
21//!     .path_meta
22//!     // The path meta defaults to no root structure,
23//!     // so we either provide a prebuilt one or initialize
24//!     // an empty one to build.
25//!     .get_or_insert_with(Field::empty)
26//!     .build_fields(|fields| {
27//!         fields
28//!             // Get or insert the named field
29//!             .field("seconds_since_existing")
30//!             .with_doc("Outer seconds_since_existing");
31//!         fields
32//!             .field("linked")
33//!             // Doc metadata is serialized preceded by three forward slashes and a space for each line
34//!             .with_doc("Optional.\nProvide another creature to be wrapped.")
35//!             // Even though it's another Creature, the fields have different paths, so they are addressed separately.
36//!             .build_fields(|fields| {
37//!                 fields
38//!                     .field("seconds_since_existing")
39//!                     .with_doc("Inner seconds_since_existing");
40//!             });
41//!     });
42//!
43//! let value = Creature {
44//!     seconds_since_existing: 0,
45//!     linked: Some(Box::new(Creature {
46//!         seconds_since_existing: 0,
47//!         linked: None,
48//!     })),
49//! };
50//!
51//! let s = ron::ser::to_string_pretty(&value, config).unwrap();
52//!
53//! assert_eq!(s, r#"(
54//!     /// Outer seconds_since_existing
55//!     seconds_since_existing: 0,
56//!     /// Optional.
57//!     /// Provide another creature to be wrapped.
58//!     linked: Some((
59//!         /// Inner seconds_since_existing
60//!         seconds_since_existing: 0,
61//!         linked: None,
62//!     )),
63//! )"#);
64//! ```
65//!
66//! # Identical paths
67//!
68//! Especially in enums and tuples it's possible for fields
69//! to share a path, thus being unable to be addressed separately.
70//!
71//! ```no_run
72//! enum Kind {
73//!     A {
74//!         field: (),
75//!     },  // ^
76//!         // cannot be addressed separately because they have the same path
77//!     B { // v
78//!         field: (),
79//!     },
80//! }
81//! ```
82//!
83//! ```no_run
84//! struct A {
85//!     field: (),
86//! }
87//!
88//! struct B {
89//!     field: (),
90//! }
91//!
92//! type Value = (
93//!     A,
94//!  // ^
95//!  // These are different types, but they share the path `field`
96//!  // v
97//!     B,
98//! );
99//! ```
100
101use std::collections::HashMap;
102
103use serde_derive::{Deserialize, Serialize};
104
105/// The metadata and inner [`Fields`] of a field.
106#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
107pub struct Field {
108    doc: String,
109    fields: Option<Fields>,
110}
111
112impl Field {
113    /// Create a new empty field metadata.
114    #[must_use]
115    pub const fn empty() -> Self {
116        Self {
117            doc: String::new(),
118            fields: None,
119        }
120    }
121
122    /// Create a new field metadata from parts.
123    pub fn new(doc: impl Into<String>, fields: Option<Fields>) -> Self {
124        Self {
125            doc: doc.into(),
126            fields,
127        }
128    }
129
130    /// Get a shared reference to the documentation metadata of this field.
131    #[inline]
132    #[must_use]
133    pub fn doc(&self) -> &str {
134        self.doc.as_str()
135    }
136
137    /// Get a mutable reference to the documentation metadata of this field.
138    #[inline]
139    #[must_use]
140    pub fn doc_mut(&mut self) -> &mut String {
141        &mut self.doc
142    }
143
144    /// Set the documentation metadata of this field.
145    ///
146    /// ```
147    /// # use ron::ser::path_meta::Field;
148    ///
149    /// let mut field = Field::empty();
150    ///
151    /// assert_eq!(field.doc(), "");
152    ///
153    /// field.with_doc("some meta");
154    ///
155    /// assert_eq!(field.doc(), "some meta");
156    /// ```
157    pub fn with_doc(&mut self, doc: impl Into<String>) -> &mut Self {
158        self.doc = doc.into();
159        self
160    }
161
162    /// Get a shared reference to the inner fields of this field, if it has any.
163    #[must_use]
164    pub fn fields(&self) -> Option<&Fields> {
165        self.fields.as_ref()
166    }
167
168    /// Get a mutable reference to the inner fields of this field, if it has any.
169    pub fn fields_mut(&mut self) -> Option<&mut Fields> {
170        self.fields.as_mut()
171    }
172
173    /// Return whether this field has inner fields.
174    ///
175    /// ```
176    /// # use ron::ser::path_meta::{Field, Fields};
177    ///
178    /// let mut field = Field::empty();
179    ///
180    /// assert!(!field.has_fields());
181    ///
182    /// field.with_fields(Some(Fields::default()));
183    ///
184    /// assert!(field.has_fields());
185    /// ```
186    #[must_use]
187    pub fn has_fields(&self) -> bool {
188        self.fields.is_some()
189    }
190
191    /// Set the inner fields of this field.
192    ///
193    /// ```
194    /// # use ron::ser::path_meta::{Field, Fields};
195    ///
196    /// let mut field = Field::empty();
197    ///
198    /// assert!(!field.has_fields());
199    ///
200    /// field.with_fields(Some(Fields::default()));
201    ///
202    /// assert!(field.has_fields());
203    ///
204    /// field.with_fields(None);
205    ///  
206    /// assert!(!field.has_fields());
207    /// ```
208    pub fn with_fields(&mut self, fields: Option<Fields>) -> &mut Self {
209        self.fields = fields;
210        self
211    }
212
213    /// Ergonomic shortcut for building some inner fields.
214    ///
215    /// ```
216    /// # use ron::ser::path_meta::Field;
217    ///
218    /// let mut field = Field::empty();
219    ///
220    /// field.build_fields(|fields| {
221    ///     fields.field("inner field");
222    /// });
223    ///
224    /// assert_eq!(field.fields().map(|fields| fields.contains("inner field")), Some(true));
225    /// ```
226    pub fn build_fields(&mut self, builder: impl FnOnce(&mut Fields)) -> &mut Self {
227        let mut fields = Fields::default();
228        builder(&mut fields);
229        self.with_fields(Some(fields));
230        self
231    }
232}
233
234/// Mapping of names to [`Field`]s.
235#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
236pub struct Fields {
237    fields: HashMap<String, Field>,
238}
239
240impl Fields {
241    /// Return a new, empty metadata field map.
242    #[must_use]
243    pub fn new() -> Self {
244        Self::default()
245    }
246
247    /// Return whether this field map contains no fields.
248    ///
249    /// ```
250    /// # use ron::ser::path_meta::{Fields, Field};
251    ///
252    /// let mut fields = Fields::default();
253    ///
254    /// assert!(fields.is_empty());
255    ///
256    /// fields.insert("", Field::empty());
257    ///
258    /// assert!(!fields.is_empty());
259    /// ```
260    #[must_use]
261    pub fn is_empty(&self) -> bool {
262        self.fields.is_empty()
263    }
264
265    /// Return whether this field map contains a field with the given name.
266    ///
267    /// ```
268    /// # use ron::ser::path_meta::{Fields, Field};
269    ///
270    /// let fields: Fields = [("a thing", Field::empty())].into_iter().collect();
271    ///
272    /// assert!(fields.contains("a thing"));
273    /// assert!(!fields.contains("not a thing"));
274    /// ```
275    pub fn contains(&self, name: impl AsRef<str>) -> bool {
276        self.fields.contains_key(name.as_ref())
277    }
278
279    /// Get a reference to the field with the provided `name`, if it exists.
280    ///
281    /// ```
282    /// # use ron::ser::path_meta::{Fields, Field};
283    ///
284    /// let fields: Fields = [("a thing", Field::empty())].into_iter().collect();
285    ///
286    /// assert!(fields.get("a thing").is_some());
287    /// assert!(fields.get("not a thing").is_none());
288    /// ```
289    pub fn get(&self, name: impl AsRef<str>) -> Option<&Field> {
290        self.fields.get(name.as_ref())
291    }
292
293    /// Get a mutable reference to the field with the provided `name`, if it exists.
294    ///
295    /// ```
296    /// # use ron::ser::path_meta::{Fields, Field};
297    ///
298    /// let mut fields: Fields = [("a thing", Field::empty())].into_iter().collect();
299    ///
300    /// assert!(fields.get_mut("a thing").is_some());
301    /// assert!(fields.get_mut("not a thing").is_none());
302    /// ```
303    pub fn get_mut(&mut self, name: impl AsRef<str>) -> Option<&mut Field> {
304        self.fields.get_mut(name.as_ref())
305    }
306
307    /// Insert a field with the given name into the map.
308    ///
309    /// ```
310    /// # use ron::ser::path_meta::{Fields, Field};
311    ///
312    /// let mut fields = Fields::default();
313    ///
314    /// assert!(fields.insert("field", Field::empty()).is_none());
315    /// assert!(fields.insert("field", Field::empty()).is_some());
316    /// ```
317    pub fn insert(&mut self, name: impl Into<String>, field: Field) -> Option<Field> {
318        self.fields.insert(name.into(), field)
319    }
320
321    /// Remove a field with the given name from the map.
322    ///
323    /// ```
324    /// # use ron::ser::path_meta::{Fields, Field};
325    ///
326    /// let mut fields: Fields = [("a", Field::empty())].into_iter().collect();
327    ///
328    /// assert_eq!(fields.remove("a"), Some(Field::empty()));
329    /// assert_eq!(fields.remove("a"), None);
330    /// ```
331    pub fn remove(&mut self, name: impl AsRef<str>) -> Option<Field> {
332        self.fields.remove(name.as_ref())
333    }
334
335    /// Get a mutable reference to the field with the provided `name`,
336    /// inserting an empty [`Field`] if it didn't exist.
337    ///
338    /// ```
339    /// # use ron::ser::path_meta::Fields;
340    ///
341    /// let mut fields = Fields::default();
342    ///
343    /// assert!(!fields.contains("thing"));
344    ///
345    /// fields.field("thing");
346    ///
347    /// assert!(fields.contains("thing"));
348    /// ```
349    pub fn field(&mut self, name: impl Into<String>) -> &mut Field {
350        self.fields.entry(name.into()).or_insert_with(Field::empty)
351    }
352}
353
354impl<K: Into<String>> FromIterator<(K, Field)> for Fields {
355    fn from_iter<T: IntoIterator<Item = (K, Field)>>(iter: T) -> Self {
356        Self {
357            fields: iter.into_iter().map(|(k, v)| (k.into(), v)).collect(),
358        }
359    }
360}