rorm_db/row/
error.rs

1use std::error::Error;
2use std::fmt;
3
4use crate::row::{OwnedRowIndex, RowIndex};
5
6/// Errors returned by [`Row::get`](super::Row::get)
7#[derive(Debug)]
8pub enum RowError<'i> {
9    /// The requested index was not found
10    NotFound {
11        /// The index which has not been found
12        index: RowIndex<'i>,
13    },
14    /// The type returned by the database and the type expected by rust don't match.
15    ///
16    /// This is checked before decoding is even attempted.
17    MismatchedTypes {
18        /// The index the error occurred at
19        index: RowIndex<'i>,
20        /// The `type_name` of the expected type
21        rust_type: &'static str,
22    },
23    /// An unexpected `NULL` was encountered during decoding.
24    ///
25    /// This is a special case of `RowError::Decode`
26    /// which the caller could handle by wrapping the type to decode in an `Option`.
27    UnexpectedNull {
28        /// The index the error occurred at
29        index: RowIndex<'i>,
30    },
31    /// An error occurred while decoding the value.
32    Decode {
33        /// The index the error occurred at
34        index: RowIndex<'i>,
35        /// The error produced by the `Decode` implementation
36        source: Box<dyn Error + Send + Sync>,
37    },
38    /// An unknown error occurred.
39    Unknown {
40        /// The index the error occurred at
41        index: RowIndex<'i>,
42        /// The underlying error which could not be mapped to one of this enum's variants.
43        ///
44        /// This is an `sqlx::Error` which should not happen according to their documentation.
45        source: Box<dyn Error + Send + Sync>,
46    },
47}
48
49impl<'i> RowError<'i> {
50    /// The index the error occurred at
51    pub fn index(&self) -> RowIndex<'i> {
52        match self {
53            RowError::NotFound { index, .. }
54            | RowError::MismatchedTypes { index, .. }
55            | RowError::UnexpectedNull { index, .. }
56            | RowError::Decode { index, .. }
57            | RowError::Unknown { index, .. } => *index,
58        }
59    }
60
61    /// Converts the error into its owned version
62    pub fn into_owned(self) -> OwnedRowError {
63        match self {
64            RowError::NotFound { index } => OwnedRowError::NotFound {
65                index: index.into_owned(),
66            },
67            RowError::MismatchedTypes { index, rust_type } => OwnedRowError::MismatchedTypes {
68                index: index.into_owned(),
69                rust_type,
70            },
71            RowError::UnexpectedNull { index } => OwnedRowError::UnexpectedNull {
72                index: index.into_owned(),
73            },
74            RowError::Decode { index, source } => OwnedRowError::Decode {
75                index: index.into_owned(),
76                source,
77            },
78            RowError::Unknown { index, source } => OwnedRowError::Unknown {
79                index: index.into_owned(),
80                source,
81            },
82        }
83    }
84}
85
86/// Owned version of [`RowError`]
87#[derive(Debug)]
88pub enum OwnedRowError {
89    /// The requested index was not found
90    NotFound {
91        /// The index which has not been found
92        index: OwnedRowIndex,
93    },
94    /// The type returned by the database and the type expected by rust don't match.
95    ///
96    /// This is checked before decoding is even attempted.
97    MismatchedTypes {
98        /// The index the error occurred at
99        index: OwnedRowIndex,
100        /// The `type_name` of the expected type
101        rust_type: &'static str,
102    },
103    /// An unexpected `NULL` was encountered during decoding.
104    ///
105    /// This is a special case of `OwnedRowError::Decode`
106    /// which the caller could handle by wrapping the type to decode in an `Option`.
107    UnexpectedNull {
108        /// The index the error occurred at
109        index: OwnedRowIndex,
110    },
111    /// An error occurred while decoding the value.
112    Decode {
113        /// The index the error occurred at
114        index: OwnedRowIndex,
115        /// The error produced by the `Decode` implementation
116        source: Box<dyn Error + Send + Sync>,
117    },
118    /// An unknown error occurred.
119    Unknown {
120        /// The index the error occurred at
121        index: OwnedRowIndex,
122        /// The underlying error which could not be mapped to one of this enum's variants.
123        ///
124        /// This is an `sqlx::Error` which should not happen according to their documentation.
125        source: Box<dyn Error + Send + Sync>,
126    },
127}
128
129impl OwnedRowError {
130    /// The index the error occurred at
131    pub fn index(&self) -> &OwnedRowIndex {
132        match self {
133            OwnedRowError::NotFound { index, .. }
134            | OwnedRowError::MismatchedTypes { index, .. }
135            | OwnedRowError::UnexpectedNull { index, .. }
136            | OwnedRowError::Decode { index, .. }
137            | OwnedRowError::Unknown { index, .. } => index,
138        }
139    }
140}
141
142impl From<OwnedRowError> for crate::Error {
143    fn from(value: OwnedRowError) -> Self {
144        crate::Error::RowError(value)
145    }
146}
147
148impl<'i> From<RowError<'i>> for OwnedRowError {
149    fn from(value: RowError<'i>) -> Self {
150        value.into_owned()
151    }
152}
153
154impl<'i> From<RowError<'i>> for crate::Error {
155    fn from(value: RowError<'i>) -> Self {
156        value.into_owned().into()
157    }
158}
159
160macro_rules! impl_display {
161    (impl$(<$lifetime:lifetime>)? fmt::Display for $Enum:ty { .. }) => {
162        impl$(<$lifetime>)? fmt::Display for $Enum {
163            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164                match self {
165                    Self::NotFound { index } => write!(f, "Column {index:?} was not found"),
166                    Self::MismatchedTypes { index, rust_type } => write!(f, "Column {index:?} does not match type {rust_type}"),
167                    Self::UnexpectedNull { index } => write!(f, "Column {index:?} is `NULL`; try decoding as `Option`"),
168                    Self::Decode { index, source } => write!(f, "Couldn't decode column {index:?}: {source}"),
169                    Self::Unknown { index, source } => write!(f, "An unknown error occurred at column {index:?}: {source}"),
170                }
171            }
172        }
173    };
174}
175impl_display!(impl<'i> fmt::Display for RowError<'i> {..});
176impl_display!(impl fmt::Display for OwnedRowError {..});
177
178macro_rules! impl_error {
179    (impl$(<$lifetime:lifetime>)? Error for $Enum:ty { .. }) => {
180        impl$(<$lifetime>)? Error for $Enum {
181            fn source(&self) -> Option<&(dyn Error + 'static)> {
182                match self {
183                    Self::Decode { source, .. } => Some(source.as_ref()),
184                    Self::Unknown { source, .. } => Some(source.as_ref()),
185                    _ => None,
186                }
187            }
188        }
189    };
190}
191impl_error!(impl<'i> Error for RowError<'i> {..});
192impl_error!(impl Error for OwnedRowError {..});