Lines
48.61 %
Functions
6.49 %
Branches
100 %
//! 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<Box<Self>>,
//! }
//! 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");
//! .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.
//! .with_doc("Inner seconds_since_existing");
//! });
//! let value = Creature {
//! seconds_since_existing: 0,
//! linked: Some(Box::new(Creature {
//! linked: None,
//! })),
//! };
//! let s = ron::ser::to_string_pretty(&value, config).unwrap();
//! assert_eq!(s, r#"(
//! /// Outer seconds_since_existing
//! /// Optional.
//! /// Provide another creature to be wrapped.
//! linked: Some((
//! /// Inner seconds_since_existing
//! )),
//! )"#);
//! # 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
//! },
//! struct A {
//! struct B {
//! type Value = (
//! A,
//! // ^
//! // These are different types, but they share the path `field`
//! // v
//! B,
//! );
use std::collections::HashMap;
use serde_derive::{Deserialize, Serialize};
/// The metadata and inner [`Fields`] of a field.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct Field {
doc: String,
fields: Option<Fields>,
}
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<String>, fields: Option<Fields>) -> Self {
doc: doc.into(),
fields,
/// Get a shared reference to the documentation metadata of this field.
#[inline]
pub fn doc(&self) -> &str {
self.doc.as_str()
/// Get a mutable reference to the documentation metadata of this field.
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<String>) -> &mut Self {
self.doc = doc.into();
self
/// Get a shared reference to the inner fields of this field, if it has any.
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};
/// assert!(!field.has_fields());
/// field.with_fields(Some(Fields::default()));
/// assert!(field.has_fields());
pub fn has_fields(&self) -> bool {
self.fields.is_some()
/// Set the inner fields of this field.
/// field.with_fields(None);
pub fn with_fields(&mut self, fields: Option<Fields>) -> &mut Self {
self.fields = fields;
/// Ergonomic shortcut for building some inner fields.
/// 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));
/// Mapping of names to [`Field`]s.
pub struct Fields {
fields: HashMap<String, Field>,
impl Fields {
/// Return a new, empty metadata field map.
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());
pub fn is_empty(&self) -> bool {
self.fields.is_empty()
/// Return whether this field map contains a field with the given name.
/// 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<str>) -> bool {
self.fields.contains_key(name.as_ref())
/// Get a reference to the field with the provided `name`, if it exists.
/// assert!(fields.get("a thing").is_some());
/// assert!(fields.get("not a thing").is_none());
pub fn get(&self, name: impl AsRef<str>) -> Option<&Field> {
self.fields.get(name.as_ref())
/// Get a mutable reference to the field with the provided `name`, if it exists.
/// 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<str>) -> Option<&mut Field> {
self.fields.get_mut(name.as_ref())
/// Insert a field with the given name into the map.
/// assert!(fields.insert("field", Field::empty()).is_none());
/// assert!(fields.insert("field", Field::empty()).is_some());
pub fn insert(&mut self, name: impl Into<String>, field: Field) -> Option<Field> {
self.fields.insert(name.into(), field)
/// Remove a field with the given name from the map.
/// 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<str>) -> Option<Field> {
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;
/// assert!(!fields.contains("thing"));
/// fields.field("thing");
/// assert!(fields.contains("thing"));
pub fn field(&mut self, name: impl Into<String>) -> &mut Field {
self.fields.entry(name.into()).or_insert_with(Field::empty)
impl<K: Into<String>> FromIterator<(K, Field)> for Fields {
fn from_iter<T: IntoIterator<Item = (K, Field)>>(iter: T) -> Self {
fields: iter.into_iter().map(|(k, v)| (k.into(), v)).collect(),