extendr_api/wrapper/
nullable.rs

1use super::*;
2use crate as extendr_api;
3
4/// Wrapper for handling potentially NULL values.
5/// ```
6/// use extendr_api::prelude::*;
7/// test! {
8///     use extendr_api::wrapper::Nullable::*;
9///
10///     // Plain integer.
11///     let s1 = r!(1);
12///     let n1 = <Nullable<i32>>::try_from(&s1)?;
13///     assert_eq!(n1, NotNull(1));
14///
15///     // NA integer - error.
16///     let sna = r!(NA_INTEGER);
17///     assert_eq!(<Nullable<i32>>::try_from(&sna).is_err(), true);
18///
19///     // NA integer - option gives none.
20///     assert_eq!(<Nullable<Option<i32>>>::try_from(&sna)?, NotNull(None));
21///
22///     // NULL object.
23///     let snull = r!(NULL);
24///     let nnull = <Nullable<i32>>::try_from(&snull)?;
25///     assert_eq!(nnull, Null);
26///
27///     assert_eq!(r!(Nullable::<i32>::Null), r!(NULL));
28///     assert_eq!(r!(Nullable::<i32>::NotNull(1)), r!(1));
29/// }
30/// ```
31#[derive(Debug, PartialEq, Clone)]
32pub enum Nullable<T> {
33    NotNull(T),
34    Null,
35}
36
37impl<T> TryFrom<Robj> for Nullable<T>
38where
39    T: TryFrom<Robj, Error = Error>,
40{
41    type Error = Error;
42
43    /// Convert an object that may be null to a rust type.
44    /// ```
45    /// use extendr_api::prelude::*;
46    /// test! {
47    ///     let s1 = r!(1);
48    ///     let n1 = <Nullable<i32>>::try_from(s1)?;
49    ///     assert_eq!(n1, Nullable::NotNull(1));
50    ///     let snull = r!(NULL);
51    ///     let nnull = <Nullable<i32>>::try_from(snull)?;
52    ///     assert_eq!(nnull, Nullable::Null);
53    /// }
54    /// ```
55    fn try_from(robj: Robj) -> std::result::Result<Self, Self::Error> {
56        if robj.is_null() {
57            Ok(Nullable::Null)
58        } else {
59            Ok(Nullable::NotNull(robj.try_into()?))
60        }
61    }
62}
63
64impl<'a, T> TryFrom<&'a Robj> for Nullable<T>
65where
66    T: TryFrom<&'a Robj, Error = Error>,
67{
68    type Error = Error;
69
70    /// Convert an object that may be null to a rust type.
71    /// ```
72    /// use extendr_api::prelude::*;
73    /// test! {
74    ///     let s1 = r!(1);
75    ///     let n1 = <Nullable<i32>>::try_from(&s1)?;
76    ///     assert_eq!(n1, Nullable::NotNull(1));
77    ///     let snull = r!(NULL);
78    ///     let nnull = <Nullable<i32>>::try_from(&snull)?;
79    ///     assert_eq!(nnull, Nullable::Null);
80    /// }
81    /// ```
82    fn try_from(robj: &'a Robj) -> std::result::Result<Self, Self::Error> {
83        if robj.is_null() {
84            Ok(Nullable::Null)
85        } else {
86            Ok(Nullable::NotNull(robj.try_into()?))
87        }
88    }
89}
90
91impl<T> From<Nullable<T>> for Robj
92where
93    T: Into<Robj>,
94{
95    /// Convert a rust object to NULL or another type.
96    /// ```
97    /// use extendr_api::prelude::*;
98    /// test! {
99    ///     assert_eq!(r!(Nullable::<i32>::Null), r!(NULL));
100    ///     assert_eq!(r!(Nullable::<i32>::NotNull(1)), r!(1));
101    /// }
102    /// ```
103    fn from(val: Nullable<T>) -> Self {
104        match val {
105            Nullable::NotNull(t) => t.into(),
106            Nullable::Null => r!(NULL),
107        }
108    }
109}
110
111impl<T> From<Nullable<T>> for Option<T>
112where
113    T: TryFrom<Robj, Error = Error>,
114{
115    /// Convert a Nullable type into Option
116    /// ```
117    /// use extendr_api::prelude::*;
118    /// test! {
119    ///     assert_eq!(<Option<i32>>::from(Nullable::Null), None);
120    ///     assert_eq!(<Option<i32>>::from(Nullable::NotNull(42)), Some(42));
121    /// }
122    /// ```
123    fn from(value: Nullable<T>) -> Self {
124        match value {
125            Nullable::NotNull(value) => Some(value),
126            _ => None,
127        }
128    }
129}
130
131impl<'a, T> From<&'a Nullable<T>> for Option<&'a T>
132where
133    T: TryFrom<Robj, Error = Error>,
134{
135    /// Convert a Nullable reference type into Option containing reference
136    /// ```
137    /// use extendr_api::prelude::*;
138    /// test! {
139    ///     assert_eq!(<Option<&i32>>::from(&Nullable::Null), None);
140    ///     assert_eq!(<Option<&i32>>::from(&Nullable::NotNull(42)), Some(&42));
141    /// }
142    /// ```
143    fn from(value: &'a Nullable<T>) -> Self {
144        match value {
145            Nullable::NotNull(value) => Some(value),
146            _ => None,
147        }
148    }
149}
150
151impl<T> From<Option<T>> for Nullable<T>
152where
153    T: Into<Robj>,
154{
155    /// Convert an Option into Nullable type
156    /// ```
157    /// use extendr_api::prelude::*;
158    /// test! {
159    ///     let x : Nullable<_> = From::<Option<i32>>::from(None);
160    ///     assert_eq!(x, Nullable::<i32>::Null);
161    ///     let x : Nullable<_> = From::<Option<i32>>::from(Some(42));
162    ///     assert_eq!(x, Nullable::<i32>::NotNull(42));
163    /// }
164    /// ```
165    fn from(value: Option<T>) -> Self {
166        match value {
167            Some(value) => Nullable::NotNull(value),
168            _ => Nullable::Null,
169        }
170    }
171}
172
173impl<T> Nullable<T>
174where
175    T: TryFrom<Robj, Error = Error>,
176{
177    /// Convert Nullable R object into `Option`
178    /// ```
179    /// use extendr_api::prelude::*;
180    /// test! {
181    ///     assert_eq!(Nullable::<Rint>::Null.into_option(), None);
182    ///     assert_eq!(Nullable::<Rint>::NotNull(Rint::from(42)).into_option(), Some(Rint::from(42)));
183    /// }
184    /// ```
185    pub fn into_option(self) -> Option<T> {
186        self.into()
187    }
188}
189impl<T> Nullable<T> {
190    /// Map `Nullable<T>` into `Nullable<U>`
191    ///
192    /// ```
193    /// use extendr_api::prelude::*;
194    /// test! {
195    ///     assert_eq!(Nullable::<Rfloat>::Null.map(|x| x.abs()), Nullable::<Rfloat>::Null);
196    ///     assert_eq!(Nullable::<Rfloat>::NotNull(Rfloat::from(42.0)).map(|x| x.abs()), Nullable::<Rfloat>::NotNull(Rfloat::from(42.0)));
197    /// }
198    /// ```
199    pub fn map<F, U>(self, f: F) -> Nullable<U>
200    where
201        F: FnOnce(T) -> U,
202    {
203        match self {
204            Nullable::NotNull(value) => Nullable::NotNull(f(value)),
205            _ => Nullable::Null,
206        }
207    }
208}