Skip to main content

aorist_extendr_api/wrapper/
nullable.rs

1use super::*;
2
3/// Wrapper for handling potentially NULL values.
4/// ```
5/// use extendr_api::prelude::*;
6/// test! {
7///     use extendr_api::wrapper::Nullable::*;
8///
9///     // Plain integer.
10///     let s1 = r!(1);
11///     let n1 = <Nullable<i32>>::from_robj(&s1)?;
12///     assert_eq!(n1, NotNull(1));
13///
14///     // NA integer - error.
15///     let sna = r!(NA_INTEGER);
16///     assert_eq!(<Nullable<i32>>::from_robj(&sna).is_err(), true);
17///
18///     // NA integer - option gives none.
19///     assert_eq!(<Nullable<Option<i32>>>::from_robj(&sna)?, NotNull(None));
20///
21///     // NULL object.
22///     let snull = r!(NULL);
23///     let nnull = <Nullable<i32>>::from_robj(&snull)?;
24///     assert_eq!(nnull, Null);
25///
26///     assert_eq!(r!(Nullable::<i32>::Null), r!(NULL));
27///     assert_eq!(r!(Nullable::<i32>::NotNull(1)), r!(1));
28/// }
29/// ```
30#[derive(Debug, PartialEq, Clone)]
31pub enum Nullable<T> {
32    NotNull(T),
33    Null,
34}
35
36impl<'a, T> FromRobj<'a> for Nullable<T>
37where
38    T: FromRobj<'a>,
39{
40    /// Convert an object that may be null to a rust type.
41    /// ```
42    /// use extendr_api::prelude::*;
43    /// test! {
44    ///     let s1 = r!(1);
45    ///     let n1 = <Nullable<i32>>::from_robj(&s1)?;
46    ///     assert_eq!(n1, Nullable::NotNull(1));
47    ///     let snull = r!(NULL);
48    ///     let nnull = <Nullable<i32>>::from_robj(&snull)?;
49    ///     assert_eq!(nnull, Nullable::Null);
50    /// }
51    /// ```
52    fn from_robj(robj: &'a Robj) -> std::result::Result<Self, &'static str> {
53        if robj.is_null() {
54            Ok(Nullable::Null)
55        } else {
56            Ok(Nullable::NotNull(<T>::from_robj(robj)?))
57        }
58    }
59}
60
61impl<T> From<Nullable<T>> for Robj
62where
63    T: Into<Robj>,
64{
65    /// Convert a rust object to NULL or another type.
66    /// ```
67    /// use extendr_api::prelude::*;
68    /// test! {
69    ///     assert_eq!(r!(Nullable::<i32>::Null), r!(NULL));
70    ///     assert_eq!(r!(Nullable::<i32>::NotNull(1)), r!(1));
71    /// }
72    /// ```
73    fn from(val: Nullable<T>) -> Self {
74        match val {
75            Nullable::NotNull(t) => t.into(),
76            Nullable::Null => r!(NULL),
77        }
78    }
79}