use crate::{
Bignum, DeError, Deserialize, DeserializerTrait, Fixnum, SerError, Serialize, SerializerTrait,
Symbol,
};
mod de;
mod ser;
pub use de::Deserializer;
pub use ser::Serializer;
#[derive(Debug, Default, Clone)]
pub struct Trace {
pub context: Vec<Context>,
}
#[derive(Debug, Clone)]
pub enum Context {
Nil,
Bool(bool),
Fixnum(Fixnum),
Bignum(Bignum),
Float(f64),
Hash(usize),
HashKey(usize),
HashValue(usize),
Array(usize),
ArrayIndex(usize),
String(String),
Symbol(Symbol),
Regex(String, u8),
Object(Symbol, usize),
Struct(Symbol, usize),
FetchingField(usize),
WritingField(Symbol, usize),
WritingFields(usize),
Field(Option<Symbol>, usize),
Class(Symbol),
Module(Symbol),
Instance,
Extended(Symbol),
UserClass(Symbol),
UserData(Symbol),
UserMarshal(Symbol),
Data(Symbol),
}
pub fn deserialize<'de, T>(input: impl DeserializerTrait<'de>) -> Result<T, (DeError, Trace)>
where
T: Deserialize<'de>,
{
let mut track = Trace::new();
let deserializer = Deserializer::new(input, &mut track);
let value = T::deserialize(deserializer);
match value {
Ok(value) => Ok(value),
Err(err) => Err((err, track)),
}
}
pub fn serialize<S>(input: impl Serialize, serializer: S) -> Result<S::Ok, (SerError, Trace)>
where
S: SerializerTrait,
{
let mut track = Trace::new();
let serializer = Serializer::new(serializer, &mut track);
let value = input.serialize(serializer);
match value {
Ok(value) => Ok(value),
Err(err) => Err((err, track)),
}
}
impl Trace {
pub fn new() -> Self {
Self::default()
}
pub(crate) fn push(&mut self, context: Context) {
self.context.push(context);
}
}
impl std::fmt::Display for Trace {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for context in self.context.iter().rev() {
writeln!(f, "{context}")?;
}
Ok(())
}
}
impl std::fmt::Display for Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Context::{
Array, ArrayIndex, Bignum, Bool, Class, Data, Extended, FetchingField, Field, Fixnum,
Float, Hash, HashKey, HashValue, Instance, Module, Nil, Object, Regex, String, Struct,
Symbol, UserClass, UserData, UserMarshal, WritingField, WritingFields,
};
match self {
Nil => write!(f, "while processing a nil"),
Bool(v) => write!(f, "while processing a boolean: {v}"),
Fixnum(v) => write!(f, "while processing a fixnum: {v}"),
Bignum(v) => write!(f, "while processing a bignum: {v}"),
Float(v) => write!(f, "while processing a float: {v}"),
Hash(len) => write!(f, "while processing a hash with {len} entries"),
HashKey(index) => write!(f, "while processing the {index} key of a hash"),
HashValue(index) => write!(f, "while processing the {index} value of a hash"),
Array(len) => write!(f, "while processing an array with {len} elements"),
ArrayIndex(index) => write!(f, "while processing the {index} element of an array"),
String(s) => write!(f, "while processing a string: {s}"),
Symbol(s) => write!(f, "while processing a symbol: {s}"),
Regex(s, flags) => write!(f, "while processing a regex: /{s}/ {flags}"),
Object(class, len) => write!(
f,
"while processing an instance of {class} with {len} ivars"
),
Struct(name, len) => write!(f, "while processing a struct of {name} with {len} ivars"),
FetchingField(index) => write!(f, "while fetching the {index} field"),
Field(Some(field), index) => {
write!(f, "while processing {field} (field index {index})")
}
WritingField(field, index) => {
write!(f, "while writing {field} (field index {index})")
}
WritingFields(len) => write!(f, "while writing {len} fields"),
Field(None, index) => write!(f, "while processing an invalid field at index {index}"),
Class(class) => write!(f, "while processing a class: {class}"),
Module(module) => write!(f, "while processing a module: {module}"),
Instance => write!(f, "while processing an instance"),
Extended(module) => write!(f, "while processing an object extended by {module}"),
UserClass(class) => write!(f, "while processing a user class: {class}"),
UserData(class) => write!(f, "while processing user data: {class}"),
UserMarshal(class) => write!(f, "while processing user marshal: {class}"),
Data(class) => write!(f, "while processing data: {class}"),
}
}
}
macro_rules! add_context {
($erroring_expr:expr, $context:expr) => {
match $erroring_expr {
Ok(value) => Ok(value),
Err(err) => {
$context;
Err(err)
}
}
};
}
pub(crate) use add_context;