Skip to main content

miden_objects/
error.rs

1use alloc::boxed::Box;
2use alloc::format;
3use alloc::string::String;
4use alloc::vec::Vec;
5use core::any::type_name;
6use core::fmt;
7
8use miden_protocol::utils::serde::DeserializationError;
9
10/// Error produced while converting a Protobuf message into a protocol object.
11#[derive(Debug)]
12pub struct ConversionError {
13    path: Vec<String>,
14    source: Box<dyn core::error::Error + Send + Sync>,
15}
16
17impl ConversionError {
18    pub fn new(source: impl core::error::Error + Send + Sync + 'static) -> Self {
19        Self {
20            path: Vec::new(),
21            source: Box::new(source),
22        }
23    }
24
25    #[must_use]
26    pub fn context(mut self, field: impl Into<String>) -> Self {
27        self.path.push(field.into());
28        self
29    }
30
31    pub fn missing_field<T: prost::Message>(field_name: &'static str) -> Self {
32        Self::message(format!("field {}::{field_name} is missing", type_name::<T>()))
33    }
34
35    pub fn deserialization(entity: &'static str, source: DeserializationError) -> Self {
36        Self::new(DeserializationConversionError { entity, source })
37    }
38
39    pub fn message(message: impl Into<String>) -> Self {
40        Self {
41            path: Vec::new(),
42            source: Box::new(StringError(message.into())),
43        }
44    }
45
46    pub(crate) fn with_source(
47        message: impl Into<String>,
48        source: impl core::error::Error + Send + Sync + 'static,
49    ) -> Self {
50        Self::new(ContextualError {
51            message: message.into(),
52            source: Box::new(source),
53        })
54    }
55}
56
57impl fmt::Display for ConversionError {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        for (index, segment) in self.path.iter().rev().enumerate() {
60            if index > 0 {
61                f.write_str(".")?;
62            }
63            f.write_str(segment)?;
64        }
65        if !self.path.is_empty() {
66            f.write_str(": ")?;
67        }
68        self.source.fmt(f)
69    }
70}
71
72impl core::error::Error for ConversionError {
73    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
74        Some(&*self.source)
75    }
76}
77
78#[derive(Debug)]
79struct StringError(String);
80
81impl fmt::Display for StringError {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        f.write_str(&self.0)
84    }
85}
86
87impl core::error::Error for StringError {}
88
89#[derive(Debug)]
90struct ContextualError {
91    message: String,
92    source: Box<dyn core::error::Error + Send + Sync>,
93}
94
95impl fmt::Display for ContextualError {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        f.write_str(&self.message)
98    }
99}
100
101impl core::error::Error for ContextualError {
102    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
103        Some(&*self.source)
104    }
105}
106
107#[derive(Debug)]
108struct DeserializationConversionError {
109    entity: &'static str,
110    source: DeserializationError,
111}
112
113impl fmt::Display for DeserializationConversionError {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        write!(f, "failed to deserialize {}: {}", self.entity, self.source)
116    }
117}
118
119impl core::error::Error for DeserializationConversionError {
120    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
121        Some(&self.source)
122    }
123}
124
125pub trait ConversionResultExt<T> {
126    fn context(self, field: impl Into<String>) -> Result<T, ConversionError>;
127}
128
129impl<T, E: Into<ConversionError>> ConversionResultExt<T> for Result<T, E> {
130    fn context(self, field: impl Into<String>) -> Result<T, ConversionError> {
131        self.map_err(|error| error.into().context(field))
132    }
133}
134
135macro_rules! impl_conversion_error_from {
136    ($($ty:ty),* $(,)?) => {$(
137        impl From<$ty> for ConversionError {
138            fn from(error: $ty) -> Self {
139                Self::new(error)
140            }
141        }
142    )*};
143}
144
145impl_conversion_error_from!(
146    core::convert::Infallible,
147    core::num::TryFromIntError,
148    DeserializationError,
149    miden_protocol::crypto::merkle::MerkleError,
150    miden_protocol::crypto::merkle::smt::SmtLeafError,
151    miden_protocol::crypto::merkle::smt::SmtProofError,
152    miden_protocol::errors::AccountError,
153    miden_protocol::errors::AccountTreeError,
154    miden_protocol::errors::AssetError,
155    miden_protocol::errors::AssetVaultError,
156    miden_protocol::errors::NoteError,
157    miden_protocol::errors::PartialAssetVaultError,
158    miden_protocol::errors::ProtocolConfigError,
159    miden_protocol::errors::StorageSlotNameError,
160    miden_protocol::errors::TransactionInputError,
161);
162
163impl From<prost::UnknownEnumValue> for ConversionError {
164    fn from(error: prost::UnknownEnumValue) -> Self {
165        Self::new(error)
166    }
167}