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