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);