Skip to main content

miniextendr_api/from_r/
logical.rs

1//! Logical type conversions (Rboolean, bool, Option variants).
2//!
3//! Handles the three R logical states (TRUE, FALSE, NA) and maps them to Rust:
4//!
5//! | Rust Type | NA Handling |
6//! |-----------|-------------|
7//! | `Rboolean` | Error on NA |
8//! | `bool` | Error on NA |
9//! | `Option<Rboolean>` | `None` on NA |
10//! | `Option<bool>` | `None` on NA |
11
12use crate::ffi::{RLogical, Rboolean, SEXP, SEXPTYPE, SexpExt};
13use crate::from_r::{SexpError, SexpNaError, TryFromSexp, is_na_real};
14
15impl TryFromSexp for Rboolean {
16    type Error = SexpError;
17
18    #[inline]
19    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
20        let raw: RLogical = TryFromSexp::try_from_sexp(sexp)?;
21        match raw.to_option_bool() {
22            Some(false) => Ok(Rboolean::FALSE),
23            Some(true) => Ok(Rboolean::TRUE),
24            None => Err(SexpNaError {
25                sexp_type: SEXPTYPE::LGLSXP,
26            }
27            .into()),
28        }
29    }
30
31    #[inline]
32    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
33        let raw: RLogical = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
34        match raw.to_option_bool() {
35            Some(false) => Ok(Rboolean::FALSE),
36            Some(true) => Ok(Rboolean::TRUE),
37            None => Err(SexpNaError {
38                sexp_type: SEXPTYPE::LGLSXP,
39            }
40            .into()),
41        }
42    }
43}
44
45impl TryFromSexp for Option<Rboolean> {
46    type Error = SexpError;
47
48    #[inline]
49    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
50        if sexp.type_of() == SEXPTYPE::NILSXP {
51            return Ok(None);
52        }
53        let raw: RLogical = TryFromSexp::try_from_sexp(sexp)?;
54        match raw.to_option_bool() {
55            Some(false) => Ok(Some(Rboolean::FALSE)),
56            Some(true) => Ok(Some(Rboolean::TRUE)),
57            None => Ok(None),
58        }
59    }
60
61    #[inline]
62    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
63        if sexp.type_of() == SEXPTYPE::NILSXP {
64            return Ok(None);
65        }
66        let raw: RLogical = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
67        match raw.to_option_bool() {
68            Some(false) => Ok(Some(Rboolean::FALSE)),
69            Some(true) => Ok(Some(Rboolean::TRUE)),
70            None => Ok(None),
71        }
72    }
73}
74
75impl TryFromSexp for bool {
76    type Error = SexpError;
77
78    #[inline]
79    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
80        let raw: RLogical = TryFromSexp::try_from_sexp(sexp)?;
81        raw.to_option_bool().ok_or_else(|| {
82            SexpNaError {
83                sexp_type: SEXPTYPE::LGLSXP,
84            }
85            .into()
86        })
87    }
88
89    #[inline]
90    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
91        let raw: RLogical = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
92        raw.to_option_bool().ok_or_else(|| {
93            SexpNaError {
94                sexp_type: SEXPTYPE::LGLSXP,
95            }
96            .into()
97        })
98    }
99}
100
101impl TryFromSexp for Option<bool> {
102    type Error = SexpError;
103
104    #[inline]
105    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
106        // NULL -> None
107        if sexp.type_of() == SEXPTYPE::NILSXP {
108            return Ok(None);
109        }
110        let raw: RLogical = TryFromSexp::try_from_sexp(sexp)?;
111        Ok(raw.to_option_bool())
112    }
113
114    #[inline]
115    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
116        // NULL -> None
117        if sexp.type_of() == SEXPTYPE::NILSXP {
118            return Ok(None);
119        }
120        let raw: RLogical = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
121        Ok(raw.to_option_bool())
122    }
123}
124
125impl TryFromSexp for Option<RLogical> {
126    type Error = SexpError;
127
128    #[inline]
129    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
130        if sexp.type_of() == SEXPTYPE::NILSXP {
131            return Ok(None);
132        }
133        let raw: RLogical = TryFromSexp::try_from_sexp(sexp)?;
134        if raw.is_na() { Ok(None) } else { Ok(Some(raw)) }
135    }
136
137    #[inline]
138    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
139        if sexp.type_of() == SEXPTYPE::NILSXP {
140            return Ok(None);
141        }
142        let raw: RLogical = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
143        if raw.is_na() { Ok(None) } else { Ok(Some(raw)) }
144    }
145}
146
147impl TryFromSexp for Option<i32> {
148    type Error = SexpError;
149
150    #[inline]
151    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
152        // NULL -> None
153        if sexp.type_of() == SEXPTYPE::NILSXP {
154            return Ok(None);
155        }
156        let value: i32 = TryFromSexp::try_from_sexp(sexp)?;
157        if value == crate::altrep_traits::NA_INTEGER {
158            Ok(None)
159        } else {
160            Ok(Some(value))
161        }
162    }
163
164    #[inline]
165    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
166        // NULL -> None
167        if sexp.type_of() == SEXPTYPE::NILSXP {
168            return Ok(None);
169        }
170        let value: i32 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
171        if value == crate::altrep_traits::NA_INTEGER {
172            Ok(None)
173        } else {
174            Ok(Some(value))
175        }
176    }
177}
178
179impl TryFromSexp for Option<f64> {
180    type Error = SexpError;
181
182    #[inline]
183    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
184        // NULL -> None
185        if sexp.type_of() == SEXPTYPE::NILSXP {
186            return Ok(None);
187        }
188        let value: f64 = TryFromSexp::try_from_sexp(sexp)?;
189        if is_na_real(value) {
190            Ok(None)
191        } else {
192            Ok(Some(value))
193        }
194    }
195
196    #[inline]
197    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
198        // NULL -> None
199        if sexp.type_of() == SEXPTYPE::NILSXP {
200            return Ok(None);
201        }
202        let value: f64 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
203        if is_na_real(value) {
204            Ok(None)
205        } else {
206            Ok(Some(value))
207        }
208    }
209}
210
211impl TryFromSexp for Option<u8> {
212    type Error = SexpError;
213
214    #[inline]
215    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
216        if sexp.type_of() == SEXPTYPE::NILSXP {
217            return Ok(None);
218        }
219        let value: u8 = TryFromSexp::try_from_sexp(sexp)?;
220        Ok(Some(value))
221    }
222
223    #[inline]
224    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
225        if sexp.type_of() == SEXPTYPE::NILSXP {
226            return Ok(None);
227        }
228        let value: u8 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
229        Ok(Some(value))
230    }
231}
232
233impl TryFromSexp for Option<crate::ffi::Rcomplex> {
234    type Error = SexpError;
235
236    #[inline]
237    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
238        use crate::altrep_traits::NA_REAL;
239
240        if sexp.type_of() == SEXPTYPE::NILSXP {
241            return Ok(None);
242        }
243        let value: crate::ffi::Rcomplex = TryFromSexp::try_from_sexp(sexp)?;
244        let na_bits = NA_REAL.to_bits();
245        if value.r.to_bits() == na_bits || value.i.to_bits() == na_bits {
246            Ok(None)
247        } else {
248            Ok(Some(value))
249        }
250    }
251
252    #[inline]
253    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
254        use crate::altrep_traits::NA_REAL;
255
256        if sexp.type_of() == SEXPTYPE::NILSXP {
257            return Ok(None);
258        }
259        let value: crate::ffi::Rcomplex = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
260        let na_bits = NA_REAL.to_bits();
261        if value.r.to_bits() == na_bits || value.i.to_bits() == na_bits {
262            Ok(None)
263        } else {
264            Ok(Some(value))
265        }
266    }
267}
268// endregion