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 alloc::string::String;
102
103use serde_derive::{Deserialize, Serialize};
104
105#[cfg(feature = "std")]
106use std::collections::HashMap as FieldsInner;
107
108#[cfg(not(feature = "std"))]
109use alloc::collections::BTreeMap as FieldsInner;
110
111/// The metadata and inner [`Fields`] of a field.
112#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
113pub struct Field {
114 doc: String,
115 fields: Option<Fields>,
116}
117
118impl Field {
119 /// Create a new empty field metadata.
120 #[must_use]
121 pub const fn empty() -> Self {
122 Self {
123 doc: String::new(),
124 fields: None,
125 }
126 }
127
128 /// Create a new field metadata from parts.
129 pub fn new(doc: impl Into<String>, fields: Option<Fields>) -> Self {
130 Self {
131 doc: doc.into(),
132 fields,
133 }
134 }
135
136 /// Get a shared reference to the documentation metadata of this field.
137 #[inline]
138 #[must_use]
139 pub fn doc(&self) -> &str {
140 self.doc.as_str()
141 }
142
143 /// Get a mutable reference to the documentation metadata of this field.
144 #[inline]
145 #[must_use]
146 pub fn doc_mut(&mut self) -> &mut String {
147 &mut self.doc
148 }
149
150 /// Set the documentation metadata of this field.
151 ///
152 /// ```
153 /// # use ron::ser::path_meta::Field;
154 ///
155 /// let mut field = Field::empty();
156 ///
157 /// assert_eq!(field.doc(), "");
158 ///
159 /// field.with_doc("some meta");
160 ///
161 /// assert_eq!(field.doc(), "some meta");
162 /// ```
163 pub fn with_doc(&mut self, doc: impl Into<String>) -> &mut Self {
164 self.doc = doc.into();
165 self
166 }
167
168 /// Get a shared reference to the inner fields of this field, if it has any.
169 #[must_use]
170 pub fn fields(&self) -> Option<&Fields> {
171 self.fields.as_ref()
172 }
173
174 /// Get a mutable reference to the inner fields of this field, if it has any.
175 pub fn fields_mut(&mut self) -> Option<&mut Fields> {
176 self.fields.as_mut()
177 }
178
179 /// Return whether this field has inner fields.
180 ///
181 /// ```
182 /// # use ron::ser::path_meta::{Field, Fields};
183 ///
184 /// let mut field = Field::empty();
185 ///
186 /// assert!(!field.has_fields());
187 ///
188 /// field.with_fields(Some(Fields::default()));
189 ///
190 /// assert!(field.has_fields());
191 /// ```
192 #[must_use]
193 pub fn has_fields(&self) -> bool {
194 self.fields.is_some()
195 }
196
197 /// Set the inner fields of this field.
198 ///
199 /// ```
200 /// # use ron::ser::path_meta::{Field, Fields};
201 ///
202 /// let mut field = Field::empty();
203 ///
204 /// assert!(!field.has_fields());
205 ///
206 /// field.with_fields(Some(Fields::default()));
207 ///
208 /// assert!(field.has_fields());
209 ///
210 /// field.with_fields(None);
211 ///
212 /// assert!(!field.has_fields());
213 /// ```
214 pub fn with_fields(&mut self, fields: Option<Fields>) -> &mut Self {
215 self.fields = fields;
216 self
217 }
218
219 /// Ergonomic shortcut for building some inner fields.
220 ///
221 /// ```
222 /// # use ron::ser::path_meta::Field;
223 ///
224 /// let mut field = Field::empty();
225 ///
226 /// field.build_fields(|fields| {
227 /// fields.field("inner field");
228 /// });
229 ///
230 /// assert_eq!(field.fields().map(|fields| fields.contains("inner field")), Some(true));
231 /// ```
232 pub fn build_fields(&mut self, builder: impl FnOnce(&mut Fields)) -> &mut Self {
233 let mut fields = Fields::default();
234 builder(&mut fields);
235 self.with_fields(Some(fields));
236 self
237 }
238}
239
240/// Mapping of names to [`Field`]s.
241#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
242pub struct Fields {
243 fields: FieldsInner<String, Field>,
244}
245
246impl Fields {
247 /// Return a new, empty metadata field map.
248 #[must_use]
249 pub fn new() -> Self {
250 Self::default()
251 }
252
253 /// Return whether this field map contains no fields.
254 ///
255 /// ```
256 /// # use ron::ser::path_meta::{Fields, Field};
257 ///
258 /// let mut fields = Fields::default();
259 ///
260 /// assert!(fields.is_empty());
261 ///
262 /// fields.insert("", Field::empty());
263 ///
264 /// assert!(!fields.is_empty());
265 /// ```
266 #[must_use]
267 pub fn is_empty(&self) -> bool {
268 self.fields.is_empty()
269 }
270
271 /// Return whether this field map contains a field with the given name.
272 ///
273 /// ```
274 /// # use ron::ser::path_meta::{Fields, Field};
275 ///
276 /// let fields: Fields = [("a thing", Field::empty())].into_iter().collect();
277 ///
278 /// assert!(fields.contains("a thing"));
279 /// assert!(!fields.contains("not a thing"));
280 /// ```
281 pub fn contains(&self, name: impl AsRef<str>) -> bool {
282 self.fields.contains_key(name.as_ref())
283 }
284
285 /// Get a reference to the field with the provided `name`, if it exists.
286 ///
287 /// ```
288 /// # use ron::ser::path_meta::{Fields, Field};
289 ///
290 /// let fields: Fields = [("a thing", Field::empty())].into_iter().collect();
291 ///
292 /// assert!(fields.get("a thing").is_some());
293 /// assert!(fields.get("not a thing").is_none());
294 /// ```
295 pub fn get(&self, name: impl AsRef<str>) -> Option<&Field> {
296 self.fields.get(name.as_ref())
297 }
298
299 /// Get a mutable reference to the field with the provided `name`, if it exists.
300 ///
301 /// ```
302 /// # use ron::ser::path_meta::{Fields, Field};
303 ///
304 /// let mut fields: Fields = [("a thing", Field::empty())].into_iter().collect();
305 ///
306 /// assert!(fields.get_mut("a thing").is_some());
307 /// assert!(fields.get_mut("not a thing").is_none());
308 /// ```
309 pub fn get_mut(&mut self, name: impl AsRef<str>) -> Option<&mut Field> {
310 self.fields.get_mut(name.as_ref())
311 }
312
313 /// Insert a field with the given name into the map.
314 ///
315 /// ```
316 /// # use ron::ser::path_meta::{Fields, Field};
317 ///
318 /// let mut fields = Fields::default();
319 ///
320 /// assert!(fields.insert("field", Field::empty()).is_none());
321 /// assert!(fields.insert("field", Field::empty()).is_some());
322 /// ```
323 pub fn insert(&mut self, name: impl Into<String>, field: Field) -> Option<Field> {
324 self.fields.insert(name.into(), field)
325 }
326
327 /// Remove a field with the given name from the map.
328 ///
329 /// ```
330 /// # use ron::ser::path_meta::{Fields, Field};
331 ///
332 /// let mut fields: Fields = [("a", Field::empty())].into_iter().collect();
333 ///
334 /// assert_eq!(fields.remove("a"), Some(Field::empty()));
335 /// assert_eq!(fields.remove("a"), None);
336 /// ```
337 pub fn remove(&mut self, name: impl AsRef<str>) -> Option<Field> {
338 self.fields.remove(name.as_ref())
339 }
340
341 /// Get a mutable reference to the field with the provided `name`,
342 /// inserting an empty [`Field`] if it didn't exist.
343 ///
344 /// ```
345 /// # use ron::ser::path_meta::Fields;
346 ///
347 /// let mut fields = Fields::default();
348 ///
349 /// assert!(!fields.contains("thing"));
350 ///
351 /// fields.field("thing");
352 ///
353 /// assert!(fields.contains("thing"));
354 /// ```
355 pub fn field(&mut self, name: impl Into<String>) -> &mut Field {
356 self.fields.entry(name.into()).or_insert_with(Field::empty)
357 }
358}
359
360impl<K: Into<String>> FromIterator<(K, Field)> for Fields {
361 fn from_iter<T: IntoIterator<Item = (K, Field)>>(iter: T) -> Self {
362 Self {
363 fields: iter.into_iter().map(|(k, v)| (k.into(), v)).collect(),
364 }
365 }
366}