From 4e448e9d5a3c0bee92cc76993961d038db9e9dfa Mon Sep 17 00:00:00 2001 From: voidentente Date: Tue, 24 Sep 2024 17:42:57 +0200 Subject: [PATCH] Refactor --- src/lib.rs | 1 - src/ser/mod.rs | 86 ++++----- src/{meta.rs => ser/path_meta.rs} | 227 ++++++++++++------------ tests/{544_meta.rs => 544_path_meta.rs} | 5 +- 4 files changed, 158 insertions(+), 161 deletions(-) rename src/{meta.rs => ser/path_meta.rs} (68%) rename tests/{544_meta.rs => 544_path_meta.rs} (97%) diff --git a/src/lib.rs b/src/lib.rs index e44d43ddd..36414b1fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,6 @@ pub mod de; pub mod ser; pub mod error; -pub mod meta; pub mod value; pub mod extensions; diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 11a39403d..7868d1d7d 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -7,11 +7,12 @@ use unicode_ident::is_xid_continue; use crate::{ error::{Error, Result}, extensions::Extensions, - meta::PathMeta, options::Options, parse::{is_ident_first_char, is_ident_raw_char, is_whitespace_char, LargeSInt, LargeUInt}, }; +pub mod path_meta; + mod raw; #[cfg(test)] mod tests; @@ -111,7 +112,7 @@ pub struct PrettyConfig { /// Enable explicit number type suffixes like `1u16` pub number_suffixes: bool, /// Additional metadata to serialize - pub meta: PathMeta, + pub path_meta: Option, } impl PrettyConfig { @@ -362,7 +363,7 @@ impl Default for PrettyConfig { compact_structs: false, compact_maps: false, number_suffixes: false, - meta: PathMeta::default(), + path_meta: None, } } } @@ -382,6 +383,15 @@ pub struct Serializer { implicit_some_depth: usize, } +fn indent(mut output: W, config: &PrettyConfig, pretty: &Pretty) -> fmt::Result { + if pretty.indent <= config.depth_limit { + for _ in 0..pretty.indent { + output.write_str(&config.indentor)?; + } + } + Ok(()) +} + impl Serializer { /// Creates a new [`Serializer`]. /// @@ -493,16 +503,16 @@ impl Serializer { Ok(()) } - fn indent(&mut self) -> fmt::Result { - if let Some((ref config, ref pretty)) = self.pretty { - if pretty.indent <= config.depth_limit { - for _ in 0..pretty.indent { - self.output.write_str(&config.indentor)?; - } - } - } - Ok(()) - } + // fn indent(&mut self) -> fmt::Result { + // if let Some((ref config, ref pretty)) = self.pretty { + // if pretty.indent <= config.depth_limit { + // for _ in 0..pretty.indent { + // self.output.write_str(&config.indentor)?; + // } + // } + // } + // Ok(()) + // } fn end_indent(&mut self) -> fmt::Result { if let Some((ref config, ref mut pretty)) = self.pretty { @@ -1126,7 +1136,9 @@ impl<'a, W: fmt::Write> ser::SerializeSeq for Compound<'a, W> { } if !self.ser.compact_arrays() { - self.ser.indent()?; + if let Some((ref config, ref pretty)) = self.ser.pretty { + indent(&mut self.ser.output, config, pretty)?; + } } if let Some((ref mut config, ref mut pretty)) = self.ser.pretty { @@ -1183,7 +1195,9 @@ impl<'a, W: fmt::Write> ser::SerializeTuple for Compound<'a, W> { } if self.ser.separate_tuple_members() { - self.ser.indent()?; + if let Some((ref config, ref pretty)) = self.ser.pretty { + indent(&mut self.ser.output, config, pretty)?; + } } guard_recursion! { self.ser => value.serialize(&mut *self.ser)? }; @@ -1268,7 +1282,9 @@ impl<'a, W: fmt::Write> ser::SerializeMap for Compound<'a, W> { } if !self.ser.compact_maps() { - self.ser.indent()?; + if let Some((ref config, ref pretty)) = self.ser.pretty { + indent(&mut self.ser.output, config, pretty)?; + } } guard_recursion! { self.ser => key.serialize(&mut *self.ser) } @@ -1318,9 +1334,9 @@ impl<'a, W: fmt::Write> ser::SerializeStruct for Compound<'a, W> { T: ?Sized + Serialize, { let mut restore_field = self.ser.pretty.as_mut().and_then(|(config, _)| { - config.meta.field.take().map(|mut field| { + config.path_meta.take().map(|mut field| { if let Some(fields) = field.fields_mut() { - config.meta.field = fields.remove(key); + config.path_meta = fields.remove(key); } field }) @@ -1341,27 +1357,15 @@ impl<'a, W: fmt::Write> ser::SerializeStruct for Compound<'a, W> { } if !self.ser.compact_structs() { - self.ser.indent()?; - - if let Some((ref config, _)) = self.ser.pretty { - if let Some(ref field) = config.meta.field { - // TODO: `self.ser.indent()` borrows the entire serializer mutably, - // consider constraining the signature in the future to avoid this heap allocation. - let doc_lines: Vec<_> = field - .doc() - .lines() - .map(|line| { - let mut buf = String::with_capacity(line.len() + 5); - buf.push_str("/// "); - buf.push_str(line); - buf.push('\n'); - buf - }) - .collect(); - - for doc_line in doc_lines { - self.ser.output.write_str(&doc_line)?; - self.ser.indent()?; + if let Some((ref config, ref pretty)) = self.ser.pretty { + indent(&mut self.ser.output, config, pretty)?; + + if let Some(ref field) = config.path_meta { + for doc_line in field.doc().lines() { + self.ser.output.write_str("/// ")?; + self.ser.output.write_str(doc_line)?; + self.ser.output.write_char('\n')?; + indent(&mut self.ser.output, config, pretty)?; } } } @@ -1377,9 +1381,9 @@ impl<'a, W: fmt::Write> ser::SerializeStruct for Compound<'a, W> { guard_recursion! { self.ser => value.serialize(&mut *self.ser)? }; if let Some((ref mut config, _)) = self.ser.pretty { - std::mem::swap(&mut config.meta.field, &mut restore_field); + std::mem::swap(&mut config.path_meta, &mut restore_field); - if let Some(ref mut field) = config.meta.field { + if let Some(ref mut field) = config.path_meta { if let Some(fields) = field.fields_mut() { if let Some(restore_field) = restore_field { fields.insert(key, restore_field); diff --git a/src/meta.rs b/src/ser/path_meta.rs similarity index 68% rename from src/meta.rs rename to src/ser/path_meta.rs index 69a8e0177..7c8b41c3f 100644 --- a/src/meta.rs +++ b/src/ser/path_meta.rs @@ -1,112 +1,107 @@ +//! 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 two fields with the same path: `buf` and `len` +//! // v +//! B, +//! ); +//! ``` + use std::collections::HashMap; use serde_derive::{Deserialize, Serialize}; -/// 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, meta::Field}; -/// -/// #[derive(serde::Serialize)] -/// struct Creature { -/// seconds_since_existing: usize, -/// linked: Option>, -/// } -/// -/// let mut config = PrettyConfig::default(); -/// -/// config -/// .meta -/// .field -/// // 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 two fields with the same path: `buf` and `len` -/// // v -/// B, -/// ); -/// ``` -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] -pub struct PathMeta { - pub field: Option, -} - /// The metadata and inner [`Fields`] of a field. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct Field { @@ -149,7 +144,7 @@ impl Field { /// Set the documentation metadata of this field. /// /// ``` - /// # use ron::meta::Field; + /// # use ron::ser::path_meta::Field; /// /// let mut field = Field::empty(); /// @@ -178,7 +173,7 @@ impl Field { /// Return whether this field has inner fields. /// /// ``` - /// # use ron::meta::{Field, Fields}; + /// # use ron::ser::path_meta::{Field, Fields}; /// /// let mut field = Field::empty(); /// @@ -196,7 +191,7 @@ impl Field { /// Set the inner fields of this field. /// /// ``` - /// # use ron::meta::{Field, Fields}; + /// # use ron::ser::path_meta::{Field, Fields}; /// /// let mut field = Field::empty(); /// @@ -218,7 +213,7 @@ impl Field { /// Ergonomic shortcut for building some inner fields. /// /// ``` - /// # use ron::meta::Field; + /// # use ron::ser::path_meta::Field; /// /// let mut field = Field::empty(); /// @@ -252,7 +247,7 @@ impl Fields { /// Return whether this field map contains no fields. /// /// ``` - /// # use ron::meta::{Fields, Field}; + /// # use ron::ser::path_meta::{Fields, Field}; /// /// let mut fields = Fields::default(); /// @@ -270,7 +265,7 @@ impl Fields { /// Return whether this field map contains a field with the given name. /// /// ``` - /// # use ron::meta::{Fields, Field}; + /// # use ron::ser::path_meta::{Fields, Field}; /// /// let fields: Fields = [("a thing", Field::empty())].into_iter().collect(); /// @@ -284,7 +279,7 @@ impl Fields { /// Get a reference to the field with the provided `name`, if it exists. /// /// ``` - /// # use ron::meta::{Fields, Field}; + /// # use ron::ser::path_meta::{Fields, Field}; /// /// let fields: Fields = [("a thing", Field::empty())].into_iter().collect(); /// @@ -298,7 +293,7 @@ impl Fields { /// Get a mutable reference to the field with the provided `name`, if it exists. /// /// ``` - /// # use ron::meta::{Fields, Field}; + /// # use ron::ser::path_meta::{Fields, Field}; /// /// let mut fields: Fields = [("a thing", Field::empty())].into_iter().collect(); /// @@ -312,7 +307,7 @@ impl Fields { /// Insert a field with the given name into the map. /// /// ``` - /// # use ron::meta::{Fields, Field}; + /// # use ron::ser::path_meta::{Fields, Field}; /// /// let mut fields = Fields::default(); /// @@ -326,7 +321,7 @@ impl Fields { /// Remove a field with the given name from the map. /// /// ``` - /// # use ron::meta::{Fields, Field}; + /// # use ron::ser::path_meta::{Fields, Field}; /// /// let mut fields: Fields = [("a", Field::empty())].into_iter().collect(); /// @@ -341,7 +336,7 @@ impl Fields { /// inserting an empty [`Field`] if it didn't exist. /// /// ``` - /// # use ron::meta::Fields; + /// # use ron::ser::path_meta::Fields; /// /// let mut fields = Fields::default(); /// diff --git a/tests/544_meta.rs b/tests/544_path_meta.rs similarity index 97% rename from tests/544_meta.rs rename to tests/544_path_meta.rs index 6193969ae..a165bcf73 100644 --- a/tests/544_meta.rs +++ b/tests/544_path_meta.rs @@ -1,4 +1,4 @@ -use ron::{meta::Field, ser::PrettyConfig}; +use ron::ser::{path_meta::Field, PrettyConfig}; #[test] fn serialize_field() { @@ -52,8 +52,7 @@ fn serialize_field() { let mut config = PrettyConfig::default(); config - .meta - .field + .path_meta .get_or_insert_with(Field::empty) .build_fields(|fields| { fields