//! Path-based metadata to serialize with a value. //! //! Path-based in this context means that the metadata is linked //! to the data in a relative and hierarchical fashion by tracking //! the current absolute path of the field being serialized. //! //! # Example //! //! ``` //! # use ron::ser::{PrettyConfig, path_meta::Field}; //! //! #[derive(serde::Serialize)] //! struct Creature { //! seconds_since_existing: usize, //! linked: Option>, //! } //! //! let mut config = PrettyConfig::default(); //! //! config //! .path_meta //! // The path meta defaults to no root structure, //! // so we either provide a prebuilt one or initialize //! // an empty one to build. //! .get_or_insert_with(Field::empty) //! .build_fields(|fields| { //! fields //! // Get or insert the named field //! .field("seconds_since_existing") //! .with_doc("Outer seconds_since_existing"); //! fields //! .field("linked") //! // Doc metadata is serialized preceded by three forward slashes and a space for each line //! .with_doc("Optional.\nProvide another creature to be wrapped.") //! // Even though it's another Creature, the fields have different paths, so they are addressed separately. //! .build_fields(|fields| { //! fields //! .field("seconds_since_existing") //! .with_doc("Inner seconds_since_existing"); //! }); //! }); //! //! let value = Creature { //! seconds_since_existing: 0, //! linked: Some(Box::new(Creature { //! seconds_since_existing: 0, //! linked: None, //! })), //! }; //! //! let s = ron::ser::to_string_pretty(&value, config).unwrap(); //! //! assert_eq!(s, r#"( //! /// Outer seconds_since_existing //! seconds_since_existing: 0, //! /// Optional. //! /// Provide another creature to be wrapped. //! linked: Some(( //! /// Inner seconds_since_existing //! seconds_since_existing: 0, //! linked: None, //! )), //! )"#); //! ``` //! //! # Identical paths //! //! Especially in enums and tuples it's possible for fields //! to share a path, thus being unable to be addressed separately. //! //! ```no_run //! enum Kind { //! A { //! field: (), //! }, // ^ //! // cannot be addressed separately because they have the same path //! B { // v //! field: (), //! }, //! } //! ``` //! //! ```no_run //! struct A { //! field: (), //! } //! //! struct B { //! field: (), //! } //! //! type Value = ( //! A, //! // ^ //! // These are different types, but they share the path `field` //! // v //! B, //! ); //! ``` use alloc::string::String; use serde_derive::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::collections::HashMap as FieldsInner; #[cfg(not(feature = "std"))] use alloc::collections::BTreeMap as FieldsInner; /// The metadata and inner [`Fields`] of a field. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct Field { doc: String, fields: Option, } impl Field { /// Create a new empty field metadata. #[must_use] pub const fn empty() -> Self { Self { doc: String::new(), fields: None, } } /// Create a new field metadata from parts. pub fn new(doc: impl Into, fields: Option) -> Self { Self { doc: doc.into(), fields, } } /// Get a shared reference to the documentation metadata of this field. #[inline] #[must_use] pub fn doc(&self) -> &str { self.doc.as_str() } /// Get a mutable reference to the documentation metadata of this field. #[inline] #[must_use] pub fn doc_mut(&mut self) -> &mut String { &mut self.doc } /// Set the documentation metadata of this field. /// /// ``` /// # use ron::ser::path_meta::Field; /// /// let mut field = Field::empty(); /// /// assert_eq!(field.doc(), ""); /// /// field.with_doc("some meta"); /// /// assert_eq!(field.doc(), "some meta"); /// ``` pub fn with_doc(&mut self, doc: impl Into) -> &mut Self { self.doc = doc.into(); self } /// Get a shared reference to the inner fields of this field, if it has any. #[must_use] pub fn fields(&self) -> Option<&Fields> { self.fields.as_ref() } /// Get a mutable reference to the inner fields of this field, if it has any. pub fn fields_mut(&mut self) -> Option<&mut Fields> { self.fields.as_mut() } /// Return whether this field has inner fields. /// /// ``` /// # use ron::ser::path_meta::{Field, Fields}; /// /// let mut field = Field::empty(); /// /// assert!(!field.has_fields()); /// /// field.with_fields(Some(Fields::default())); /// /// assert!(field.has_fields()); /// ``` #[must_use] pub fn has_fields(&self) -> bool { self.fields.is_some() } /// Set the inner fields of this field. /// /// ``` /// # use ron::ser::path_meta::{Field, Fields}; /// /// let mut field = Field::empty(); /// /// assert!(!field.has_fields()); /// /// field.with_fields(Some(Fields::default())); /// /// assert!(field.has_fields()); /// /// field.with_fields(None); /// /// assert!(!field.has_fields()); /// ``` pub fn with_fields(&mut self, fields: Option) -> &mut Self { self.fields = fields; self } /// Ergonomic shortcut for building some inner fields. /// /// ``` /// # use ron::ser::path_meta::Field; /// /// let mut field = Field::empty(); /// /// field.build_fields(|fields| { /// fields.field("inner field"); /// }); /// /// assert_eq!(field.fields().map(|fields| fields.contains("inner field")), Some(true)); /// ``` pub fn build_fields(&mut self, builder: impl FnOnce(&mut Fields)) -> &mut Self { let mut fields = Fields::default(); builder(&mut fields); self.with_fields(Some(fields)); self } } /// Mapping of names to [`Field`]s. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct Fields { fields: FieldsInner, } impl Fields { /// Return a new, empty metadata field map. #[must_use] pub fn new() -> Self { Self::default() } /// Return whether this field map contains no fields. /// /// ``` /// # use ron::ser::path_meta::{Fields, Field}; /// /// let mut fields = Fields::default(); /// /// assert!(fields.is_empty()); /// /// fields.insert("", Field::empty()); /// /// assert!(!fields.is_empty()); /// ``` #[must_use] pub fn is_empty(&self) -> bool { self.fields.is_empty() } /// Return whether this field map contains a field with the given name. /// /// ``` /// # use ron::ser::path_meta::{Fields, Field}; /// /// let fields: Fields = [("a thing", Field::empty())].into_iter().collect(); /// /// assert!(fields.contains("a thing")); /// assert!(!fields.contains("not a thing")); /// ``` pub fn contains(&self, name: impl AsRef) -> bool { self.fields.contains_key(name.as_ref()) } /// Get a reference to the field with the provided `name`, if it exists. /// /// ``` /// # use ron::ser::path_meta::{Fields, Field}; /// /// let fields: Fields = [("a thing", Field::empty())].into_iter().collect(); /// /// assert!(fields.get("a thing").is_some()); /// assert!(fields.get("not a thing").is_none()); /// ``` pub fn get(&self, name: impl AsRef) -> Option<&Field> { self.fields.get(name.as_ref()) } /// Get a mutable reference to the field with the provided `name`, if it exists. /// /// ``` /// # use ron::ser::path_meta::{Fields, Field}; /// /// let mut fields: Fields = [("a thing", Field::empty())].into_iter().collect(); /// /// assert!(fields.get_mut("a thing").is_some()); /// assert!(fields.get_mut("not a thing").is_none()); /// ``` pub fn get_mut(&mut self, name: impl AsRef) -> Option<&mut Field> { self.fields.get_mut(name.as_ref()) } /// Insert a field with the given name into the map. /// /// ``` /// # use ron::ser::path_meta::{Fields, Field}; /// /// let mut fields = Fields::default(); /// /// assert!(fields.insert("field", Field::empty()).is_none()); /// assert!(fields.insert("field", Field::empty()).is_some()); /// ``` pub fn insert(&mut self, name: impl Into, field: Field) -> Option { self.fields.insert(name.into(), field) } /// Remove a field with the given name from the map. /// /// ``` /// # use ron::ser::path_meta::{Fields, Field}; /// /// let mut fields: Fields = [("a", Field::empty())].into_iter().collect(); /// /// assert_eq!(fields.remove("a"), Some(Field::empty())); /// assert_eq!(fields.remove("a"), None); /// ``` pub fn remove(&mut self, name: impl AsRef) -> Option { self.fields.remove(name.as_ref()) } /// Get a mutable reference to the field with the provided `name`, /// inserting an empty [`Field`] if it didn't exist. /// /// ``` /// # use ron::ser::path_meta::Fields; /// /// let mut fields = Fields::default(); /// /// assert!(!fields.contains("thing")); /// /// fields.field("thing"); /// /// assert!(fields.contains("thing")); /// ``` pub fn field(&mut self, name: impl Into) -> &mut Field { self.fields.entry(name.into()).or_insert_with(Field::empty) } } impl> FromIterator<(K, Field)> for Fields { fn from_iter>(iter: T) -> Self { Self { fields: iter.into_iter().map(|(k, v)| (k.into(), v)).collect(), } } }