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                    let val: i64 = row.try_get(0).map_err(|e| Error::Decode(Box::new(e)))?;
127                    <$t>::try_from(val).map_err(|e| Error::Decode(Box::new(e)))
128                }
129
130                fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
131                    if *index >= row.len() {
132                        return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
133                    }
134                    let res = row.try_get::<i64, _>(*index);
135                    *index += 1;
136                    let val = res.map_err(|e| Error::Decode(Box::new(e)))?;
137                    <$t>::try_from(val).map_err(|e| Error::Decode(Box::new(e)))
138                }
139            }
140        )*
141    };
142}
143
144// Primitives that might need casting from i64
145impl_cast_primitive!(i8, isize, u8, u16, u32, u64, usize);
146
147// ============================================================================
148// Array and JSON Implementations
149// ============================================================================
150
151impl<T> AnyImpl for Vec<T>
152where
153    T: AnyImpl + Serialize + for<'de> Deserialize<'de>,
154{
155    fn columns() -> Vec<AnyInfo> {
156        Vec::new()
157    }
158    fn to_map(&self) -> HashMap<String, Option<String>> {
159        let mut map = HashMap::new();
160        if let Ok(json) = serde_json::to_string(self) {
161            map.insert("".to_string(), Some(json));
162        }
163        map
164    }
165}
166
167impl<T> FromAnyRow for Vec<T>
168where
169    T: Serialize + for<'de> Deserialize<'de> + Send,
170{
171    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
172        let mut index = 0;
173        Self::from_any_row_at(row, &mut index)
174    }
175
176    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
177        if *index >= row.len() {
178            return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
179        }
180        let res = row.try_get::<String, _>(*index);
181        *index += 1;
182        let s = res.map_err(|e| Error::Decode(Box::new(e)))?;
183        serde_json::from_str(&s).map_err(|e| Error::Decode(Box::new(e)))
184    }
185}
186
187impl AnyImpl for serde_json::Value {
188    fn columns() -> Vec<AnyInfo> {
189        Vec::new()
190    }
191    fn to_map(&self) -> HashMap<String, Option<String>> {
192        let mut map = HashMap::new();
193        map.insert("".to_string(), Some(self.to_string()));
194        map
195    }
196}
197
198impl FromAnyRow for serde_json::Value {
199    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
200        let mut index = 0;
201        Self::from_any_row_at(row, &mut index)
202    }
203
204    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
205        if *index >= row.len() {
206            return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
207        }
208        let res = row.try_get::<String, _>(*index);
209        *index += 1;
210        let s = res.map_err(|e| Error::Decode(Box::new(e)))?;
211        serde_json::from_str(&s).map_err(|e| Error::Decode(Box::new(e)))
212    }
213}
214
215// ============================================================================
216// External Type Implementations
217// ============================================================================
218
219impl AnyImpl for uuid::Uuid {
220    fn columns() -> Vec<AnyInfo> {
221        Vec::new()
222    }
223    fn to_map(&self) -> HashMap<String, Option<String>> {
224        HashMap::new()
225    }
226}
227
228impl FromAnyRow for uuid::Uuid {
229    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
230        let mut index = 0;
231        Self::from_any_row_at(row, &mut index)
232    }
233
234    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
235        if *index >= row.len() {
236            return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
237        }
238        let res = row.try_get::<String, _>(*index);
239        *index += 1;
240        let s = res.map_err(|e| Error::Decode(Box::new(e)))?;
241        s.parse().map_err(|e| Error::Decode(Box::new(e)))
242    }
243}
244
245impl AnyImpl for chrono::NaiveDateTime {
246    fn columns() -> Vec<AnyInfo> {
247        Vec::new()
248    }
249    fn to_map(&self) -> HashMap<String, Option<String>> {
250        HashMap::new()
251    }
252}
253
254impl FromAnyRow for chrono::NaiveDateTime {
255    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
256        let mut index = 0;
257        Self::from_any_row_at(row, &mut index)
258    }
259
260    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
261        if *index >= row.len() {
262            return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
263        }
264        let res = row.try_get::<String, _>(*index);
265        match res {
266            Ok(s) => {
267                *index += 1;
268                crate::temporal::parse_naive_datetime(&s).map_err(|e| Error::Decode(Box::new(e)))
269            }
270            Err(e) => {
271                // Try numeric fallback (some drivers might return i64 for timestamps)
272                if let Ok(i) = row.try_get::<i64, _>(*index) {
273                    *index += 1;
274                    return Ok(chrono::DateTime::from_timestamp(i, 0).map(|dt| dt.naive_utc()).unwrap_or_default());
275                }
276                // If both fail, we should still increment if it's likely a column was there but we couldn't decode it
277                // Actually, for temporal it's tricky, but if it's NULL, both try_get will fail.
278                // Let's check for NULL explicitly.
279                if let Ok(None) = row.try_get::<Option<String>, _>(*index) {
280                     *index += 1;
281                     return Err(Error::Decode(Box::new(e))); // Option will catch this
282                }
283
284                Err(Error::Decode(Box::new(e)))
285            }
286        }
287    }
288}
289
290impl AnyImpl for chrono::NaiveDate {
291    fn columns() -> Vec<AnyInfo> {
292        Vec::new()
293    }
294    fn to_map(&self) -> HashMap<String, Option<String>> {
295        HashMap::new()
296    }
297}
298
299impl FromAnyRow for chrono::NaiveDate {
300    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
301        let mut index = 0;
302        Self::from_any_row_at(row, &mut index)
303    }
304
305    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
306        if *index >= row.len() {
307            return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
308        }
309        let res = row.try_get::<String, _>(*index);
310        *index += 1;
311        let s = res.map_err(|e| Error::Decode(Box::new(e)))?;
312        crate::temporal::parse_naive_date(&s).map_err(|e| Error::Decode(Box::new(e)))
313    }
314}
315
316impl AnyImpl for chrono::NaiveTime {
317    fn columns() -> Vec<AnyInfo> {
318        Vec::new()
319    }
320    fn to_map(&self) -> HashMap<String, Option<String>> {
321        HashMap::new()
322    }
323}
324
325impl FromAnyRow for chrono::NaiveTime {
326    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
327        let mut index = 0;
328        Self::from_any_row_at(row, &mut index)
329    }
330
331    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
332        if *index >= row.len() {
333            return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
334        }
335        let res = row.try_get::<String, _>(*index);
336        *index += 1;
337        let s = res.map_err(|e| Error::Decode(Box::new(e)))?;
338        crate::temporal::parse_naive_time(&s).map_err(|e| Error::Decode(Box::new(e)))
339    }
340}
341
342impl AnyImpl for chrono::DateTime<chrono::Utc> {
343    fn columns() -> Vec<AnyInfo> {
344        Vec::new()
345    }
346    fn to_map(&self) -> HashMap<String, Option<String>> {
347        HashMap::new()
348    }
349}
350
351impl FromAnyRow for chrono::DateTime<chrono::Utc> {
352    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
353        let mut index = 0;
354        Self::from_any_row_at(row, &mut index)
355    }
356
357    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
358        if *index >= row.len() {
359            return Err(Error::ColumnIndexOutOfBounds { index: *index, len: row.len() });
360        }
361        let res = row.try_get::<String, _>(*index);
362        match res {
363            Ok(s) => {
364                *index += 1;
365                crate::temporal::parse_datetime_utc(&s).map_err(|e| Error::Decode(Box::new(e)))
366            }
367            Err(e) => {
368                // Try numeric fallback
369                if let Ok(i) = row.try_get::<i64, _>(*index) {
370                    *index += 1;
371                    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)));
372                }
373                
374                if let Ok(None) = row.try_get::<Option<String>, _>(*index) {
375                    *index += 1;
376                    return Err(Error::Decode(Box::new(e)));
377                }
378
379                Err(Error::Decode(Box::new(e)))
380            }
381        }
382    }
383}
384
385// ============================================================================
386// Option Implementation
387// ============================================================================
388
389impl<T: AnyImpl> AnyImpl for Option<T> {
390    fn columns() -> Vec<AnyInfo> {
391        T::columns()
392    }
393    fn to_map(&self) -> HashMap<String, Option<String>> {
394        match self {
395            Some(v) => v.to_map(),
396            None => HashMap::new(),
397        }
398    }
399}
400
401impl<T: FromAnyRow> FromAnyRow for Option<T> {
402    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
403        match T::from_any_row(row) {
404            Ok(v) => Ok(Some(v)),
405            Err(_) => Ok(None),
406        }
407    }
408
409    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
410        match T::from_any_row_at(row, index) {
411            Ok(v) => Ok(Some(v)),
412            Err(_) => Ok(None),
413        }
414    }
415}
416
417// ============================================================================
418// Tuple Implementations
419// ============================================================================
420
421macro_rules! impl_any_tuple {
422    ($($T:ident),+) => {
423        impl<$($T: AnyImpl),+> AnyImpl for ($($T,)+) {
424            fn columns() -> Vec<AnyInfo> {
425                let mut cols = Vec::new();
426                $(
427                    cols.extend($T::columns());
428                )+
429                cols
430            }
431
432            fn to_map(&self) -> HashMap<String, Option<String>> {
433                let mut map = HashMap::new();
434                #[allow(non_snake_case)]
435                let ($($T,)+) = self;
436                $(
437                    map.extend($T.to_map());
438                )+
439                map
440            }
441        }
442
443        impl<$($T: FromAnyRow),+> FromAnyRow for ($($T,)+) {
444            fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
445                let mut index = 0;
446                Ok((
447                    $(
448                        $T::from_any_row_at(row, &mut index)?,
449                    )+
450                ))
451            }
452
453            fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
454                Ok((
455                    $(
456                        $T::from_any_row_at(row, index)?,
457                    )+
458                ))
459            }
460        }
461    };
462}
463
464impl_any_tuple!(T1);
465impl_any_tuple!(T1, T2);
466impl_any_tuple!(T1, T2, T3);
467impl_any_tuple!(T1, T2, T3, T4);
468impl_any_tuple!(T1, T2, T3, T4, T5);
469impl_any_tuple!(T1, T2, T3, T4, T5, T6);
470impl_any_tuple!(T1, T2, T3, T4, T5, T6, T7);
471impl_any_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);