1use super::*;
2use crate::scalar::Scalar;
3use crate::single_threaded;
4use extendr_ffi::{
5 cetype_t, R_BlankString, R_NaInt, R_NaReal, R_NaString, R_NilValue, Rcomplex, Rf_mkCharLenCE,
6 COMPLEX, INTEGER, LOGICAL, RAW, REAL, SET_STRING_ELT, SEXPTYPE,
7};
8mod repeat_into_robj;
9
10pub(crate) fn str_to_character(s: &str) -> SEXP {
17 unsafe {
18 if s.is_na() {
19 R_NaString
20 } else if s.is_empty() {
21 R_BlankString
22 } else {
23 single_threaded(|| {
24 Rf_mkCharLenCE(s.as_ptr().cast(), s.len() as i32, cetype_t::CE_UTF8)
26 })
27 }
28 }
29}
30
31impl From<()> for Robj {
33 fn from(_: ()) -> Self {
34 unsafe { Robj::from_sexp(R_NilValue) }
36 }
37}
38
39#[cfg(not(any(feature = "result_list", feature = "result_condition")))]
68impl<T, E> From<std::result::Result<T, E>> for Robj
69where
70 T: Into<Robj>,
71 E: std::fmt::Debug,
72{
73 fn from(res: std::result::Result<T, E>) -> Self {
74 res.unwrap().into()
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 std::i32::MIN
200 }
201
202 fn to_logical(&self) -> i32
203 where
204 Self: Sized,
205 {
206 std::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 bool {
414 fn sexptype() -> SEXPTYPE {
415 SEXPTYPE::LGLSXP
416 }
417
418 fn to_logical(&self) -> i32
419 where
420 Self: Sized,
421 {
422 *self as i32
423 }
424}
425
426impl ToVectorValue for &bool {
427 fn sexptype() -> SEXPTYPE {
428 SEXPTYPE::LGLSXP
429 }
430
431 fn to_logical(&self) -> i32
432 where
433 Self: Sized,
434 {
435 **self as i32
436 }
437}
438
439impl ToVectorValue for Rbool {
440 fn sexptype() -> SEXPTYPE {
441 SEXPTYPE::LGLSXP
442 }
443
444 fn to_logical(&self) -> i32
445 where
446 Self: Sized,
447 {
448 self.inner()
449 }
450}
451
452impl ToVectorValue for &Rbool {
453 fn sexptype() -> SEXPTYPE {
454 SEXPTYPE::LGLSXP
455 }
456
457 fn to_logical(&self) -> i32
458 where
459 Self: Sized,
460 {
461 self.inner()
462 }
463}
464
465impl ToVectorValue for Option<bool> {
466 fn sexptype() -> SEXPTYPE {
467 SEXPTYPE::LGLSXP
468 }
469
470 fn to_logical(&self) -> i32 {
471 if self.is_some() {
472 self.unwrap() as i32
473 } else {
474 unsafe { R_NaInt }
475 }
476 }
477}
478
479fn fixed_size_collect<I>(iter: I, len: usize) -> Robj
481where
482 I: Iterator,
483 I: Sized,
484 I::Item: ToVectorValue,
485{
486 single_threaded(|| unsafe {
487 let sexptype = I::Item::sexptype();
489 if sexptype != SEXPTYPE::NILSXP {
490 let res = Robj::alloc_vector(sexptype, len);
491 let sexp = res.get();
492 match sexptype {
493 SEXPTYPE::REALSXP => {
494 let ptr = REAL(sexp);
495 for (i, v) in iter.enumerate() {
496 *ptr.add(i) = v.to_real();
497 }
498 }
499 SEXPTYPE::CPLXSXP => {
500 let ptr = COMPLEX(sexp);
501 for (i, v) in iter.enumerate() {
502 *ptr.add(i) = v.to_complex();
503 }
504 }
505 SEXPTYPE::INTSXP => {
506 let ptr = INTEGER(sexp);
507 for (i, v) in iter.enumerate() {
508 *ptr.add(i) = v.to_integer();
509 }
510 }
511 SEXPTYPE::LGLSXP => {
512 let ptr = LOGICAL(sexp);
513 for (i, v) in iter.enumerate() {
514 *ptr.add(i) = v.to_logical();
515 }
516 }
517 SEXPTYPE::STRSXP => {
518 for (i, v) in iter.enumerate() {
519 SET_STRING_ELT(sexp, i as isize, v.to_sexp());
520 }
521 }
522 SEXPTYPE::RAWSXP => {
523 let ptr = RAW(sexp);
524 for (i, v) in iter.enumerate() {
525 *ptr.add(i) = v.to_raw();
526 }
527 }
528 _ => {
529 panic!("unexpected SEXPTYPE in collect_robj");
530 }
531 }
532 res
533 } else {
534 Robj::from(())
535 }
536 })
537}
538
539pub trait RobjItertools: Iterator {
541 fn collect_robj(self) -> Robj
564 where
565 Self: Iterator,
566 Self: Sized,
567 Self::Item: ToVectorValue,
568 {
569 if let (len, Some(max)) = self.size_hint() {
570 if len == max {
571 return fixed_size_collect(self, len);
572 }
573 }
574 let vec: Vec<_> = self.collect();
576 assert!(vec.iter().size_hint() == (vec.len(), Some(vec.len())));
577 vec.into_iter().collect_robj()
578 }
579
580 fn collect_rarray<const LEN: usize>(self, dims: [usize; LEN]) -> Result<RArray<Self::Item, LEN>>
587 where
588 Self: Iterator,
589 Self: Sized,
590 Self::Item: ToVectorValue,
591 Robj: for<'a> AsTypedSlice<'a, Self::Item>,
592 {
593 let mut vector = self.collect_robj();
594 let prod = dims.iter().product::<usize>();
595 if prod != vector.len() {
596 return Err(Error::Other(format!(
597 "The vector length ({}) does not match the length implied by the dimensions ({})",
598 vector.len(),
599 prod
600 )));
601 }
602 vector.set_attrib(wrapper::symbol::dim_symbol(), dims.iter().collect_robj())?;
603 let _data = vector.as_typed_slice().ok_or(Error::Other(
604 "Unknown error in converting to slice".to_string(),
605 ))?;
606 Ok(RArray::from_parts(vector))
607 }
608}
609
610impl<T> RobjItertools for T where T: Iterator {}
612
613impl<T> From<T> for Robj
615where
616 T: ToVectorValue,
617{
618 fn from(scalar: T) -> Self {
619 Some(scalar).into_iter().collect_robj()
620 }
621}
622
623macro_rules! impl_from_as_iterator {
624 ($t: ty) => {
625 impl<T> From<$t> for Robj
626 where
627 $t: RobjItertools,
628 <$t as Iterator>::Item: ToVectorValue,
629 T: ToVectorValue,
630 {
631 fn from(val: $t) -> Self {
632 val.collect_robj()
633 }
634 }
635 };
636}
637
638impl<'a, T, const N: usize> From<[T; N]> for Robj
650where
651 Self: 'a,
652 T: ToVectorValue,
653{
654 fn from(val: [T; N]) -> Self {
655 fixed_size_collect(val.into_iter(), N)
656 }
657}
658
659impl<'a, T, const N: usize> From<&'a [T; N]> for Robj
660where
661 Self: 'a,
662 &'a T: ToVectorValue + 'a,
663{
664 fn from(val: &'a [T; N]) -> Self {
665 fixed_size_collect(val.iter(), N)
666 }
667}
668
669impl<'a, T, const N: usize> From<&'a mut [T; N]> for Robj
670where
671 Self: 'a,
672 &'a mut T: ToVectorValue + 'a,
673{
674 fn from(val: &'a mut [T; N]) -> Self {
675 fixed_size_collect(val.iter_mut(), N)
676 }
677}
678
679impl<T: ToVectorValue + Clone> From<&Vec<T>> for Robj {
680 fn from(value: &Vec<T>) -> Self {
681 let len = value.len();
682 fixed_size_collect(value.iter().cloned(), len)
683 }
684}
685
686impl<T: ToVectorValue> From<Vec<T>> for Robj {
687 fn from(value: Vec<T>) -> Self {
688 let len = value.len();
689 fixed_size_collect(value.into_iter(), len)
690 }
691}
692
693impl<'a, T> From<&'a [T]> for Robj
694where
695 Self: 'a,
696 T: 'a,
697 &'a T: ToVectorValue,
698{
699 fn from(val: &'a [T]) -> Self {
700 val.iter().collect_robj()
701 }
702}
703
704impl_from_as_iterator! {Range<T>}
705impl_from_as_iterator! {RangeInclusive<T>}
706
707impl From<Vec<Robj>> for Robj {
708 fn from(val: Vec<Robj>) -> Self {
710 List::from_values(val.iter()).into()
711 }
712}
713
714impl From<Vec<Rstr>> for Robj {
715 fn from(val: Vec<Rstr>) -> Self {
717 Strings::from_values(val).into()
718 }
719}
720
721#[cfg(test)]
722mod test {
723 use super::*;
724 use crate as extendr_api;
725
726 #[test]
727 fn test_vec_rint_to_robj() {
728 test! {
729 let int_vec = vec![3,4,0,-2];
730 let int_vec_robj: Robj = int_vec.clone().into();
731 assert_eq!(int_vec_robj.as_integer_slice().unwrap(), &int_vec);
733
734 let rint_vec = vec![Rint::new(3), Rint::new(4), Rint::new(0), Rint::new(-2)];
735 let rint_vec_robj: Robj = rint_vec.into();
736 assert_eq!(rint_vec_robj.as_integer_slice().unwrap(), &int_vec);
738 }
739 }
740
741 #[test]
742 fn test_collect_rarray_matrix() {
743 test! {
744 let rmat = (1i32..=16).collect_rarray([4, 4]);
746 assert!(rmat.is_ok());
747 assert_eq!(Robj::from(rmat), R!("matrix(1:16, nrow=4)").unwrap());
748 }
749 }
750
751 #[test]
752 fn test_collect_rarray_tensor() {
753 test! {
754 let rmat = (1i32..=16).collect_rarray([2, 4, 2]);
756 assert!(rmat.is_ok());
757 assert_eq!(Robj::from(rmat), R!("array(1:16, dim=c(2, 4, 2))").unwrap());
758 }
759 }
760
761 #[test]
762 fn test_collect_rarray_matrix_failure() {
763 test! {
764 let rmat = (1i32..=16).collect_rarray([3, 3]);
766 assert!(rmat.is_err());
767 let msg = rmat.unwrap_err().to_string();
768 assert!(msg.contains('9'));
769 assert!(msg.contains("dimension"));
770 }
771 }
772
773 #[test]
774 fn test_collect_tensor_failure() {
775 test! {
776 let rmat = (1i32..=16).collect_rarray([3, 3, 3]);
778 assert!(rmat.is_err());
779 let msg = rmat.unwrap_err().to_string();
780 assert!(msg.contains("27"));
781 assert!(msg.contains("dimension"));
782 }
783 }
784
785 #[test]
786 #[cfg(all(feature = "result_condition", not(feature = "result_list")))]
787 fn test_result_condition() {
788 use crate::prelude::*;
789 fn my_err_f() -> std::result::Result<f64, f64> {
790 Err(42.0) }
792
793 test! {
794 assert_eq!(
795 r!(my_err_f()),
796 R!(
797 "structure(list(message = 'extendr_err',
798 value = 42.0), class = c('extendr_error', 'error', 'condition'))"
799 ).unwrap()
800 );
801 }
802 }
803
804 #[test]
805 #[cfg(feature = "result_list")]
806 fn test_result_list() {
807 use crate::prelude::*;
808 fn my_err_f() -> std::result::Result<f64, String> {
809 Err("We have water in the engine room!".to_string())
810 }
811
812 fn my_ok_f() -> std::result::Result<f64, String> {
813 Ok(123.123)
814 }
815
816 test! {
817 assert_eq!(
818 r!(my_err_f()),
819 R!("x=list(ok=NULL, err='We have water in the engine room!')
820 class(x)='extendr_result'
821 x"
822 ).unwrap()
823 );
824 assert_eq!(
825 r!(my_ok_f()),
826 R!("x = list(ok=123.123, err=NULL)
827 class(x)='extendr_result'
828 x"
829 ).unwrap()
830 );
831 }
832 }
833}