Skip to main content

bottle_orm/
any_struct.rs

1use sqlx::{any::AnyRow, Error, Row};
2use std::collections::HashMap;
3
4// ============================================================================
5// AnyInfo Structure
6// ============================================================================
7
8/// Contains metadata about a database column.
9///
10/// This struct is used to describe the schema of a model or query result,
11/// providing the necessary information for the query builder to construct
12/// valid SQL statements.
13#[derive(Debug, Clone)]
14pub struct AnyInfo {
15    /// The name of the column in the database.
16    pub column: &'static str,
17
18    /// The SQL type of the column (e.g., "INTEGER", "TEXT", "UUID").
19    pub sql_type: &'static str,
20
21    /// The name of the table this column belongs to (empty for un-associated columns).
22    pub table: &'static str,
23}
24
25// ============================================================================
26// AnyImpl Trait
27// ============================================================================
28
29/// A trait for types that can be mapped from an `AnyRow` and provide column metadata.
30///
31/// This trait is the backbone of the ORM's reflection capabilities. It allows the
32/// system to know which columns correspond to which fields in a Rust struct.
33///
34/// This trait is typically implemented automatically via the `FromAnyRow` derive macro,
35/// but can be implemented manually for custom scenarios.
36pub trait AnyImpl {
37    /// Returns a vector of `AnyInfo` describing the columns associated with this type.
38    fn columns() -> Vec<AnyInfo>;
39
40    /// Converts this instance to a HashMap for dynamic query building.
41    fn to_map(&self) -> HashMap<String, Option<String>>;
42}
43
44/// A trait for types that can be mapped from an `AnyRow`.
45pub trait FromAnyRow: Sized {
46    /// Constructs the type from the whole row.
47    fn from_any_row(row: &AnyRow) -> Result<Self, Error>;
48
49    /// Constructs the type from the row starting at the given index,
50    /// incrementing the index for each column consumed.
51    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error>;
52}
53
54// ============================================================================
55// Primitive Implementations
56// ============================================================================
57
58macro_rules! impl_supported_primitive {
59    ($($t:ty),*) => {
60        $(
61            impl AnyImpl for $t {
62                fn columns() -> Vec<AnyInfo> { Vec::new() }
63                fn to_map(&self) -> HashMap<String, Option<String>> { HashMap::new() }
64            }
65
66            impl FromAnyRow for $t {
67                fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
68                    row.try_get(0).map_err(|e| Error::Decode(Box::new(e)))
69                }
70
71                fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
72                    let val = row.try_get(*index).map_err(|e| Error::Decode(Box::new(e)))?;
73                    *index += 1;
74                    Ok(val)
75                }
76            }
77        )*
78    };
79}
80
81// Primitives directly supported by sqlx::Any (Decode implemented)
82impl_supported_primitive!(bool, i16, i32, i64, f32, f64, String);
83
84macro_rules! impl_cast_primitive {
85    ($($t:ty),*) => {
86        $(
87            impl AnyImpl for $t {
88                fn columns() -> Vec<AnyInfo> { Vec::new() }
89                fn to_map(&self) -> HashMap<String, Option<String>> { HashMap::new() }
90            }
91
92            impl FromAnyRow for $t {
93                fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
94                    // Try to get as i64 and cast
95                    let val: i64 = row.try_get(0).map_err(|e| Error::Decode(Box::new(e)))?;
96                    Ok(val as $t)
97                }
98
99                fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
100                    let val: i64 = row.try_get(*index).map_err(|e| Error::Decode(Box::new(e)))?;
101                    *index += 1;
102                    Ok(val as $t)
103                }
104            }
105        )*
106    };
107}
108
109// Primitives that might need casting from i64
110impl_cast_primitive!(i8, isize, u8, u16, u32, u64, usize);
111
112// ============================================================================
113// External Type Implementations
114// ============================================================================
115
116impl AnyImpl for uuid::Uuid {
117    fn columns() -> Vec<AnyInfo> {
118        Vec::new()
119    }
120    fn to_map(&self) -> HashMap<String, Option<String>> {
121        HashMap::new()
122    }
123}
124
125impl FromAnyRow for uuid::Uuid {
126    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
127        let mut index = 0;
128        Self::from_any_row_at(row, &mut index)
129    }
130
131    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
132        let s: String = row.try_get(*index).map_err(|e| Error::Decode(Box::new(e)))?;
133        *index += 1;
134        s.parse().map_err(|e| Error::Decode(Box::new(e)))
135    }
136}
137
138impl AnyImpl for chrono::NaiveDateTime {
139    fn columns() -> Vec<AnyInfo> {
140        Vec::new()
141    }
142    fn to_map(&self) -> HashMap<String, Option<String>> {
143        HashMap::new()
144    }
145}
146
147impl FromAnyRow for chrono::NaiveDateTime {
148    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
149        let mut index = 0;
150        Self::from_any_row_at(row, &mut index)
151    }
152
153    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
154        let res = row.try_get::<String, _>(*index);
155        match res {
156            Ok(s) => {
157                *index += 1;
158                crate::temporal::parse_naive_datetime(&s).map_err(|e| Error::Decode(Box::new(e)))
159            }
160            Err(e) => {
161                // Try numeric fallback (some drivers might return i64 for timestamps)
162                if let Ok(i) = row.try_get::<i64, _>(*index) {
163                    *index += 1;
164                    return Ok(chrono::DateTime::from_timestamp(i, 0).map(|dt| dt.naive_utc()).unwrap_or_default());
165                }
166                Err(Error::Decode(Box::new(e)))
167            }
168        }
169    }
170}
171
172impl AnyImpl for chrono::NaiveDate {
173    fn columns() -> Vec<AnyInfo> {
174        Vec::new()
175    }
176    fn to_map(&self) -> HashMap<String, Option<String>> {
177        HashMap::new()
178    }
179}
180
181impl FromAnyRow for chrono::NaiveDate {
182    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
183        let mut index = 0;
184        Self::from_any_row_at(row, &mut index)
185    }
186
187    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
188        let s: String = row.try_get(*index).map_err(|e| Error::Decode(Box::new(e)))?;
189        *index += 1;
190        crate::temporal::parse_naive_date(&s).map_err(|e| Error::Decode(Box::new(e)))
191    }
192}
193
194impl AnyImpl for chrono::NaiveTime {
195    fn columns() -> Vec<AnyInfo> {
196        Vec::new()
197    }
198    fn to_map(&self) -> HashMap<String, Option<String>> {
199        HashMap::new()
200    }
201}
202
203impl FromAnyRow for chrono::NaiveTime {
204    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
205        let mut index = 0;
206        Self::from_any_row_at(row, &mut index)
207    }
208
209    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
210        let s: String = row.try_get(*index).map_err(|e| Error::Decode(Box::new(e)))?;
211        *index += 1;
212        crate::temporal::parse_naive_time(&s).map_err(|e| Error::Decode(Box::new(e)))
213    }
214}
215
216impl AnyImpl for chrono::DateTime<chrono::Utc> {
217    fn columns() -> Vec<AnyInfo> {
218        Vec::new()
219    }
220    fn to_map(&self) -> HashMap<String, Option<String>> {
221        HashMap::new()
222    }
223}
224
225impl FromAnyRow for chrono::DateTime<chrono::Utc> {
226    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
227        let mut index = 0;
228        Self::from_any_row_at(row, &mut index)
229    }
230
231    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
232        let res = row.try_get::<String, _>(*index);
233        match res {
234            Ok(s) => {
235                *index += 1;
236                crate::temporal::parse_datetime_utc(&s).map_err(|e| Error::Decode(Box::new(e)))
237            }
238            Err(e) => {
239                // Try numeric fallback
240                if let Ok(i) = row.try_get::<i64, _>(*index) {
241                    *index += 1;
242                    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)));
243                }
244                Err(Error::Decode(Box::new(e)))
245            }
246        }
247    }
248}
249
250// ============================================================================
251// Option Implementation
252// ============================================================================
253
254impl<T: AnyImpl> AnyImpl for Option<T> {
255    fn columns() -> Vec<AnyInfo> {
256        T::columns()
257    }
258    fn to_map(&self) -> HashMap<String, Option<String>> {
259        match self {
260            Some(v) => v.to_map(),
261            None => HashMap::new(),
262        }
263    }
264}
265
266impl<T: FromAnyRow> FromAnyRow for Option<T> {
267    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
268        match T::from_any_row(row) {
269            Ok(v) => Ok(Some(v)),
270            Err(_) => Ok(None),
271        }
272    }
273
274    fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
275        match T::from_any_row_at(row, index) {
276            Ok(v) => Ok(Some(v)),
277            Err(_) => Ok(None),
278        }
279    }
280}
281
282// ============================================================================
283// Tuple Implementations
284// ============================================================================
285
286macro_rules! impl_any_tuple {
287    ($($T:ident),+) => {
288        impl<$($T: AnyImpl),+> AnyImpl for ($($T,)+) {
289            fn columns() -> Vec<AnyInfo> {
290                let mut cols = Vec::new();
291                $(
292                    cols.extend($T::columns());
293                )+
294                cols
295            }
296
297            fn to_map(&self) -> HashMap<String, Option<String>> {
298                let mut map = HashMap::new();
299                #[allow(non_snake_case)]
300                let ($($T,)+) = self;
301                $(
302                    map.extend($T.to_map());
303                )+
304                map
305            }
306        }
307
308        impl<$($T: FromAnyRow),+> FromAnyRow for ($($T,)+) {
309            fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
310                let mut index = 0;
311                Ok((
312                    $(
313                        $T::from_any_row_at(row, &mut index)?,
314                    )+
315                ))
316            }
317
318            fn from_any_row_at(row: &AnyRow, index: &mut usize) -> Result<Self, Error> {
319                Ok((
320                    $(
321                        $T::from_any_row_at(row, index)?,
322                    )+
323                ))
324            }
325        }
326    };
327}
328
329impl_any_tuple!(T1);
330impl_any_tuple!(T1, T2);
331impl_any_tuple!(T1, T2, T3);
332impl_any_tuple!(T1, T2, T3, T4);
333impl_any_tuple!(T1, T2, T3, T4, T5);
334impl_any_tuple!(T1, T2, T3, T4, T5, T6);
335impl_any_tuple!(T1, T2, T3, T4, T5, T6, T7);
336impl_any_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);