use std::{cell::RefCell, fmt::Display};
use crate::{path::PathTracker, DeserializationError};
pub struct ErrorReporter;
impl ErrorReporter {
#[must_use = "The guard returned by this method must be kept alive for the duration of the whole \
deserialization operation to ensure that errors are correctly reported."]
pub fn start_deserialization() -> ErrorReporterGuard {
PathTracker::init();
DESERIALIZATION_ERRORS.set(Some(Vec::new()));
ErrorReporterGuard
}
pub fn report<E: Display>(e: E) {
let path = match PathTracker::unstash_current_path_for_error() {
Some(p) => Some(p),
None => PathTracker::current_path(),
};
let error = DeserializationError {
path,
details: e.to_string(),
};
let success = DESERIALIZATION_ERRORS.with_borrow_mut(|v| {
if let Some(v) = v {
v.push(error);
true
} else {
false
}
});
if !success {
panic!("Attempted to report an error outside of a deserialization operation. \
You can't call `ErrorReporter::report_error` without first calling `ErrorReporter::start_deserialization`. \
This error may be triggered by a top-level invocation of `EDeserialize::deserialize_for_errors` without \
a preceding call to `ErrorReporter::start_deserialization`. \
This initialization step is usually taken care of by the format-specific functions provided by `eserde`, \
such as `eserde::json::from_str`. If you're implementing your own deserialization logic, you \
need to take care of this initialization step yourself.");
};
}
pub fn take_errors() -> Vec<DeserializationError> {
DESERIALIZATION_ERRORS.with_borrow_mut(|v| v.replace(Vec::new()))
.expect(
"Attempted to collect deserialization errors outside of a deserialization operation. \
You can't call `ErrorReporter::take_errors` without first calling `ErrorReporter::start_deserialization`. \
This error may be triggered by a top-level invocation of `EDeserialize::deserialize_for_errors` without \
a preceding call to `ErrorReporter::start_deserialization`. \
This initialization step is usually taken care of by the format-specific functions provided by `eserde`, \
such as `eserde::json::from_str`. If you're implementing your own deserialization logic, you \
need to take care of this initialization step yourself.")
}
pub fn n_errors() -> usize {
DESERIALIZATION_ERRORS.with_borrow(|v| v.as_ref().map(|v| v.len()))
.expect(
"Attempted to count the number of deserialization errors outside of a deserialization operation. \
You can't call `ErrorReporter::take_errors` without first calling `ErrorReporter::start_deserialization`. \
This error may be triggered by a top-level invocation of `EDeserialize::deserialize_for_errors` without \
a preceding call to `ErrorReporter::start_deserialization`. \
This initialization step is usually taken care of by the format-specific functions provided by `eserde`, \
such as `eserde::json::from_str`. If you're implementing your own deserialization logic, you \
need to take care of this initialization step yourself.")
}
}
#[non_exhaustive]
pub struct ErrorReporterGuard;
impl Drop for ErrorReporterGuard {
fn drop(&mut self) {
let _ = DESERIALIZATION_ERRORS.try_with(|c| {
if let Ok(mut v) = c.try_borrow_mut() {
*v = None;
}
});
PathTracker::try_unset();
}
}
thread_local! {
static DESERIALIZATION_ERRORS: RefCell<Option<Vec<DeserializationError>>> = const { RefCell::new(None) };
}