serde_odbc/
nullable.rs

1/*
2This file is part of serde-odbc.
3
4serde-odbc is free software: you can redistribute it and/or modify
5it under the terms of the GNU Lesser General Public License as published by
6the Free Software Foundation, either version 3 of the License, or
7(at your option) any later version.
8
9serde-odbc is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12GNU Lesser General Public License for more details.
13
14You should have received a copy of the GNU Lesser General Public License
15along with serde-odbc.  If not, see <http://www.gnu.org/licenses/>.
16*/
17use std::mem::size_of;
18
19use odbc_sys::{SQLLEN, SQL_NULL_DATA};
20use serde::ser::{Serialize, SerializeStruct, Serializer};
21
22use crate::binder::with_indicator;
23
24#[derive(Clone, Copy, Debug)]
25pub struct Nullable<T> {
26    indicator: SQLLEN,
27    value: T,
28}
29
30impl<T: Serialize> Serialize for Nullable<T> {
31    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
32        let mut serializer = serializer.serialize_struct("Nullable", 1)?;
33        with_indicator(&self.indicator as *const _ as *mut _, || {
34            serializer.serialize_field("value", &self.value)
35        })?;
36        serializer.end()
37    }
38}
39
40impl<T> Nullable<T> {
41    pub fn as_ref(&self) -> Option<&T> {
42        match self.indicator {
43            SQL_NULL_DATA => None,
44            _ => Some(&self.value),
45        }
46    }
47
48    pub fn as_mut(&mut self) -> Option<&mut T> {
49        match self.indicator {
50            SQL_NULL_DATA => None,
51            _ => Some(&mut self.value),
52        }
53    }
54}
55
56impl<T: Default> Default for Nullable<T> {
57    fn default() -> Self {
58        Self {
59            indicator: SQL_NULL_DATA,
60            value: Default::default(),
61        }
62    }
63}
64
65impl<T: Default> From<Option<T>> for Nullable<T> {
66    fn from(value: Option<T>) -> Self {
67        match value {
68            None => Default::default(),
69            Some(value) => Nullable {
70                indicator: size_of::<T>() as SQLLEN,
71                value,
72            },
73        }
74    }
75}
76
77impl<T> Into<Option<T>> for Nullable<T> {
78    fn into(self) -> Option<T> {
79        match self.indicator {
80            SQL_NULL_DATA => None,
81            _ => Some(self.value),
82        }
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    use crate::{
91        col_binding::Cols,
92        connection::{Connection, Environment},
93        param_binding::Params,
94        statement::Statement,
95        tests::CONN_STR,
96    };
97
98    #[test]
99    fn bind_nullable_param() {
100        let env = Environment::new().unwrap();
101        let conn = Connection::new(&env, CONN_STR).unwrap();
102
103        let mut stmt: Statement<Params<Nullable<i32>>, Cols<i32>> =
104            Statement::new(&conn, "SELECT ?").unwrap();
105        *stmt.params() = Some(42).into();
106        stmt.exec().unwrap();
107        assert!(stmt.fetch().unwrap());
108        assert_eq!(42, *stmt.cols());
109        assert!(!stmt.fetch().unwrap());
110    }
111
112    #[test]
113    fn bind_nullable_col() {
114        let env = Environment::new().unwrap();
115        let conn = Connection::new(&env, CONN_STR).unwrap();
116
117        let mut stmt: Statement<Params<i32>, Cols<Nullable<i32>>> =
118            Statement::new(&conn, "SELECT ?").unwrap();
119        *stmt.params() = 42;
120        stmt.exec().unwrap();
121        assert!(stmt.fetch().unwrap());
122        assert_eq!(Some(42), (*stmt.cols()).into());
123        assert!(!stmt.fetch().unwrap());
124    }
125}