1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
This file is part of serde-odbc.

serde-odbc is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

serde-odbc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with serde-odbc.  If not, see <http://www.gnu.org/licenses/>.
*/
use std::mem::size_of;

use odbc_sys::{SQLLEN, SQL_NULL_DATA};
use serde::ser::{Serialize, SerializeStruct, Serializer};

use crate::binder::with_indicator;

#[derive(Clone, Copy, Debug)]
pub struct Nullable<T> {
    indicator: SQLLEN,
    value: T,
}

impl<T: Serialize> Serialize for Nullable<T> {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut serializer = serializer.serialize_struct("Nullable", 1)?;
        with_indicator(&self.indicator as *const _ as *mut _, || {
            serializer.serialize_field("value", &self.value)
        })?;
        serializer.end()
    }
}

impl<T> Nullable<T> {
    pub fn as_ref(&self) -> Option<&T> {
        match self.indicator {
            SQL_NULL_DATA => None,
            _ => Some(&self.value),
        }
    }

    pub fn as_mut(&mut self) -> Option<&mut T> {
        match self.indicator {
            SQL_NULL_DATA => None,
            _ => Some(&mut self.value),
        }
    }
}

impl<T: Default> Default for Nullable<T> {
    fn default() -> Self {
        Self {
            indicator: SQL_NULL_DATA,
            value: Default::default(),
        }
    }
}

impl<T: Default> From<Option<T>> for Nullable<T> {
    fn from(value: Option<T>) -> Self {
        match value {
            None => Default::default(),
            Some(value) => Nullable {
                indicator: size_of::<T>() as SQLLEN,
                value,
            },
        }
    }
}

impl<T> Into<Option<T>> for Nullable<T> {
    fn into(self) -> Option<T> {
        match self.indicator {
            SQL_NULL_DATA => None,
            _ => Some(self.value),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::{
        col_binding::Cols,
        connection::{Connection, Environment},
        param_binding::Params,
        statement::Statement,
        tests::CONN_STR,
    };

    #[test]
    fn bind_nullable_param() {
        let env = Environment::new().unwrap();
        let conn = Connection::new(&env, CONN_STR).unwrap();

        let mut stmt: Statement<Params<Nullable<i32>>, Cols<i32>> =
            Statement::new(&conn, "SELECT ?").unwrap();
        *stmt.params() = Some(42).into();
        stmt.exec().unwrap();
        assert!(stmt.fetch().unwrap());
        assert_eq!(42, *stmt.cols());
        assert!(!stmt.fetch().unwrap());
    }

    #[test]
    fn bind_nullable_col() {
        let env = Environment::new().unwrap();
        let conn = Connection::new(&env, CONN_STR).unwrap();

        let mut stmt: Statement<Params<i32>, Cols<Nullable<i32>>> =
            Statement::new(&conn, "SELECT ?").unwrap();
        *stmt.params() = 42;
        stmt.exec().unwrap();
        assert!(stmt.fetch().unwrap());
        assert_eq!(Some(42), (*stmt.cols()).into());
        assert!(!stmt.fetch().unwrap());
    }
}