audb_runtime/
types.rs

1//! Runtime type definitions
2//!
3//! This module defines the core types used for query results and data representation.
4
5use crate::error::{QueryError, Result};
6use chrono::{DateTime, Utc};
7use std::collections::HashMap;
8use uuid::Uuid;
9
10/// Result of a query execution
11///
12/// Contains the rows returned by the query and metadata about the execution.
13#[derive(Debug, Clone)]
14pub struct QueryResult {
15    /// Rows returned by the query
16    pub rows: Vec<Row>,
17
18    /// Number of rows affected by the query (for INSERT, UPDATE, DELETE)
19    pub affected_rows: usize,
20}
21
22impl QueryResult {
23    /// Create a new empty query result
24    pub fn new() -> Self {
25        Self {
26            rows: Vec::new(),
27            affected_rows: 0,
28        }
29    }
30
31    /// Create a new query result with rows
32    pub fn with_rows(rows: Vec<Row>) -> Self {
33        Self {
34            rows,
35            affected_rows: 0,
36        }
37    }
38
39    /// Create a new query result for a modification query
40    pub fn with_affected_rows(affected_rows: usize) -> Self {
41        Self {
42            rows: Vec::new(),
43            affected_rows,
44        }
45    }
46
47    /// Get the number of rows returned
48    pub fn len(&self) -> usize {
49        self.rows.len()
50    }
51
52    /// Check if the result is empty
53    pub fn is_empty(&self) -> bool {
54        self.rows.is_empty()
55    }
56
57    /// Get a single row, returning an error if zero or multiple rows exist
58    pub fn one(self) -> Result<Row> {
59        match self.rows.len() {
60            0 => Err(QueryError::RowNotFound),
61            1 => Ok(self.rows.into_iter().next().unwrap()),
62            n => Err(QueryError::MultipleRowsFound { count: n }),
63        }
64    }
65
66    /// Get an optional single row, returning None if zero rows, error if multiple
67    pub fn optional(self) -> Result<Option<Row>> {
68        match self.rows.len() {
69            0 => Ok(None),
70            1 => Ok(Some(self.rows.into_iter().next().unwrap())),
71            n => Err(QueryError::MultipleRowsFound { count: n }),
72        }
73    }
74
75    /// Get all rows as a Vec
76    pub fn all(self) -> Vec<Row> {
77        self.rows
78    }
79
80    /// Iterate over rows
81    pub fn iter(&self) -> impl Iterator<Item = &Row> {
82        self.rows.iter()
83    }
84}
85
86impl Default for QueryResult {
87    fn default() -> Self {
88        Self::new()
89    }
90}
91
92impl IntoIterator for QueryResult {
93    type Item = Row;
94    type IntoIter = std::vec::IntoIter<Row>;
95
96    fn into_iter(self) -> Self::IntoIter {
97        self.rows.into_iter()
98    }
99}
100
101/// A single row from a query result
102///
103/// Rows are represented as maps from column names to values.
104#[derive(Debug, Clone, PartialEq)]
105pub struct Row {
106    /// Column values
107    columns: HashMap<String, Value>,
108}
109
110impl Row {
111    /// Create a new empty row
112    pub fn new() -> Self {
113        Self {
114            columns: HashMap::new(),
115        }
116    }
117
118    /// Create a row from a map of columns
119    pub fn from_map(columns: HashMap<String, Value>) -> Self {
120        Self { columns }
121    }
122
123    /// Get a value by column name
124    pub fn get(&self, column: &str) -> Option<&Value> {
125        self.columns.get(column)
126    }
127
128    /// Get a required value by column name
129    pub fn get_required(&self, column: &str) -> Result<&Value> {
130        self.columns
131            .get(column)
132            .ok_or_else(|| QueryError::missing_field(column))
133    }
134
135    /// Set a column value
136    pub fn insert(&mut self, column: String, value: Value) {
137        self.columns.insert(column, value);
138    }
139
140    /// Check if a column exists
141    pub fn has_column(&self, column: &str) -> bool {
142        self.columns.contains_key(column)
143    }
144
145    /// Get all column names
146    pub fn columns(&self) -> Vec<&str> {
147        self.columns.keys().map(|s| s.as_str()).collect()
148    }
149
150    /// Get the number of columns
151    pub fn len(&self) -> usize {
152        self.columns.len()
153    }
154
155    /// Check if the row is empty
156    pub fn is_empty(&self) -> bool {
157        self.columns.is_empty()
158    }
159}
160
161impl Default for Row {
162    fn default() -> Self {
163        Self::new()
164    }
165}
166
167/// A value that can be stored in a database
168///
169/// This enum represents all possible value types in AuDB.
170#[derive(Debug, Clone, PartialEq)]
171pub enum Value {
172    /// Null value
173    Null,
174
175    /// Boolean value
176    Bool(bool),
177
178    /// Integer value (i64)
179    Integer(i64),
180
181    /// Float value (f64)
182    Float(f64),
183
184    /// String value
185    String(String),
186
187    /// UUID value (entity ID)
188    Uuid(Uuid),
189
190    /// Timestamp value
191    Timestamp(DateTime<Utc>),
192
193    /// Binary data
194    Bytes(Vec<u8>),
195
196    /// Array of values
197    Array(Vec<Value>),
198
199    /// Object (key-value pairs)
200    Object(HashMap<String, Value>),
201}
202
203impl Value {
204    /// Check if the value is null
205    pub fn is_null(&self) -> bool {
206        matches!(self, Value::Null)
207    }
208
209    /// Try to convert to a boolean
210    pub fn as_bool(&self) -> Result<bool> {
211        match self {
212            Value::Bool(b) => Ok(*b),
213            _ => Err(QueryError::type_mismatch("Bool", self.type_name())),
214        }
215    }
216
217    /// Try to convert to an integer
218    pub fn as_i64(&self) -> Result<i64> {
219        match self {
220            Value::Integer(i) => Ok(*i),
221            _ => Err(QueryError::type_mismatch("Integer", self.type_name())),
222        }
223    }
224
225    /// Try to convert to a float
226    pub fn as_f64(&self) -> Result<f64> {
227        match self {
228            Value::Float(f) => Ok(*f),
229            Value::Integer(i) => Ok(*i as f64),
230            _ => Err(QueryError::type_mismatch("Float", self.type_name())),
231        }
232    }
233
234    /// Try to convert to a string
235    pub fn as_str(&self) -> Result<&str> {
236        match self {
237            Value::String(s) => Ok(s.as_str()),
238            _ => Err(QueryError::type_mismatch("String", self.type_name())),
239        }
240    }
241
242    /// Try to convert to a UUID
243    pub fn as_uuid(&self) -> Result<Uuid> {
244        match self {
245            Value::Uuid(u) => Ok(*u),
246            _ => Err(QueryError::type_mismatch("Uuid", self.type_name())),
247        }
248    }
249
250    /// Try to convert to a timestamp
251    pub fn as_timestamp(&self) -> Result<DateTime<Utc>> {
252        match self {
253            Value::Timestamp(t) => Ok(*t),
254            _ => Err(QueryError::type_mismatch("Timestamp", self.type_name())),
255        }
256    }
257
258    /// Try to convert to bytes
259    pub fn as_bytes(&self) -> Result<&Vec<u8>> {
260        match self {
261            Value::Bytes(b) => Ok(b),
262            _ => Err(QueryError::type_mismatch("Bytes", self.type_name())),
263        }
264    }
265
266    /// Try to convert to an array
267    pub fn as_array(&self) -> Result<&Vec<Value>> {
268        match self {
269            Value::Array(a) => Ok(a),
270            _ => Err(QueryError::type_mismatch("Array", self.type_name())),
271        }
272    }
273
274    /// Try to convert to an object
275    pub fn as_object(&self) -> Result<&HashMap<String, Value>> {
276        match self {
277            Value::Object(o) => Ok(o),
278            _ => Err(QueryError::type_mismatch("Object", self.type_name())),
279        }
280    }
281
282    /// Get the type name as a string
283    pub fn type_name(&self) -> &str {
284        match self {
285            Value::Null => "Null",
286            Value::Bool(_) => "Bool",
287            Value::Integer(_) => "Integer",
288            Value::Float(_) => "Float",
289            Value::String(_) => "String",
290            Value::Bytes(_) => "Bytes",
291            Value::Uuid(_) => "Uuid",
292            Value::Timestamp(_) => "Timestamp",
293            Value::Array(_) => "Array",
294            Value::Object(_) => "Object",
295        }
296    }
297}
298
299// Conversions from Rust types to Value
300
301impl From<bool> for Value {
302    fn from(b: bool) -> Self {
303        Value::Bool(b)
304    }
305}
306
307impl From<i64> for Value {
308    fn from(i: i64) -> Self {
309        Value::Integer(i)
310    }
311}
312
313impl From<i32> for Value {
314    fn from(i: i32) -> Self {
315        Value::Integer(i as i64)
316    }
317}
318
319impl From<f64> for Value {
320    fn from(f: f64) -> Self {
321        Value::Float(f)
322    }
323}
324
325impl From<String> for Value {
326    fn from(s: String) -> Self {
327        Value::String(s)
328    }
329}
330
331impl From<&str> for Value {
332    fn from(s: &str) -> Self {
333        Value::String(s.to_string())
334    }
335}
336
337impl From<Uuid> for Value {
338    fn from(u: Uuid) -> Self {
339        Value::Uuid(u)
340    }
341}
342
343impl From<DateTime<Utc>> for Value {
344    fn from(t: DateTime<Utc>) -> Self {
345        Value::Timestamp(t)
346    }
347}
348
349impl From<Vec<u8>> for Value {
350    fn from(b: Vec<u8>) -> Self {
351        Value::Bytes(b)
352    }
353}
354
355#[cfg(test)]
356mod tests {
357    use super::*;
358
359    #[test]
360    fn test_query_result_new() {
361        let result = QueryResult::new();
362        assert!(result.is_empty());
363        assert_eq!(result.len(), 0);
364        assert_eq!(result.affected_rows, 0);
365    }
366
367    #[test]
368    fn test_query_result_with_rows() {
369        let rows = vec![Row::new(), Row::new()];
370        let result = QueryResult::with_rows(rows);
371        assert_eq!(result.len(), 2);
372        assert!(!result.is_empty());
373    }
374
375    #[test]
376    fn test_query_result_one() {
377        let result = QueryResult::with_rows(vec![Row::new()]);
378        assert!(result.one().is_ok());
379
380        let empty = QueryResult::new();
381        assert!(empty.one().is_err());
382
383        let multiple = QueryResult::with_rows(vec![Row::new(), Row::new()]);
384        assert!(multiple.one().is_err());
385    }
386
387    #[test]
388    fn test_query_result_optional() {
389        let result = QueryResult::new();
390        assert_eq!(result.optional().unwrap(), None);
391
392        let one = QueryResult::with_rows(vec![Row::new()]);
393        assert!(one.optional().unwrap().is_some());
394
395        let multiple = QueryResult::with_rows(vec![Row::new(), Row::new()]);
396        assert!(multiple.optional().is_err());
397    }
398
399    #[test]
400    fn test_row_operations() {
401        let mut row = Row::new();
402        assert!(row.is_empty());
403
404        row.insert("id".to_string(), Value::Integer(1));
405        row.insert("name".to_string(), Value::String("Alice".to_string()));
406
407        assert_eq!(row.len(), 2);
408        assert!(row.get("id").is_some());
409        assert!(row.get("name").is_some());
410        assert!(row.get("missing").is_none());
411    }
412
413    #[test]
414    fn test_row_get_required() {
415        let mut row = Row::new();
416        row.insert("id".to_string(), Value::Integer(1));
417
418        assert!(row.get_required("id").is_ok());
419        assert!(row.get_required("missing").is_err());
420    }
421
422    #[test]
423    fn test_value_types() {
424        assert!(Value::Null.is_null());
425        assert!(!Value::Bool(true).is_null());
426
427        let bool_val = Value::Bool(true);
428        assert_eq!(bool_val.as_bool().unwrap(), true);
429
430        let int_val = Value::Integer(42);
431        assert_eq!(int_val.as_i64().unwrap(), 42);
432
433        let float_val = Value::Float(3.14);
434        assert!((float_val.as_f64().unwrap() - 3.14).abs() < 0.01);
435
436        let str_val = Value::String("hello".to_string());
437        assert_eq!(str_val.as_str().unwrap(), "hello");
438    }
439
440    #[test]
441    fn test_value_type_mismatch() {
442        let int_val = Value::Integer(42);
443        assert!(int_val.as_bool().is_err());
444        assert!(int_val.as_str().is_err());
445
446        let str_val = Value::String("hello".to_string());
447        assert!(str_val.as_i64().is_err());
448    }
449
450    #[test]
451    fn test_value_type_name() {
452        assert_eq!(Value::Null.type_name(), "Null");
453        assert_eq!(Value::Bool(true).type_name(), "Bool");
454        assert_eq!(Value::Integer(1).type_name(), "Integer");
455        assert_eq!(Value::Float(1.0).type_name(), "Float");
456        assert_eq!(Value::String("".to_string()).type_name(), "String");
457        assert_eq!(Value::Uuid(Uuid::new_v4()).type_name(), "Uuid");
458    }
459
460    #[test]
461    fn test_value_conversions() {
462        let bool_val: Value = true.into();
463        assert!(matches!(bool_val, Value::Bool(true)));
464
465        let int_val: Value = 42i64.into();
466        assert!(matches!(int_val, Value::Integer(42)));
467
468        let str_val: Value = "hello".into();
469        assert!(matches!(str_val, Value::String(_)));
470
471        let uuid = Uuid::new_v4();
472        let uuid_val: Value = uuid.into();
473        assert!(matches!(uuid_val, Value::Uuid(_)));
474    }
475
476    #[test]
477    fn test_value_float_from_int() {
478        let int_val = Value::Integer(42);
479        assert_eq!(int_val.as_f64().unwrap(), 42.0);
480    }
481
482    #[test]
483    fn test_value_array() {
484        let arr = Value::Array(vec![Value::Integer(1), Value::Integer(2)]);
485        let arr_ref = arr.as_array().unwrap();
486        assert_eq!(arr_ref.len(), 2);
487    }
488
489    #[test]
490    fn test_value_object() {
491        let mut map = HashMap::new();
492        map.insert("key".to_string(), Value::String("value".to_string()));
493        let obj = Value::Object(map);
494        let obj_ref = obj.as_object().unwrap();
495        assert_eq!(obj_ref.len(), 1);
496    }
497}