use std::any::type_name;
use std::fmt;
pub use miden_node_grpc_error_macro::GrpcError;
use miden_protocol::utils::serde::DeserializationError;
#[cfg(test)]
mod test_macro;
#[derive(Debug)]
pub struct ConversionError {
path: Vec<&'static str>,
source: Box<dyn std::error::Error + Send + Sync>,
}
impl ConversionError {
pub fn new(source: impl std::error::Error + Send + Sync + 'static) -> Self {
Self {
path: Vec::new(),
source: Box::new(source),
}
}
#[must_use]
pub fn context(mut self, field: &'static str) -> Self {
self.path.push(field);
self
}
pub fn missing_field<T: prost::Message>(field_name: &'static str) -> Self {
Self {
path: Vec::new(),
source: Box::new(MissingFieldError { entity: type_name::<T>(), field_name }),
}
}
pub fn deserialization(entity: &'static str, source: DeserializationError) -> Self {
Self {
path: Vec::new(),
source: Box::new(DeserializationErrorWrapper { entity, source }),
}
}
pub fn message(msg: impl Into<String>) -> Self {
Self {
path: Vec::new(),
source: Box::new(StringError(msg.into())),
}
}
}
impl fmt::Display for ConversionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.path.is_empty() {
for (i, segment) in self.path.iter().rev().enumerate() {
if i > 0 {
f.write_str(".")?;
}
f.write_str(segment)?;
}
f.write_str(": ")?;
}
write!(f, "{}", self.source)
}
}
impl std::error::Error for ConversionError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&*self.source)
}
}
impl From<ConversionError> for tonic::Status {
fn from(value: ConversionError) -> Self {
tonic::Status::invalid_argument(value.to_string())
}
}
#[derive(Debug)]
struct MissingFieldError {
entity: &'static str,
field_name: &'static str,
}
impl fmt::Display for MissingFieldError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "field `{}::{}` is missing", self.entity, self.field_name)
}
}
impl std::error::Error for MissingFieldError {}
#[derive(Debug)]
struct DeserializationErrorWrapper {
entity: &'static str,
source: DeserializationError,
}
impl fmt::Display for DeserializationErrorWrapper {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "failed to deserialize {}: {}", self.entity, self.source)
}
}
impl std::error::Error for DeserializationErrorWrapper {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.source)
}
}
#[derive(Debug)]
struct StringError(String);
impl fmt::Display for StringError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for StringError {}
pub trait ConversionResultExt<T> {
fn context(self, field: &'static str) -> Result<T, ConversionError>;
}
impl<T, E: Into<ConversionError>> ConversionResultExt<T> for Result<T, E> {
fn context(self, field: &'static str) -> Result<T, ConversionError> {
self.map_err(|e| e.into().context(field))
}
}
macro_rules! impl_from_for_conversion_error {
($($ty:ty),* $(,)?) => {
$(
impl From<$ty> for ConversionError {
fn from(e: $ty) -> Self {
Self::new(e)
}
}
)*
};
}
impl_from_for_conversion_error!(
hex::FromHexError,
miden_protocol::errors::AccountError,
miden_protocol::errors::AssetError,
miden_protocol::errors::AssetVaultError,
miden_protocol::errors::FeeError,
miden_protocol::errors::NoteError,
miden_protocol::errors::StorageSlotNameError,
miden_protocol::crypto::merkle::MerkleError,
miden_protocol::crypto::merkle::smt::SmtLeafError,
miden_protocol::crypto::merkle::smt::SmtProofError,
miden_standards::note::NetworkAccountTargetError,
std::num::TryFromIntError,
DeserializationError,
);