Skip to main content

a3s_orm/
decode.rs

1use crate::Value;
2
3#[derive(Debug, thiserror::Error, PartialEq, Eq)]
4pub enum DecodeError {
5    #[error("result row has no column at index {index}")]
6    MissingColumn { index: usize },
7    #[error("cannot decode {actual} at column {index} as {expected}")]
8    TypeMismatch {
9        index: usize,
10        expected: &'static str,
11        actual: &'static str,
12    },
13    #[error("integer at column {index} is outside the range of {target}")]
14    IntegerOverflow { index: usize, target: &'static str },
15    #[error("cannot decode array element {element} at column {index}: {source}")]
16    ArrayElement {
17        index: usize,
18        element: usize,
19        source: Box<DecodeError>,
20    },
21}
22
23pub trait Row {
24    fn value(&self, index: usize) -> Option<&Value>;
25}
26
27pub trait FromValue: Sized {
28    const EXPECTED: &'static str;
29
30    fn from_value(value: &Value, index: usize) -> Result<Self, DecodeError>;
31}
32
33pub trait FromRow: Sized {
34    fn from_row(row: &impl Row) -> Result<Self, DecodeError>;
35}
36
37impl<T: FromValue> FromRow for T {
38    fn from_row(row: &impl Row) -> Result<Self, DecodeError> {
39        decode_at(row, 0)
40    }
41}
42
43fn decode_at<T: FromValue>(row: &impl Row, index: usize) -> Result<T, DecodeError> {
44    let value = row
45        .value(index)
46        .ok_or(DecodeError::MissingColumn { index })?;
47    T::from_value(value, index)
48}
49
50fn mismatch<T: FromValue>(value: &Value, index: usize) -> DecodeError {
51    DecodeError::TypeMismatch {
52        index,
53        expected: T::EXPECTED,
54        actual: value.kind(),
55    }
56}
57
58impl FromValue for bool {
59    const EXPECTED: &'static str = "boolean";
60
61    fn from_value(value: &Value, index: usize) -> Result<Self, DecodeError> {
62        match value {
63            Value::Bool(value) => Ok(*value),
64            Value::I64(0) => Ok(false),
65            Value::I64(1) => Ok(true),
66            _ => Err(mismatch::<Self>(value, index)),
67        }
68    }
69}
70
71macro_rules! signed_decoders {
72    ($($type:ty),+ $(,)?) => {$(
73        impl FromValue for $type {
74            const EXPECTED: &'static str = stringify!($type);
75
76            fn from_value(value: &Value, index: usize) -> Result<Self, DecodeError> {
77                let value = match value {
78                    Value::I64(value) => *value,
79                    _ => return Err(mismatch::<Self>(value, index)),
80                };
81                <$type>::try_from(value).map_err(|_| DecodeError::IntegerOverflow {
82                    index,
83                    target: stringify!($type),
84                })
85            }
86        }
87    )+};
88}
89
90macro_rules! unsigned_decoders {
91    ($($type:ty),+ $(,)?) => {$(
92        impl FromValue for $type {
93            const EXPECTED: &'static str = stringify!($type);
94
95            fn from_value(value: &Value, index: usize) -> Result<Self, DecodeError> {
96                let value = match value {
97                    Value::U64(value) => *value,
98                    Value::I64(value) => u64::try_from(*value).map_err(|_| {
99                        DecodeError::IntegerOverflow { index, target: stringify!($type) }
100                    })?,
101                    _ => return Err(mismatch::<Self>(value, index)),
102                };
103                <$type>::try_from(value).map_err(|_| DecodeError::IntegerOverflow {
104                    index,
105                    target: stringify!($type),
106                })
107            }
108        }
109    )+};
110}
111
112signed_decoders!(i8, i16, i32, i64, isize);
113unsigned_decoders!(u8, u16, u32, u64, usize);
114
115impl FromValue for f64 {
116    const EXPECTED: &'static str = "f64";
117
118    fn from_value(value: &Value, index: usize) -> Result<Self, DecodeError> {
119        match value {
120            Value::F64(value) => Ok(*value),
121            Value::I64(value) => Ok(*value as f64),
122            _ => Err(mismatch::<Self>(value, index)),
123        }
124    }
125}
126
127impl FromValue for f32 {
128    const EXPECTED: &'static str = "f32";
129
130    fn from_value(value: &Value, index: usize) -> Result<Self, DecodeError> {
131        f64::from_value(value, index).map(|value| value as f32)
132    }
133}
134
135impl FromValue for String {
136    const EXPECTED: &'static str = "string";
137
138    fn from_value(value: &Value, index: usize) -> Result<Self, DecodeError> {
139        match value {
140            Value::String(value) => Ok(value.clone()),
141            _ => Err(mismatch::<Self>(value, index)),
142        }
143    }
144}
145
146impl FromValue for Vec<u8> {
147    const EXPECTED: &'static str = "bytes";
148
149    fn from_value(value: &Value, index: usize) -> Result<Self, DecodeError> {
150        match value {
151            Value::Bytes(value) => Ok(value.clone()),
152            _ => Err(mismatch::<Self>(value, index)),
153        }
154    }
155}
156
157impl<T: FromValue> FromValue for crate::SqlArray<T> {
158    const EXPECTED: &'static str = "array";
159
160    fn from_value(value: &Value, index: usize) -> Result<Self, DecodeError> {
161        let Value::Array(values) = value else {
162            return Err(mismatch::<Self>(value, index));
163        };
164        values
165            .iter()
166            .enumerate()
167            .map(|(element, value)| {
168                T::from_value(value, index).map_err(|source| DecodeError::ArrayElement {
169                    index,
170                    element,
171                    source: Box::new(source),
172                })
173            })
174            .collect::<Result<Vec<_>, _>>()
175            .map(crate::SqlArray)
176    }
177}
178
179macro_rules! direct_decoder {
180    ($feature:literal, $type:ty, $variant:ident, $expected:literal) => {
181        #[cfg(feature = $feature)]
182        impl FromValue for $type {
183            const EXPECTED: &'static str = $expected;
184
185            fn from_value(value: &Value, index: usize) -> Result<Self, DecodeError> {
186                match value {
187                    Value::$variant(value) => Ok(value.clone()),
188                    _ => Err(mismatch::<Self>(value, index)),
189                }
190            }
191        }
192    };
193}
194
195direct_decoder!("uuid", uuid::Uuid, Uuid, "uuid");
196direct_decoder!("json", serde_json::Value, Json, "json");
197direct_decoder!("chrono", chrono::NaiveDate, Date, "date");
198direct_decoder!("chrono", chrono::NaiveTime, Time, "time");
199direct_decoder!("chrono", chrono::NaiveDateTime, DateTime, "timestamp");
200direct_decoder!(
201    "chrono",
202    chrono::DateTime<chrono::Utc>,
203    DateTimeUtc,
204    "timestamp with time zone"
205);
206direct_decoder!("decimal", rust_decimal::Decimal, Decimal, "decimal");
207
208impl<T: FromValue> FromValue for Option<T> {
209    const EXPECTED: &'static str = T::EXPECTED;
210
211    fn from_value(value: &Value, index: usize) -> Result<Self, DecodeError> {
212        match value {
213            Value::Null => Ok(None),
214            value => T::from_value(value, index).map(Some),
215        }
216    }
217}
218
219macro_rules! tuple_rows {
220    ($(($type:ident, $index:tt)),+ $(,)?) => {
221        impl<$($type: FromValue),+> FromRow for ($($type,)+) {
222            fn from_row(row: &impl Row) -> Result<Self, DecodeError> {
223                Ok(($(decode_at::<$type>(row, $index)?,)+))
224            }
225        }
226    };
227}
228
229tuple_rows!((A, 0), (B, 1));
230tuple_rows!((A, 0), (B, 1), (C, 2));
231tuple_rows!((A, 0), (B, 1), (C, 2), (D, 3));
232tuple_rows!((A, 0), (B, 1), (C, 2), (D, 3), (E, 4));
233tuple_rows!((A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5));
234tuple_rows!((A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6));
235tuple_rows!(
236    (A, 0),
237    (B, 1),
238    (C, 2),
239    (D, 3),
240    (E, 4),
241    (F, 5),
242    (G, 6),
243    (H, 7)
244);