Skip to main content

bottle_orm/
any_struct.rs

1use sqlx::{any::AnyRow, Error, Row};
2use std::collections::HashMap;
3use serde::{Serialize, Deserialize};
4
5// ============================================================================
6// AnyInfo Structure
7// ============================================================================
8
9/// Contains metadata about a database column.
10///
11/// This struct is used to describe the schema of a model or query result,
12/// providing the necessary information for the query builder to construct
13/// valid SQL statements.
14#[derive(Debug, Clone)]
15pub struct AnyInfo {
16    /// The name of the column in the database.
17    pub column: &'static str,
18
19    /// The SQL type of the column (e.g., "INTEGER", "TEXT", "UUID").
20    pub sql_type: &'static str,
21
22    /// The name of the table this column belongs to (empty for un-associated columns).
23    pub table: &'static str,
24}
25
26// ============================================================================
27// AnyImpl Trait
28// ============================================================================
29
30/// A trait for types that can be mapped from an `AnyRow` and provide column metadata.
31///
32/// This trait is the backbone of the ORM's reflection capabilities. It allows the
33/// system to know which columns correspond to which fields in a Rust struct.
34///
35/// This trait is typically implemented automatically via the `FromAnyRow` derive macro,
36/// but can be implemented manually for custom scenarios.
37pub trait AnyImpl {
38    /// Returns a vector of `AnyInfo` describing the columns associated with this type.
39    fn columns() -> Vec<AnyInfo>;
40
41    /// Converts this instance to a HashMap for dynamic query building.
42    fn to_map(&self) -> HashMap<String, Option<String>>;
43}
44
45/// A trait for types that can be mapped from an `AnyRow`.
46pub trait FromAnyRow: Sized {
47    /// Constructs the type from the whole row.
48    fn from_any_row(row: &AnyRow) -> Result<Self, Error>;
49
50    /// Constructs the type from the row starting at the given index,
51    /// incrementing the index for each column consumed.
52    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error>;
53}
54
55// ============================================================================
56// Primitive Implementations
57// ============================================================================
58
59macro_rules! impl_supported_primitive {
60    ($($t:ty),*) => {
61        $(
62            impl AnyImpl for $t {
63                fn columns() -> Vec<AnyInfo> { Vec::new() }
64                fn to_map(&self) -> HashMap<String, Option<String>> { HashMap::new() }
65            }
66
67            impl FromAnyRow for $t {
68                fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
69                    row.try_get(0).map_err(|e| Error::Decode(Box::new(e)))
70                }
71
72                fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
73                    if *index >= row.len() {
74                        return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
75                    }
76                    let res = row.try_get(*index);
77                    *index += 1;
78                    res.map_err(|e| Error::Decode(Box::new(e)))
79                }
80            }
81        )*
82    };
83}
84
85// Primitives directly supported by sqlx::Any (Decode implemented)
86impl_supported_primitive!(bool, i16, i32, i64, f32, f64, String);
87
88macro_rules! impl_cast_primitive {
89    ($($t:ty),*) => {
90        $(
91            impl AnyImpl for $t {
92                fn columns() -> Vec<AnyInfo> { Vec::new() }
93                fn to_map(&self) -> HashMap<String, Option<String>> { HashMap::new() }
94            }
95
96            impl FromAnyRow for $t {
97                fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
98                    // Try to get as i64 and cast
99                    let val: i64 = row.try_get(0).map_err(|e| Error::Decode(Box::new(e)))?;
100                    Ok(val as $t)
101                }
102
103                fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
104                    if *index >= row.len() {
105                        return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
106                    }
107                    let res = row.try_get::<i64, _>(*index);
108                    *index += 1;
109                    let val = res.map_err(|e| Error::Decode(Box::new(e)))?;
110                    Ok(val as $t)
111                }
112            }
113        )*
114    };
115}
116
117// Primitives that might need casting from i64
118impl_cast_primitive!(i8, isize, u8, u16, u32, u64, usize);
119
120// ============================================================================
121// Array and JSON Implementations
122// ============================================================================
123
124impl<T> AnyImpl for Vec<T>
125where
126    T: AnyImpl + Serialize + for<'de> Deserialize<'de>,
127{
128    fn columns() -> Vec<AnyInfo> {
129        Vec::new()
130    }
131    fn to_map(&self) -> HashMap<String, Option<String>> {
132        let mut map = HashMap::new();
133        if let Ok(json) = serde_json::to_string(self) {
134            map.insert("".to_string(), Some(json));
135        }
136        map
137    }
138}
139
140impl<T> FromAnyRow for Vec<T>
141where
142    T: Serialize + for<'de> Deserialize<'de> + Send,
143{
144    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
145        let mut index = 0;
146        Self::from_any_row_at(row, &mut index)
147    }
148
149    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
150        if *index >= row.len() {
151            return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
152        }
153        let res = row.try_get::<String, _>(*index);
154        *index += 1;
155        let s = res.map_err(|e| Error::Decode(Box::new(e)))?;
156        serde_json::from_str(&s).map_err(|e| Error::Decode(Box::new(e)))
157    }
158}
159
160impl AnyImpl for serde_json::Value {
161    fn columns() -> Vec<AnyInfo> {
162        Vec::new()
163    }
164    fn to_map(&self) -> HashMap<String, Option<String>> {
165        let mut map = HashMap::new();
166        map.insert("".to_string(), Some(self.to_string()));
167        map
168    }
169}
170
171impl FromAnyRow for serde_json::Value {
172    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
173        let mut index = 0;
174        Self::from_any_row_at(row, &mut index)
175    }
176
177    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
178        if *index >= row.len() {
179            return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
180        }
181        let res = row.try_get::<String, _>(*index);
182        *index += 1;
183        let s = res.map_err(|e| Error::Decode(Box::new(e)))?;
184        serde_json::from_str(&s).map_err(|e| Error::Decode(Box::new(e)))
185    }
186}
187
188// ============================================================================
189// External Type Implementations
190// ============================================================================
191
192impl AnyImpl for uuid::Uuid {
193    fn columns() -> Vec<AnyInfo> {
194        Vec::new()
195    }
196    fn to_map(&self) -> HashMap<String, Option<String>> {
197        HashMap::new()
198    }
199}
200
201impl FromAnyRow for uuid::Uuid {
202    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
203        let mut index = 0;
204        Self::from_any_row_at(row, &mut index)
205    }
206
207    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
208        if *index >= row.len() {
209            return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
210        }
211        let res = row.try_get::<String, _>(*index);
212        *index += 1;
213        let s = res.map_err(|e| Error::Decode(Box::new(e)))?;
214        s.parse().map_err(|e| Error::Decode(Box::new(e)))
215    }
216}
217
218impl AnyImpl for chrono::NaiveDateTime {
219    fn columns() -> Vec<AnyInfo> {
220        Vec::new()
221    }
222    fn to_map(&self) -> HashMap<String, Option<String>> {
223        HashMap::new()
224    }
225}
226
227impl FromAnyRow for chrono::NaiveDateTime {
228    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
229        let mut index = 0;
230        Self::from_any_row_at(row, &mut index)
231    }
232
233    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
234        if *index >= row.len() {
235            return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
236        }
237        let res = row.try_get::<String, _>(*index);
238        match res {
239            Ok(s) => {
240                *index += 1;
241                crate::temporal::parse_naive_datetime(&s).map_err(|e| Error::Decode(Box::new(e)))
242            }
243            Err(e) => {
244                // Try numeric fallback (some drivers might return i64 for timestamps)
245                if let Ok(i) = row.try_get::<i64, _>(*index) {
246                    *index += 1;
247                    return Ok(chrono::DateTime::from_timestamp(i, 0).map(|dt| dt.naive_utc()).unwrap_or_default());
248                }
249                // If both fail, we should still increment if it's likely a column was there but we couldn't decode it
250                // Actually, for temporal it's tricky, but if it's NULL, both try_get will fail.
251                // Let's check for NULL explicitly.
252                if let Ok(None) = row.try_get::<Option<String>, _>(*index) {
253                     *index += 1;
254                     return Err(Error::Decode(Box::new(e))); // Option will catch this
255                }
256
257                Err(Error::Decode(Box::new(e)))
258            }
259        }
260    }
261}
262
263impl AnyImpl for chrono::NaiveDate {
264    fn columns() -> Vec<AnyInfo> {
265        Vec::new()
266    }
267    fn to_map(&self) -> HashMap<String, Option<String>> {
268        HashMap::new()
269    }
270}
271
272impl FromAnyRow for chrono::NaiveDate {
273    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
274        let mut index = 0;
275        Self::from_any_row_at(row, &mut index)
276    }
277
278    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
279        if *index >= row.len() {
280            return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
281        }
282        let res = row.try_get::<String, _>(*index);
283        *index += 1;
284        let s = res.map_err(|e| Error::Decode(Box::new(e)))?;
285        crate::temporal::parse_naive_date(&s).map_err(|e| Error::Decode(Box::new(e)))
286    }
287}
288
289impl AnyImpl for chrono::NaiveTime {
290    fn columns() -> Vec<AnyInfo> {
291        Vec::new()
292    }
293    fn to_map(&self) -> HashMap<String, Option<String>> {
294        HashMap::new()
295    }
296}
297
298impl FromAnyRow for chrono::NaiveTime {
299    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
300        let mut index = 0;
301        Self::from_any_row_at(row, &mut index)
302    }
303
304    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
305        if *index >= row.len() {
306            return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
307        }
308        let res = row.try_get::<String, _>(*index);
309        *index += 1;
310        let s = res.map_err(|e| Error::Decode(Box::new(e)))?;
311        crate::temporal::parse_naive_time(&s).map_err(|e| Error::Decode(Box::new(e)))
312    }
313}
314
315impl AnyImpl for chrono::DateTime<chrono::Utc> {
316    fn columns() -> Vec<AnyInfo> {
317        Vec::new()
318    }
319    fn to_map(&self) -> HashMap<String, Option<String>> {
320        HashMap::new()
321    }
322}
323
324impl FromAnyRow for chrono::DateTime<chrono::Utc> {
325    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
326        let mut index = 0;
327        Self::from_any_row_at(row, &mut index)
328    }
329
330    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
331        if *index >= row.len() {
332            return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
333        }
334        let res = row.try_get::<String, _>(*index);
335        match res {
336            Ok(s) => {
337                *index += 1;
338                crate::temporal::parse_datetime_utc(&s).map_err(|e| Error::Decode(Box::new(e)))
339            }
340            Err(e) => {
341                // Try numeric fallback
342                if let Ok(i) = row.try_get::<i64, _>(*index) {
343                    *index += 1;
344                    return Ok(chrono::DateTime::from_timestamp(i, 0).unwrap_or_else(|| chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(chrono::NaiveDateTime::default(), chrono::Utc)));
345                }
346                
347                if let Ok(None) = row.try_get::<Option<String>, _>(*index) {
348                    *index += 1;
349                    return Err(Error::Decode(Box::new(e)));
350                }
351
352                Err(Error::Decode(Box::new(e)))
353            }
354        }
355    }
356}
357
358// ============================================================================
359// Option Implementation
360// ============================================================================
361
362impl<T: AnyImpl> AnyImpl for Option<T> {
363    fn columns() -> Vec<AnyInfo> {
364        T::columns()
365    }
366    fn to_map(&self) -> HashMap<String, Option<String>> {
367        match self {
368            Some(v) => v.to_map(),
369            None => HashMap::new(),
370        }
371    }
372}
373
374impl<T: FromAnyRow> FromAnyRow for Option<T> {
375    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
376        match T::from_any_row(row) {
377            Ok(v) => Ok(Some(v)),
378            Err(_) => Ok(None),
379        }
380    }
381
382    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
383        match T::from_any_row_at(row, index) {
384            Ok(v) => Ok(Some(v)),
385            Err(_) => Ok(None),
386        }
387    }
388}
389
390// ============================================================================
391// Tuple Implementations
392// ============================================================================
393
394macro_rules! impl_any_tuple {
395    ($($T:ident),+) => {
396        impl<$($T: AnyImpl),+> AnyImpl for ($($T,)+) {
397            fn columns() -> Vec<AnyInfo> {
398                let mut cols = Vec::new();
399                $(
400                    cols.extend($T::columns());
401                )+
402                cols
403            }
404
405            fn to_map(&self) -> HashMap<String, Option<String>> {
406                let mut map = HashMap::new();
407                #[allow(non_snake_case)]
408                let ($($T,)+) = self;
409                $(
410                    map.extend($T.to_map());
411                )+
412                map
413            }
414        }
415
416        impl<$($T: FromAnyRow),+> FromAnyRow for ($($T,)+) {
417            fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
418                let mut index = 0;
419                Ok((
420                    $(
421                        $T::from_any_row_at(row, &mut index)?,
422                    )+
423                ))
424            }
425
426            fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
427                Ok((
428                    $(
429                        $T::from_any_row_at(row, index)?,
430                    )+
431                ))
432            }
433        }
434    };
435}
436
437impl_any_tuple!(T1);
438impl_any_tuple!(T1, T2);
439impl_any_tuple!(T1, T2, T3);
440impl_any_tuple!(T1, T2, T3, T4);
441impl_any_tuple!(T1, T2, T3, T4, T5);
442impl_any_tuple!(T1, T2, T3, T4, T5, T6);
443impl_any_tuple!(T1, T2, T3, T4, T5, T6, T7);
444impl_any_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);