Skip to main content

prax_sqlite/
row.rs

1//! Row deserialization traits for SQLite.
2
3use rusqlite::Row;
4use serde_json::Value as JsonValue;
5
6/// Trait for converting a SQLite row to a Rust type.
7///
8/// This trait is implemented for types that can be deserialized from a SQLite row.
9pub trait FromSqliteRow: Sized {
10    /// Convert a SQLite row to this type.
11    fn from_row(row: &Row<'_>) -> Result<Self, FromSqliteRowError>;
12}
13
14/// Error type for row deserialization.
15#[derive(Debug)]
16pub struct FromSqliteRowError {
17    /// The error message.
18    pub message: String,
19    /// The column that caused the error, if known.
20    pub column: Option<String>,
21}
22
23impl FromSqliteRowError {
24    /// Create a new error.
25    pub fn new(message: impl Into<String>) -> Self {
26        Self {
27            message: message.into(),
28            column: None,
29        }
30    }
31
32    /// Create a new error with a column name.
33    pub fn with_column(message: impl Into<String>, column: impl Into<String>) -> Self {
34        Self {
35            message: message.into(),
36            column: Some(column.into()),
37        }
38    }
39}
40
41impl std::fmt::Display for FromSqliteRowError {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        if let Some(ref column) = self.column {
44            write!(f, "column '{}': {}", column, self.message)
45        } else {
46            write!(f, "{}", self.message)
47        }
48    }
49}
50
51impl std::error::Error for FromSqliteRowError {}
52
53impl From<rusqlite::Error> for FromSqliteRowError {
54    fn from(err: rusqlite::Error) -> Self {
55        Self::new(err.to_string())
56    }
57}
58
59/// Implement FromSqliteRow for JSON values.
60impl FromSqliteRow for JsonValue {
61    fn from_row(row: &Row<'_>) -> Result<Self, FromSqliteRowError> {
62        let column_count = row.as_ref().column_count();
63        let mut map = serde_json::Map::new();
64
65        for i in 0..column_count {
66            let name = row
67                .as_ref()
68                .column_name(i)
69                .map_err(|e| FromSqliteRowError::new(e.to_string()))?
70                .to_string();
71            let value = crate::types::get_value_at_index(row, i);
72            map.insert(name, value);
73        }
74
75        Ok(JsonValue::Object(map))
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn test_from_sqlite_row_error_new() {
85        let err = FromSqliteRowError::new("test error");
86        assert_eq!(err.message, "test error");
87        assert!(err.column.is_none());
88    }
89
90    #[test]
91    fn test_from_sqlite_row_error_with_column() {
92        let err = FromSqliteRowError::with_column("invalid type", "user_id");
93        assert_eq!(err.message, "invalid type");
94        assert_eq!(err.column, Some("user_id".to_string()));
95    }
96
97    #[test]
98    fn test_from_sqlite_row_error_display() {
99        let err = FromSqliteRowError::with_column("missing value", "email");
100        let display = format!("{}", err);
101        assert!(display.contains("email"));
102        assert!(display.contains("missing value"));
103    }
104}