diff --git a/Cargo.lock b/Cargo.lock index c1614a4..36ecec4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,18 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "windows-sys", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -50,11 +62,18 @@ dependencies = [ name = "duper" version = "0.1.0" dependencies = [ + "insta", "json-escape", "pest", "pest_derive", ] +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "generic-array" version = "0.14.7" @@ -65,6 +84,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "insta" +version = "1.43.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fdb647ebde000f43b5b53f773c30cf9b0cb4300453208713fa38b2c70935a0" +dependencies = [ + "console", + "once_cell", + "similar", +] + [[package]] name = "json-escape" version = "0.3.0" @@ -86,6 +116,12 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + [[package]] name = "pest" version = "2.8.3" @@ -198,6 +234,12 @@ dependencies = [ "digest", ] +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + [[package]] name = "syn" version = "2.0.106" @@ -232,3 +274,76 @@ name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/duper/Cargo.toml b/duper/Cargo.toml index b64d856..664e19c 100644 --- a/duper/Cargo.toml +++ b/duper/Cargo.toml @@ -7,3 +7,6 @@ edition = "2024" json-escape = "0.3.0" pest = "2.8.3" pest_derive = "2.8.3" + +[dev-dependencies] +insta = "1.43.2" diff --git a/duper/src/ast.rs b/duper/src/ast.rs index a0d33b4..4a4a4da 100644 --- a/duper/src/ast.rs +++ b/duper/src/ast.rs @@ -1,20 +1,202 @@ -use std::{borrow::Cow, fmt::Debug}; +use std::{ + borrow::Cow, + fmt::{Debug, Display}, +}; use crate::visitor::DuperVisitor; +#[derive(Debug, Clone)] +pub struct DuperIdentifier<'a>(pub(crate) Cow<'a, str>); + +impl<'a> DuperIdentifier<'a> { + pub fn into_inner(self) -> Cow<'a, str> { + self.0 + } +} + +impl<'a> Display for DuperIdentifier<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.0) + } +} + +impl<'a> AsRef for DuperIdentifier<'a> { + fn as_ref(&self) -> &str { + &self.0 + } +} + +impl<'a> From> for DuperIdentifier<'a> { + fn from(value: Cow<'a, str>) -> Self { + Self(value) + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct DuperKey<'a>(pub(crate) Cow<'a, str>); + +impl<'a> DuperKey<'a> { + pub fn into_inner(self) -> Cow<'a, str> { + self.0 + } +} + +impl<'a> AsRef for DuperKey<'a> { + fn as_ref(&self) -> &str { + &self.0 + } +} + +impl<'a> From> for DuperKey<'a> { + fn from(value: Cow<'a, str>) -> Self { + Self(value) + } +} + #[derive(Debug, Clone)] pub struct DuperValue<'a> { - pub identifier: Option>, + pub identifier: Option>, pub inner: DuperInner<'a>, } +#[derive(Debug, Clone)] +pub struct DuperObject<'a>(pub(crate) Vec<(DuperKey<'a>, DuperValue<'a>)>); + +impl<'a> DuperObject<'a> { + pub fn into_inner(self) -> Vec<(DuperKey<'a>, DuperValue<'a>)> { + self.0 + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn iter(&self) -> impl Iterator, DuperValue<'a>)> { + self.0.iter() + } +} + +impl<'a> From, DuperValue<'a>)>> for DuperObject<'a> { + fn from(value: Vec<(DuperKey<'a>, DuperValue<'a>)>) -> Self { + Self(value) + } +} + +#[derive(Debug, Clone)] +pub struct DuperArray<'a>(pub(crate) Vec>); + +impl<'a> DuperArray<'a> { + pub fn into_inner(self) -> Vec> { + self.0 + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn iter(&self) -> impl Iterator> { + self.0.iter() + } + + pub fn get(&self, index: usize) -> Option<&DuperValue<'a>> { + self.0.get(index) + } +} + +impl<'a> From>> for DuperArray<'a> { + fn from(value: Vec>) -> Self { + Self(value) + } +} + +#[derive(Debug, Clone)] +pub struct DuperTuple<'a>(pub(crate) Vec>); + +impl<'a> DuperTuple<'a> { + pub fn into_inner(self) -> Vec> { + self.0 + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn iter(&self) -> impl Iterator> { + self.0.iter() + } + + pub fn get(&self, index: usize) -> Option<&DuperValue<'a>> { + self.0.get(index) + } +} + +impl<'a> From>> for DuperTuple<'a> { + fn from(value: Vec>) -> Self { + Self(value) + } +} + +#[derive(Debug, Clone)] +pub struct DuperString<'a>(pub(crate) Cow<'a, str>); + +impl<'a> DuperString<'a> { + pub fn into_inner(self) -> Cow<'a, str> { + self.0 + } +} + +impl<'a> From> for DuperString<'a> { + fn from(value: Cow<'a, str>) -> Self { + Self(value) + } +} + +impl<'a> AsRef for DuperString<'a> { + fn as_ref(&self) -> &str { + &self.0 + } +} + +#[derive(Debug, Clone)] +pub struct DuperBytes<'a>(pub(crate) Cow<'a, [u8]>); + +impl<'a> DuperBytes<'a> { + pub fn into_inner(self) -> Cow<'a, [u8]> { + self.0 + } +} + +impl<'a> From> for DuperBytes<'a> { + fn from(value: Cow<'a, [u8]>) -> Self { + Self(value) + } +} + +impl<'a> AsRef<[u8]> for DuperBytes<'a> { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + #[derive(Debug, Clone)] pub enum DuperInner<'a> { - Object(Vec<(Cow<'a, str>, DuperValue<'a>)>), - Array(Vec>), - Tuple(Vec>), - String(Cow<'a, str>), - Bytes(Cow<'a, [u8]>), + Object(DuperObject<'a>), + Array(DuperArray<'a>), + Tuple(DuperTuple<'a>), + String(DuperString<'a>), + Bytes(DuperBytes<'a>), Integer(i64), Float(f64), Boolean(bool), diff --git a/duper/src/builder.rs b/duper/src/builder.rs index be135a9..c87946e 100644 --- a/duper/src/builder.rs +++ b/duper/src/builder.rs @@ -7,7 +7,10 @@ use pest::{ }; use crate::{ - ast::{DuperInner, DuperValue}, + ast::{ + DuperArray, DuperBytes, DuperIdentifier, DuperInner, DuperKey, DuperObject, DuperString, + DuperTuple, DuperValue, + }, parser::Rule, }; @@ -34,7 +37,7 @@ impl DuperBuilder { Rule::identifier => { let identifier = next.as_str(); next = duper_trunk.next().unwrap(); - Some(Cow::Borrowed(identifier)) + Some(DuperIdentifier(Cow::Borrowed(identifier))) } _ => None, }; @@ -58,12 +61,10 @@ impl DuperBuilder { }) } - fn build_object( - pair: Pair<'_, Rule>, - ) -> Result, DuperValue<'_>)>, Error> { + fn build_object(pair: Pair<'_, Rule>) -> Result, Error> { debug_assert!(matches!(pair.as_rule(), Rule::object)); let span = pair.as_span().clone(); - let kv_pairs: Result, DuperValue<'_>)>, _> = pair + let kv_pairs: Result, DuperValue<'_>)>, _> = pair .into_inner() .map(|pair| { let span = pair.as_span().clone(); @@ -87,13 +88,13 @@ impl DuperBuilder { } }; let value = Self::build_value(inner_pair.next().unwrap()); - value.map(|v| (key, v)) + value.map(|v| (DuperKey(key), v)) }) .collect(); let kv_pairs = kv_pairs?; - let unique_keys: HashSet<&Cow<'_, str>> = kv_pairs.iter().map(|(k, _)| k).collect(); + let unique_keys: HashSet<_> = kv_pairs.iter().map(|(k, _)| k).collect(); if unique_keys.len() == kv_pairs.len() { - Ok(kv_pairs) + Ok(DuperObject(kv_pairs)) } else { Err(Error::new_from_span( ErrorVariant::CustomError { @@ -104,11 +105,22 @@ impl DuperBuilder { } } - fn build_array(pair: Pair<'_, Rule>) -> Result>, Error> { - debug_assert!(matches!(pair.as_rule(), Rule::array | Rule::tuple)); - pair.into_inner() + fn build_array(pair: Pair<'_, Rule>) -> Result, Error> { + debug_assert!(matches!(pair.as_rule(), Rule::array)); + let vec: Result>, _> = pair + .into_inner() .map(|pair| Self::build_value(pair)) - .collect() + .collect(); + Ok(DuperArray(vec?)) + } + + fn build_tuple(pair: Pair<'_, Rule>) -> Result, Error> { + debug_assert!(matches!(pair.as_rule(), Rule::tuple)); + let vec: Result>, _> = pair + .into_inner() + .map(|pair| Self::build_value(pair)) + .collect(); + Ok(DuperTuple(vec?)) } fn build_value(pair: Pair<'_, Rule>) -> Result, Error> { @@ -119,7 +131,7 @@ impl DuperBuilder { Rule::identifier => { let identifier = next.as_str(); next = inner_pair.next().unwrap(); - Some(Cow::Borrowed(identifier)) + Some(DuperIdentifier(Cow::Borrowed(identifier))) } _ => None, }; @@ -128,21 +140,21 @@ impl DuperBuilder { inner: match next.as_rule() { Rule::object => DuperInner::Object(Self::build_object(next)?), Rule::array => DuperInner::Array(Self::build_array(next)?), - Rule::tuple => DuperInner::Tuple(Self::build_array(next)?), - Rule::string => DuperInner::String(Self::unescape_str( + Rule::tuple => DuperInner::Tuple(Self::build_tuple(next)?), + Rule::string => DuperInner::String(DuperString(Self::unescape_str( next.into_inner().next().unwrap().as_str(), - )), - Rule::raw_string => { - DuperInner::String(Cow::Borrowed(next.into_inner().next().unwrap().as_str())) - } - Rule::bytes => DuperInner::Bytes(Cow::Owned( + ))), + Rule::raw_string => DuperInner::String(DuperString(Cow::Borrowed( + next.into_inner().next().unwrap().as_str(), + ))), + Rule::bytes => DuperInner::Bytes(DuperBytes(Cow::Owned( Self::unescape_str(next.into_inner().next().unwrap().as_str()) .as_bytes() .to_vec(), - )), - Rule::raw_bytes => DuperInner::Bytes(Cow::Borrowed( + ))), + Rule::raw_bytes => DuperInner::Bytes(DuperBytes(Cow::Borrowed( next.into_inner().next().unwrap().as_str().as_bytes(), - )), + ))), Rule::integer => DuperInner::Integer({ let integer_inner = next.into_inner().next().unwrap(); match integer_inner.as_rule() { diff --git a/duper/src/format.rs b/duper/src/format.rs index d6b4dfc..06fa49a 100644 --- a/duper/src/format.rs +++ b/duper/src/format.rs @@ -1,31 +1,40 @@ use json_escape::explicit::escape_str; use std::{ascii, borrow::Cow}; -use crate::types::DuperTypes; +use crate::{ + ast::{DuperBytes, DuperKey, DuperString}, + types::DuperTypes, +}; -pub(crate) fn format_key<'a>(key: &'a str) -> Cow<'a, str> { - if key.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { - Cow::Borrowed(key) +pub(crate) fn format_key<'a>(key: &'a DuperKey<'a>) -> Cow<'a, str> { + if key.0.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { + Cow::Borrowed(key.0.as_ref()) + } else if key.0.is_empty() { + Cow::Borrowed(r#""""#) } else { - format_string(key) + // TO-DO raw string heuristics + let escaped_key = Cow::from(escape_str(&key.0)).into_owned(); + Cow::Owned(format!(r#""{escaped_key}""#)) } } -pub(crate) fn format_string<'a>(string: &'a str) -> Cow<'a, str> { - if string.is_empty() { - return Cow::Borrowed(r#""""#); +pub(crate) fn format_string<'a>(string: &'a DuperString<'a>) -> Cow<'a, str> { + if string.0.is_empty() { + Cow::Borrowed(r#""""#) + } else { + // TO-DO raw string heuristics + let escaped_key = Cow::from(escape_str(&string.0)).into_owned(); + Cow::Owned(format!(r#""{escaped_key}""#)) } - // TO-DO raw string heuristics - let escaped_key = Cow::from(escape_str(&string)).into_owned(); - Cow::Owned(format!(r#""{escaped_key}""#)) } -pub(crate) fn format_bytes<'a>(bytes: &'a [u8]) -> Cow<'a, str> { - if bytes.is_empty() { +pub(crate) fn format_bytes<'a>(bytes: &'a DuperBytes<'a>) -> Cow<'a, str> { + if bytes.0.is_empty() { return Cow::Borrowed(r#"b"""#); } // TO-DO raw bytes heuristics let escaped_bytes: String = bytes + .0 .into_iter() .copied() .flat_map(ascii::escape_default) diff --git a/duper/src/lib.rs b/duper/src/lib.rs index 0a802d8..7c70511 100644 --- a/duper/src/lib.rs +++ b/duper/src/lib.rs @@ -7,7 +7,10 @@ mod serializer; pub mod types; pub mod visitor; -pub use ast::{DuperInner, DuperValue}; +pub use ast::{ + DuperArray, DuperBytes, DuperIdentifier, DuperInner, DuperKey, DuperObject, DuperString, + DuperTuple, DuperValue, +}; pub use pretty_printer::PrettyPrinter; pub use serializer::Serializer; diff --git a/duper/src/pretty_printer.rs b/duper/src/pretty_printer.rs index 182cbf6..ae045aa 100644 --- a/duper/src/pretty_printer.rs +++ b/duper/src/pretty_printer.rs @@ -1,7 +1,7 @@ -use std::borrow::Cow; - use crate::{ - ast::DuperValue, + ast::{ + DuperArray, DuperBytes, DuperIdentifier, DuperObject, DuperString, DuperTuple, DuperValue, + }, format::{ format_boolean, format_bytes, format_float, format_integer, format_key, format_string, }, @@ -17,7 +17,7 @@ impl PrettyPrinter { Self { indent: 0 } } - pub fn serialize<'a>(&mut self, value: DuperValue<'a>) -> String { + pub fn pretty_print<'a>(&mut self, value: DuperValue<'a>) -> String { value.accept(self) } @@ -39,8 +39,8 @@ impl DuperVisitor for PrettyPrinter { fn visit_object<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - object: &Vec<(Cow<'a, str>, DuperValue<'a>)>, + identifier: Option<&DuperIdentifier<'a>>, + object: &DuperObject<'a>, ) -> Self::Value { let mut string = String::new(); @@ -51,7 +51,7 @@ impl DuperVisitor for PrettyPrinter { } else { string.push_str("({\n"); self.increase_indentation(); - for (key, value) in object.into_iter() { + for (key, value) in object.iter() { string.push_str(&self.indentation()); string.push_str(&format_key(key)); string.push_str(": "); @@ -68,13 +68,15 @@ impl DuperVisitor for PrettyPrinter { } else { string.push_str("{\n"); self.increase_indentation(); - for (key, value) in object.into_iter() { + for (key, value) in object.iter() { + string.push_str(&self.indentation()); string.push_str(&format_key(key)); string.push_str(": "); string.push_str(&value.accept(self)); string.push_str(",\n"); } self.decrease_indentation(); + string.push_str(&self.indentation()); string.push('}'); } } @@ -84,8 +86,8 @@ impl DuperVisitor for PrettyPrinter { fn visit_array<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - array: &Vec>, + identifier: Option<&DuperIdentifier<'a>>, + array: &DuperArray<'a>, ) -> Self::Value { let mut string = String::new(); @@ -94,9 +96,9 @@ impl DuperVisitor for PrettyPrinter { if array.is_empty() { string.push_str("([])"); } else { - string.push_str("(["); + string.push_str("([\n"); self.increase_indentation(); - for value in array.into_iter() { + for value in array.iter() { string.push_str(&self.indentation()); string.push_str(&value.accept(self)); string.push_str(",\n"); @@ -109,9 +111,9 @@ impl DuperVisitor for PrettyPrinter { if array.is_empty() { string.push_str("[]"); } else { - string.push('['); + string.push_str("[\n"); self.increase_indentation(); - for value in array.into_iter() { + for value in array.iter() { string.push_str(&self.indentation()); string.push_str(&value.accept(self)); string.push_str(",\n"); @@ -127,8 +129,8 @@ impl DuperVisitor for PrettyPrinter { fn visit_tuple<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - tuple: &Vec>, + identifier: Option<&DuperIdentifier<'a>>, + tuple: &DuperTuple<'a>, ) -> Self::Value { let mut string = String::new(); @@ -141,9 +143,9 @@ impl DuperVisitor for PrettyPrinter { string.push_str(&tuple.get(0).unwrap().accept(self)); string.push_str(",))"); } else { - string.push_str("(("); + string.push_str("((\n"); self.increase_indentation(); - for value in tuple.into_iter() { + for value in tuple.iter() { string.push_str(&self.indentation()); string.push_str(&value.accept(self)); string.push_str(",\n"); @@ -160,14 +162,15 @@ impl DuperVisitor for PrettyPrinter { string.push_str(&tuple.get(0).unwrap().accept(self)); string.push_str(",)"); } else { - string.push('('); + string.push_str("(\n"); self.increase_indentation(); - for value in tuple.into_iter() { + for value in tuple.iter() { string.push_str(&self.indentation()); string.push_str(&value.accept(self)); string.push_str(",\n"); } self.decrease_indentation(); + string.push_str(&self.indentation()); string.push(')'); } } @@ -177,18 +180,19 @@ impl DuperVisitor for PrettyPrinter { fn visit_string<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - value: &Cow<'a, str>, + identifier: Option<&DuperIdentifier<'a>>, + value: &DuperString<'a>, ) -> Self::Value { if let Some(identifier) = identifier { let value = format_string(value); if value.len() + self.indent > 60 { let mut string = String::new(); - string.push_str(&identifier); + string.push_str(identifier.as_ref()); string.push_str("(\n"); self.increase_indentation(); string.push_str(&self.indentation()); string.push_str(&value); + string.push('\n'); self.decrease_indentation(); string.push_str(&self.indentation()); string.push_str(")"); @@ -203,14 +207,14 @@ impl DuperVisitor for PrettyPrinter { fn visit_bytes<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - bytes: &Cow<'a, [u8]>, + identifier: Option<&DuperIdentifier<'a>>, + bytes: &DuperBytes<'a>, ) -> Self::Value { if let Some(identifier) = identifier { let bytes = format_bytes(bytes); if bytes.len() + self.indent > 60 { let mut string = String::new(); - string.push_str(&identifier); + string.push_str(identifier.as_ref()); string.push_str("(\n"); self.increase_indentation(); string.push_str(&self.indentation()); @@ -227,16 +231,20 @@ impl DuperVisitor for PrettyPrinter { } } - fn visit_integer(&mut self, identifier: Option<&Cow<'_, str>>, integer: i64) -> Self::Value { + fn visit_integer( + &mut self, + identifier: Option<&DuperIdentifier<'_>>, + integer: i64, + ) -> Self::Value { if let Some(identifier) = identifier { - let value = format_integer(integer, identifier.as_ref().try_into().ok()); + let value = format_integer(integer, identifier.try_into().ok()); format!("{identifier}({value})") } else { format_integer(integer, None) } } - fn visit_float(&mut self, identifier: Option<&Cow<'_, str>>, float: f64) -> Self::Value { + fn visit_float(&mut self, identifier: Option<&DuperIdentifier<'_>>, float: f64) -> Self::Value { if let Some(identifier) = identifier { let value = format_float(float); format!("{identifier}({value})") @@ -245,7 +253,11 @@ impl DuperVisitor for PrettyPrinter { } } - fn visit_boolean(&mut self, identifier: Option<&Cow<'_, str>>, boolean: bool) -> Self::Value { + fn visit_boolean( + &mut self, + identifier: Option<&DuperIdentifier<'_>>, + boolean: bool, + ) -> Self::Value { if let Some(identifier) = identifier { let value = format_boolean(boolean); format!("{identifier}({value})") @@ -254,7 +266,7 @@ impl DuperVisitor for PrettyPrinter { } } - fn visit_null(&mut self, identifier: Option<&Cow<'_, str>>) -> Self::Value { + fn visit_null(&mut self, identifier: Option<&DuperIdentifier<'_>>) -> Self::Value { if let Some(identifier) = identifier { format!("{identifier}(null)") } else { @@ -262,3 +274,211 @@ impl DuperVisitor for PrettyPrinter { } } } + +#[cfg(test)] +mod pretty_printer_tests { + use std::borrow::Cow; + + use insta::assert_snapshot; + + use crate::{ + DuperArray, DuperBytes, DuperIdentifier, DuperInner, DuperKey, DuperObject, DuperString, + DuperTuple, DuperValue, PrettyPrinter, parser::DuperParser, + }; + + #[test] + fn empty_object() { + let value = DuperValue { + identifier: None, + inner: DuperInner::Object(DuperObject(vec![])), + }; + let pp = PrettyPrinter::new().pretty_print(value); + assert_snapshot!(pp); + let _ = DuperParser::parse_duper(&pp).unwrap(); + } + + #[test] + fn empty_array() { + let value = DuperValue { + identifier: None, + inner: DuperInner::Array(DuperArray(vec![])), + }; + let pp = PrettyPrinter::new().pretty_print(value); + assert_snapshot!(pp); + let _ = DuperParser::parse_duper(&pp).unwrap(); + } + + #[test] + fn single_element_object() { + let value = DuperValue { + identifier: None, + inner: DuperInner::Object(DuperObject(vec![( + DuperKey::from(Cow::Borrowed("chess")), + DuperValue { + identifier: None, + inner: DuperInner::String(DuperString::from(Cow::Borrowed("✅"))), + }, + )])), + }; + let pp = PrettyPrinter::new().pretty_print(value); + assert_snapshot!(pp); + let _ = DuperParser::parse_duper(&pp).unwrap(); + } + + #[test] + fn single_element_array() { + let value = DuperValue { + identifier: None, + inner: DuperInner::Array(DuperArray(vec![DuperValue { + identifier: None, + inner: DuperInner::Integer(42), + }])), + }; + let pp = PrettyPrinter::new().pretty_print(value); + assert_snapshot!(pp); + let _ = DuperParser::parse_duper(&pp).unwrap(); + } + + #[test] + fn basic_object() { + let value = DuperValue { + identifier: None, + inner: DuperInner::Object(DuperObject(vec![ + ( + DuperKey::from(Cow::Borrowed("zero")), + DuperValue { + identifier: None, + inner: DuperInner::Tuple(DuperTuple::from(vec![])), + }, + ), + ( + DuperKey::from(Cow::Borrowed("one")), + DuperValue { + identifier: None, + inner: DuperInner::Tuple(DuperTuple::from(vec![DuperValue { + identifier: None, + inner: DuperInner::String(DuperString::from(Cow::Borrowed("Sandhole"))), + }])), + }, + ), + ( + DuperKey::from(Cow::Borrowed("two")), + DuperValue { + identifier: None, + inner: DuperInner::Tuple(DuperTuple::from(vec![ + DuperValue { + identifier: None, + inner: DuperInner::String(DuperString::from(Cow::Borrowed("rust"))), + }, + DuperValue { + identifier: None, + inner: DuperInner::String(DuperString::from(Cow::Borrowed("pest"))), + }, + ])), + }, + ), + ])), + }; + let pp = PrettyPrinter::new().pretty_print(value); + assert_snapshot!(pp); + let _ = DuperParser::parse_duper(&pp).unwrap(); + } + + #[test] + fn basic_array() { + let value = DuperValue { + identifier: None, + inner: DuperInner::Array(DuperArray(vec![ + DuperValue { + identifier: None, + inner: DuperInner::Bytes(DuperBytes::from(Cow::Borrowed(b"foobar".as_ref()))), + }, + DuperValue { + identifier: None, + inner: DuperInner::Null, + }, + DuperValue { + identifier: None, + inner: DuperInner::Boolean(false), + }, + ])), + }; + let pp = PrettyPrinter::new().pretty_print(value); + assert_snapshot!(pp); + let _ = DuperParser::parse_duper(&pp).unwrap(); + } + + #[test] + fn complex_object() { + let value = DuperValue { + identifier: Some(DuperIdentifier::from(Cow::Borrowed("X-Start"))), + inner: DuperInner::Object(DuperObject(vec![( + DuperKey::from(Cow::Borrowed("first object")), + DuperValue { + identifier: None, + inner: DuperInner::Object(DuperObject(vec![( + DuperKey::from(Cow::Borrowed("second_object")), + DuperValue { + identifier: None, + inner: DuperInner::Object(DuperObject(vec![ + ( + DuperKey::from(Cow::Borrowed("third object")), + DuperValue { + identifier: Some(DuperIdentifier::from(Cow::Borrowed( + "X-Msg", + ))), + inner: DuperInner::String(DuperString::from( + Cow::Borrowed( + "This is a very long string that will push itself into the next line.", + ), + )), + }, + ), + ( + DuperKey::from(Cow::Borrowed("addendum")), + DuperValue { + identifier: None, + inner: DuperInner::Null, + }, + ), + ])), + }, + )])), + }, + )])), + }; + let pp = PrettyPrinter::new().pretty_print(value); + assert_snapshot!(pp); + let _ = DuperParser::parse_duper(&pp).unwrap(); + } + + #[test] + fn complex_array() { + let value = DuperValue { + identifier: None, + inner: DuperInner::Array(DuperArray(vec![ + DuperValue { + identifier: None, + inner: DuperInner::Array(DuperArray(vec![DuperValue { + identifier: None, + inner: DuperInner::Array(DuperArray(vec![DuperValue { + identifier: None, + inner: DuperInner::String(DuperString::from(Cow::Borrowed( + "So many arrays!", + ))), + }])), + }])), + }, + DuperValue { + identifier: None, + inner: DuperInner::String(DuperString::from(Cow::Borrowed( + r#""Hello world!""#, + ))), + }, + ])), + }; + let pp = PrettyPrinter::new().pretty_print(value); + assert_snapshot!(pp); + let _ = DuperParser::parse_duper(&pp).unwrap(); + } +} diff --git a/duper/src/serializer.rs b/duper/src/serializer.rs index e78479c..9f6232b 100644 --- a/duper/src/serializer.rs +++ b/duper/src/serializer.rs @@ -1,7 +1,7 @@ -use std::borrow::Cow; - use crate::{ - ast::DuperValue, + ast::{ + DuperArray, DuperBytes, DuperIdentifier, DuperObject, DuperString, DuperTuple, DuperValue, + }, format::{ format_boolean, format_bytes, format_float, format_integer, format_key, format_string, }, @@ -25,8 +25,8 @@ impl DuperVisitor for Serializer { fn visit_object<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - object: &Vec<(Cow<'a, str>, DuperValue<'a>)>, + identifier: Option<&DuperIdentifier<'a>>, + object: &DuperObject<'a>, ) -> Self::Value { let mut string = String::new(); let len = object.len(); @@ -34,7 +34,7 @@ impl DuperVisitor for Serializer { if let Some(identifier) = identifier { string.push_str(identifier.as_ref()); string.push_str("({"); - for (i, (key, value)) in object.into_iter().enumerate() { + for (i, (key, value)) in object.iter().enumerate() { string.push_str(&format_key(key)); string.push_str(": "); string.push_str(&value.accept(self)); @@ -45,7 +45,7 @@ impl DuperVisitor for Serializer { string.push_str("})"); } else { string.push('{'); - for (i, (key, value)) in object.into_iter().enumerate() { + for (i, (key, value)) in object.iter().enumerate() { string.push_str(&format_key(key)); string.push_str(": "); string.push_str(&value.accept(self)); @@ -61,8 +61,8 @@ impl DuperVisitor for Serializer { fn visit_array<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - array: &Vec>, + identifier: Option<&DuperIdentifier<'a>>, + array: &DuperArray<'a>, ) -> Self::Value { let mut string = String::new(); let len = array.len(); @@ -70,7 +70,7 @@ impl DuperVisitor for Serializer { if let Some(identifier) = identifier { string.push_str(identifier.as_ref()); string.push_str("(["); - for (i, value) in array.into_iter().enumerate() { + for (i, value) in array.iter().enumerate() { string.push_str(&value.accept(self)); if i < len - 1 { string.push_str(", "); @@ -79,7 +79,7 @@ impl DuperVisitor for Serializer { string.push_str("])"); } else { string.push('['); - for (i, value) in array.into_iter().enumerate() { + for (i, value) in array.iter().enumerate() { string.push_str(&value.accept(self)); if i < len - 1 { string.push_str(", "); @@ -93,8 +93,8 @@ impl DuperVisitor for Serializer { fn visit_tuple<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - tuple: &Vec>, + identifier: Option<&DuperIdentifier<'a>>, + tuple: &DuperTuple<'a>, ) -> Self::Value { let mut string = String::new(); let len = tuple.len(); @@ -102,7 +102,7 @@ impl DuperVisitor for Serializer { if let Some(identifier) = identifier { string.push_str(identifier.as_ref()); string.push_str("(("); - for (i, value) in tuple.into_iter().enumerate() { + for (i, value) in tuple.iter().enumerate() { string.push_str(&value.accept(self)); if i < len - 1 { string.push_str(", "); @@ -114,7 +114,7 @@ impl DuperVisitor for Serializer { string.push_str("))"); } else { string.push('('); - for (i, value) in tuple.into_iter().enumerate() { + for (i, value) in tuple.iter().enumerate() { string.push_str(&value.accept(self)); if i < len - 1 { string.push_str(", "); @@ -131,8 +131,8 @@ impl DuperVisitor for Serializer { fn visit_string<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - value: &Cow<'a, str>, + identifier: Option<&DuperIdentifier<'a>>, + value: &DuperString<'a>, ) -> Self::Value { if let Some(identifier) = identifier { let value = format_string(value); @@ -144,8 +144,8 @@ impl DuperVisitor for Serializer { fn visit_bytes<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - bytes: &Cow<'a, [u8]>, + identifier: Option<&DuperIdentifier<'a>>, + bytes: &DuperBytes<'a>, ) -> Self::Value { if let Some(identifier) = identifier { let bytes = format_bytes(bytes); @@ -155,16 +155,20 @@ impl DuperVisitor for Serializer { } } - fn visit_integer(&mut self, identifier: Option<&Cow<'_, str>>, integer: i64) -> Self::Value { + fn visit_integer( + &mut self, + identifier: Option<&DuperIdentifier<'_>>, + integer: i64, + ) -> Self::Value { if let Some(identifier) = identifier { - let value = format_integer(integer, identifier.as_ref().try_into().ok()); + let value = format_integer(integer, identifier.try_into().ok()); format!("{identifier}({value})") } else { format_integer(integer, None) } } - fn visit_float(&mut self, identifier: Option<&Cow<'_, str>>, float: f64) -> Self::Value { + fn visit_float(&mut self, identifier: Option<&DuperIdentifier<'_>>, float: f64) -> Self::Value { if let Some(identifier) = identifier { let value = format_float(float); format!("{identifier}({value})") @@ -173,7 +177,11 @@ impl DuperVisitor for Serializer { } } - fn visit_boolean(&mut self, identifier: Option<&Cow<'_, str>>, boolean: bool) -> Self::Value { + fn visit_boolean( + &mut self, + identifier: Option<&DuperIdentifier<'_>>, + boolean: bool, + ) -> Self::Value { if let Some(identifier) = identifier { let value = format_boolean(boolean); format!("{identifier}({value})") @@ -182,7 +190,7 @@ impl DuperVisitor for Serializer { } } - fn visit_null(&mut self, identifier: Option<&Cow<'_, str>>) -> Self::Value { + fn visit_null(&mut self, identifier: Option<&DuperIdentifier<'_>>) -> Self::Value { if let Some(identifier) = identifier { format!("{identifier}(null)") } else { diff --git a/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__basic_array.snap b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__basic_array.snap new file mode 100644 index 0000000..d92f891 --- /dev/null +++ b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__basic_array.snap @@ -0,0 +1,9 @@ +--- +source: duper/src/pretty_printer.rs +expression: pp +--- +[ + b"foobar", + null, + false, +] diff --git a/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__basic_object.snap b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__basic_object.snap new file mode 100644 index 0000000..1ed40bb --- /dev/null +++ b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__basic_object.snap @@ -0,0 +1,12 @@ +--- +source: duper/src/pretty_printer.rs +expression: pp +--- +{ + zero: (,), + one: ("Sandhole",), + two: ( + "rust", + "pest", + ), +} diff --git a/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__complex_array.snap b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__complex_array.snap new file mode 100644 index 0000000..b8f10b6 --- /dev/null +++ b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__complex_array.snap @@ -0,0 +1,12 @@ +--- +source: duper/src/pretty_printer.rs +expression: pp +--- +[ + [ + [ + "So many arrays!", + ], + ], + "\"Hello world!\"", +] diff --git a/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__complex_object.snap b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__complex_object.snap new file mode 100644 index 0000000..76aea4d --- /dev/null +++ b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__complex_object.snap @@ -0,0 +1,14 @@ +--- +source: duper/src/pretty_printer.rs +expression: pp +--- +X-Start({ + "first object": { + second_object: { + "third object": X-Msg( + "This is a very long string that will push itself into the next line." + ), + addendum: null, + }, + }, +}) diff --git a/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__empty_array.snap b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__empty_array.snap new file mode 100644 index 0000000..0347f00 --- /dev/null +++ b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__empty_array.snap @@ -0,0 +1,5 @@ +--- +source: duper/src/pretty_printer.rs +expression: pp +--- +[] diff --git a/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__empty_object.snap b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__empty_object.snap new file mode 100644 index 0000000..97fc07e --- /dev/null +++ b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__empty_object.snap @@ -0,0 +1,5 @@ +--- +source: duper/src/pretty_printer.rs +expression: pp +--- +{} diff --git a/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__single_element_array.snap b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__single_element_array.snap new file mode 100644 index 0000000..561938e --- /dev/null +++ b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__single_element_array.snap @@ -0,0 +1,7 @@ +--- +source: duper/src/pretty_printer.rs +expression: pp +--- +[ + 42, +] diff --git a/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__single_element_object.snap b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__single_element_object.snap new file mode 100644 index 0000000..bf30fb7 --- /dev/null +++ b/duper/src/snapshots/duper__pretty_printer__pretty_printer_tests__single_element_object.snap @@ -0,0 +1,7 @@ +--- +source: duper/src/pretty_printer.rs +expression: pp +--- +{ + chess: "✅", +} diff --git a/duper/src/types.rs b/duper/src/types.rs index 810f368..213628b 100644 --- a/duper/src/types.rs +++ b/duper/src/types.rs @@ -1,21 +1,55 @@ +use crate::ast::DuperIdentifier; + #[non_exhaustive] pub enum DuperTypes { + // Integer types DecInteger, HexInteger, OctInteger, BinInteger, + // String types + Uuid, + Uuid4, + Url, + IsoDatetime, + IsoDate, + IsoTime, + Ip, + Ipv4, + Ipv6, + Cidr, + Cidrv4, + Cidrv6, + Regex, + Base64, + Jwt, } -impl TryFrom<&str> for DuperTypes { - type Error = (); +impl TryFrom<&DuperIdentifier<'_>> for DuperTypes { + type Error = String; - fn try_from(value: &str) -> Result { - match value { + fn try_from(value: &DuperIdentifier<'_>) -> Result { + match value.0.as_ref() { "Integer" | "DecInteger" => Ok(DuperTypes::DecInteger), "HexInteger" => Ok(DuperTypes::HexInteger), "OctInteger" => Ok(DuperTypes::OctInteger), "BinInteger" => Ok(DuperTypes::BinInteger), - _ => Err(()), + "Uuid" => Ok(DuperTypes::Uuid), + "Uuid4" => Ok(DuperTypes::Uuid4), + "Url" => Ok(DuperTypes::Url), + "Datetime" | "IsoDatetime" => Ok(DuperTypes::IsoDatetime), + "Date" | "IsoDate" => Ok(DuperTypes::IsoDate), + "Time" | "IsoTime" => Ok(DuperTypes::IsoTime), + "Ip" => Ok(DuperTypes::Ip), + "Ipv4" => Ok(DuperTypes::Ipv4), + "Ipv6" => Ok(DuperTypes::Ipv6), + "Cidr" => Ok(DuperTypes::Cidr), + "Cidrv4" => Ok(DuperTypes::Cidrv4), + "Cidrv6" => Ok(DuperTypes::Cidrv6), + "Regex" => Ok(DuperTypes::Regex), + "Base64" => Ok(DuperTypes::Base64), + "Jwt" => Ok(DuperTypes::Jwt), + _ => Err(format!("Unknown type {}", value.0.as_ref())), } } } diff --git a/duper/src/visitor.rs b/duper/src/visitor.rs index 7b0cc75..97f36ed 100644 --- a/duper/src/visitor.rs +++ b/duper/src/visitor.rs @@ -1,50 +1,55 @@ -use std::borrow::Cow; - -use crate::ast::DuperValue; +use crate::ast::{DuperArray, DuperBytes, DuperIdentifier, DuperObject, DuperString, DuperTuple}; pub trait DuperVisitor { type Value; fn visit_object<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - object: &Vec<(Cow<'a, str>, DuperValue<'a>)>, + identifier: Option<&DuperIdentifier<'a>>, + object: &DuperObject<'a>, ) -> Self::Value; fn visit_array<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - array: &Vec>, + identifier: Option<&DuperIdentifier<'a>>, + array: &DuperArray<'a>, ) -> Self::Value; fn visit_tuple<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - tuple: &Vec>, + identifier: Option<&DuperIdentifier<'a>>, + tuple: &DuperTuple<'a>, ) -> Self::Value; fn visit_string<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - string: &Cow<'a, str>, + identifier: Option<&DuperIdentifier<'a>>, + string: &DuperString<'a>, ) -> Self::Value; fn visit_bytes<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, - bytes: &Cow<'a, [u8]>, + identifier: Option<&DuperIdentifier<'a>>, + bytes: &DuperBytes<'a>, ) -> Self::Value; - fn visit_integer<'a>(&mut self, identifier: Option<&Cow<'a, str>>, integer: i64) - -> Self::Value; + fn visit_integer<'a>( + &mut self, + identifier: Option<&DuperIdentifier<'a>>, + integer: i64, + ) -> Self::Value; - fn visit_float<'a>(&mut self, identifier: Option<&Cow<'a, str>>, float: f64) -> Self::Value; + fn visit_float<'a>( + &mut self, + identifier: Option<&DuperIdentifier<'a>>, + float: f64, + ) -> Self::Value; fn visit_boolean<'a>( &mut self, - identifier: Option<&Cow<'a, str>>, + identifier: Option<&DuperIdentifier<'a>>, boolean: bool, ) -> Self::Value; - fn visit_null<'a>(&mut self, identifier: Option<&Cow<'a, str>>) -> Self::Value; + fn visit_null<'a>(&mut self, identifier: Option<&DuperIdentifier<'a>>) -> Self::Value; } diff --git a/serde_duper/src/deserializer.rs b/serde_duper/src/deserializer.rs index f2988f7..6e5a59d 100644 --- a/serde_duper/src/deserializer.rs +++ b/serde_duper/src/deserializer.rs @@ -1,5 +1,4 @@ -use std::borrow::Cow; - +use duper::DuperKey; use duper::{DuperInner, DuperValue, parser::DuperParser}; use serde_core::Deserialize; use serde_core::de::{self, DeserializeSeed, IntoDeserializer, Visitor}; @@ -53,31 +52,31 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { inner: DuperInner::Object(object), .. }) => { - let map = MapDeserializer::new(object); + let map = MapDeserializer::new(object.into_inner()); visitor.visit_map(map) } Some(DuperValue { inner: DuperInner::Array(array), .. }) => { - let seq = SequenceDeserializer::new(array); + let seq = SequenceDeserializer::new(array.into_inner()); visitor.visit_seq(seq) } Some(DuperValue { inner: DuperInner::Tuple(tuple), .. }) => { - let seq = SequenceDeserializer::new(tuple); + let seq = SequenceDeserializer::new(tuple.into_inner()); visitor.visit_seq(seq) } Some(DuperValue { inner: DuperInner::String(string), .. - }) => visitor.visit_str(&string), + }) => visitor.visit_str(string.as_ref()), Some(DuperValue { inner: DuperInner::Bytes(bytes), .. - }) => visitor.visit_bytes(&bytes), + }) => visitor.visit_bytes(bytes.as_ref()), Some(DuperValue { inner: DuperInner::Integer(integer), .. @@ -221,11 +220,12 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { Some(DuperValue { inner: DuperInner::String(string), .. - }) => visitor.visit_enum(string.into_deserializer()), + }) => visitor.visit_enum(string.as_ref().into_deserializer()), Some(DuperValue { - inner: DuperInner::Object(mut object), + inner: DuperInner::Object(object), .. }) if object.len() == 1 => { + let mut object = object.into_inner(); let pair = object.remove(0); visitor.visit_enum(EnumDeserializer { variant: pair.0, @@ -380,12 +380,12 @@ impl<'de> de::SeqAccess<'de> for SequenceDeserializer<'de> { } struct MapDeserializer<'de> { - iter: std::vec::IntoIter<(Cow<'de, str>, DuperValue<'de>)>, + iter: std::vec::IntoIter<(DuperKey<'de>, DuperValue<'de>)>, value: Option>, } impl<'de> MapDeserializer<'de> { - fn new(vec: Vec<(Cow<'de, str>, DuperValue<'de>)>) -> Self { + fn new(vec: Vec<(DuperKey<'de>, DuperValue<'de>)>) -> Self { Self { iter: vec.into_iter(), value: None, @@ -403,7 +403,7 @@ impl<'de> de::MapAccess<'de> for MapDeserializer<'de> { match self.iter.next() { Some((key, value)) => { self.value = Some(value); - seed.deserialize(key.into_deserializer()).map(Some) + seed.deserialize(key.as_ref().into_deserializer()).map(Some) } None => Ok(None), } @@ -421,7 +421,7 @@ impl<'de> de::MapAccess<'de> for MapDeserializer<'de> { } struct EnumDeserializer<'de> { - variant: Cow<'de, str>, + variant: DuperKey<'de>, value: DuperValue<'de>, } @@ -433,7 +433,7 @@ impl<'de> de::EnumAccess<'de> for EnumDeserializer<'de> { where V: DeserializeSeed<'de>, { - let variant = seed.deserialize(self.variant.into_deserializer())?; + let variant = seed.deserialize(self.variant.as_ref().into_deserializer())?; Ok(( variant, VariantDeserializer { @@ -474,7 +474,7 @@ impl<'de> de::VariantAccess<'de> for VariantDeserializer<'de> { { match self.value.map(|value| value.inner) { Some(DuperInner::Array(vec)) => { - let seq = SequenceDeserializer::new(vec); + let seq = SequenceDeserializer::new(vec.into_inner()); visitor.visit_seq(seq) } Some(_) => Err(de::Error::custom("expected array for tuple variant")), @@ -492,7 +492,7 @@ impl<'de> de::VariantAccess<'de> for VariantDeserializer<'de> { { match self.value.map(|value| value.inner) { Some(DuperInner::Object(obj)) => { - let map = MapDeserializer::new(obj); + let map = MapDeserializer::new(obj.into_inner()); visitor.visit_map(map) } Some(_) => Err(de::Error::custom("expected object for struct variant")), diff --git a/serde_duper/src/serializer.rs b/serde_duper/src/serializer.rs index 39ba7d5..43148e1 100644 --- a/serde_duper/src/serializer.rs +++ b/serde_duper/src/serializer.rs @@ -1,6 +1,9 @@ use std::{borrow::Cow, marker::PhantomData}; -use duper::{DuperInner, DuperValue, Serializer as DuperSerializer}; +use duper::{ + DuperArray, DuperBytes, DuperIdentifier, DuperInner, DuperKey, DuperObject, DuperString, + DuperTuple, DuperValue, Serializer as DuperSerializer, +}; use serde_core::{Serialize, ser}; use crate::Error; @@ -65,21 +68,21 @@ pub struct SerializeTupleVariant<'a, 'b> { pub struct SerializeMap<'a, 'b> { serializer: &'a mut Serializer<'b>, - entries: Vec<(Cow<'b, str>, DuperValue<'b>)>, - next_key: Option>, + entries: Vec<(DuperKey<'b>, DuperValue<'b>)>, + next_key: Option>, } pub struct SerializeStruct<'a, 'b> { serializer: &'a mut Serializer<'b>, name: &'static str, - fields: Vec<(Cow<'b, str>, DuperValue<'b>)>, + fields: Vec<(DuperKey<'b>, DuperValue<'b>)>, } pub struct SerializeStructVariant<'a, 'b> { serializer: &'a mut Serializer<'b>, name: &'static str, variant: &'static str, - fields: Vec<(Cow<'b, str>, DuperValue<'b>)>, + fields: Vec<(DuperKey<'b>, DuperValue<'b>)>, } impl<'a, 'b> ser::Serializer for &'a mut Serializer<'b> { @@ -178,21 +181,21 @@ impl<'a, 'b> ser::Serializer for &'a mut Serializer<'b> { fn serialize_char(self, v: char) -> Result { Ok(DuperValue { identifier: None, - inner: DuperInner::String(Cow::Owned(v.into())), + inner: DuperInner::String(DuperString::from(Cow::Owned(v.into()))), }) } fn serialize_str(self, v: &str) -> Result { Ok(DuperValue { identifier: None, - inner: DuperInner::String(Cow::Owned(v.into())), + inner: DuperInner::String(DuperString::from(Cow::Owned(v.into()))), }) } fn serialize_bytes(self, v: &[u8]) -> Result { Ok(DuperValue { identifier: None, - inner: DuperInner::Bytes(Cow::Owned(v.into())), + inner: DuperInner::Bytes(DuperBytes::from(Cow::Owned(v.into()))), }) } @@ -219,7 +222,7 @@ impl<'a, 'b> ser::Serializer for &'a mut Serializer<'b> { fn serialize_unit_struct(self, name: &'static str) -> Result { Ok(DuperValue { - identifier: Some(Cow::Owned(format!("X-{name}"))), + identifier: Some(DuperIdentifier::from(Cow::Owned(format!("X-{name}")))), inner: DuperInner::Null, }) } @@ -231,8 +234,8 @@ impl<'a, 'b> ser::Serializer for &'a mut Serializer<'b> { variant: &'static str, ) -> Result { Ok(DuperValue { - identifier: Some(Cow::Owned(format!("X-{name}"))), - inner: DuperInner::String(Cow::Borrowed(variant)), + identifier: Some(DuperIdentifier::from(Cow::Owned(format!("X-{name}")))), + inner: DuperInner::String(DuperString::from(Cow::Borrowed(variant))), }) } @@ -246,7 +249,7 @@ impl<'a, 'b> ser::Serializer for &'a mut Serializer<'b> { { let value = value.serialize(self)?; Ok(DuperValue { - identifier: Some(Cow::Owned(format!("X-{name}"))), + identifier: Some(DuperIdentifier::from(Cow::Owned(format!("X-{name}")))), inner: value.inner, }) } @@ -263,7 +266,7 @@ impl<'a, 'b> ser::Serializer for &'a mut Serializer<'b> { { let value = value.serialize(self)?; Ok(DuperValue { - identifier: Some(Cow::Owned(format!("X-{name}"))), + identifier: Some(DuperIdentifier::from(Cow::Owned(format!("X-{name}")))), inner: value.inner, }) } @@ -361,7 +364,7 @@ impl<'a, 'b> ser::SerializeSeq for SerializeSeq<'a, 'b> { fn end(self) -> Result { Ok(DuperValue { identifier: None, - inner: DuperInner::Array(self.elements), + inner: DuperInner::Array(DuperArray::from(self.elements)), }) } } @@ -382,7 +385,7 @@ impl<'a, 'b> ser::SerializeTuple for SerializeTuple<'a, 'b> { fn end(self) -> Result { Ok(DuperValue { identifier: None, - inner: DuperInner::Tuple(self.elements), + inner: DuperInner::Tuple(DuperTuple::from(self.elements)), }) } } @@ -402,8 +405,11 @@ impl<'a, 'b> ser::SerializeTupleStruct for SerializeTupleStruct<'a, 'b> { fn end(self) -> Result { Ok(DuperValue { - identifier: Some(Cow::Owned(format!("X-{}", self.name))), - inner: DuperInner::Tuple(self.elements), + identifier: Some(DuperIdentifier::from(Cow::Owned(format!( + "X-{}", + self.name + )))), + inner: DuperInner::Tuple(DuperTuple::from(self.elements)), }) } } @@ -424,16 +430,19 @@ impl<'a, 'b> ser::SerializeTupleVariant for SerializeTupleVariant<'a, 'b> { fn end(self) -> Result { let mut fields = Vec::new(); fields.push(( - Cow::Borrowed(self.variant), + DuperKey::from(Cow::Borrowed(self.variant)), DuperValue { identifier: None, - inner: DuperInner::Tuple(self.elements), + inner: DuperInner::Tuple(DuperTuple::from(self.elements)), }, )); Ok(DuperValue { - identifier: Some(Cow::Owned(format!("X-{}", self.name))), - inner: DuperInner::Object(fields), + identifier: Some(DuperIdentifier::from(Cow::Owned(format!( + "X-{}", + self.name + )))), + inner: DuperInner::Object(DuperObject::from(fields)), }) } } @@ -449,7 +458,7 @@ impl<'a, 'b> ser::SerializeMap for SerializeMap<'a, 'b> { let key_value = key.serialize(&mut *self.serializer)?; match key_value.inner { DuperInner::String(s) => { - self.next_key = Some(s); + self.next_key = Some(DuperKey::from(s.into_inner())); Ok(()) } _ => Err(Error::invalid_value("map key must be a string")), @@ -474,7 +483,7 @@ impl<'a, 'b> ser::SerializeMap for SerializeMap<'a, 'b> { fn end(self) -> Result { Ok(DuperValue { identifier: None, - inner: DuperInner::Object(self.entries), + inner: DuperInner::Object(DuperObject::from(self.entries)), }) } } @@ -488,14 +497,18 @@ impl<'a, 'b> ser::SerializeStruct for SerializeStruct<'a, 'b> { T: ?Sized + Serialize, { let value = value.serialize(&mut *self.serializer)?; - self.fields.push((Cow::Borrowed(key), value)); + self.fields + .push((DuperKey::from(Cow::Borrowed(key)), value)); Ok(()) } fn end(self) -> Result { Ok(DuperValue { - identifier: Some(Cow::Owned(format!("X-{}", self.name))), - inner: DuperInner::Object(self.fields), + identifier: Some(DuperIdentifier::from(Cow::Owned(format!( + "X-{}", + self.name + )))), + inner: DuperInner::Object(DuperObject::from(self.fields)), }) } } @@ -509,23 +522,27 @@ impl<'a, 'b> ser::SerializeStructVariant for SerializeStructVariant<'a, 'b> { T: ?Sized + Serialize, { let value = value.serialize(&mut *self.serializer)?; - self.fields.push((Cow::Borrowed(key), value)); + self.fields + .push((DuperKey::from(Cow::Borrowed(key)), value)); Ok(()) } fn end(self) -> Result { let mut variant_obj = Vec::new(); variant_obj.push(( - Cow::Borrowed(self.variant), + DuperKey::from(Cow::Borrowed(self.variant)), DuperValue { identifier: None, - inner: DuperInner::Object(self.fields), + inner: DuperInner::Object(DuperObject::from(self.fields)), }, )); Ok(DuperValue { - identifier: Some(Cow::Owned(format!("X-{}", self.name))), - inner: DuperInner::Object(variant_obj), + identifier: Some(DuperIdentifier::from(Cow::Owned(format!( + "X-{}", + self.name + )))), + inner: DuperInner::Object(DuperObject::from(variant_obj)), }) } } diff --git a/serde_duper/tests/struct.rs b/serde_duper/tests/struct.rs index 0cebbf1..37b2fb7 100644 --- a/serde_duper/tests/struct.rs +++ b/serde_duper/tests/struct.rs @@ -19,7 +19,7 @@ fn serialize() { "string": r#"Hello world!"#, bools: [true, true, false,], map: { - r#"quantum"#: X-Measurement((-7, null)), + r#"quantum"#: X-Measurement((-7, "whatever")), }, } "##,