extendr_api/scalar/
rbool.rs

1use crate::scalar::macros::*;
2use crate::scalar::Scalar;
3use crate::*;
4use std::convert::TryFrom;
5
6/// `Rbool` is a wrapper for `i32` in the context of an R's logical vector.
7///
8/// `Rbool` can have a value of `0`, `1` or `i32::MIN`.
9///
10/// The value `i32::MIN` is used as `NA`.
11///
12/// `Rbool` has the same footprint as an `i32` value allowing us to use it in zero copy slices.
13#[repr(transparent)]
14pub struct Rbool(i32);
15
16impl Scalar<i32> for Rbool {
17    fn inner(&self) -> i32 {
18        self.0
19    }
20
21    fn new(val: i32) -> Self {
22        Rbool(val)
23    }
24}
25
26impl Rbool {
27    /// Return a `true` `Rbool`.
28    pub const fn true_value() -> Rbool {
29        Rbool(1)
30    }
31
32    /// Return a `false` `Rbool`.
33    pub const fn false_value() -> Rbool {
34        Rbool(0)
35    }
36
37    /// Return a `NA` `Rbool`.
38    pub const fn na_value() -> Rbool {
39        Rbool(i32::MIN)
40    }
41
42    /// Return `true` if this triboolean is `true` but not `NA`.
43    pub fn is_true(&self) -> bool {
44        self.inner() != 0 && !self.is_na()
45    }
46
47    /// Return `true` if this triboolean is `false` but not `NA`.
48    pub fn is_false(&self) -> bool {
49        self.inner() == 0 && !self.is_na()
50    }
51
52    /// Convert this `Rbool` to a bool. Note `NA` will be true.
53    pub fn to_bool(&self) -> bool {
54        self.inner() != 0
55    }
56
57    /// Convert this construct a `Rbool` from a rust boolean.
58    pub fn from_bool(val: bool) -> Self {
59        Rbool(val as i32)
60    }
61}
62
63gen_trait_impl!(Rbool, bool, |x: &Rbool| x.inner() == i32::MIN, i32::MIN);
64gen_from_primitive!(Rbool, i32);
65gen_partial_ord!(Rbool, bool);
66
67impl From<bool> for Rbool {
68    fn from(v: bool) -> Self {
69        Rbool(i32::from(v))
70    }
71}
72
73impl From<Option<bool>> for Rbool {
74    fn from(v: Option<bool>) -> Self {
75        if let Some(v) = v {
76            Rbool::from(v)
77        } else {
78            Rbool::na()
79        }
80    }
81}
82
83impl From<Rbool> for Option<bool> {
84    fn from(v: Rbool) -> Self {
85        if v.inner().is_na() {
86            None
87        } else {
88            Some(v.inner() != 0)
89        }
90    }
91}
92
93impl std::ops::Not for Rbool {
94    type Output = Self;
95
96    fn not(self) -> Self::Output {
97        if self.is_na() {
98            Rbool::na()
99        } else if self.is_true() {
100            Rbool::false_value()
101        } else {
102            Rbool::true_value()
103        }
104    }
105}
106
107impl std::fmt::Debug for Rbool {
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        write!(
110            f,
111            "{}",
112            if self.is_na() {
113                "NA_LOGICAL"
114            } else if self.is_true() {
115                "TRUE"
116            } else {
117                "FALSE"
118            }
119        )
120    }
121}
122
123impl TryFrom<&Robj> for Rbool {
124    type Error = Error;
125
126    /// Convert an `LGLSXP` object into a `Rbool` (tri-state boolean).
127    /// Use `value.is_na()` to detect NA values.
128    fn try_from(robj: &Robj) -> Result<Self> {
129        if let Some(v) = robj.as_logical_slice() {
130            match v.len() {
131                0 => Err(Error::ExpectedNonZeroLength(robj.clone())),
132                1 => Ok(v[0]),
133                _ => Err(Error::ExpectedScalar(robj.clone())),
134            }
135        } else {
136            Err(Error::ExpectedLogical(robj.clone()))
137        }
138    }
139}