Skip to main content

miniextendr_api/from_r/
coerced_scalars.rs

1//! Coerced scalar conversions (multi-source numeric) and large integer scalars.
2//!
3//! These types accept multiple R source types (INTSXP, REALSXP, RAWSXP, LGLSXP)
4//! and coerce to the target Rust type via [`TryCoerce`](crate::coerce::TryCoerce).
5//!
6//! Covers: `i8`, `i16`, `u16`, `u32`, `f32` (sub-native scalars) and
7//! `i64`, `u64`, `isize`, `usize` (large integers via f64 intermediary).
8
9use crate::coerce::TryCoerce;
10use crate::ffi::{RLogical, SEXP, SEXPTYPE, SexpExt};
11use crate::from_r::{SexpError, TryFromSexp, is_na_real};
12
13#[inline]
14pub(crate) fn coerce_value<R, T>(value: R) -> Result<T, SexpError>
15where
16    R: TryCoerce<T>,
17    <R as TryCoerce<T>>::Error: std::fmt::Debug,
18{
19    value
20        .try_coerce()
21        .map_err(|e| SexpError::InvalidValue(format!("{e:?}")))
22}
23
24#[inline]
25fn try_from_sexp_numeric_scalar<T>(sexp: SEXP) -> Result<T, SexpError>
26where
27    i32: TryCoerce<T>,
28    f64: TryCoerce<T>,
29    u8: TryCoerce<T>,
30    <i32 as TryCoerce<T>>::Error: std::fmt::Debug,
31    <f64 as TryCoerce<T>>::Error: std::fmt::Debug,
32    <u8 as TryCoerce<T>>::Error: std::fmt::Debug,
33{
34    let actual = sexp.type_of();
35    match actual {
36        SEXPTYPE::INTSXP => {
37            let value: i32 = TryFromSexp::try_from_sexp(sexp)?;
38            coerce_value(value)
39        }
40        SEXPTYPE::REALSXP => {
41            let value: f64 = TryFromSexp::try_from_sexp(sexp)?;
42            coerce_value(value)
43        }
44        SEXPTYPE::RAWSXP => {
45            let value: u8 = TryFromSexp::try_from_sexp(sexp)?;
46            coerce_value(value)
47        }
48        SEXPTYPE::LGLSXP => {
49            let value: RLogical = TryFromSexp::try_from_sexp(sexp)?;
50            coerce_value(value.to_i32())
51        }
52        _ => Err(SexpError::InvalidValue(format!(
53            "expected integer, numeric, logical, or raw; got {:?}",
54            actual
55        ))),
56    }
57}
58
59#[inline]
60unsafe fn try_from_sexp_numeric_scalar_unchecked<T>(sexp: SEXP) -> Result<T, SexpError>
61where
62    i32: TryCoerce<T>,
63    f64: TryCoerce<T>,
64    u8: TryCoerce<T>,
65    <i32 as TryCoerce<T>>::Error: std::fmt::Debug,
66    <f64 as TryCoerce<T>>::Error: std::fmt::Debug,
67    <u8 as TryCoerce<T>>::Error: std::fmt::Debug,
68{
69    let actual = sexp.type_of();
70    match actual {
71        SEXPTYPE::INTSXP => {
72            let value: i32 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
73            coerce_value(value)
74        }
75        SEXPTYPE::REALSXP => {
76            let value: f64 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
77            coerce_value(value)
78        }
79        SEXPTYPE::RAWSXP => {
80            let value: u8 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
81            coerce_value(value)
82        }
83        SEXPTYPE::LGLSXP => {
84            let value: RLogical = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
85            coerce_value(value.to_i32())
86        }
87        _ => Err(SexpError::InvalidValue(format!(
88            "expected integer, numeric, logical, or raw; got {:?}",
89            actual
90        ))),
91    }
92}
93
94#[inline]
95fn try_from_sexp_numeric_option<T>(sexp: SEXP) -> Result<Option<T>, SexpError>
96where
97    i32: TryCoerce<T>,
98    f64: TryCoerce<T>,
99    u8: TryCoerce<T>,
100    <i32 as TryCoerce<T>>::Error: std::fmt::Debug,
101    <f64 as TryCoerce<T>>::Error: std::fmt::Debug,
102    <u8 as TryCoerce<T>>::Error: std::fmt::Debug,
103{
104    if sexp.type_of() == SEXPTYPE::NILSXP {
105        return Ok(None);
106    }
107
108    let actual = sexp.type_of();
109    match actual {
110        SEXPTYPE::INTSXP => {
111            let value: i32 = TryFromSexp::try_from_sexp(sexp)?;
112            if value == crate::altrep_traits::NA_INTEGER {
113                Ok(None)
114            } else {
115                coerce_value(value).map(Some)
116            }
117        }
118        SEXPTYPE::REALSXP => {
119            let value: f64 = TryFromSexp::try_from_sexp(sexp)?;
120            if is_na_real(value) {
121                Ok(None)
122            } else {
123                coerce_value(value).map(Some)
124            }
125        }
126        SEXPTYPE::RAWSXP => {
127            let value: u8 = TryFromSexp::try_from_sexp(sexp)?;
128            coerce_value(value).map(Some)
129        }
130        SEXPTYPE::LGLSXP => {
131            let value: RLogical = TryFromSexp::try_from_sexp(sexp)?;
132            if value.is_na() {
133                Ok(None)
134            } else {
135                coerce_value(value.to_i32()).map(Some)
136            }
137        }
138        _ => Err(SexpError::InvalidValue(format!(
139            "expected integer, numeric, logical, or raw; got {:?}",
140            actual
141        ))),
142    }
143}
144
145#[inline]
146unsafe fn try_from_sexp_numeric_option_unchecked<T>(sexp: SEXP) -> Result<Option<T>, SexpError>
147where
148    i32: TryCoerce<T>,
149    f64: TryCoerce<T>,
150    u8: TryCoerce<T>,
151    <i32 as TryCoerce<T>>::Error: std::fmt::Debug,
152    <f64 as TryCoerce<T>>::Error: std::fmt::Debug,
153    <u8 as TryCoerce<T>>::Error: std::fmt::Debug,
154{
155    if sexp.type_of() == SEXPTYPE::NILSXP {
156        return Ok(None);
157    }
158
159    let actual = sexp.type_of();
160    match actual {
161        SEXPTYPE::INTSXP => {
162            let value: i32 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
163            if value == crate::altrep_traits::NA_INTEGER {
164                Ok(None)
165            } else {
166                coerce_value(value).map(Some)
167            }
168        }
169        SEXPTYPE::REALSXP => {
170            let value: f64 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
171            if is_na_real(value) {
172                Ok(None)
173            } else {
174                coerce_value(value).map(Some)
175            }
176        }
177        SEXPTYPE::RAWSXP => {
178            let value: u8 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
179            coerce_value(value).map(Some)
180        }
181        SEXPTYPE::LGLSXP => {
182            let value: RLogical = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
183            if value.is_na() {
184                Ok(None)
185            } else {
186                coerce_value(value.to_i32()).map(Some)
187            }
188        }
189        _ => Err(SexpError::InvalidValue(format!(
190            "expected integer, numeric, logical, or raw; got {:?}",
191            actual
192        ))),
193    }
194}
195
196impl TryFromSexp for i8 {
197    type Error = SexpError;
198
199    #[inline]
200    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
201        try_from_sexp_numeric_scalar(sexp)
202    }
203
204    #[inline]
205    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
206        unsafe { try_from_sexp_numeric_scalar_unchecked(sexp) }
207    }
208}
209
210impl TryFromSexp for i16 {
211    type Error = SexpError;
212
213    #[inline]
214    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
215        try_from_sexp_numeric_scalar(sexp)
216    }
217
218    #[inline]
219    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
220        unsafe { try_from_sexp_numeric_scalar_unchecked(sexp) }
221    }
222}
223
224impl TryFromSexp for u16 {
225    type Error = SexpError;
226
227    #[inline]
228    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
229        try_from_sexp_numeric_scalar(sexp)
230    }
231
232    #[inline]
233    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
234        unsafe { try_from_sexp_numeric_scalar_unchecked(sexp) }
235    }
236}
237
238impl TryFromSexp for u32 {
239    type Error = SexpError;
240
241    #[inline]
242    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
243        try_from_sexp_numeric_scalar(sexp)
244    }
245
246    #[inline]
247    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
248        unsafe { try_from_sexp_numeric_scalar_unchecked(sexp) }
249    }
250}
251
252impl TryFromSexp for f32 {
253    type Error = SexpError;
254
255    #[inline]
256    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
257        let actual = sexp.type_of();
258        match actual {
259            SEXPTYPE::INTSXP => {
260                let value: i32 = TryFromSexp::try_from_sexp(sexp)?;
261                Ok(value as f32)
262            }
263            SEXPTYPE::REALSXP => {
264                let value: f64 = TryFromSexp::try_from_sexp(sexp)?;
265                Ok(value as f32)
266            }
267            SEXPTYPE::RAWSXP => {
268                let value: u8 = TryFromSexp::try_from_sexp(sexp)?;
269                Ok(value as f32)
270            }
271            SEXPTYPE::LGLSXP => {
272                let value: RLogical = TryFromSexp::try_from_sexp(sexp)?;
273                Ok(value.to_i32() as f32)
274            }
275            _ => Err(SexpError::InvalidValue(format!(
276                "expected integer, numeric, logical, or raw; got {:?}",
277                actual
278            ))),
279        }
280    }
281
282    #[inline]
283    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
284        let actual = sexp.type_of();
285        match actual {
286            SEXPTYPE::INTSXP => {
287                let value: i32 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
288                Ok(value as f32)
289            }
290            SEXPTYPE::REALSXP => {
291                let value: f64 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
292                Ok(value as f32)
293            }
294            SEXPTYPE::RAWSXP => {
295                let value: u8 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
296                Ok(value as f32)
297            }
298            SEXPTYPE::LGLSXP => {
299                let value: RLogical = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
300                Ok(value.to_i32() as f32)
301            }
302            _ => Err(SexpError::InvalidValue(format!(
303                "expected integer, numeric, logical, or raw; got {:?}",
304                actual
305            ))),
306        }
307    }
308}
309
310impl TryFromSexp for Option<i8> {
311    type Error = SexpError;
312
313    #[inline]
314    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
315        try_from_sexp_numeric_option(sexp)
316    }
317
318    #[inline]
319    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
320        unsafe { try_from_sexp_numeric_option_unchecked(sexp) }
321    }
322}
323
324impl TryFromSexp for Option<i16> {
325    type Error = SexpError;
326
327    #[inline]
328    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
329        try_from_sexp_numeric_option(sexp)
330    }
331
332    #[inline]
333    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
334        unsafe { try_from_sexp_numeric_option_unchecked(sexp) }
335    }
336}
337
338impl TryFromSexp for Option<u16> {
339    type Error = SexpError;
340
341    #[inline]
342    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
343        try_from_sexp_numeric_option(sexp)
344    }
345
346    #[inline]
347    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
348        unsafe { try_from_sexp_numeric_option_unchecked(sexp) }
349    }
350}
351
352impl TryFromSexp for Option<u32> {
353    type Error = SexpError;
354
355    #[inline]
356    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
357        try_from_sexp_numeric_option(sexp)
358    }
359
360    #[inline]
361    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
362        unsafe { try_from_sexp_numeric_option_unchecked(sexp) }
363    }
364}
365
366impl TryFromSexp for Option<f32> {
367    type Error = SexpError;
368
369    #[inline]
370    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
371        if sexp.type_of() == SEXPTYPE::NILSXP {
372            return Ok(None);
373        }
374        let actual = sexp.type_of();
375        match actual {
376            SEXPTYPE::INTSXP => {
377                let value: i32 = TryFromSexp::try_from_sexp(sexp)?;
378                if value == crate::altrep_traits::NA_INTEGER {
379                    Ok(None)
380                } else {
381                    Ok(Some(value as f32))
382                }
383            }
384            SEXPTYPE::REALSXP => {
385                let value: f64 = TryFromSexp::try_from_sexp(sexp)?;
386                if is_na_real(value) {
387                    Ok(None)
388                } else {
389                    Ok(Some(value as f32))
390                }
391            }
392            SEXPTYPE::RAWSXP => {
393                let value: u8 = TryFromSexp::try_from_sexp(sexp)?;
394                Ok(Some(value as f32))
395            }
396            SEXPTYPE::LGLSXP => {
397                let value: RLogical = TryFromSexp::try_from_sexp(sexp)?;
398                if value.is_na() {
399                    Ok(None)
400                } else {
401                    Ok(Some(value.to_i32() as f32))
402                }
403            }
404            _ => Err(SexpError::InvalidValue(format!(
405                "expected integer, numeric, logical, or raw; got {:?}",
406                actual
407            ))),
408        }
409    }
410
411    #[inline]
412    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
413        if sexp.type_of() == SEXPTYPE::NILSXP {
414            return Ok(None);
415        }
416        let actual = sexp.type_of();
417        match actual {
418            SEXPTYPE::INTSXP => {
419                let value: i32 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
420                if value == crate::altrep_traits::NA_INTEGER {
421                    Ok(None)
422                } else {
423                    Ok(Some(value as f32))
424                }
425            }
426            SEXPTYPE::REALSXP => {
427                let value: f64 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
428                if is_na_real(value) {
429                    Ok(None)
430                } else {
431                    Ok(Some(value as f32))
432                }
433            }
434            SEXPTYPE::RAWSXP => {
435                let value: u8 = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
436                Ok(Some(value as f32))
437            }
438            SEXPTYPE::LGLSXP => {
439                let value: RLogical = unsafe { TryFromSexp::try_from_sexp_unchecked(sexp)? };
440                if value.is_na() {
441                    Ok(None)
442                } else {
443                    Ok(Some(value.to_i32() as f32))
444                }
445            }
446            _ => Err(SexpError::InvalidValue(format!(
447                "expected integer, numeric, logical, or raw; got {:?}",
448                actual
449            ))),
450        }
451    }
452}
453// endregion
454
455// region: Large integer scalar conversions (via f64)
456//
457// R doesn't have native 64-bit integers, so these read from REALSXP (f64)
458// and convert with range/precision checking.
459//
460// **Round-trip precision:**
461// - Values in [-2^53, 2^53] round-trip exactly: i64 → R → i64
462// - Values outside this range may not round-trip due to f64 precision loss
463//
464// **Conversion behavior:**
465// - Reads from REALSXP (f64) or INTSXP (i32)
466// - Validates the value is a whole number (no fractional part)
467// - Validates the value fits in the target type's range
468// - Returns error for NA values (use Option<i64> for nullable)
469
470/// Convert R numeric scalar to `i64`.
471///
472/// # Behavior
473///
474/// - Reads from REALSXP (f64) or INTSXP (i32)
475/// - Validates value is a whole number (no fractional part)
476/// - Validates value fits in i64 range
477/// - Returns `Err` for NA values
478///
479/// # Example
480///
481/// ```ignore
482/// // From R integer
483/// let val: i64 = TryFromSexp::try_from_sexp(int_sexp)?;
484///
485/// // From R numeric (must be whole number)
486/// let val: i64 = TryFromSexp::try_from_sexp(real_sexp)?;
487///
488/// // Error: 3.14 is not a whole number
489/// let val: Result<i64, _> = TryFromSexp::try_from_sexp(pi_sexp);
490/// ```
491impl TryFromSexp for i64 {
492    type Error = SexpError;
493
494    #[inline]
495    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
496        try_from_sexp_numeric_scalar(sexp)
497    }
498
499    #[inline]
500    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
501        unsafe { try_from_sexp_numeric_scalar_unchecked(sexp) }
502    }
503}
504
505/// Convert R numeric scalar to `u64`.
506///
507/// Same behavior as [`i64`](impl TryFromSexp for i64), but also validates
508/// the value is non-negative.
509impl TryFromSexp for u64 {
510    type Error = SexpError;
511
512    #[inline]
513    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
514        try_from_sexp_numeric_scalar(sexp)
515    }
516
517    #[inline]
518    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
519        unsafe { try_from_sexp_numeric_scalar_unchecked(sexp) }
520    }
521}
522
523/// Convert R numeric scalar to `Option<i64>`, with NA → `None`.
524impl TryFromSexp for Option<i64> {
525    type Error = SexpError;
526
527    #[inline]
528    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
529        try_from_sexp_numeric_option(sexp)
530    }
531
532    #[inline]
533    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
534        unsafe { try_from_sexp_numeric_option_unchecked(sexp) }
535    }
536}
537
538impl TryFromSexp for Option<u64> {
539    type Error = SexpError;
540
541    #[inline]
542    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
543        try_from_sexp_numeric_option(sexp)
544    }
545
546    #[inline]
547    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
548        unsafe { try_from_sexp_numeric_option_unchecked(sexp) }
549    }
550}
551
552impl TryFromSexp for usize {
553    type Error = SexpError;
554
555    #[inline]
556    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
557        // Try i32 first (more common), fall back to f64 for large values
558        let actual = sexp.type_of();
559        match actual {
560            SEXPTYPE::INTSXP => {
561                use crate::coerce::TryCoerce;
562                let value: i32 = TryFromSexp::try_from_sexp(sexp)?;
563                value
564                    .try_coerce()
565                    .map_err(|e| SexpError::InvalidValue(format!("{e}")))
566            }
567            SEXPTYPE::REALSXP => {
568                use crate::coerce::TryCoerce;
569                let value: f64 = TryFromSexp::try_from_sexp(sexp)?;
570                let u: u64 = value
571                    .try_coerce()
572                    .map_err(|e| SexpError::InvalidValue(format!("{e}")))?;
573                u.try_into()
574                    .map_err(|_| SexpError::InvalidValue("value out of usize range".into()))
575            }
576            SEXPTYPE::RAWSXP => {
577                use crate::coerce::Coerce;
578                let value: u8 = TryFromSexp::try_from_sexp(sexp)?;
579                Ok(value.coerce())
580            }
581            SEXPTYPE::LGLSXP => {
582                use crate::coerce::TryCoerce;
583                let value: RLogical = TryFromSexp::try_from_sexp(sexp)?;
584                value
585                    .to_i32()
586                    .try_coerce()
587                    .map_err(|e| SexpError::InvalidValue(format!("{e}")))
588            }
589            _ => Err(SexpError::InvalidValue(format!(
590                "expected integer, numeric, logical, or raw; got {:?}",
591                actual
592            ))),
593        }
594    }
595}
596
597impl TryFromSexp for Option<usize> {
598    type Error = SexpError;
599
600    #[inline]
601    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
602        if sexp.type_of() == SEXPTYPE::NILSXP {
603            return Ok(None);
604        }
605        let actual = sexp.type_of();
606        match actual {
607            SEXPTYPE::INTSXP => {
608                use crate::coerce::TryCoerce;
609                let value: i32 = TryFromSexp::try_from_sexp(sexp)?;
610                if value == crate::altrep_traits::NA_INTEGER {
611                    Ok(None)
612                } else {
613                    value
614                        .try_coerce()
615                        .map(Some)
616                        .map_err(|e| SexpError::InvalidValue(format!("{e}")))
617                }
618            }
619            SEXPTYPE::REALSXP => {
620                use crate::coerce::TryCoerce;
621                let value: f64 = TryFromSexp::try_from_sexp(sexp)?;
622                if is_na_real(value) {
623                    return Ok(None);
624                }
625                let u: u64 = value
626                    .try_coerce()
627                    .map_err(|e| SexpError::InvalidValue(format!("{e}")))?;
628                u.try_into()
629                    .map(Some)
630                    .map_err(|_| SexpError::InvalidValue("value out of usize range".into()))
631            }
632            SEXPTYPE::RAWSXP => {
633                use crate::coerce::Coerce;
634                let value: u8 = TryFromSexp::try_from_sexp(sexp)?;
635                Ok(Some(value.coerce()))
636            }
637            SEXPTYPE::LGLSXP => {
638                use crate::coerce::TryCoerce;
639                let value: RLogical = TryFromSexp::try_from_sexp(sexp)?;
640                if value.is_na() {
641                    Ok(None)
642                } else {
643                    value
644                        .to_i32()
645                        .try_coerce()
646                        .map(Some)
647                        .map_err(|e| SexpError::InvalidValue(format!("{e}")))
648                }
649            }
650            _ => Err(SexpError::InvalidValue(format!(
651                "expected integer, numeric, logical, or raw; got {:?}",
652                actual
653            ))),
654        }
655    }
656
657    #[inline]
658    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
659        Self::try_from_sexp(sexp)
660    }
661}
662
663impl TryFromSexp for isize {
664    type Error = SexpError;
665
666    #[inline]
667    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
668        // Try i32 first (more common), fall back to f64 for large values
669        let actual = sexp.type_of();
670        match actual {
671            SEXPTYPE::INTSXP => {
672                use crate::coerce::Coerce;
673                let value: i32 = TryFromSexp::try_from_sexp(sexp)?;
674                Ok(value.coerce())
675            }
676            SEXPTYPE::REALSXP => {
677                use crate::coerce::TryCoerce;
678                let value: f64 = TryFromSexp::try_from_sexp(sexp)?;
679                let i: i64 = value
680                    .try_coerce()
681                    .map_err(|e| SexpError::InvalidValue(format!("{e}")))?;
682                i.try_into()
683                    .map_err(|_| SexpError::InvalidValue("value out of isize range".into()))
684            }
685            SEXPTYPE::RAWSXP => {
686                use crate::coerce::Coerce;
687                let value: u8 = TryFromSexp::try_from_sexp(sexp)?;
688                Ok(value.coerce())
689            }
690            SEXPTYPE::LGLSXP => {
691                use crate::coerce::Coerce;
692                let value: RLogical = TryFromSexp::try_from_sexp(sexp)?;
693                Ok(value.to_i32().coerce())
694            }
695            _ => Err(SexpError::InvalidValue(format!(
696                "expected integer, numeric, logical, or raw; got {:?}",
697                actual
698            ))),
699        }
700    }
701}
702
703impl TryFromSexp for Option<isize> {
704    type Error = SexpError;
705
706    #[inline]
707    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
708        if sexp.type_of() == SEXPTYPE::NILSXP {
709            return Ok(None);
710        }
711        let actual = sexp.type_of();
712        match actual {
713            SEXPTYPE::INTSXP => {
714                use crate::coerce::Coerce;
715                let value: i32 = TryFromSexp::try_from_sexp(sexp)?;
716                if value == crate::altrep_traits::NA_INTEGER {
717                    Ok(None)
718                } else {
719                    Ok(Some(value.coerce()))
720                }
721            }
722            SEXPTYPE::REALSXP => {
723                use crate::coerce::TryCoerce;
724                let value: f64 = TryFromSexp::try_from_sexp(sexp)?;
725                if is_na_real(value) {
726                    return Ok(None);
727                }
728                let i: i64 = value
729                    .try_coerce()
730                    .map_err(|e| SexpError::InvalidValue(format!("{e}")))?;
731                i.try_into()
732                    .map(Some)
733                    .map_err(|_| SexpError::InvalidValue("value out of isize range".into()))
734            }
735            SEXPTYPE::RAWSXP => {
736                use crate::coerce::Coerce;
737                let value: u8 = TryFromSexp::try_from_sexp(sexp)?;
738                Ok(Some(value.coerce()))
739            }
740            SEXPTYPE::LGLSXP => {
741                use crate::coerce::Coerce;
742                let value: RLogical = TryFromSexp::try_from_sexp(sexp)?;
743                if value.is_na() {
744                    Ok(None)
745                } else {
746                    Ok(Some(value.to_i32().coerce()))
747                }
748            }
749            _ => Err(SexpError::InvalidValue(format!(
750                "expected integer, numeric, logical, or raw; got {:?}",
751                actual
752            ))),
753        }
754    }
755
756    #[inline]
757    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
758        Self::try_from_sexp(sexp)
759    }
760}
761// endregion