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}