Skip to main content

extendr_api/robj/
into_robj.rs

1use super::*;
2use crate::single_threaded;
3use extendr_ffi::{
4    cetype_t, R_BlankString, R_NaInt, R_NaReal, R_NaString, R_NilValue, R_xlen_t, Rcomplex,
5    Rf_mkCharLenCE, Rf_xlengthgets, COMPLEX, INTEGER, LOGICAL, RAW, REAL, SET_STRING_ELT, SEXPTYPE,
6};
7mod repeat_into_robj;
8
9/// Returns an `CHARSXP` based on the provided `&str`.
10///
11/// Note that R does string interning, thus repeated application of this
12/// function on the same string, will incur little computational cost.
13///
14/// Note, that you must protect the return value somehow.
15pub(crate) fn str_to_character(s: &str) -> SEXP {
16    unsafe {
17        if s.is_na() {
18            R_NaString
19        } else if s.is_empty() {
20            R_BlankString
21        } else {
22            single_threaded(|| {
23                // this function embeds a terminating \nul
24                Rf_mkCharLenCE(s.as_ptr().cast(), s.len() as i32, cetype_t::CE_UTF8)
25            })
26        }
27    }
28}
29
30/// Convert a null to an Robj.
31impl From<()> for Robj {
32    fn from(_: ()) -> Self {
33        // Note: we do not need to protect this.
34        unsafe { Robj::from_sexp(R_NilValue) }
35    }
36}
37
38/// Convert a [`Result`] to an [`Robj`].
39///
40/// To use the `?`-operator, an extendr-function must return either [`extendr_api::error::Result`] or [`std::result::Result`].
41/// Use of `panic!` in extendr is discouraged due to memory leakage.
42///
43/// Alternative behaviors enabled by feature toggles:
44/// extendr-api supports different conversions from [`Result<T,E>`] into `Robj`.
45/// Below, `x_ok` represents an R variable on R side which was returned from rust via `T::into_robj()` or similar.
46/// Likewise, `x_err` was returned to R side from rust via `E::into_robj()` or similar.
47/// extendr-api
48/// * `result_list`: `Ok(T)` is encoded as `list(ok = x_ok, err = NULL)` and `Err` as `list(ok = NULL, err = e_err)`.
49/// * `result_condition'`: `Ok(T)` is encoded as `x_ok` and `Err(E)` as `condition(msg="extendr_error", value = x_err, class=c("extendr_error", "error", "condition"))`
50/// * More than one enabled feature: Only one feature gate will take effect, the current order of precedence is [`result_list`, `result_condition`, ... ].
51/// * Neither of the above (default): `Ok(T)` is encoded as `x_ok` and `Err(E)` will trigger `throw_r_error()` with the error message.
52/// ```
53/// use extendr_api::prelude::*;
54/// fn my_func() -> extendr_api::error::Result<f64> {
55///     Ok(1.0)
56/// }
57///
58/// test! {
59///     assert_eq!(r!(my_func()), r!(1.0));
60/// }
61/// ```
62///
63/// [`extendr_api::error::Result`]: crate::error::Result
64#[cfg(not(any(feature = "result_list", feature = "result_condition")))]
65impl<T, E> From<std::result::Result<T, E>> for Robj
66where
67    T: Into<Robj>,
68    E: std::fmt::Debug + std::fmt::Display,
69{
70    fn from(res: std::result::Result<T, E>) -> Self {
71        match res {
72            Ok(val) => val.into(),
73            Err(err) => panic!("{}", err),
74        }
75    }
76}
77
78/// Convert a [`Result`] to an [`Robj`]. Return either `Ok` value or `Err` value wrapped in an
79/// error condition. This allows using `?` operator in functions
80/// and returning [`Result<T>`] without panicking on `Err`. `T` must implement [`IntoRobj`].
81///
82/// Returns `Ok` value as is. Returns `Err` wrapped in an R error condition. The `Err` is placed in
83/// $value field of the condition, and its message is set to 'extendr_err'
84#[cfg(all(feature = "result_condition", not(feature = "result_list")))]
85impl<T, E> From<std::result::Result<T, E>> for Robj
86where
87    T: Into<Robj>,
88    E: Into<Robj>,
89{
90    fn from(res: std::result::Result<T, E>) -> Self {
91        use crate as extendr_api;
92        match res {
93            Ok(x) => x.into(),
94            Err(x) => {
95                let mut err = list!(message = "extendr_err", value = x.into());
96                err.set_class(["extendr_error", "error", "condition"])
97                    .expect("internal error: failed to set class");
98                err.into()
99            }
100        }
101    }
102}
103
104/// Convert a `Result` to an R `List` with an `ok` and `err` elements.
105/// This allows using `?` operator in functions
106/// and returning [`std::result::Result`] or [`extendr_api::error::Result`]
107/// without panicking on `Err`.
108///
109/// [`extendr_api::error::Result`]: crate::error::Result
110#[cfg(feature = "result_list")]
111impl<T, E> From<std::result::Result<T, E>> for Robj
112where
113    T: Into<Robj>,
114    E: Into<Robj>,
115{
116    fn from(res: std::result::Result<T, E>) -> Self {
117        use crate as extendr_api;
118        let mut result = match res {
119            Ok(x) => list!(ok = x.into(), err = NULL),
120            Err(x) => {
121                let err_robj = x.into();
122                if err_robj.is_null() {
123                    panic!("Internal error: result_list not allowed to return NULL as err-value")
124                }
125                list!(ok = NULL, err = err_robj)
126            }
127        };
128        result
129            .set_class(&["extendr_result"])
130            .expect("Internal error: failed to set class");
131        result.into()
132    }
133}
134
135// string conversions from Error trait to Robj and String
136impl From<Error> for Robj {
137    fn from(res: Error) -> Self {
138        res.to_string().into()
139    }
140}
141impl From<Error> for String {
142    fn from(res: Error) -> Self {
143        res.to_string()
144    }
145}
146
147/// Convert an Robj reference into a borrowed Robj.
148impl From<&Robj> for Robj {
149    // Note: we should probably have a much better reference
150    // mechanism as double-free or underprotection is a distinct possibility.
151    fn from(val: &Robj) -> Self {
152        unsafe { Robj::from_sexp(val.get()) }
153    }
154}
155
156/// This is an extension trait to provide a convenience method `into_robj()`.
157///
158/// Defer to `From<T> for Robj`-impls if you have custom types.
159///
160pub trait IntoRobj {
161    fn into_robj(self) -> Robj;
162}
163
164impl<T> IntoRobj for T
165where
166    Robj: From<T>,
167{
168    fn into_robj(self) -> Robj {
169        self.into()
170    }
171}
172
173/// `ToVectorValue` is a trait that allows many different types
174/// to be converted to vectors. It is used as a type parameter
175/// to `collect::<Robj>()`.
176pub trait ToVectorValue {
177    fn sexptype() -> SEXPTYPE {
178        SEXPTYPE::NILSXP
179    }
180
181    fn to_real(&self) -> f64
182    where
183        Self: Sized,
184    {
185        0.
186    }
187
188    fn to_complex(&self) -> Rcomplex
189    where
190        Self: Sized,
191    {
192        Rcomplex { r: 0., i: 0. }
193    }
194
195    fn to_integer(&self) -> i32
196    where
197        Self: Sized,
198    {
199        i32::MIN
200    }
201
202    fn to_logical(&self) -> i32
203    where
204        Self: Sized,
205    {
206        i32::MIN
207    }
208
209    fn to_raw(&self) -> u8
210    where
211        Self: Sized,
212    {
213        0
214    }
215
216    fn to_sexp(&self) -> SEXP
217    where
218        Self: Sized,
219    {
220        unsafe { R_NilValue }
221    }
222}
223
224macro_rules! impl_real_tvv {
225    ($t: ty) => {
226        impl ToVectorValue for $t {
227            fn sexptype() -> SEXPTYPE {
228                SEXPTYPE::REALSXP
229            }
230
231            fn to_real(&self) -> f64 {
232                *self as f64
233            }
234        }
235
236        impl ToVectorValue for &$t {
237            fn sexptype() -> SEXPTYPE {
238                SEXPTYPE::REALSXP
239            }
240
241            fn to_real(&self) -> f64 {
242                **self as f64
243            }
244        }
245
246        impl ToVectorValue for Option<$t> {
247            fn sexptype() -> SEXPTYPE {
248                SEXPTYPE::REALSXP
249            }
250
251            fn to_real(&self) -> f64 {
252                if self.is_some() {
253                    self.unwrap() as f64
254                } else {
255                    unsafe { R_NaReal }
256                }
257            }
258        }
259    };
260}
261
262impl_real_tvv!(f64);
263impl_real_tvv!(f32);
264
265// Since these types might exceeds the max or min of R's 32bit integer, we need
266// to return as REALSXP
267impl_real_tvv!(i64);
268impl_real_tvv!(u32);
269impl_real_tvv!(u64);
270impl_real_tvv!(usize);
271
272macro_rules! impl_complex_tvv {
273    ($t: ty) => {
274        impl ToVectorValue for $t {
275            fn sexptype() -> SEXPTYPE {
276                SEXPTYPE::CPLXSXP
277            }
278
279            fn to_complex(&self) -> Rcomplex {
280                unsafe { std::mem::transmute(*self) }
281            }
282        }
283
284        impl ToVectorValue for &$t {
285            fn sexptype() -> SEXPTYPE {
286                SEXPTYPE::CPLXSXP
287            }
288
289            fn to_complex(&self) -> Rcomplex {
290                unsafe { std::mem::transmute(**self) }
291            }
292        }
293    };
294}
295
296impl_complex_tvv!(c64);
297impl_complex_tvv!(Rcplx);
298impl_complex_tvv!((f64, f64));
299
300macro_rules! impl_integer_tvv {
301    ($t: ty) => {
302        impl ToVectorValue for $t {
303            fn sexptype() -> SEXPTYPE {
304                SEXPTYPE::INTSXP
305            }
306
307            fn to_integer(&self) -> i32 {
308                *self as i32
309            }
310        }
311
312        impl ToVectorValue for &$t {
313            fn sexptype() -> SEXPTYPE {
314                SEXPTYPE::INTSXP
315            }
316
317            fn to_integer(&self) -> i32 {
318                **self as i32
319            }
320        }
321
322        impl ToVectorValue for Option<$t> {
323            fn sexptype() -> SEXPTYPE {
324                SEXPTYPE::INTSXP
325            }
326
327            fn to_integer(&self) -> i32 {
328                if self.is_some() {
329                    self.unwrap() as i32
330                } else {
331                    unsafe { R_NaInt }
332                }
333            }
334        }
335    };
336}
337
338impl_integer_tvv!(i8);
339impl_integer_tvv!(i16);
340impl_integer_tvv!(i32);
341impl_integer_tvv!(u16);
342
343impl ToVectorValue for u8 {
344    fn sexptype() -> SEXPTYPE {
345        SEXPTYPE::RAWSXP
346    }
347
348    fn to_raw(&self) -> u8 {
349        *self
350    }
351}
352
353impl ToVectorValue for &u8 {
354    fn sexptype() -> SEXPTYPE {
355        SEXPTYPE::RAWSXP
356    }
357
358    fn to_raw(&self) -> u8 {
359        **self
360    }
361}
362
363macro_rules! impl_str_tvv {
364    ($t: ty) => {
365        impl ToVectorValue for $t {
366            fn sexptype() -> SEXPTYPE {
367                SEXPTYPE::STRSXP
368            }
369
370            fn to_sexp(&self) -> SEXP
371            where
372                Self: Sized,
373            {
374                str_to_character(self.as_ref())
375            }
376        }
377
378        impl ToVectorValue for &$t {
379            fn sexptype() -> SEXPTYPE {
380                SEXPTYPE::STRSXP
381            }
382
383            fn to_sexp(&self) -> SEXP
384            where
385                Self: Sized,
386            {
387                str_to_character(self.as_ref())
388            }
389        }
390
391        impl ToVectorValue for Option<$t> {
392            fn sexptype() -> SEXPTYPE {
393                SEXPTYPE::STRSXP
394            }
395
396            fn to_sexp(&self) -> SEXP
397            where
398                Self: Sized,
399            {
400                if let Some(s) = self {
401                    str_to_character(s.as_ref())
402                } else {
403                    unsafe { R_NaString }
404                }
405            }
406        }
407    };
408}
409
410impl_str_tvv! {&str}
411impl_str_tvv! {String}
412
413impl ToVectorValue for Rstr {
414    fn sexptype() -> SEXPTYPE {
415        SEXPTYPE::STRSXP
416    }
417
418    fn to_sexp(&self) -> SEXP
419    where
420        Self: Sized,
421    {
422        unsafe { self.get() }
423    }
424}
425
426impl ToVectorValue for &Rstr {
427    fn sexptype() -> SEXPTYPE {
428        SEXPTYPE::STRSXP
429    }
430
431    fn to_sexp(&self) -> SEXP
432    where
433        Self: Sized,
434    {
435        unsafe { self.get() }
436    }
437}
438
439impl ToVectorValue for Option<Rstr> {
440    fn sexptype() -> SEXPTYPE {
441        SEXPTYPE::STRSXP
442    }
443
444    fn to_sexp(&self) -> SEXP
445    where
446        Self: Sized,
447    {
448        if let Some(s) = self {
449            unsafe { s.get() }
450        } else {
451            unsafe { R_NaString }
452        }
453    }
454}
455
456impl TryFrom<&Robj> for Rstr {
457    type Error = crate::Error;
458
459    fn try_from(robj: &Robj) -> Result<Self> {
460        let sexptype = robj.sexptype();
461        if let SEXPTYPE::STRSXP = sexptype {
462            if robj.len() == 1 {
463                let strs = Strings::try_from(robj)?;
464                Ok(strs.elt(0))
465            } else {
466                Err(Error::ExpectedRstr(robj.clone()))
467            }
468        } else if let SEXPTYPE::CHARSXP = sexptype {
469            Ok(Rstr { robj: robj.clone() })
470        } else {
471            Err(Error::ExpectedRstr(robj.clone()))
472        }
473    }
474}
475
476impl TryFrom<Robj> for Rstr {
477    type Error = crate::Error;
478
479    fn try_from(value: Robj) -> std::result::Result<Self, Self::Error> {
480        Self::try_from(&value)
481    }
482}
483
484impl GetSexp for Rstr {
485    unsafe fn get(&self) -> SEXP {
486        self.robj.get()
487    }
488
489    unsafe fn get_mut(&mut self) -> SEXP {
490        self.robj.get_mut()
491    }
492
493    fn as_robj(&self) -> &Robj {
494        &self.robj
495    }
496
497    fn as_robj_mut(&mut self) -> &mut Robj {
498        &mut self.robj
499    }
500}
501
502// These traits all derive from GetSexp with default implementations
503impl Length for Rstr {}
504impl Types for Rstr {}
505impl Conversions for Rstr {}
506impl Rinternals for Rstr {}
507impl Slices for Rstr {}
508impl Operators for Rstr {}
509
510impl ToVectorValue for bool {
511    fn sexptype() -> SEXPTYPE {
512        SEXPTYPE::LGLSXP
513    }
514
515    fn to_logical(&self) -> i32
516    where
517        Self: Sized,
518    {
519        *self as i32
520    }
521}
522
523impl ToVectorValue for &bool {
524    fn sexptype() -> SEXPTYPE {
525        SEXPTYPE::LGLSXP
526    }
527
528    fn to_logical(&self) -> i32
529    where
530        Self: Sized,
531    {
532        **self as i32
533    }
534}
535
536impl ToVectorValue for Rbool {
537    fn sexptype() -> SEXPTYPE {
538        SEXPTYPE::LGLSXP
539    }
540
541    fn to_logical(&self) -> i32
542    where
543        Self: Sized,
544    {
545        self.0
546    }
547}
548
549impl ToVectorValue for &Rbool {
550    fn sexptype() -> SEXPTYPE {
551        SEXPTYPE::LGLSXP
552    }
553
554    fn to_logical(&self) -> i32
555    where
556        Self: Sized,
557    {
558        self.0
559    }
560}
561
562impl ToVectorValue for Option<bool> {
563    fn sexptype() -> SEXPTYPE {
564        SEXPTYPE::LGLSXP
565    }
566
567    fn to_logical(&self) -> i32 {
568        if self.is_some() {
569            self.unwrap() as i32
570        } else {
571            unsafe { R_NaInt }
572        }
573    }
574}
575
576impl<T> From<&Option<T>> for Robj
577where
578    Option<T>: ToVectorValue + Clone,
579{
580    fn from(value: &Option<T>) -> Self {
581        value.clone().into()
582    }
583}
584
585// Not thread safe.
586fn fixed_size_collect<I>(iter: I, len: usize) -> Robj
587where
588    I: Iterator,
589    I: Sized,
590    I::Item: ToVectorValue,
591{
592    single_threaded(|| unsafe {
593        let sexptype = I::Item::sexptype();
594        if sexptype == SEXPTYPE::NILSXP {
595            return Robj::null();
596        }
597        let mut res = Robj::alloc_vector(sexptype, len);
598        let mut capacity = len;
599        let mut n = 0;
600        match sexptype {
601            SEXPTYPE::REALSXP => {
602                let mut ptr = REAL(res.get());
603                for v in iter {
604                    if n == capacity {
605                        capacity = (capacity * 2).max(1);
606                        res = Robj::from_sexp(Rf_xlengthgets(res.get(), capacity as R_xlen_t));
607                        ptr = REAL(res.get());
608                    }
609                    *ptr.add(n) = v.to_real();
610                    n += 1;
611                }
612            }
613            SEXPTYPE::CPLXSXP => {
614                let mut ptr = COMPLEX(res.get());
615                for v in iter {
616                    if n == capacity {
617                        capacity = (capacity * 2).max(1);
618                        res = Robj::from_sexp(Rf_xlengthgets(res.get(), capacity as R_xlen_t));
619                        ptr = COMPLEX(res.get());
620                    }
621                    *ptr.add(n) = v.to_complex();
622                    n += 1;
623                }
624            }
625            SEXPTYPE::INTSXP => {
626                let mut ptr = INTEGER(res.get());
627                for v in iter {
628                    if n == capacity {
629                        capacity = (capacity * 2).max(1);
630                        res = Robj::from_sexp(Rf_xlengthgets(res.get(), capacity as R_xlen_t));
631                        ptr = INTEGER(res.get());
632                    }
633                    *ptr.add(n) = v.to_integer();
634                    n += 1;
635                }
636            }
637            SEXPTYPE::LGLSXP => {
638                let mut ptr = LOGICAL(res.get());
639                for v in iter {
640                    if n == capacity {
641                        capacity = (capacity * 2).max(1);
642                        res = Robj::from_sexp(Rf_xlengthgets(res.get(), capacity as R_xlen_t));
643                        ptr = LOGICAL(res.get());
644                    }
645                    *ptr.add(n) = v.to_logical();
646                    n += 1;
647                }
648            }
649            SEXPTYPE::RAWSXP => {
650                let mut ptr = RAW(res.get());
651                for v in iter {
652                    if n == capacity {
653                        capacity = (capacity * 2).max(1);
654                        res = Robj::from_sexp(Rf_xlengthgets(res.get(), capacity as R_xlen_t));
655                        ptr = RAW(res.get());
656                    }
657                    *ptr.add(n) = v.to_raw();
658                    n += 1;
659                }
660            }
661            SEXPTYPE::STRSXP => {
662                for v in iter {
663                    if n == capacity {
664                        capacity = (capacity * 2).max(1);
665                        res = Robj::from_sexp(Rf_xlengthgets(res.get(), capacity as R_xlen_t));
666                    }
667                    SET_STRING_ELT(res.get(), n as isize, v.to_sexp());
668                    n += 1;
669                }
670            }
671            _ => panic!("unexpected SEXPTYPE in collect"),
672        }
673        if n < capacity {
674            res = Robj::from_sexp(Rf_xlengthgets(res.get(), n as R_xlen_t));
675        }
676        res
677    })
678}
679
680/// Extensions to iterators for R objects including [RobjItertools::collect_rarray()].
681pub trait RobjItertools: Iterator {
682    /// Convert a wide range of iterators to Robj.
683    #[deprecated(since = "0.10.0", note = "Use `collect::<Robj>()` instead")]
684    fn collect_robj(self) -> Robj
685    where
686        Self: Iterator,
687        Self: Sized,
688        Self::Item: ToVectorValue,
689    {
690        self.collect()
691    }
692
693    /// Collects an iterable into an [`RArray`].
694    /// The iterable must yield items column by column (aka Fortan order)
695    ///
696    /// # Arguments
697    ///
698    /// * `dims` - an array containing the length of each dimension
699    fn collect_rarray<const LEN: usize>(self, dims: [usize; LEN]) -> Result<RArray<Self::Item, LEN>>
700    where
701        Self: Iterator,
702        Self: Sized,
703        Self::Item: ToVectorValue,
704        Robj: for<'a> AsTypedSlice<'a, Self::Item>,
705    {
706        let mut vector = self.collect::<Robj>();
707        let prod = dims.iter().product::<usize>();
708        if prod != vector.len() {
709            return Err(Error::Other(format!(
710                "The vector length ({}) does not match the length implied by the dimensions ({})",
711                vector.len(),
712                prod
713            )));
714        }
715        vector.set_attrib(wrapper::symbol::dim_symbol(), dims.iter().collect::<Robj>())?;
716        let _data = vector.as_typed_slice().ok_or(Error::Other(
717            "Unknown error in converting to slice".to_string(),
718        ))?;
719        Ok(RArray::from_parts(vector))
720    }
721}
722
723// Thanks to *pretzelhammer* on stackoverflow for this.
724impl<T> RobjItertools for T where T: Iterator {}
725
726/// Collect an iterator into an R vector.
727///
728/// ```
729/// use extendr_api::prelude::*;
730///
731/// test! {
732/// let robj = (0..3).collect::<Robj>();
733/// assert_eq!(robj.as_integer_vector().unwrap(), vec![0, 1, 2]);
734///
735/// let robj = (0..3).map(|x| x % 2 == 0).collect::<Robj>();
736/// assert_eq!(robj.as_logical_vector().unwrap(), vec![TRUE, FALSE, TRUE]);
737///
738/// let robj = (0..3).map(|x| x as f64).collect::<Robj>();
739/// assert_eq!(robj.as_real_vector().unwrap(), vec![0., 1., 2.]);
740///
741/// let robj = (0..3).map(|x| format!("{}", x)).collect::<Robj>();
742/// assert_eq!(robj.as_str_vector().unwrap(), vec!["0", "1", "2"]);
743/// }
744/// ```
745impl<A> FromIterator<A> for Robj
746where
747    A: ToVectorValue,
748{
749    fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
750        let iter = iter.into_iter();
751        if let (len, Some(max)) = iter.size_hint() {
752            if len == max {
753                return fixed_size_collect(iter, len);
754            }
755        }
756        // FIXME: remove allocation in the future
757        let vec = iter.collect::<Vec<_>>();
758        let len = vec.len();
759        fixed_size_collect(vec.into_iter(), len)
760    }
761}
762
763// Scalars which are ToVectorValue
764impl<T> From<T> for Robj
765where
766    T: ToVectorValue,
767{
768    fn from(scalar: T) -> Self {
769        Some(scalar).into_iter().collect::<Robj>()
770    }
771}
772
773macro_rules! impl_from_as_iterator {
774    ($t: ty) => {
775        impl<T> From<$t> for Robj
776        where
777            $t: RobjItertools,
778            <$t as Iterator>::Item: ToVectorValue,
779            T: ToVectorValue,
780        {
781            fn from(val: $t) -> Self {
782                val.collect::<Robj>()
783            }
784        }
785    };
786}
787
788// impl<T> From<Range<T>> for Robj
789// where
790//     Range<T> : RobjItertools,
791//     <Range<T> as Iterator>::Item: ToVectorValue,
792//     T : ToVectorValue
793// {
794//     fn from(val: Range<T>) -> Self {
795//         val.collect::<Robj>()
796//     }
797// } //
798
799impl<T, const N: usize> From<[T; N]> for Robj
800where
801    T: ToVectorValue,
802{
803    fn from(val: [T; N]) -> Self {
804        fixed_size_collect(val.into_iter(), N)
805    }
806}
807
808impl<'a, T, const N: usize> From<&'a [T; N]> for Robj
809where
810    Self: 'a,
811    &'a T: ToVectorValue + 'a,
812{
813    fn from(val: &'a [T; N]) -> Self {
814        fixed_size_collect(val.iter(), N)
815    }
816}
817
818impl<'a, T, const N: usize> From<&'a mut [T; N]> for Robj
819where
820    Self: 'a,
821    &'a mut T: ToVectorValue + 'a,
822{
823    fn from(val: &'a mut [T; N]) -> Self {
824        fixed_size_collect(val.iter_mut(), N)
825    }
826}
827
828impl<T: ToVectorValue + Clone> From<&Vec<T>> for Robj {
829    fn from(value: &Vec<T>) -> Self {
830        let len = value.len();
831        fixed_size_collect(value.iter().cloned(), len)
832    }
833}
834
835impl<T: ToVectorValue> From<Vec<T>> for Robj {
836    fn from(value: Vec<T>) -> Self {
837        let len = value.len();
838        fixed_size_collect(value.into_iter(), len)
839    }
840}
841
842impl<'a, T> From<&'a [T]> for Robj
843where
844    Self: 'a,
845    T: 'a,
846    &'a T: ToVectorValue,
847{
848    fn from(val: &'a [T]) -> Self {
849        val.iter().collect::<Robj>()
850    }
851}
852
853impl_from_as_iterator! {Range<T>}
854impl_from_as_iterator! {RangeInclusive<T>}
855
856impl From<Vec<Robj>> for Robj {
857    /// Convert a vector of Robj into a list.
858    fn from(val: Vec<Robj>) -> Self {
859        Self::from(&val)
860    }
861}
862
863impl From<&Vec<Robj>> for Robj {
864    fn from(val: &Vec<Robj>) -> Self {
865        List::from_values(val.iter()).into()
866    }
867}
868
869#[cfg(test)]
870mod test {
871    use super::*;
872    use crate as extendr_api;
873
874    struct WrongSizeHint {
875        i: i32,
876        n: i32,
877        hint: usize,
878    }
879
880    impl Iterator for WrongSizeHint {
881        type Item = i32;
882
883        fn next(&mut self) -> Option<i32> {
884            (self.i < self.n).then(|| {
885                self.i += 1;
886                self.i
887            })
888        }
889
890        fn size_hint(&self) -> (usize, Option<usize>) {
891            (self.hint, Some(self.hint))
892        }
893    }
894
895    #[test]
896    fn collect_wrong_size_hint() {
897        test! {
898            let expected: Vec<i32> = (1..=100).collect();
899
900            let low: Robj = WrongSizeHint { i: 0, n: 100, hint: 1 }.collect();
901            assert_eq!(low.as_integer_slice().unwrap(), &expected);
902
903            let high: Robj = WrongSizeHint { i: 0, n: 100, hint: 500 }.collect();
904            assert_eq!(high.as_integer_slice().unwrap(), &expected);
905
906            let strs: Robj = WrongSizeHint { i: 0, n: 3, hint: 1 }
907                .map(|i| i.to_string())
908                .collect();
909            assert_eq!(strs.as_str_vector().unwrap(), vec!["1", "2", "3"]);
910        }
911    }
912
913    #[test]
914    fn collect_typed_wrong_size_hint() {
915        test! {
916            let expected: Vec<i32> = (1..=100).collect();
917            for hint in [0, 1, 100, 500] {
918                let ints: Integers = WrongSizeHint { i: 0, n: 100, hint }.collect();
919                assert_eq!(ints.as_robj().as_integer_slice().unwrap(), &expected);
920
921                let dbls: Doubles = WrongSizeHint { i: 0, n: 100, hint }.map(f64::from).collect();
922                assert_eq!(dbls.len(), 100);
923                assert_eq!(dbls.elt(99), 100.0);
924
925                let lgls: Logicals = WrongSizeHint { i: 0, n: 100, hint }.map(|i| i % 2 == 0).collect();
926                assert_eq!(lgls.len(), 100);
927                assert_eq!(lgls.elt(99), TRUE);
928
929                let strs: Strings = WrongSizeHint { i: 0, n: 100, hint }.map(|i| i.to_string()).collect();
930                assert_eq!(strs.len(), 100);
931                assert_eq!(strs.elt(99), "100");
932            }
933            for hint in [0, 1, 99, 500] {
934                let robj: Robj = WrongSizeHint { i: 0, n: 100, hint }.collect();
935                assert_eq!(robj.as_integer_slice().unwrap(), &expected);
936            }
937
938            let empty: Robj = WrongSizeHint { i: 0, n: 0, hint: 5 }.collect();
939            assert_eq!(empty.len(), 0);
940            assert!(empty.is_integer());
941
942            let reals: Robj = WrongSizeHint { i: 0, n: 3, hint: 1 }
943                .map(|i| i as f64)
944                .collect();
945            assert_eq!(reals.as_real_slice().unwrap(), &[1., 2., 3.]);
946
947            let lgls: Robj = WrongSizeHint { i: 0, n: 3, hint: 1 }
948                .map(|i| i % 2 == 0)
949                .collect();
950            assert_eq!(lgls.as_logical_vector().unwrap(), vec![FALSE, TRUE, FALSE]);
951
952            let raws: Robj = WrongSizeHint { i: 0, n: 3, hint: 1 }
953                .map(|i| i as u8)
954                .collect();
955            assert_eq!(raws.as_raw_slice().unwrap(), &[1, 2, 3]);
956
957            let cplx: Robj = WrongSizeHint { i: 0, n: 3, hint: 1 }
958                .map(|i| c64::new(i as f64, 0.))
959                .collect();
960            assert_eq!(cplx.len(), 3);
961            assert!(cplx.is_complex());
962
963            let strs: Robj = WrongSizeHint { i: 0, n: 3, hint: 1 }
964                .map(|i| i.to_string())
965                .collect();
966            assert_eq!(strs.as_str_vector().unwrap(), vec!["1", "2", "3"]);
967        }
968    }
969
970    #[test]
971    fn test_vec_rint_to_robj() {
972        test! {
973            let int_vec = vec![3,4,0,-2];
974            let int_vec_robj: Robj = int_vec.clone().into();
975            // unsafe { extendr_ffi::Rf_PrintValue(int_vec_robj.get())}
976            assert_eq!(int_vec_robj.as_integer_slice().unwrap(), &int_vec);
977
978            let rint_vec = vec![Rint::from(3), Rint::from(4), Rint::from(0), Rint::from(-2)];
979            let rint_vec_robj: Robj = rint_vec.into();
980            // unsafe { extendr_ffi::Rf_PrintValue(rint_vec_robj.get())}
981            assert_eq!(rint_vec_robj.as_integer_slice().unwrap(), &int_vec);
982        }
983    }
984
985    #[test]
986    fn test_collect_rarray_matrix() {
987        test! {
988            // Check that collect_rarray works the same as R's matrix() function
989            let rmat = (1i32..=16).collect_rarray([4, 4]);
990            assert!(rmat.is_ok());
991            assert_eq!(Robj::from(rmat), R!("matrix(1:16, nrow=4)").unwrap());
992        }
993    }
994
995    #[test]
996    fn test_collect_rarray_tensor() {
997        test! {
998            // Check that collect_rarray works the same as R's array() function
999            let rmat = (1i32..=16).collect_rarray([2, 4, 2]);
1000            assert!(rmat.is_ok());
1001            assert_eq!(Robj::from(rmat), R!("array(1:16, dim=c(2, 4, 2))").unwrap());
1002        }
1003    }
1004
1005    #[test]
1006    fn test_collect_rarray_matrix_failure() {
1007        test! {
1008            // Check that collect_rarray fails when given an invalid shape
1009            let rmat = (1i32..=16).collect_rarray([3, 3]);
1010            assert!(rmat.is_err());
1011            let msg = rmat.unwrap_err().to_string();
1012            assert!(msg.contains('9'));
1013            assert!(msg.contains("dimension"));
1014        }
1015    }
1016
1017    #[test]
1018    fn test_collect_tensor_failure() {
1019        test! {
1020            // Check that collect_rarray fails when given an invalid shape
1021            let rmat = (1i32..=16).collect_rarray([3, 3, 3]);
1022            assert!(rmat.is_err());
1023            let msg = rmat.unwrap_err().to_string();
1024            assert!(msg.contains("27"));
1025            assert!(msg.contains("dimension"));
1026        }
1027    }
1028
1029    #[test]
1030    #[cfg(all(feature = "result_condition", not(feature = "result_list")))]
1031    fn test_result_condition() {
1032        use crate::prelude::*;
1033        fn my_err_f() -> std::result::Result<f64, f64> {
1034            Err(42.0) // return err float
1035        }
1036
1037        test! {
1038                  assert_eq!(
1039                    r!(my_err_f()),
1040                    R!(
1041        "structure(list(message = 'extendr_err',
1042        value = 42.0), class = c('extendr_error', 'error', 'condition'))"
1043                    ).unwrap()
1044                );
1045            }
1046    }
1047
1048    #[test]
1049    #[cfg(feature = "result_list")]
1050    fn test_result_list() {
1051        use crate::prelude::*;
1052        fn my_err_f() -> std::result::Result<f64, String> {
1053            Err("We have water in the engine room!".to_string())
1054        }
1055
1056        fn my_ok_f() -> std::result::Result<f64, String> {
1057            Ok(123.123)
1058        }
1059
1060        test! {
1061            assert_eq!(
1062                r!(my_err_f()),
1063                R!("x=list(ok=NULL, err='We have water in the engine room!')
1064                    class(x)='extendr_result'
1065                    x"
1066                ).unwrap()
1067            );
1068            assert_eq!(
1069                r!(my_ok_f()),
1070                R!("x = list(ok=123.123, err=NULL)
1071                    class(x)='extendr_result'
1072                    x"
1073                ).unwrap()
1074            );
1075        }
1076    }
1077}