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
9pub(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 Rf_mkCharLenCE(s.as_ptr().cast(), s.len() as i32, cetype_t::CE_UTF8)
25 })
26 }
27 }
28}
29
30impl From<()> for Robj {
32 fn from(_: ()) -> Self {
33 unsafe { Robj::from_sexp(R_NilValue) }
35 }
36}
37
38#[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#[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#[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
135impl 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
147impl From<&Robj> for Robj {
149 fn from(val: &Robj) -> Self {
152 unsafe { Robj::from_sexp(val.get()) }
153 }
154}
155
156pub 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
173pub 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
265impl_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
502impl 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
585fn 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
680pub trait RobjItertools: Iterator {
682 #[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 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
723impl<T> RobjItertools for T where T: Iterator {}
725
726impl<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 let vec = iter.collect::<Vec<_>>();
758 let len = vec.len();
759 fixed_size_collect(vec.into_iter(), len)
760 }
761}
762
763impl<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
788impl<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 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 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 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 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 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 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 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) }
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}