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, String>;
42}
43
44/// A trait for types that can be mapped from an `AnyRow`.
45///
46/// This trait replaces `sqlx::FromRow` for `bottle-orm` to support
47/// Tuple mapping where each element constructs itself from the whole row
48/// (e.g. `(User, Account)`), rather than consuming columns positionally.
49pub trait FromAnyRow: Sized {
50    fn from_any_row(row: &AnyRow) -> Result<Self, Error>;
51}
52
53// ============================================================================
54// Primitive Implementations
55// ============================================================================
56
57macro_rules! impl_supported_primitive {
58    ($($t:ty),*) => {
59        $(
60            impl AnyImpl for $t {
61                fn columns() -> Vec<AnyInfo> { Vec::new() }
62                fn to_map(&self) -> HashMap<String, String> { HashMap::new() }
63            }
64
65            impl FromAnyRow for $t {
66                fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
67                    row.try_get(0)
68                }
69            }
70        )*
71    };
72}
73
74// Primitives directly supported by sqlx::Any (Decode implemented)
75impl_supported_primitive!(bool, i16, i32, i64, f32, f64, String);
76
77macro_rules! impl_cast_primitive {
78    ($($t:ty),*) => {
79        $(
80            impl AnyImpl for $t {
81                fn columns() -> Vec<AnyInfo> { Vec::new() }
82                fn to_map(&self) -> HashMap<String, String> { HashMap::new() }
83            }
84
85            impl FromAnyRow for $t {
86                fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
87                    // Try to get as i64 and cast
88                    let val: i64 = row.try_get(0)?;
89                    Ok(val as $t)
90                }
91            }
92        )*
93    };
94}
95
96// Primitives that might need casting from i64
97impl_cast_primitive!(i8, isize, u8, u16, u32, u64, usize);
98
99// ============================================================================
100// External Type Implementations
101// ============================================================================
102
103impl AnyImpl for uuid::Uuid {
104    fn columns() -> Vec<AnyInfo> {
105        Vec::new()
106    }
107    fn to_map(&self) -> HashMap<String, String> {
108        HashMap::new()
109    }
110}
111
112impl FromAnyRow for uuid::Uuid {
113    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
114        let s: String = row.try_get(0)?;
115        s.parse().map_err(|e| Error::Decode(Box::new(e)))
116    }
117}
118
119impl AnyImpl for chrono::NaiveDateTime {
120    fn columns() -> Vec<AnyInfo> {
121        Vec::new()
122    }
123    fn to_map(&self) -> HashMap<String, String> {
124        HashMap::new()
125    }
126}
127
128impl FromAnyRow for chrono::NaiveDateTime {
129    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
130        let s: String = row.try_get(0)?;
131        s.parse().map_err(|e| Error::Decode(Box::new(e)))
132    }
133}
134
135impl AnyImpl for chrono::NaiveDate {
136    fn columns() -> Vec<AnyInfo> {
137        Vec::new()
138    }
139    fn to_map(&self) -> HashMap<String, String> {
140        HashMap::new()
141    }
142}
143
144impl FromAnyRow for chrono::NaiveDate {
145    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
146        let s: String = row.try_get(0)?;
147        s.parse().map_err(|e| Error::Decode(Box::new(e)))
148    }
149}
150
151impl AnyImpl for chrono::NaiveTime {
152    fn columns() -> Vec<AnyInfo> {
153        Vec::new()
154    }
155    fn to_map(&self) -> HashMap<String, String> {
156        HashMap::new()
157    }
158}
159
160impl FromAnyRow for chrono::NaiveTime {
161    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
162        let s: String = row.try_get(0)?;
163        s.parse().map_err(|e| Error::Decode(Box::new(e)))
164    }
165}
166
167impl AnyImpl for chrono::DateTime<chrono::Utc> {
168    fn columns() -> Vec<AnyInfo> {
169        Vec::new()
170    }
171    fn to_map(&self) -> HashMap<String, String> {
172        HashMap::new()
173    }
174}
175
176impl FromAnyRow for chrono::DateTime<chrono::Utc> {
177    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
178        let s: String = row.try_get(0)?;
179        s.parse().map_err(|e| Error::Decode(Box::new(e)))
180    }
181}
182
183// ============================================================================
184// Option Implementation
185// ============================================================================
186
187impl<T: AnyImpl> AnyImpl for Option<T> {
188    fn columns() -> Vec<AnyInfo> {
189        T::columns()
190    }
191    fn to_map(&self) -> HashMap<String, String> {
192        match self {
193            Some(v) => v.to_map(),
194            None => HashMap::new(),
195        }
196    }
197}
198
199impl<T: FromAnyRow> FromAnyRow for Option<T> {
200    fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
201        // If not null, try to decode T
202        match T::from_any_row(row) {
203            Ok(v) => Ok(Some(v)),
204            Err(_) => Ok(None), // Fallback? Or propagate error?
205                                // If T fails to decode (e.g. invalid format), we probably should propagate error.
206                                // But if T fails because "column not found" (unlikely for index 0) or type mismatch...
207        }
208    }
209}
210
211// Special impl for Option<String> to avoid recursion issues if needed,
212// but T=String is in impl_supported_primitive.
213// Actually, generic impl above relies on T::from_any_row returning Error on NULL.
214// We need to intercept NULL before calling T.
215
216// ============================================================================
217// Tuple Implementations
218// ============================================================================
219
220macro_rules! impl_any_tuple {
221    ($($T:ident),+) => {
222        impl<$($T: AnyImpl),+> AnyImpl for ($($T,)+) {
223            fn columns() -> Vec<AnyInfo> {
224                let mut cols = Vec::new();
225                $(
226                    cols.extend($T::columns());
227                )+
228                cols
229            }
230
231            fn to_map(&self) -> HashMap<String, String> {
232                let mut map = HashMap::new();
233                #[allow(non_snake_case)]
234                let ($($T,)+) = self;
235                $(
236                    map.extend($T.to_map());
237                )+
238                map
239            }
240        }
241
242        impl<$($T: FromAnyRow),+> FromAnyRow for ($($T,)+) {
243            fn from_any_row(row: &AnyRow) -> Result<Self, Error> {
244                Ok((
245                    $(
246                        $T::from_any_row(row)?,
247                    )+
248                ))
249            }
250        }
251    };
252}
253
254impl_any_tuple!(T1);
255impl_any_tuple!(T1, T2);
256impl_any_tuple!(T1, T2, T3);
257impl_any_tuple!(T1, T2, T3, T4);
258impl_any_tuple!(T1, T2, T3, T4, T5);
259impl_any_tuple!(T1, T2, T3, T4, T5, T6);
260impl_any_tuple!(T1, T2, T3, T4, T5, T6, T7);
261impl_any_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);