re_types_core/
result.rs

1use std::{any, fmt::Display, ops::Deref};
2
3// ---
4
5// NOTE: We have to make an alias, otherwise we'll trigger `thiserror`'s magic codepath which will
6// attempt to use nightly features.
7pub type _Backtrace = std::backtrace::Backtrace;
8
9#[derive(thiserror::Error)]
10pub enum SerializationError {
11    #[error("Failed to serialize {location:?}")]
12    Context {
13        location: String,
14        source: Box<SerializationError>,
15    },
16
17    #[error("Trying to serialize a field lacking extension metadata: {fqname:?}")]
18    MissingExtensionMetadata {
19        fqname: String,
20        backtrace: Box<_Backtrace>,
21    },
22
23    #[error("{fqname} doesn't support Serialization: {reason}")]
24    NotImplemented {
25        fqname: String,
26        reason: String,
27        backtrace: Box<_Backtrace>,
28    },
29
30    /// E.g. too many values (overflows i32).
31    #[error(transparent)]
32    ArrowError(#[from] ArcArrowError),
33}
34
35#[test]
36fn test_serialization_error_size() {
37    assert!(
38        std::mem::size_of::<SerializationError>() <= 64,
39        "Size of error is {} bytes. Let's try to keep errors small.",
40        std::mem::size_of::<SerializationError>()
41    );
42}
43
44impl std::fmt::Debug for SerializationError {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        if let Some(backtrace) = self.backtrace() {
47            f.write_fmt(format_args!("{self}:\n{backtrace:#?}"))
48        } else {
49            f.write_fmt(format_args!("{self}"))
50        }
51    }
52}
53
54impl SerializationError {
55    #[inline]
56    pub fn missing_extension_metadata(fqname: impl AsRef<str>) -> Self {
57        Self::MissingExtensionMetadata {
58            fqname: fqname.as_ref().into(),
59            backtrace: Box::new(std::backtrace::Backtrace::capture()),
60        }
61    }
62
63    #[inline]
64    pub fn not_implemented(fqname: impl AsRef<str>, reason: impl AsRef<str>) -> Self {
65        Self::NotImplemented {
66            fqname: fqname.as_ref().into(),
67            reason: reason.as_ref().into(),
68            backtrace: Box::new(std::backtrace::Backtrace::capture()),
69        }
70    }
71
72    /// Returns the _unresolved_ backtrace associated with this error, if it exists.
73    ///
74    /// Call `resolve()` on the returned [`_Backtrace`] to resolve it (costly!).
75    pub fn backtrace(&self) -> Option<&_Backtrace> {
76        match self {
77            Self::MissingExtensionMetadata { backtrace, .. }
78            | Self::NotImplemented { backtrace, .. } => Some(backtrace),
79            Self::ArrowError { .. } | Self::Context { .. } => None,
80        }
81    }
82}
83
84// ----------------------------------------------------------------------------
85
86/// A cloneable wrapper around [`arrow::error::ArrowError`], for easier use.
87///
88/// The motivation behind this type is that we often use code that can return a [`arrow::error::ArrowError`]
89/// inside functions that return a `SerializationError`. By wrapping it we can use the ? operator and simplify the code.
90/// Second, normally also [`arrow::error::ArrowError`] isn't cloneable, but `SerializationError` is.
91#[derive(Clone, Debug)]
92pub struct ArcArrowError(std::sync::Arc<arrow::error::ArrowError>);
93
94impl From<arrow::error::ArrowError> for ArcArrowError {
95    fn from(e: arrow::error::ArrowError) -> Self {
96        Self(std::sync::Arc::new(e))
97    }
98}
99
100impl From<arrow::error::ArrowError> for SerializationError {
101    fn from(e: arrow::error::ArrowError) -> Self {
102        Self::ArrowError(ArcArrowError::from(e))
103    }
104}
105
106impl Deref for ArcArrowError {
107    type Target = arrow::error::ArrowError;
108
109    #[inline]
110    fn deref(&self) -> &Self::Target {
111        self.0.as_ref()
112    }
113}
114
115impl Display for ArcArrowError {
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        self.0.fmt(f)
118    }
119}
120
121// ----------------------------------------------------------------------------
122
123pub type SerializationResult<T> = ::std::result::Result<T, SerializationError>;
124
125// ---
126
127#[derive(thiserror::Error)]
128pub enum DeserializationError {
129    #[error("Failed to deserialize {location:?}")]
130    Context {
131        location: String,
132        #[source]
133        source: Box<DeserializationError>,
134    },
135
136    #[error("{fqname} doesn't support deserialization")]
137    NotImplemented {
138        fqname: String,
139        backtrace: Box<_Backtrace>,
140    },
141
142    #[error("Expected non-nullable data but didn't find any")]
143    MissingData { backtrace: Box<_Backtrace> },
144
145    #[error("Expected field {field_name:?} to be present in {datatype}")]
146    MissingStructField {
147        datatype: arrow::datatypes::DataType,
148        field_name: String,
149        backtrace: Box<_Backtrace>,
150    },
151
152    #[error(
153        "Found {field1_length} {field1_name:?} values vs. {field2_length} {field2_name:?} values"
154    )]
155    MismatchedStructFieldLengths {
156        field1_name: String,
157        field1_length: usize,
158        field2_name: String,
159        field2_length: usize,
160        backtrace: Box<_Backtrace>,
161    },
162
163    #[error("Expected union arm {arm_name:?} (#{arm_index}) to be present in {datatype}")]
164    MissingUnionArm {
165        datatype: arrow::datatypes::DataType,
166        arm_name: String,
167        arm_index: usize,
168        backtrace: Box<_Backtrace>,
169    },
170
171    #[error("Expected {expected} but found {got} instead")]
172    DatatypeMismatch {
173        expected: arrow::datatypes::DataType,
174        got: arrow::datatypes::DataType,
175        backtrace: Box<_Backtrace>,
176    },
177
178    #[error("Offset ouf of bounds: trying to read at offset #{offset} in an array of size {len}")]
179    OffsetOutOfBounds {
180        offset: usize,
181        len: usize,
182        backtrace: Box<_Backtrace>,
183    },
184
185    #[error(
186        "Offset slice ouf of bounds: trying to read offset slice at [#{from}..#{to}] in an array of size {len}"
187    )]
188    OffsetSliceOutOfBounds {
189        from: usize,
190        to: usize,
191        len: usize,
192        backtrace: Box<_Backtrace>,
193    },
194
195    #[error("Downcast to {to} failed")]
196    DowncastError {
197        to: String,
198        backtrace: Box<_Backtrace>,
199    },
200
201    #[error("Datacell deserialization Failed: {0}")]
202    DataCellError(String),
203
204    #[error("Validation Error: {0}")]
205    ValidationError(String),
206}
207
208#[test]
209fn test_derserialization_error_size() {
210    assert!(
211        std::mem::size_of::<DeserializationError>() <= 72,
212        "Size of error is {} bytes. Let's try to keep errors small.",
213        std::mem::size_of::<DeserializationError>()
214    );
215}
216
217impl std::fmt::Debug for DeserializationError {
218    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
219        if let Some(backtrace) = self.backtrace() {
220            f.write_fmt(format_args!("{self}:\n{backtrace:#?}"))
221        } else {
222            f.write_fmt(format_args!("{self}"))
223        }
224    }
225}
226
227impl DeserializationError {
228    #[inline]
229    pub fn missing_data() -> Self {
230        Self::MissingData {
231            backtrace: Box::new(std::backtrace::Backtrace::capture()),
232        }
233    }
234
235    #[inline]
236    pub fn missing_struct_field(
237        datatype: impl Into<arrow::datatypes::DataType>,
238        field_name: impl AsRef<str>,
239    ) -> Self {
240        Self::MissingStructField {
241            datatype: datatype.into(),
242            field_name: field_name.as_ref().into(),
243            backtrace: Box::new(std::backtrace::Backtrace::capture()),
244        }
245    }
246
247    #[inline]
248    pub fn mismatched_struct_field_lengths(
249        field1_name: impl AsRef<str>,
250        field1_length: usize,
251        field2_name: impl AsRef<str>,
252        field2_length: usize,
253    ) -> Self {
254        Self::MismatchedStructFieldLengths {
255            field1_name: field1_name.as_ref().into(),
256            field1_length,
257            field2_name: field2_name.as_ref().into(),
258            field2_length,
259            backtrace: Box::new(std::backtrace::Backtrace::capture()),
260        }
261    }
262
263    #[inline]
264    pub fn missing_union_arm(
265        datatype: impl Into<arrow::datatypes::DataType>,
266        arm_name: impl AsRef<str>,
267        arm_index: usize,
268    ) -> Self {
269        Self::MissingUnionArm {
270            datatype: datatype.into(),
271            arm_name: arm_name.as_ref().into(),
272            arm_index,
273            backtrace: Box::new(std::backtrace::Backtrace::capture()),
274        }
275    }
276
277    #[inline]
278    pub fn datatype_mismatch(
279        expected: impl Into<arrow::datatypes::DataType>,
280        got: impl Into<arrow::datatypes::DataType>,
281    ) -> Self {
282        Self::DatatypeMismatch {
283            expected: expected.into(),
284            got: got.into(),
285            backtrace: Box::new(std::backtrace::Backtrace::capture()),
286        }
287    }
288
289    #[inline]
290    pub fn offset_oob(offset: usize, len: usize) -> Self {
291        Self::OffsetOutOfBounds {
292            offset,
293            len,
294            backtrace: Box::new(std::backtrace::Backtrace::capture()),
295        }
296    }
297
298    #[inline]
299    pub fn offset_slice_oob((from, to): (usize, usize), len: usize) -> Self {
300        Self::OffsetSliceOutOfBounds {
301            from,
302            to,
303            len,
304            backtrace: Box::new(std::backtrace::Backtrace::capture()),
305        }
306    }
307
308    #[inline]
309    pub fn downcast_error<ToType>() -> Self {
310        Self::DowncastError {
311            to: any::type_name::<ToType>().to_owned(),
312            backtrace: Box::new(std::backtrace::Backtrace::capture()),
313        }
314    }
315
316    /// Returns the _unresolved_ backtrace associated with this error, if it exists.
317    ///
318    /// Call `resolve()` on the returned [`_Backtrace`] to resolve it (costly!).
319    #[inline]
320    pub fn backtrace(&self) -> Option<&_Backtrace> {
321        match self {
322            Self::Context {
323                location: _,
324                source,
325            } => source.backtrace(),
326            Self::NotImplemented { backtrace, .. }
327            | Self::MissingStructField { backtrace, .. }
328            | Self::MismatchedStructFieldLengths { backtrace, .. }
329            | Self::MissingUnionArm { backtrace, .. }
330            | Self::MissingData { backtrace }
331            | Self::DatatypeMismatch { backtrace, .. }
332            | Self::OffsetOutOfBounds { backtrace, .. }
333            | Self::OffsetSliceOutOfBounds { backtrace, .. }
334            | Self::DowncastError { backtrace, .. } => Some(backtrace),
335            Self::DataCellError(_) | Self::ValidationError(_) => None,
336        }
337    }
338
339    /// The source of the error, without any [`Self::Context`].
340    pub fn without_context(self) -> Self {
341        match self {
342            Self::Context { source, .. } => source.without_context(),
343            _ => self,
344        }
345    }
346}
347
348pub type DeserializationResult<T> = ::std::result::Result<T, DeserializationError>;
349
350pub trait ResultExt<T> {
351    fn with_context(self, location: impl AsRef<str>) -> Self;
352}
353
354impl<T> ResultExt<T> for SerializationResult<T> {
355    #[inline]
356    fn with_context(self, location: impl AsRef<str>) -> Self {
357        self.map_err(|err| SerializationError::Context {
358            location: location.as_ref().into(),
359            source: Box::new(err),
360        })
361    }
362}
363
364impl<T> ResultExt<T> for DeserializationResult<T> {
365    #[inline]
366    fn with_context(self, location: impl AsRef<str>) -> Self {
367        self.map_err(|err| DeserializationError::Context {
368            location: location.as_ref().into(),
369            source: Box::new(err),
370        })
371    }
372}