logo
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
126
127
128
129
use crate::scalar::macros::*;
use crate::*;
use std::convert::TryFrom;

/// Rbool is a wrapper for i32 in the context of an R's logical vector.
///
/// Rbool can have a value of 0, 1 or i32::MIN.
///
/// The value i32::MIN is used as "NA".
///
/// Rbool has the same footprint as an i32 value allowing us to use it in zero copy slices.
pub struct Rbool(i32);

impl Rbool {
    gen_impl!(Rbool, i32);

    /// Return a `true` `Rbool`.
    pub const fn true_value() -> Rbool {
        Rbool(1)
    }

    /// Return a `false` `Rbool`.
    pub const fn false_value() -> Rbool {
        Rbool(0)
    }

    /// Return a `NA` `Rbool`.
    pub const fn na_value() -> Rbool {
        Rbool(i32::MIN)
    }

    /// Return `true` if this triboolean is `true` but not NA.
    pub fn is_true(&self) -> bool {
        self.inner() != 0 && !self.is_na()
    }

    /// Return `true` if this triboolean is `false` but not NA.
    pub fn is_false(&self) -> bool {
        self.inner() == 0 && !self.is_na()
    }

    /// Convert this Rbool to a bool. Note NA will be true.
    pub fn to_bool(&self) -> bool {
        self.inner() != 0
    }

    /// Convert this construct a Rbool from a rust boolean.
    pub fn from_bool(val: bool) -> Self {
        Rbool(val as i32)
    }
}

gen_trait_impl!(Rbool, bool, |x: &Rbool| x.inner() == i32::MIN, i32::MIN);
gen_from_primitive!(Rbool, i32);

impl From<bool> for Rbool {
    fn from(v: bool) -> Self {
        Rbool(if v { 1 } else { 0 })
    }
}

impl From<Option<bool>> for Rbool {
    fn from(v: Option<bool>) -> Self {
        if let Some(v) = v {
            Rbool::from(v)
        } else {
            Rbool::na()
        }
    }
}

impl From<Rbool> for Option<bool> {
    fn from(v: Rbool) -> Self {
        if v.inner().is_na() {
            None
        } else {
            Some(v.inner() != 0)
        }
    }
}

impl std::ops::Not for Rbool {
    type Output = Self;

    fn not(self) -> Self::Output {
        if self.is_na() {
            Rbool::na()
        } else if self.is_true() {
            Rbool::false_value()
        } else {
            Rbool::true_value()
        }
    }
}

impl std::fmt::Debug for Rbool {
    /// Debug format.
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}",
            if self.is_na() {
                "NA_LOGICAL"
            } else if self.is_true() {
                "TRUE"
            } else {
                "FALSE"
            }
        )
    }
}

impl TryFrom<&Robj> for Rbool {
    type Error = Error;

    /// Convert an LGLSXP object into a Rbool (tri-state boolean).
    /// Use `value.is_na()` to detect NA values.
    fn try_from(robj: &Robj) -> Result<Self> {
        if let Some(v) = robj.as_logical_slice() {
            match v.len() {
                0 => Err(Error::ExpectedNonZeroLength(robj.clone())),
                1 => Ok(v[0]),
                _ => Err(Error::ExpectedScalar(robj.clone())),
            }
        } else {
            Err(Error::ExpectedLogical(robj.clone()))
        }
    }
}