From 8a2da95223b66102fa6bd5c306974cad4de280b1 Mon Sep 17 00:00:00 2001 From: Eric Rodrigues Pires Date: Sat, 11 Oct 2025 14:40:47 -0300 Subject: [PATCH] Add pretty printer --- Cargo.lock | 1 + duper/src/ast.rs | 26 ++-- duper/src/builder.rs | 76 +++++++-- duper/src/format.rs | 10 +- duper/src/grammar.pest | 4 +- duper/src/lib.rs | 17 +- duper/src/pretty_printer.rs | 264 ++++++++++++++++++++++++++++++++ duper/src/serializer.rs | 38 ++--- duper/src/visitor.rs | 34 ++-- serde_duper/Cargo.toml | 1 + serde_duper/src/deserializer.rs | 18 +-- serde_duper/src/error.rs | 30 ++-- serde_duper/src/serializer.rs | 10 +- 13 files changed, 430 insertions(+), 99 deletions(-) create mode 100644 duper/src/pretty_printer.rs diff --git a/Cargo.lock b/Cargo.lock index 5e48fe7..c1614a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -182,6 +182,7 @@ name = "serde_duper" version = "0.1.0" dependencies = [ "duper", + "pest", "serde", "serde_core", ] diff --git a/duper/src/ast.rs b/duper/src/ast.rs index d9e88e4..a0d33b4 100644 --- a/duper/src/ast.rs +++ b/duper/src/ast.rs @@ -22,17 +22,21 @@ pub enum DuperInner<'a> { } impl<'a> DuperValue<'a> { - pub fn accept<'v, V: DuperVisitor>(self, visitor: &'v mut V) -> V::Value { - match self.inner { - DuperInner::Object(object) => visitor.visit_object(self.identifier, object), - DuperInner::Array(array) => visitor.visit_array(self.identifier, array), - DuperInner::Tuple(tuple) => visitor.visit_tuple(self.identifier, tuple), - DuperInner::String(string) => visitor.visit_string(self.identifier, string), - DuperInner::Bytes(bytes) => visitor.visit_bytes(self.identifier, bytes), - DuperInner::Integer(integer) => visitor.visit_integer(self.identifier, integer), - DuperInner::Float(float) => visitor.visit_float(self.identifier, float), - DuperInner::Boolean(boolean) => visitor.visit_boolean(self.identifier, boolean), - DuperInner::Null => visitor.visit_null(self.identifier), + pub fn accept<'v, V: DuperVisitor>(&self, visitor: &'v mut V) -> V::Value { + match &self.inner { + DuperInner::Object(object) => visitor.visit_object(self.identifier.as_ref(), object), + DuperInner::Array(array) => visitor.visit_array(self.identifier.as_ref(), array), + DuperInner::Tuple(tuple) => visitor.visit_tuple(self.identifier.as_ref(), tuple), + DuperInner::String(string) => visitor.visit_string(self.identifier.as_ref(), string), + DuperInner::Bytes(bytes) => visitor.visit_bytes(self.identifier.as_ref(), bytes), + DuperInner::Integer(integer) => { + visitor.visit_integer(self.identifier.as_ref(), *integer) + } + DuperInner::Float(float) => visitor.visit_float(self.identifier.as_ref(), *float), + DuperInner::Boolean(boolean) => { + visitor.visit_boolean(self.identifier.as_ref(), *boolean) + } + DuperInner::Null => visitor.visit_null(self.identifier.as_ref()), } } } diff --git a/duper/src/builder.rs b/duper/src/builder.rs index 628f9a2..be135a9 100644 --- a/duper/src/builder.rs +++ b/duper/src/builder.rs @@ -1,7 +1,10 @@ -use std::borrow::Cow; +use std::{borrow::Cow, collections::HashSet}; use json_escape::explicit::unescape; -use pest::{error::Error, iterators::Pair}; +use pest::{ + error::{Error, ErrorVariant}, + iterators::Pair, +}; use crate::{ ast::{DuperInner, DuperValue}, @@ -24,6 +27,7 @@ impl DuperBuilder { } fn build_duper_trunk(pair: Pair<'_, Rule>) -> Result, Error> { + let span = pair.as_span().clone(); let mut duper_trunk = pair.into_inner(); let mut next = duper_trunk.next().unwrap(); let identifier = match next.as_rule() { @@ -34,25 +38,35 @@ impl DuperBuilder { } _ => None, }; - match next.as_rule() { - Rule::object => Ok(DuperValue { + Ok(match next.as_rule() { + Rule::object => DuperValue { identifier, inner: DuperInner::Object(Self::build_object(next)?), - }), - Rule::array => Ok(DuperValue { + }, + Rule::array => DuperValue { identifier, inner: DuperInner::Array(Self::build_array(next)?), - }), - _ => unreachable!(), - } + }, + rule => { + return Err(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("unexpected rule in trunk {rule:?}"), + }, + span, + )); + } + }) } fn build_object( pair: Pair<'_, Rule>, ) -> Result, DuperValue<'_>)>, Error> { debug_assert!(matches!(pair.as_rule(), Rule::object)); - pair.into_inner() + let span = pair.as_span().clone(); + let kv_pairs: Result, DuperValue<'_>)>, _> = pair + .into_inner() .map(|pair| { + let span = pair.as_span().clone(); let mut inner_pair = pair.into_inner(); let key_pair = inner_pair.next().unwrap(); let key = match key_pair.as_rule() { @@ -63,12 +77,31 @@ impl DuperBuilder { Cow::Borrowed(key_pair.into_inner().next().unwrap().as_str()) } Rule::plain_key => Cow::Borrowed(key_pair.as_str()), - _ => unreachable!(), + rule => { + return Err(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("unexpected rule in object key {rule:?}"), + }, + span, + )); + } }; let value = Self::build_value(inner_pair.next().unwrap()); value.map(|v| (key, v)) }) - .collect() + .collect(); + let kv_pairs = kv_pairs?; + let unique_keys: HashSet<&Cow<'_, str>> = kv_pairs.iter().map(|(k, _)| k).collect(); + if unique_keys.len() == kv_pairs.len() { + Ok(kv_pairs) + } else { + Err(Error::new_from_span( + ErrorVariant::CustomError { + message: "duplicate keys in object".into(), + }, + span, + )) + } } fn build_array(pair: Pair<'_, Rule>) -> Result>, Error> { @@ -79,6 +112,7 @@ impl DuperBuilder { } fn build_value(pair: Pair<'_, Rule>) -> Result, Error> { + let span = pair.as_span().clone(); let mut inner_pair = pair.into_inner(); let mut next = inner_pair.next().unwrap(); let identifier = match next.as_rule() { @@ -124,13 +158,27 @@ impl DuperBuilder { Rule::binary_integer => { i64::from_str_radix(integer_inner.as_str().split_at(2).1, 2).unwrap() } - _ => unreachable!(), + rule => { + return Err(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("unexpected rule in integer value {rule:?}"), + }, + span, + )); + } } }), Rule::float => DuperInner::Float(next.as_str().replace('_', "").parse().unwrap()), Rule::boolean => DuperInner::Boolean(next.as_str().parse().unwrap()), Rule::null => DuperInner::Null, - _ => unreachable!(), + rule => { + return Err(Error::new_from_span( + ErrorVariant::CustomError { + message: format!("unexpected rule in value {rule:?}"), + }, + span, + )); + } }, }) } diff --git a/duper/src/format.rs b/duper/src/format.rs index d477837..d6b4dfc 100644 --- a/duper/src/format.rs +++ b/duper/src/format.rs @@ -3,15 +3,15 @@ use std::{ascii, borrow::Cow}; use crate::types::DuperTypes; -pub(crate) fn format_key<'a>(key: Cow<'a, str>) -> Cow<'a, str> { +pub(crate) fn format_key<'a>(key: &'a str) -> Cow<'a, str> { if key.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { - key + Cow::Borrowed(key) } else { format_string(key) } } -pub(crate) fn format_string<'a>(string: Cow<'a, str>) -> Cow<'a, str> { +pub(crate) fn format_string<'a>(string: &'a str) -> Cow<'a, str> { if string.is_empty() { return Cow::Borrowed(r#""""#); } @@ -20,7 +20,7 @@ pub(crate) fn format_string<'a>(string: Cow<'a, str>) -> Cow<'a, str> { Cow::Owned(format!(r#""{escaped_key}""#)) } -pub(crate) fn format_bytes<'a>(bytes: Cow<'a, [u8]>) -> Cow<'a, str> { +pub(crate) fn format_bytes<'a>(bytes: &'a [u8]) -> Cow<'a, str> { if bytes.is_empty() { return Cow::Borrowed(r#"b"""#); } @@ -43,7 +43,7 @@ pub(crate) fn format_integer(integer: i64, typ: Option) -> String { } } -pub(crate) fn format_float(float: f64, _typ: Option) -> String { +pub(crate) fn format_float(float: f64) -> String { float.to_string() } diff --git a/duper/src/grammar.pest b/duper/src/grammar.pest index d7781f4..3bc14db 100644 --- a/duper/src/grammar.pest +++ b/duper/src/grammar.pest @@ -19,8 +19,8 @@ key = _{ string | raw_string | plain_key } plain_key = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* } tuple = { - "(" ~ "," ~ ")" | - "(" ~ described_value ~ ("," ~ described_value)* ~ ","? ~ ")" + "(" ~ described_value? ~ "," ~ ")" | + "(" ~ described_value ~ ("," ~ described_value)+ ~ ","? ~ ")" } array = { diff --git a/duper/src/lib.rs b/duper/src/lib.rs index 0e8669c..0a802d8 100644 --- a/duper/src/lib.rs +++ b/duper/src/lib.rs @@ -1,20 +1,19 @@ mod ast; mod builder; mod format; -mod parser; +pub mod parser; +mod pretty_printer; mod serializer; -mod types; -mod visitor; +pub mod types; +pub mod visitor; pub use ast::{DuperInner, DuperValue}; -pub use parser::DuperParser; -pub use serializer::DuperSerializer; -pub use types::DuperTypes; -pub use visitor::DuperVisitor; +pub use pretty_printer::PrettyPrinter; +pub use serializer::Serializer; #[cfg(test)] mod tests { - use crate::{ast::DuperValue, parser::DuperParser, serializer::DuperSerializer}; + use crate::{ast::DuperValue, parser::DuperParser, serializer::Serializer}; #[test] fn it_works() { @@ -24,6 +23,6 @@ mod tests { ) .unwrap(); println!("{:?}", duper); - println!("{}", DuperSerializer::new().serialize(duper)); + println!("{}", Serializer::new().serialize(duper)); } } diff --git a/duper/src/pretty_printer.rs b/duper/src/pretty_printer.rs new file mode 100644 index 0000000..182cbf6 --- /dev/null +++ b/duper/src/pretty_printer.rs @@ -0,0 +1,264 @@ +use std::borrow::Cow; + +use crate::{ + ast::DuperValue, + format::{ + format_boolean, format_bytes, format_float, format_integer, format_key, format_string, + }, + visitor::DuperVisitor, +}; + +pub struct PrettyPrinter { + indent: usize, +} + +impl PrettyPrinter { + pub fn new() -> Self { + Self { indent: 0 } + } + + pub fn serialize<'a>(&mut self, value: DuperValue<'a>) -> String { + value.accept(self) + } + + fn increase_indentation(&mut self) { + self.indent += 2 + } + + fn decrease_indentation(&mut self) { + self.indent -= 2 + } + + fn indentation(&self) -> String { + (0..self.indent).map(|_| ' ').collect() + } +} + +impl DuperVisitor for PrettyPrinter { + type Value = String; + + fn visit_object<'a>( + &mut self, + identifier: Option<&Cow<'a, str>>, + object: &Vec<(Cow<'a, str>, DuperValue<'a>)>, + ) -> Self::Value { + let mut string = String::new(); + + if let Some(identifier) = identifier { + string.push_str(identifier.as_ref()); + if object.is_empty() { + string.push_str("({})"); + } else { + string.push_str("({\n"); + self.increase_indentation(); + for (key, value) in object.into_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_str("})\n"); + } + } else { + if object.is_empty() { + string.push_str("{}"); + } else { + string.push_str("{\n"); + self.increase_indentation(); + for (key, value) in object.into_iter() { + string.push_str(&format_key(key)); + string.push_str(": "); + string.push_str(&value.accept(self)); + string.push_str(",\n"); + } + self.decrease_indentation(); + string.push('}'); + } + } + + string + } + + fn visit_array<'a>( + &mut self, + identifier: Option<&Cow<'a, str>>, + array: &Vec>, + ) -> Self::Value { + let mut string = String::new(); + + if let Some(identifier) = identifier { + string.push_str(identifier.as_ref()); + if array.is_empty() { + string.push_str("([])"); + } else { + string.push_str("(["); + self.increase_indentation(); + for value in array.into_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_str("])\n"); + } + } else { + if array.is_empty() { + string.push_str("[]"); + } else { + string.push('['); + self.increase_indentation(); + for value in array.into_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(']'); + } + } + + string + } + + fn visit_tuple<'a>( + &mut self, + identifier: Option<&Cow<'a, str>>, + tuple: &Vec>, + ) -> Self::Value { + let mut string = String::new(); + + if let Some(identifier) = identifier { + string.push_str(identifier.as_ref()); + if tuple.is_empty() { + string.push_str("((,))"); + } else if tuple.len() == 1 { + string.push_str("(("); + string.push_str(&tuple.get(0).unwrap().accept(self)); + string.push_str(",))"); + } else { + string.push_str("(("); + self.increase_indentation(); + for value in tuple.into_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_str("))"); + } + } else { + if tuple.is_empty() { + string.push_str("(,)"); + } else if tuple.len() == 1 { + string.push_str("("); + string.push_str(&tuple.get(0).unwrap().accept(self)); + string.push_str(",)"); + } else { + string.push('('); + self.increase_indentation(); + for value in tuple.into_iter() { + string.push_str(&self.indentation()); + string.push_str(&value.accept(self)); + string.push_str(",\n"); + } + self.decrease_indentation(); + string.push(')'); + } + } + + string + } + + fn visit_string<'a>( + &mut self, + identifier: Option<&Cow<'a, str>>, + value: &Cow<'a, str>, + ) -> 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("(\n"); + self.increase_indentation(); + string.push_str(&self.indentation()); + string.push_str(&value); + self.decrease_indentation(); + string.push_str(&self.indentation()); + string.push_str(")"); + string + } else { + format!("{identifier}({value})") + } + } else { + format_string(value).into_owned() + } + } + + fn visit_bytes<'a>( + &mut self, + identifier: Option<&Cow<'a, str>>, + bytes: &Cow<'a, [u8]>, + ) -> 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("(\n"); + self.increase_indentation(); + string.push_str(&self.indentation()); + string.push_str(&bytes); + self.decrease_indentation(); + string.push_str(&self.indentation()); + string.push_str(")"); + string + } else { + format!("{identifier}({bytes})") + } + } else { + format_bytes(bytes).into_owned() + } + } + + fn visit_integer(&mut self, identifier: Option<&Cow<'_, str>>, integer: i64) -> Self::Value { + if let Some(identifier) = identifier { + let value = format_integer(integer, identifier.as_ref().try_into().ok()); + format!("{identifier}({value})") + } else { + format_integer(integer, None) + } + } + + fn visit_float(&mut self, identifier: Option<&Cow<'_, str>>, float: f64) -> Self::Value { + if let Some(identifier) = identifier { + let value = format_float(float); + format!("{identifier}({value})") + } else { + format_float(float) + } + } + + fn visit_boolean(&mut self, identifier: Option<&Cow<'_, str>>, boolean: bool) -> Self::Value { + if let Some(identifier) = identifier { + let value = format_boolean(boolean); + format!("{identifier}({value})") + } else { + format_boolean(boolean) + } + } + + fn visit_null(&mut self, identifier: Option<&Cow<'_, str>>) -> Self::Value { + if let Some(identifier) = identifier { + format!("{identifier}(null)") + } else { + "null".into() + } + } +} diff --git a/duper/src/serializer.rs b/duper/src/serializer.rs index 66bb26d..e78479c 100644 --- a/duper/src/serializer.rs +++ b/duper/src/serializer.rs @@ -8,9 +8,9 @@ use crate::{ visitor::DuperVisitor, }; -pub struct DuperSerializer; +pub struct Serializer; -impl DuperSerializer { +impl Serializer { pub fn new() -> Self { Self } @@ -20,13 +20,13 @@ impl DuperSerializer { } } -impl DuperVisitor for DuperSerializer { +impl DuperVisitor for Serializer { type Value = String; fn visit_object<'a>( &mut self, - identifier: Option>, - object: Vec<(Cow<'a, str>, DuperValue<'a>)>, + identifier: Option<&Cow<'a, str>>, + object: &Vec<(Cow<'a, str>, DuperValue<'a>)>, ) -> Self::Value { let mut string = String::new(); let len = object.len(); @@ -61,8 +61,8 @@ impl DuperVisitor for DuperSerializer { fn visit_array<'a>( &mut self, - identifier: Option>, - array: Vec>, + identifier: Option<&Cow<'a, str>>, + array: &Vec>, ) -> Self::Value { let mut string = String::new(); let len = array.len(); @@ -93,8 +93,8 @@ impl DuperVisitor for DuperSerializer { fn visit_tuple<'a>( &mut self, - identifier: Option>, - tuple: Vec>, + identifier: Option<&Cow<'a, str>>, + tuple: &Vec>, ) -> Self::Value { let mut string = String::new(); let len = tuple.len(); @@ -131,8 +131,8 @@ impl DuperVisitor for DuperSerializer { fn visit_string<'a>( &mut self, - identifier: Option>, - value: Cow<'a, str>, + identifier: Option<&Cow<'a, str>>, + value: &Cow<'a, str>, ) -> Self::Value { if let Some(identifier) = identifier { let value = format_string(value); @@ -144,8 +144,8 @@ impl DuperVisitor for DuperSerializer { fn visit_bytes<'a>( &mut self, - identifier: Option>, - bytes: Cow<'a, [u8]>, + identifier: Option<&Cow<'a, str>>, + bytes: &Cow<'a, [u8]>, ) -> Self::Value { if let Some(identifier) = identifier { let bytes = format_bytes(bytes); @@ -155,7 +155,7 @@ impl DuperVisitor for DuperSerializer { } } - fn visit_integer(&mut self, identifier: Option>, integer: i64) -> Self::Value { + fn visit_integer(&mut self, identifier: Option<&Cow<'_, str>>, integer: i64) -> Self::Value { if let Some(identifier) = identifier { let value = format_integer(integer, identifier.as_ref().try_into().ok()); format!("{identifier}({value})") @@ -164,16 +164,16 @@ impl DuperVisitor for DuperSerializer { } } - fn visit_float(&mut self, identifier: Option>, float: f64) -> Self::Value { + fn visit_float(&mut self, identifier: Option<&Cow<'_, str>>, float: f64) -> Self::Value { if let Some(identifier) = identifier { - let value = format_float(float, identifier.as_ref().try_into().ok()); + let value = format_float(float); format!("{identifier}({value})") } else { - format_float(float, None) + format_float(float) } } - fn visit_boolean(&mut self, identifier: Option>, boolean: bool) -> Self::Value { + fn visit_boolean(&mut self, identifier: Option<&Cow<'_, str>>, boolean: bool) -> Self::Value { if let Some(identifier) = identifier { let value = format_boolean(boolean); format!("{identifier}({value})") @@ -182,7 +182,7 @@ impl DuperVisitor for DuperSerializer { } } - fn visit_null(&mut self, identifier: Option>) -> Self::Value { + fn visit_null(&mut self, identifier: Option<&Cow<'_, str>>) -> Self::Value { if let Some(identifier) = identifier { format!("{identifier}(null)") } else { diff --git a/duper/src/visitor.rs b/duper/src/visitor.rs index 4a42746..7b0cc75 100644 --- a/duper/src/visitor.rs +++ b/duper/src/visitor.rs @@ -7,40 +7,44 @@ pub trait DuperVisitor { fn visit_object<'a>( &mut self, - identifier: Option>, - object: Vec<(Cow<'a, str>, DuperValue<'a>)>, + identifier: Option<&Cow<'a, str>>, + object: &Vec<(Cow<'a, str>, DuperValue<'a>)>, ) -> Self::Value; fn visit_array<'a>( &mut self, - identifier: Option>, - array: Vec>, + identifier: Option<&Cow<'a, str>>, + array: &Vec>, ) -> Self::Value; fn visit_tuple<'a>( &mut self, - identifier: Option>, - tuple: Vec>, + identifier: Option<&Cow<'a, str>>, + tuple: &Vec>, ) -> Self::Value; fn visit_string<'a>( &mut self, - identifier: Option>, - string: Cow<'a, str>, + identifier: Option<&Cow<'a, str>>, + string: &Cow<'a, str>, ) -> Self::Value; fn visit_bytes<'a>( &mut self, - identifier: Option>, - bytes: Cow<'a, [u8]>, + identifier: Option<&Cow<'a, str>>, + bytes: &Cow<'a, [u8]>, ) -> Self::Value; - fn visit_integer<'a>(&mut self, identifier: Option>, integer: i64) -> Self::Value; + fn visit_integer<'a>(&mut self, identifier: Option<&Cow<'a, str>>, integer: i64) + -> Self::Value; - fn visit_float<'a>(&mut self, identifier: Option>, float: f64) -> Self::Value; + fn visit_float<'a>(&mut self, identifier: Option<&Cow<'a, str>>, float: f64) -> Self::Value; - fn visit_boolean<'a>(&mut self, identifier: Option>, boolean: bool) - -> Self::Value; + fn visit_boolean<'a>( + &mut self, + identifier: Option<&Cow<'a, str>>, + boolean: bool, + ) -> Self::Value; - fn visit_null<'a>(&mut self, identifier: Option>) -> Self::Value; + fn visit_null<'a>(&mut self, identifier: Option<&Cow<'a, str>>) -> Self::Value; } diff --git a/serde_duper/Cargo.toml b/serde_duper/Cargo.toml index eee4c0b..9ea07f7 100644 --- a/serde_duper/Cargo.toml +++ b/serde_duper/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] serde_core = "1.0.228" duper = { path = "../duper" } +pest = "2.8.3" [dev-dependencies] serde = { version = "1.0.228", features = ["derive"] } diff --git a/serde_duper/src/deserializer.rs b/serde_duper/src/deserializer.rs index 6b5e0ac..f2988f7 100644 --- a/serde_duper/src/deserializer.rs +++ b/serde_duper/src/deserializer.rs @@ -1,16 +1,18 @@ use std::borrow::Cow; -use duper::{DuperInner, DuperParser, DuperValue}; +use duper::{DuperInner, DuperValue, parser::DuperParser}; use serde_core::Deserialize; use serde_core::de::{self, DeserializeSeed, IntoDeserializer, Visitor}; +use crate::Error; + pub struct Deserializer<'de> { value: Option>, } impl<'de> Deserializer<'de> { - pub fn from_str(input: &'de str) -> Result { - let value = DuperParser::parse_duper(input).map_err(|e| format!("Parse error: {e:?}"))?; + pub fn from_str(input: &'de str) -> Result { + let value = DuperParser::parse_duper(input)?; Ok(Self { value: Some(value) }) } @@ -19,23 +21,21 @@ impl<'de> Deserializer<'de> { } } -pub fn from_str<'a, T>(input: &'a str) -> Result +pub fn from_str<'a, T>(input: &'a str) -> Result where T: Deserialize<'a>, { let mut deserializer = Deserializer::from_str(input)?; - let t = - T::deserialize(&mut deserializer).map_err(|e| format!("Deserialization error: {e:?}"))?; + let t = T::deserialize(&mut deserializer)?; Ok(t) } -pub fn from_value<'a, T>(value: DuperValue<'a>) -> Result +pub fn from_value<'a, T>(value: DuperValue<'a>) -> Result where T: Deserialize<'a>, { let mut deserializer = Deserializer::from_value(value); - let t = - T::deserialize(&mut deserializer).map_err(|e| format!("Deserialization error: {e:?}"))?; + let t = T::deserialize(&mut deserializer)?; Ok(t) } diff --git a/serde_duper/src/error.rs b/serde_duper/src/error.rs index 2dad98a..12315c4 100644 --- a/serde_duper/src/error.rs +++ b/serde_duper/src/error.rs @@ -2,10 +2,11 @@ use std::fmt::{self, Display}; #[derive(Debug, Clone)] pub enum ErrorKind { + ParseError(pest::error::Error), SerializationError, + DeserializationError(serde_core::de::value::Error), InvalidValue, - UnsupportedType, - Custom(String), + Custom, } #[derive(Debug, Clone)] @@ -30,7 +31,7 @@ impl Error { } pub fn custom(msg: impl Into + Clone) -> Self { - Self::new(ErrorKind::Custom(msg.clone().into()), msg) + Self::new(ErrorKind::Custom, msg) } pub fn serialization(msg: impl Into) -> Self { @@ -40,10 +41,6 @@ impl Error { pub fn invalid_value(msg: impl Into) -> Self { Self::new(ErrorKind::InvalidValue, msg) } - - pub fn unsupported_type(msg: impl Into) -> Self { - Self::new(ErrorKind::UnsupportedType, msg) - } } impl Display for Error { @@ -52,10 +49,11 @@ impl Display for Error { f, "{}: {}", match self.inner.kind { + ErrorKind::ParseError(_) => "ParseError", ErrorKind::SerializationError => "SerializationError", + ErrorKind::DeserializationError(_) => "DeserializationError", ErrorKind::InvalidValue => "InvalidValue", - ErrorKind::UnsupportedType => "UnsupportedType", - ErrorKind::Custom(_) => "Custom", + ErrorKind::Custom => "Custom", }, self.inner.message ) @@ -72,3 +70,17 @@ impl serde_core::ser::Error for Error { Self::custom(msg.to_string()) } } + +impl From for Error { + fn from(value: serde_core::de::value::Error) -> Self { + let message = value.to_string(); + Self::new(ErrorKind::DeserializationError(value), message) + } +} + +impl From> for Error { + fn from(value: pest::error::Error) -> Self { + let message = value.variant.message().into_owned(); + Self::new(ErrorKind::ParseError(value), message) + } +} diff --git a/serde_duper/src/serializer.rs b/serde_duper/src/serializer.rs index d9a1e0d..39ba7d5 100644 --- a/serde_duper/src/serializer.rs +++ b/serde_duper/src/serializer.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, marker::PhantomData}; -use duper::{DuperInner, DuperSerializer, DuperValue}; +use duper::{DuperInner, DuperValue, Serializer as DuperSerializer}; use serde_core::{Serialize, ser}; use crate::Error; @@ -219,7 +219,6 @@ impl<'a, 'b> ser::Serializer for &'a mut Serializer<'b> { fn serialize_unit_struct(self, name: &'static str) -> Result { Ok(DuperValue { - // TO-DO identifier: Some(Cow::Owned(format!("X-{name}"))), inner: DuperInner::Null, }) @@ -232,7 +231,6 @@ impl<'a, 'b> ser::Serializer for &'a mut Serializer<'b> { variant: &'static str, ) -> Result { Ok(DuperValue { - // TO-DO identifier: Some(Cow::Owned(format!("X-{name}"))), inner: DuperInner::String(Cow::Borrowed(variant)), }) @@ -248,7 +246,6 @@ impl<'a, 'b> ser::Serializer for &'a mut Serializer<'b> { { let value = value.serialize(self)?; Ok(DuperValue { - // TO-DO identifier: Some(Cow::Owned(format!("X-{name}"))), inner: value.inner, }) @@ -266,7 +263,6 @@ impl<'a, 'b> ser::Serializer for &'a mut Serializer<'b> { { let value = value.serialize(self)?; Ok(DuperValue { - // TO-DO identifier: Some(Cow::Owned(format!("X-{name}"))), inner: value.inner, }) @@ -469,7 +465,9 @@ impl<'a, 'b> ser::SerializeMap for SerializeMap<'a, 'b> { self.entries.push((key, value)); Ok(()) } else { - Err(Error::custom("serialize_value called before serialize_key")) + Err(Error::serialization( + "serialize_value called before serialize_key", + )) } } -- 2.51.2