use std::cell::RefCell;
use std::fmt;
use std::sync::Arc;
use crate::store::NodeStore;
use super::{Hash, Node, TreeError};
#[derive(Debug)]
pub enum TreeOperationError<E> {
Tree(TreeError),
NodeGet { hash: Hash, source: E },
NodePut { source: E },
}
impl<E> TreeOperationError<E> {
pub(crate) fn into_tree_error(self) -> TreeError {
match self {
Self::Tree(error) => error,
Self::NodeGet { hash, .. } => TreeError::MissingNode { hash },
Self::NodePut { .. } => TreeError::InvalidNode,
}
}
}
impl<E: fmt::Display> fmt::Display for TreeOperationError<E> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Tree(error) => error.fmt(formatter),
Self::NodeGet { hash, source } => {
write!(formatter, "node get for {hash} failed: {source}")
}
Self::NodePut { source } => write!(formatter, "node put failed: {source}"),
}
}
}
impl<E: std::error::Error + 'static> std::error::Error for TreeOperationError<E> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Tree(error) => Some(error),
Self::NodeGet { source, .. } | Self::NodePut { source } => Some(source),
}
}
}
#[derive(Debug)]
pub(super) struct ErrorCapturingStore<'a, S: NodeStore + ?Sized> {
inner: &'a mut S,
captured: RefCell<Option<TreeOperationError<S::Error>>>,
}
impl<'a, S: NodeStore + ?Sized> ErrorCapturingStore<'a, S> {
pub(super) const fn new(inner: &'a mut S) -> Self {
Self {
inner,
captured: RefCell::new(None),
}
}
pub(super) fn finish<T>(
self,
result: Result<T, TreeError>,
) -> Result<T, TreeOperationError<S::Error>> {
match result {
Ok(value) => Ok(value),
Err(error) => Err(self
.captured
.into_inner()
.unwrap_or(TreeOperationError::Tree(error))),
}
}
}
#[derive(Debug)]
pub(super) struct CapturedBackendError;
impl fmt::Display for CapturedBackendError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("backend error retained by source-preserving tree core")
}
}
impl std::error::Error for CapturedBackendError {}
impl<S: NodeStore + ?Sized> NodeStore for ErrorCapturingStore<'_, S> {
type Error = CapturedBackendError;
fn get(&self, hash: &Hash) -> Result<Option<Arc<Node>>, Self::Error> {
self.inner.get(hash).map_err(|source| {
self.captured.replace(Some(TreeOperationError::NodeGet {
hash: *hash,
source,
}));
CapturedBackendError
})
}
fn put(&mut self, node: &Node) -> Result<Hash, Self::Error> {
self.inner.put(node).map_err(|source| {
self.captured
.replace(Some(TreeOperationError::NodePut { source }));
CapturedBackendError
})
}
}