Skip to main content

prax_mssql/
row.rs

1//! Microsoft SQL Server row types and deserialization.
2
3use tiberius::Row;
4
5use crate::error::{MssqlError, MssqlResult};
6
7/// Extension trait for SQL Server rows.
8pub trait MssqlRow {
9    /// Get a column value by name.
10    fn get_value<'a, T>(&'a self, column: &str) -> MssqlResult<T>
11    where
12        T: tiberius::FromSql<'a>;
13
14    /// Get an optional column value by name.
15    fn get_opt<'a, T>(&'a self, column: &str) -> MssqlResult<Option<T>>
16    where
17        T: tiberius::FromSql<'a>;
18
19    /// Try to get a column value by index.
20    fn try_get_by_index<'a, T>(&'a self, index: usize) -> Option<T>
21    where
22        T: tiberius::FromSql<'a>;
23}
24
25impl MssqlRow for Row {
26    fn get_value<'a, T>(&'a self, column: &str) -> MssqlResult<T>
27    where
28        T: tiberius::FromSql<'a>,
29    {
30        self.try_get(column)
31            .map_err(|e| {
32                MssqlError::deserialization(format!("failed to get column '{}': {}", column, e))
33            })?
34            .ok_or_else(|| MssqlError::deserialization(format!("column '{}' is null", column)))
35    }
36
37    fn get_opt<'a, T>(&'a self, column: &str) -> MssqlResult<Option<T>>
38    where
39        T: tiberius::FromSql<'a>,
40    {
41        self.try_get(column).map_err(|e| {
42            MssqlError::deserialization(format!("failed to get column '{}': {}", column, e))
43        })
44    }
45
46    fn try_get_by_index<'a, T>(&'a self, index: usize) -> Option<T>
47    where
48        T: tiberius::FromSql<'a>,
49    {
50        self.get(index)
51    }
52}
53
54/// Trait for deserializing a SQL Server row into a type.
55pub trait FromMssqlRow: Sized {
56    /// Deserialize from a SQL Server row.
57    fn from_row(row: &Row) -> MssqlResult<Self>;
58}
59
60/// Macro to implement FromMssqlRow for simple structs.
61///
62/// Usage:
63/// ```rust,ignore
64/// impl_from_row!(User {
65///     id: i32,
66///     email: String,
67///     name: Option<String>,
68/// });
69/// ```
70#[macro_export]
71macro_rules! impl_from_mssql_row {
72    ($type:ident { $($field:ident : $field_type:ty),* $(,)? }) => {
73        impl $crate::row::FromMssqlRow for $type {
74            fn from_row(row: &tiberius::Row) -> $crate::error::MssqlResult<Self> {
75                use $crate::row::MssqlRow;
76                Ok(Self {
77                    $(
78                        $field: row.get_value(stringify!($field))?,
79                    )*
80                })
81            }
82        }
83    };
84}
85
86#[cfg(test)]
87mod tests {
88    // Row tests require integration testing with a real SQL Server database
89}