extendr_api/scalar/
rbool.rs

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