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#[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}