audb_runtime/
executor.rs

1//! Query execution traits
2//!
3//! This module defines traits for type-safe query execution and result deserialization.
4
5use crate::Database;
6use crate::error::Result;
7use crate::types::{QueryResult, Row, Value};
8use async_trait::async_trait;
9use chrono::{DateTime, Utc};
10use uuid::Uuid;
11
12/// Trait for executing queries with type-safe results
13///
14/// This trait is implemented by generated query functions to provide
15/// type-safe query execution.
16///
17/// ## Example
18///
19/// ```ignore
20/// // Generated code might look like:
21/// struct GetUserQuery {
22///     id: Uuid,
23/// }
24///
25/// #[async_trait]
26/// impl QueryExecutor for GetUserQuery {
27///     type Output = User;
28///
29///     async fn execute(&self, db: &Database) -> Result<Self::Output> {
30///         let result = db.execute_hyperql("SELECT * FROM users WHERE id = :id").await?;
31///         let row = result.one()?;
32///         User::from_row(row)
33///     }
34/// }
35/// ```
36#[async_trait]
37pub trait QueryExecutor {
38    /// The output type of the query
39    type Output;
40
41    /// Execute the query against the database
42    async fn execute(&self, db: &Database) -> Result<Self::Output>;
43}
44
45/// Trait for deserializing a row into a Rust type
46///
47/// This trait is implemented by schema types to enable conversion
48/// from database rows to strongly-typed structs.
49///
50/// ## Example
51///
52/// ```ignore
53/// struct User {
54///     id: Uuid,
55///     name: String,
56///     age: i64,
57/// }
58///
59/// impl FromRow for User {
60///     fn from_row(row: Row) -> Result<Self> {
61///         Ok(User {
62///             id: row.get_required("id")?.as_uuid()?,
63///             name: row.get_required("name")?.as_str()?.to_string(),
64///             age: row.get_required("age")?.as_i64()?,
65///         })
66///     }
67/// }
68/// ```
69pub trait FromRow: Sized {
70    /// Convert a row into this type
71    fn from_row(row: Row) -> Result<Self>;
72
73    /// Convert multiple rows into a vector of this type
74    fn from_rows(result: QueryResult) -> Result<Vec<Self>> {
75        result.into_iter().map(|row| Self::from_row(row)).collect()
76    }
77
78    /// Convert a single row result, returning an error if zero or multiple rows
79    fn from_single_row(result: QueryResult) -> Result<Self> {
80        let row = result.one()?;
81        Self::from_row(row)
82    }
83
84    /// Convert an optional single row result
85    fn from_optional_row(result: QueryResult) -> Result<Option<Self>> {
86        match result.optional()? {
87            Some(row) => Ok(Some(Self::from_row(row)?)),
88            None => Ok(None),
89        }
90    }
91}
92
93// Implement FromRow for primitive types
94
95impl FromRow for bool {
96    fn from_row(row: Row) -> Result<Self> {
97        // For primitives, assume single column named "value"
98        row.get_required("value")?.as_bool()
99    }
100}
101
102impl FromRow for i64 {
103    fn from_row(row: Row) -> Result<Self> {
104        row.get_required("value")?.as_i64()
105    }
106}
107
108impl FromRow for f64 {
109    fn from_row(row: Row) -> Result<Self> {
110        row.get_required("value")?.as_f64()
111    }
112}
113
114impl FromRow for String {
115    fn from_row(row: Row) -> Result<Self> {
116        Ok(row.get_required("value")?.as_str()?.to_string())
117    }
118}
119
120impl FromRow for Uuid {
121    fn from_row(row: Row) -> Result<Self> {
122        row.get_required("value")?.as_uuid()
123    }
124}
125
126impl FromRow for DateTime<Utc> {
127    fn from_row(row: Row) -> Result<Self> {
128        row.get_required("value")?.as_timestamp()
129    }
130}
131
132// Implement FromRow for Option<T>
133impl<T: FromRow> FromRow for Option<T> {
134    fn from_row(row: Row) -> Result<Self> {
135        if row.is_empty() {
136            return Ok(None);
137        }
138        Ok(Some(T::from_row(row)?))
139    }
140}
141
142/// Helper trait for converting Values to specific types
143///
144/// This is used internally by FromRow implementations.
145pub trait FromValue: Sized {
146    /// Convert a Value to this type
147    fn from_value(value: &Value) -> Result<Self>;
148}
149
150impl FromValue for bool {
151    fn from_value(value: &Value) -> Result<Self> {
152        value.as_bool()
153    }
154}
155
156impl FromValue for i64 {
157    fn from_value(value: &Value) -> Result<Self> {
158        value.as_i64()
159    }
160}
161
162impl FromValue for f64 {
163    fn from_value(value: &Value) -> Result<Self> {
164        value.as_f64()
165    }
166}
167
168impl FromValue for String {
169    fn from_value(value: &Value) -> Result<Self> {
170        Ok(value.as_str()?.to_string())
171    }
172}
173
174impl FromValue for Uuid {
175    fn from_value(value: &Value) -> Result<Self> {
176        value.as_uuid()
177    }
178}
179
180impl FromValue for DateTime<Utc> {
181    fn from_value(value: &Value) -> Result<Self> {
182        value.as_timestamp()
183    }
184}
185
186impl<T: FromValue> FromValue for Option<T> {
187    fn from_value(value: &Value) -> Result<Self> {
188        if value.is_null() {
189            return Ok(None);
190        }
191        Ok(Some(T::from_value(value)?))
192    }
193}
194
195impl<T: FromValue> FromValue for Vec<T> {
196    fn from_value(value: &Value) -> Result<Self> {
197        let array = value.as_array()?;
198        array.iter().map(|v| T::from_value(v)).collect()
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205
206    #[test]
207    fn test_from_row_primitives() {
208        let mut row = Row::new();
209        row.insert("value".to_string(), Value::Bool(true));
210        assert_eq!(bool::from_row(row).unwrap(), true);
211
212        let mut row = Row::new();
213        row.insert("value".to_string(), Value::Integer(42));
214        assert_eq!(i64::from_row(row).unwrap(), 42);
215
216        let mut row = Row::new();
217        row.insert("value".to_string(), Value::Float(3.14));
218        assert!((f64::from_row(row).unwrap() - 3.14).abs() < 0.01);
219
220        let mut row = Row::new();
221        row.insert("value".to_string(), Value::String("hello".to_string()));
222        assert_eq!(String::from_row(row).unwrap(), "hello");
223    }
224
225    #[test]
226    fn test_from_row_uuid() {
227        let uuid = Uuid::new_v4();
228        let mut row = Row::new();
229        row.insert("value".to_string(), Value::Uuid(uuid));
230        assert_eq!(Uuid::from_row(row).unwrap(), uuid);
231    }
232
233    #[test]
234    fn test_from_row_timestamp() {
235        let now = Utc::now();
236        let mut row = Row::new();
237        row.insert("value".to_string(), Value::Timestamp(now));
238        assert_eq!(DateTime::<Utc>::from_row(row).unwrap(), now);
239    }
240
241    #[test]
242    fn test_from_row_option() {
243        let empty_row = Row::new();
244        let result: Option<String> = Option::from_row(empty_row).unwrap();
245        assert_eq!(result, None);
246
247        let mut row = Row::new();
248        row.insert("value".to_string(), Value::String("hello".to_string()));
249        let result: Option<String> = Option::from_row(row).unwrap();
250        assert_eq!(result, Some("hello".to_string()));
251    }
252
253    #[test]
254    fn test_from_rows() {
255        let mut row1 = Row::new();
256        row1.insert("value".to_string(), Value::Integer(1));
257        let mut row2 = Row::new();
258        row2.insert("value".to_string(), Value::Integer(2));
259
260        let result = QueryResult::with_rows(vec![row1, row2]);
261        let values = i64::from_rows(result).unwrap();
262        assert_eq!(values, vec![1, 2]);
263    }
264
265    #[test]
266    fn test_from_single_row() {
267        let mut row = Row::new();
268        row.insert("value".to_string(), Value::Integer(42));
269
270        let result = QueryResult::with_rows(vec![row]);
271        let value = i64::from_single_row(result).unwrap();
272        assert_eq!(value, 42);
273
274        // Test error cases
275        let empty = QueryResult::new();
276        assert!(i64::from_single_row(empty).is_err());
277
278        let mut row1 = Row::new();
279        row1.insert("value".to_string(), Value::Integer(1));
280        let mut row2 = Row::new();
281        row2.insert("value".to_string(), Value::Integer(2));
282        let multiple = QueryResult::with_rows(vec![row1, row2]);
283        assert!(i64::from_single_row(multiple).is_err());
284    }
285
286    #[test]
287    fn test_from_optional_row() {
288        let empty = QueryResult::new();
289        let result: Option<i64> = i64::from_optional_row(empty).unwrap();
290        assert_eq!(result, None);
291
292        let mut row = Row::new();
293        row.insert("value".to_string(), Value::Integer(42));
294        let single = QueryResult::with_rows(vec![row]);
295        let result: Option<i64> = i64::from_optional_row(single).unwrap();
296        assert_eq!(result, Some(42));
297    }
298
299    #[test]
300    fn test_from_value_primitives() {
301        assert_eq!(bool::from_value(&Value::Bool(true)).unwrap(), true);
302        assert_eq!(i64::from_value(&Value::Integer(42)).unwrap(), 42);
303        assert_eq!(
304            String::from_value(&Value::String("hi".to_string())).unwrap(),
305            "hi"
306        );
307    }
308
309    #[test]
310    fn test_from_value_option() {
311        let null = Value::Null;
312        let result: Option<i64> = Option::from_value(&null).unwrap();
313        assert_eq!(result, None);
314
315        let value = Value::Integer(42);
316        let result: Option<i64> = Option::from_value(&value).unwrap();
317        assert_eq!(result, Some(42));
318    }
319
320    #[test]
321    fn test_from_value_vec() {
322        let array = Value::Array(vec![
323            Value::Integer(1),
324            Value::Integer(2),
325            Value::Integer(3),
326        ]);
327        let result: Vec<i64> = Vec::from_value(&array).unwrap();
328        assert_eq!(result, vec![1, 2, 3]);
329    }
330
331    #[test]
332    fn test_from_value_type_mismatch() {
333        let value = Value::Integer(42);
334        assert!(bool::from_value(&value).is_err());
335        assert!(String::from_value(&value).is_err());
336    }
337
338    // Example of a custom type implementing FromRow
339    #[derive(Debug, PartialEq)]
340    struct TestUser {
341        id: i64,
342        name: String,
343        active: bool,
344    }
345
346    impl FromRow for TestUser {
347        fn from_row(row: Row) -> Result<Self> {
348            Ok(TestUser {
349                id: row.get_required("id")?.as_i64()?,
350                name: row.get_required("name")?.as_str()?.to_string(),
351                active: row.get_required("active")?.as_bool()?,
352            })
353        }
354    }
355
356    #[test]
357    fn test_custom_from_row() {
358        let mut row = Row::new();
359        row.insert("id".to_string(), Value::Integer(1));
360        row.insert("name".to_string(), Value::String("Alice".to_string()));
361        row.insert("active".to_string(), Value::Bool(true));
362
363        let user = TestUser::from_row(row).unwrap();
364        assert_eq!(
365            user,
366            TestUser {
367                id: 1,
368                name: "Alice".to_string(),
369                active: true,
370            }
371        );
372    }
373
374    #[test]
375    fn test_custom_from_rows() {
376        let mut row1 = Row::new();
377        row1.insert("id".to_string(), Value::Integer(1));
378        row1.insert("name".to_string(), Value::String("Alice".to_string()));
379        row1.insert("active".to_string(), Value::Bool(true));
380
381        let mut row2 = Row::new();
382        row2.insert("id".to_string(), Value::Integer(2));
383        row2.insert("name".to_string(), Value::String("Bob".to_string()));
384        row2.insert("active".to_string(), Value::Bool(false));
385
386        let result = QueryResult::with_rows(vec![row1, row2]);
387        let users = TestUser::from_rows(result).unwrap();
388
389        assert_eq!(users.len(), 2);
390        assert_eq!(users[0].name, "Alice");
391        assert_eq!(users[1].name, "Bob");
392    }
393}