diff --git a/extendr-api/src/deserializer.rs b/extendr-api/src/deserializer.rs index d52f250295..a15237d775 100644 --- a/extendr-api/src/deserializer.rs +++ b/extendr-api/src/deserializer.rs @@ -11,7 +11,6 @@ use serde::de::{ Visitor, }; use serde::forward_to_deserialize_any; -use std::convert::TryFrom; /// Convert any R object to a Deserialize object. pub fn from_robj<'de, T>(robj: &'de Robj) -> Result diff --git a/extendr-api/src/iter.rs b/extendr-api/src/iter.rs index 1c528b1079..2baf69b85e 100644 --- a/extendr-api/src/iter.rs +++ b/extendr-api/src/iter.rs @@ -204,3 +204,13 @@ pub trait AsStrIter: GetSexp + Types + Length + Attributes + Rinternals { } impl AsStrIter for Robj {} + +impl TryFrom<&Robj> for StrIter { + type Error = Error; + + fn try_from(value: &Robj) -> Result { + value + .as_str_iter() + .ok_or_else(|| Error::ExpectedString(value.clone())) + } +} diff --git a/extendr-api/src/lib.rs b/extendr-api/src/lib.rs index b99dc5fb9d..de08cc9378 100644 --- a/extendr-api/src/lib.rs +++ b/extendr-api/src/lib.rs @@ -954,7 +954,7 @@ mod tests { person.set_name("fred"); let robj = r!(person); assert_eq!(robj.check_external_ptr_type::(), true); - let person2 = <&Person>::from_robj(&robj).unwrap(); + let person2 = <&Person>::try_from(&robj).unwrap(); assert_eq!(person2.name(), "fred"); } } diff --git a/extendr-api/src/metadata.rs b/extendr-api/src/metadata.rs index 07e19031a4..7cfaeb2ea9 100644 --- a/extendr-api/src/metadata.rs +++ b/extendr-api/src/metadata.rs @@ -76,16 +76,17 @@ impl From<&Arg> for RArg { impl From for Robj { fn from(val: Arg) -> Self { - List::from_values(&[r!(val.name), r!(val.arg_type)]) - .into_robj() - .set_names(&["name", "arg_type"]) + let mut robj: Robj = List::from_values(&[r!(val.name), r!(val.arg_type)]) + .try_into() + .unwrap(); + robj.set_names(&["name", "arg_type"]) .expect("From failed") } } impl From for Robj { fn from(val: Func) -> Self { - List::from_values(&[ + let mut robj: Robj = List::from_values(&[ r!(val.doc), r!(val.rust_name), r!(val.mod_name), @@ -94,8 +95,9 @@ impl From for Robj { r!(val.return_type), r!(val.hidden), ]) - .into_robj() - .set_names(&[ + .try_into() + .unwrap(); + robj.set_names(&[ "doc", "rust_name", "mod_name", @@ -110,27 +112,29 @@ impl From for Robj { impl From for Robj { fn from(val: Impl) -> Self { - List::from_values(&[ + let mut robj: Robj = List::from_values(&[ r!(val.doc), r!(val.name), r!(List::from_values(val.methods)), ]) - .into_robj() - .set_names(&["doc", "name", "methods"]) - .expect("From failed") + .try_into() + .unwrap(); + robj.set_names(&["doc", "name", "methods"]) + .expect("From failed") } } impl From for Robj { fn from(val: Metadata) -> Self { - List::from_values(&[ + let mut robj: Robj = List::from_values(&[ r!(val.name), r!(List::from_values(val.functions)), r!(List::from_values(val.impls)), ]) - .into_robj() - .set_names(&["name", "functions", "impls"]) - .expect("From failed") + .try_into() + .unwrap(); + robj.set_names(&["name", "functions", "impls"]) + .expect("From failed") } } diff --git a/extendr-api/src/optional/either.rs b/extendr-api/src/optional/either.rs index b76aa3a2a0..ce3b13e7fd 100644 --- a/extendr-api/src/optional/either.rs +++ b/extendr-api/src/optional/either.rs @@ -30,7 +30,6 @@ fn type_aware_sum(input : Either) -> Either { ``` */ use crate::prelude::*; -use crate::{Error, Robj}; impl<'a, L, R> TryFrom<&'a Robj> for Either where diff --git a/extendr-api/src/optional/ndarray.rs b/extendr-api/src/optional/ndarray.rs index db284f3607..52fef7120c 100644 --- a/extendr-api/src/optional/ndarray.rs +++ b/extendr-api/src/optional/ndarray.rs @@ -56,25 +56,11 @@ For all array uses in Rust, refer to the [`ndarray::ArrayBase`] documentation, w */ #[doc(hidden)] use ndarray::prelude::*; -use ndarray::{Data, ShapeBuilder}; +use ndarray::Data; use crate::prelude::{c64, dim_symbol, Rcplx, Rfloat, Rint}; use crate::*; -impl<'a, T> FromRobj<'a> for ArrayView1<'a, T> -where - Robj: AsTypedSlice<'a, T>, -{ - /// Convert an R object to a `ndarray` ArrayView1. - fn from_robj(robj: &'a Robj) -> std::result::Result { - if let Some(v) = robj.as_typed_slice() { - Ok(ArrayView1::<'a, T>::from(v)) - } else { - Err("Not a vector of the correct type.") - } - } -} - macro_rules! make_array_view_1 { ($type: ty, $error_fn: expr) => { impl<'a> TryFrom<&'_ Robj> for ArrayView1<'a, $type> { @@ -101,13 +87,6 @@ macro_rules! make_array_view_1 { macro_rules! make_array_view_2 { ($type: ty, $error_str: expr, $error_fn: expr) => { - impl<'a> FromRobj<'a> for ArrayView2<'a, $type> { - /// Convert an R object to a `ndarray` ArrayView2. - fn from_robj(robj: &'a Robj) -> std::result::Result { - >::try_from(robj).map_err(|_| $error_str) - } - } - impl<'a> TryFrom<&'_ Robj> for ArrayView2<'a, $type> { type Error = crate::Error; fn try_from(robj: &Robj) -> Result { @@ -216,7 +195,7 @@ where #[cfg(test)] mod test { use super::*; - use crate::FromRobj; + use crate as extendr_api; use ndarray::array; use rstest::rstest; @@ -252,19 +231,21 @@ mod test { "matrix(c(T, T, T, T, F, F, F, F), ncol=2, nrow=4)", >::from_shape_vec((4, 2).f(), vec![true.into(), true.into(), true.into(), true.into(), false.into(), false.into(), false.into(), false.into()]).unwrap() )] - fn test_from_robj( + fn test_from_robj( #[case] left: &'static str, #[case] right: ArrayBase, ) where DataType: Data, - for<'a> ArrayView<'a, ::Elem, DimType>: FromRobj<'a>, + for<'a> ArrayView<'a, ::Elem, DimType>: + TryFrom<&'a Robj, Error = Error>, DimType: Dimension, + Error: std::fmt::Debug, ::Elem: PartialEq + std::fmt::Debug, { // Tests for the R → Rust conversion test! { let left_robj = eval_string(left).unwrap(); - let left_array = >::from_robj(&left_robj).unwrap(); + let left_array = >::try_from(&left_robj).unwrap(); assert_eq!( left_array, right ); } } @@ -337,7 +318,7 @@ mod test { ]; for rval in rvals { let rval = rval.unwrap(); - let rust_arr= >::from_robj(&rval).unwrap(); + let rust_arr= >::try_from(&rval).unwrap(); let r_arr: Robj = (&rust_arr).try_into().unwrap(); assert_eq!( rval, diff --git a/extendr-api/src/prelude.rs b/extendr-api/src/prelude.rs index 2a9fe4e18c..f692795868 100644 --- a/extendr-api/src/prelude.rs +++ b/extendr-api/src/prelude.rs @@ -4,8 +4,8 @@ //! using deprecated features. pub use super::{ - print_r_error, print_r_output, CanBeNA, FromRobj, Rtype, FALSE, NA_INTEGER, NA_LOGICAL, - NA_REAL, NA_STRING, NULL, TRUE, + print_r_error, print_r_output, CanBeNA, Rtype, FALSE, NA_INTEGER, NA_LOGICAL, NA_REAL, + NA_STRING, NULL, TRUE, }; pub use super::na::*; @@ -49,8 +49,8 @@ pub use super::wrapper::s4::S4; pub use super::wrapper::{Conversions, MatrixConversions}; pub use super::robj::{ - AsStrIter, Attributes, Eval, GetSexp, IntoRobj, Length, Operators, Rinternals, Robj, - RobjItertools, Slices, Types, + AsStrIter, Attributes, Eval, GetSexp, Length, Operators, Rinternals, Robj, RobjItertools, + Slices, Types, }; pub use super::thread_safety::{catch_r_error, handle_panic, single_threaded, throw_r_error}; diff --git a/extendr-api/src/robj/from_robj.rs b/extendr-api/src/robj/from_robj.rs deleted file mode 100644 index fbbbf17d90..0000000000 --- a/extendr-api/src/robj/from_robj.rs +++ /dev/null @@ -1,290 +0,0 @@ -use super::*; - -/// Trait used for incoming parameter conversion. -pub trait FromRobj<'a>: Sized { - // Convert an incoming Robj from R into a value or an error. - fn from_robj(_robj: &'a Robj) -> std::result::Result { - Err("unable to convert value from R object") - } -} - -macro_rules! impl_prim_from_robj { - ($t: ty) => { - impl<'a> FromRobj<'a> for $t { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if let Some(v) = robj.as_integer_slice() { - match v.len() { - 0 => Err("Input must be of length 1. Vector of length zero given."), - 1 => { - if !v[0].is_na() { - Ok(v[0] as Self) - } else { - Err("Input must not be NA.") - } - } - _ => Err("Input must be of length 1. Vector of length >1 given."), - } - } else if let Some(v) = robj.as_real_slice() { - match v.len() { - 0 => Err("Input must be of length 1. Vector of length zero given."), - 1 => { - if !v[0].is_na() { - Ok(v[0] as Self) - } else { - Err("Input must not be NA.") - } - } - _ => Err("Input must be of length 1. Vector of length >1 given."), - } - } else { - Err("unable to convert R object to primitive") - } - } - } - }; -} - -impl_prim_from_robj!(u8); -impl_prim_from_robj!(u16); -impl_prim_from_robj!(u32); -impl_prim_from_robj!(u64); -impl_prim_from_robj!(i8); -impl_prim_from_robj!(i16); -impl_prim_from_robj!(i32); -impl_prim_from_robj!(i64); -impl_prim_from_robj!(f32); -impl_prim_from_robj!(f64); - -impl<'a> FromRobj<'a> for bool { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if let Some(v) = robj.as_logical_slice() { - match v.len() { - 0 => Err("Input must be of length 1. Vector of length zero given."), - 1 => { - if !v[0].is_na() { - Ok(v[0].to_bool()) - } else { - Err("Input must not be NA.") - } - } - _ => Err("Input must be of length 1. Vector of length >1 given."), - } - } else { - Err("Not a logical object.") - } - } -} - -impl<'a> FromRobj<'a> for &'a str { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if robj.is_na() { - Err("Input must not be NA.") - } else if let Some(s) = robj.as_str() { - Ok(s) - } else { - Err("Not a string object.") - } - } -} - -impl<'a> FromRobj<'a> for String { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if robj.is_na() { - Err("Input must not be NA.") - } else if let Some(s) = robj.as_str() { - Ok(s.to_string()) - } else { - Err("not a string object") - } - } -} - -impl<'a> FromRobj<'a> for Vec { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if let Some(v) = robj.as_integer_slice() { - Ok(Vec::from(v)) - } else { - Err("not an integer or logical vector") - } - } -} - -impl<'a> FromRobj<'a> for Vec { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if let Some(v) = robj.as_real_slice() { - Ok(Vec::from(v)) - } else { - Err("not a floating point vector") - } - } -} - -impl<'a> FromRobj<'a> for Vec { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if robj.is_na() { - Err("Input must be a character vector. Got 'NA'.") - } else if let Some(v) = robj.as_string_vector() { - let str_vec = v.to_vec(); - // check for NA's in the string vector - // The check is by-value, so `<&str>::is_na()` cannot be used - if let Some(_str) = str_vec.iter().find(|&s| *s == <&str>::na()) { - Err("Input vector cannot contain NA's.") - } else { - Ok(str_vec) - } - } else { - Err("Input must be a character vector.") - } - } -} - -macro_rules! impl_iter_from_robj { - ($t: ty, $iter_fn: ident, $msg: expr) => { - impl<'a> FromRobj<'a> for $t { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if let Some(v) = robj.$iter_fn() { - Ok(v) - } else { - Err($msg) - } - } - } - }; -} - -impl_iter_from_robj!(StrIter, as_str_iter, "Not a character vector."); - -/// Pass-through Robj conversion, essentially a clone. -impl<'a> FromRobj<'a> for Robj { - fn from_robj(robj: &'a Robj) -> std::result::Result { - Ok(unsafe { Robj::from_sexp(robj.get()) }) - } -} - -impl<'a> FromRobj<'a> for HashMap { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if let Some(iter) = robj.as_list().map(|l| l.iter()) { - Ok(iter - .map(|(k, v)| (k.to_string(), v)) - .collect::>()) - } else { - Err("expected a list") - } - } -} - -impl<'a> FromRobj<'a> for HashMap<&str, Robj> { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if let Some(iter) = robj.as_list().map(|l| l.iter()) { - Ok(iter.collect::>()) - } else { - Err("expected a list") - } - } -} - -// NA-sensitive integer input handling -impl<'a> FromRobj<'a> for Option { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if robj.is_na() { - Ok(None) - } else if let Some(val) = robj.as_integer() { - Ok(Some(val)) - } else { - Err("expected an integer scalar") - } - } -} - -// NA-sensitive logical input handling -impl<'a> FromRobj<'a> for Option { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if let Some(val) = robj.as_logical() { - if val.is_na() { - Ok(None) - } else { - Ok(Some(val.is_true())) - } - } else { - Err("expected a logical scalar") - } - } -} - -// NA-sensitive real input handling -impl<'a> FromRobj<'a> for Option { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if robj.is_na() { - Ok(None) - } else if let Some(val) = robj.as_real() { - Ok(Some(val)) - } else { - Err("expected a real scalar") - } - } -} - -// NA-sensitive string input handling -impl<'a> FromRobj<'a> for Option<&'a str> { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if robj.is_na() { - Ok(None) - } else if let Some(val) = robj.as_str() { - Ok(Some(val)) - } else { - Err("expected a character scalar") - } - } -} - -// NA-sensitive string input handling -impl<'a> FromRobj<'a> for Option { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if robj.is_na() { - Ok(None) - } else if let Some(val) = robj.as_str() { - Ok(Some(val.to_string())) - } else { - Err("expected a character scalar") - } - } -} - -impl<'a, T> FromRobj<'a> for &'a [T] -where - Robj: AsTypedSlice<'a, T>, -{ - fn from_robj(robj: &'a Robj) -> std::result::Result { - if let Some(slice) = robj.as_typed_slice() { - Ok(slice) - } else { - Err("Expected a vector type.") - } - } -} - -// Matrix input parameters. -impl<'a, T: 'a> FromRobj<'a> for RArray -where - Robj: AsTypedSlice<'a, T>, -{ - fn from_robj(robj: &'a Robj) -> std::result::Result { - match robj.as_matrix() { - Some(x) => Ok(x), - _ => Err("Expected a matrix."), - } - } -} - -// Matrix input parameters. -impl<'a, T: 'a> FromRobj<'a> for RMatrix3D -where - Robj: AsTypedSlice<'a, T>, -{ - fn from_robj(robj: &'a Robj) -> std::result::Result { - match robj.as_matrix3d() { - Some(x) => Ok(x), - _ => Err("Expected a matrix."), - } - } -} diff --git a/extendr-api/src/robj/into_robj.rs b/extendr-api/src/robj/into_robj.rs index 5108131cca..f77c2d0b4b 100644 --- a/extendr-api/src/robj/into_robj.rs +++ b/extendr-api/src/robj/into_robj.rs @@ -141,19 +141,6 @@ impl From<&Robj> for Robj { } } -pub trait IntoRobj { - fn into_robj(self) -> Robj; -} - -impl IntoRobj for T -where - Robj: From, -{ - fn into_robj(self) -> Robj { - self.into() - } -} - /// `ToVectorValue` is a trait that allows many different types /// to be converted to vectors. It is used as a type parameter /// to `collect_robj()`. diff --git a/extendr-api/src/robj/mod.rs b/extendr-api/src/robj/mod.rs index a5cebd4a6e..a553d1af74 100644 --- a/extendr-api/src/robj/mod.rs +++ b/extendr-api/src/robj/mod.rs @@ -19,9 +19,6 @@ use crate::scalar::{Rbool, Rfloat, Rint}; use std::collections::HashMap; use std::ops::{Range, RangeInclusive}; -// deprecated -mod from_robj; - mod debug; mod into_robj; mod operators; @@ -31,7 +28,6 @@ mod try_from_robj; #[cfg(test)] mod tests; -pub use from_robj::*; pub use into_robj::*; pub use iter::*; pub use operators::Operators; diff --git a/extendr-api/src/robj/tests.rs b/extendr-api/src/robj/tests.rs index 8fb21722a3..d93cbe62e1 100644 --- a/extendr-api/src/robj/tests.rs +++ b/extendr-api/src/robj/tests.rs @@ -4,39 +4,39 @@ use crate::*; use crate as extendr_api; #[test] -fn test_from_robj() { +fn test_try_from() { test! { - assert_eq!(::from_robj(&Robj::from(true)), Ok(true)); - assert_eq!(::from_robj(&Robj::from(1)), Ok(1)); - assert_eq!(::from_robj(&Robj::from(1)), Ok(1)); - assert_eq!(::from_robj(&Robj::from(1)), Ok(1)); - assert_eq!(::from_robj(&Robj::from(1)), Ok(1)); - assert_eq!(::from_robj(&Robj::from(1)), Ok(1)); - assert_eq!(::from_robj(&Robj::from(1)), Ok(1)); - assert_eq!(::from_robj(&Robj::from(1)), Ok(1)); - assert_eq!(::from_robj(&Robj::from(1)), Ok(1)); - assert_eq!(::from_robj(&Robj::from(1)), Ok(1.)); - assert_eq!(::from_robj(&Robj::from(1)), Ok(1.)); - - assert_eq!(>::from_robj(&Robj::from(1)), Ok(vec![1])); - assert_eq!(>::from_robj(&Robj::from(1.)), Ok(vec![1.])); + assert_eq!(::try_from(&Robj::from(true)), Ok(true)); + assert_eq!(::try_from(&Robj::from(1)), Ok(1)); + assert_eq!(::try_from(&Robj::from(1)), Ok(1)); + assert_eq!(::try_from(&Robj::from(1)), Ok(1)); + assert_eq!(::try_from(&Robj::from(1)), Ok(1)); + assert_eq!(::try_from(&Robj::from(1)), Ok(1)); + assert_eq!(::try_from(&Robj::from(1)), Ok(1)); + assert_eq!(::try_from(&Robj::from(1)), Ok(1)); + assert_eq!(::try_from(&Robj::from(1)), Ok(1)); + assert_eq!(::try_from(&Robj::from(1)), Ok(1.)); + assert_eq!(::try_from(&Robj::from(1)), Ok(1.)); + + assert_eq!(>::try_from(&Robj::from(1)), Ok(vec![1])); + assert_eq!(>::try_from(&Robj::from(1.)), Ok(vec![1.])); let hello = Robj::from("hello"); - assert_eq!(<&str>::from_robj(&hello), Ok("hello")); + assert_eq!(<&str>::try_from(&hello), Ok("hello")); // conversion from a vector to a scalar value + // assert_eq!( + // ::try_from(&Robj::from(vec![].as_slice() as &[i32])), + // Err("Input must be of length 1. Vector of length zero given.") + // ); assert_eq!( - ::from_robj(&Robj::from(vec![].as_slice() as &[i32])), - Err("Input must be of length 1. Vector of length zero given.") - ); - assert_eq!( - ::from_robj(&Robj::from(vec![1].as_slice() as &[i32])), + ::try_from(&Robj::from(vec![1].as_slice() as &[i32])), Ok(1) ); - assert_eq!( - ::from_robj(&Robj::from(vec![1, 2].as_slice() as &[i32])), - Err("Input must be of length 1. Vector of length >1 given.") - ); + // assert_eq!( + // ::try_from(&Robj::from(vec![1, 2].as_slice() as &[i32])), + // Err("Input must be of length 1. Vector of length >1 given.") + // ); use std::collections::HashMap; let list = eval_string("list(a = 1L, b = 2L)").unwrap(); @@ -48,8 +48,8 @@ fn test_from_robj() { .iter() .cloned() .collect::>(); - let hmap_owned = >::from_robj(&list).unwrap(); - let hmap_borrowed = >::from_robj(&list).unwrap(); + let hmap_owned = >::try_from(&list).unwrap(); + let hmap_borrowed = >::try_from(&list).unwrap(); assert_eq!(hmap_owned, hmap1); assert_eq!(hmap_borrowed, hmap2); @@ -60,42 +60,42 @@ fn test_from_robj() { assert_eq!(hmap_borrowed["b"], Robj::from(2)); let na_integer = eval_string("NA_integer_").unwrap(); - assert!(::from_robj(&na_integer).is_err()); - assert_eq!(>::from_robj(&na_integer), Ok(None)); - assert_eq!(>::from_robj(&Robj::from(1)), Ok(Some(1))); - assert!(>::from_robj(&Robj::from([1, 2])).is_err()); + assert!(::try_from(&na_integer).is_err()); + assert_eq!(>::try_from(&na_integer), Ok(None)); + assert_eq!(>::try_from(&Robj::from(1)), Ok(Some(1))); + assert!(>::try_from(&Robj::from([1, 2])).is_err()); let na_bool = eval_string("TRUE == NA").unwrap(); - assert!(::from_robj(&na_bool).is_err()); - assert_eq!(>::from_robj(&na_bool), Ok(None)); - assert_eq!(>::from_robj(&Robj::from(true)), Ok(Some(true))); - assert!(>::from_robj(&Robj::from([true, false])).is_err()); + assert!(::try_from(&na_bool).is_err()); + assert_eq!(>::try_from(&na_bool), Ok(None)); + assert_eq!(>::try_from(&Robj::from(true)), Ok(Some(true))); + assert!(>::try_from(&Robj::from([true, false])).is_err()); let na_real = eval_string("NA_real_").unwrap(); - assert!(::from_robj(&na_real).is_err()); - assert_eq!(>::from_robj(&na_real), Ok(None)); - assert_eq!(>::from_robj(&Robj::from(1.)), Ok(Some(1.))); - assert!(>::from_robj(&Robj::from([1., 2.])).is_err()); + assert!(::try_from(&na_real).is_err()); + assert_eq!(>::try_from(&na_real), Ok(None)); + assert_eq!(>::try_from(&Robj::from(1.)), Ok(Some(1.))); + assert!(>::try_from(&Robj::from([1., 2.])).is_err()); let na_string = eval_string("NA_character_").unwrap(); - assert!(<&str>::from_robj(&na_string).is_err()); - assert_eq!(>::from_robj(&na_string), Ok(None)); - assert_eq!(>::from_robj(&Robj::from("1")), Ok(Some("1"))); - assert!(>::from_robj(&Robj::from(["1", "2"])).is_err()); + assert!(<&str>::try_from(&na_string).is_err()); + assert_eq!(>::try_from(&na_string), Ok(None)); + assert_eq!(>::try_from(&Robj::from("1")), Ok(Some("1"))); + assert!(>::try_from(&Robj::from(["1", "2"])).is_err()); let na_string = eval_string("NA_character_").unwrap(); - assert!(::from_robj(&na_string).is_err()); - assert_eq!(>::from_robj(&na_string), Ok(None)); + assert!(::try_from(&na_string).is_err()); + assert_eq!(>::try_from(&na_string), Ok(None)); assert_eq!( - >::from_robj(&Robj::from("1")), + >::try_from(&Robj::from("1")), Ok(Some("1".to_string())) ); - assert!(>::from_robj(&Robj::from(["1", "2"])).is_err()); + assert!(>::try_from(&Robj::from(["1", "2"])).is_err()); } } #[test] -fn test_try_from_robj() { +fn test_try_try_from() { test! { assert_eq!(::try_from(Robj::from(true)), Ok(true)); assert_eq!(::try_from(Robj::from(1)), Ok(1)); @@ -334,28 +334,28 @@ fn input_iterator_test() { test! { let src: &[&str] = &["1", "2", "3"]; let robj = Robj::from(src); - let iter = ::from_robj(&robj).unwrap(); + let iter = ::try_from(&robj).unwrap(); assert_eq!(iter.collect::>(), src); let src = &[Robj::from(1), Robj::from(2), Robj::from(3)]; let robj = Robj::from(List::from_values(src)); - let iter = ::from_robj(&robj).unwrap(); + let iter = ::try_from(&robj).unwrap(); assert_eq!(iter.collect::>(), src); let src: &[i32] = &[1, 2, 3]; let robj = Robj::from(src); - let iter = ::from_robj(&robj).unwrap().iter(); + let iter = ::try_from(&robj).unwrap().iter(); assert_eq!(iter.collect::>(), src); let src: &[f64] = &[1., 2., 3.]; let robj = Robj::from(src); - let iter = ::from_robj(&robj).unwrap().iter(); + let iter = ::try_from(&robj).unwrap().iter(); assert_eq!(iter.collect::>(), src); /* let src: &[Rbool] = &[TRUE, FALSE, TRUE]; let robj = Robj::from(src); - let iter = ::from_robj(&robj).unwrap(); + let iter = ::try_from(&robj).unwrap(); assert_eq!(iter.collect::>(), src); */ } diff --git a/extendr-api/src/robj/try_from_robj.rs b/extendr-api/src/robj/try_from_robj.rs index 83ff5bdf0f..09535595a6 100644 --- a/extendr-api/src/robj/try_from_robj.rs +++ b/extendr-api/src/robj/try_from_robj.rs @@ -586,3 +586,26 @@ impl_try_from_robj_mut!( ); // endregion + +impl TryFrom<&Robj> for HashMap { + type Error = Error; + fn try_from(robj: &Robj) -> Result { + Ok(robj + .as_list() + .map(|l| l.iter()) + .ok_or_else(|| Error::ExpectedList(robj.clone()))? + .map(|(k, v)| (k.to_string(), v)) + .collect::>()) + } +} + +impl TryFrom<&Robj> for HashMap<&str, Robj> { + type Error = Error; + fn try_from(robj: &Robj) -> Result { + Ok(robj + .as_list() + .map(|l| l.iter()) + .ok_or_else(|| Error::ExpectedList(robj.clone()))? + .collect::>()) + } +} diff --git a/extendr-api/src/scalar/rcplx_full.rs b/extendr-api/src/scalar/rcplx_full.rs index a7f1d4fecb..bf078af3bf 100644 --- a/extendr-api/src/scalar/rcplx_full.rs +++ b/extendr-api/src/scalar/rcplx_full.rs @@ -1,7 +1,6 @@ use crate::scalar::macros::*; use crate::scalar::{Rfloat, Scalar}; use crate::*; -use std::convert::TryFrom; use std::ops::{Add, Div, Mul, Neg, Sub}; use std::ops::{AddAssign, DivAssign, MulAssign, SubAssign}; diff --git a/extendr-api/src/wrapper/list.rs b/extendr-api/src/wrapper/list.rs index fc42ec8c92..1672b53466 100644 --- a/extendr-api/src/wrapper/list.rs +++ b/extendr-api/src/wrapper/list.rs @@ -354,12 +354,6 @@ impl From for Robj { } } -impl<'a> FromRobj<'a> for ListIter { - fn from_robj(robj: &'a Robj) -> std::result::Result { - robj.as_list().map(|l| l.values()).ok_or("Not a list.") - } -} - // TODO: use Rstr or Sym instead of String. pub trait KeyValue { fn key(&self) -> String; diff --git a/extendr-api/src/wrapper/mod.rs b/extendr-api/src/wrapper/mod.rs index 7f189bb224..5c121f1d81 100644 --- a/extendr-api/src/wrapper/mod.rs +++ b/extendr-api/src/wrapper/mod.rs @@ -79,16 +79,6 @@ where macro_rules! make_conversions { ($typename: ident, $errname: ident, $isfunc: ident, $errstr: expr) => { - impl<'a> FromRobj<'a> for $typename { - fn from_robj(robj: &'a Robj) -> std::result::Result { - if let Ok(f) = $typename::try_from(robj.clone()) { - Ok(f) - } else { - Err($errstr) - } - } - } - impl From<$typename> for Robj { /// Make an robj from a wrapper. fn from(val: $typename) -> Self { diff --git a/extendr-api/src/wrapper/nullable.rs b/extendr-api/src/wrapper/nullable.rs index 59751648da..e8877678c2 100644 --- a/extendr-api/src/wrapper/nullable.rs +++ b/extendr-api/src/wrapper/nullable.rs @@ -8,19 +8,19 @@ use super::*; /// /// // Plain integer. /// let s1 = r!(1); -/// let n1 = >::from_robj(&s1)?; +/// let n1 = >::try_from(&s1)?; /// assert_eq!(n1, NotNull(1)); /// /// // NA integer - error. /// let sna = r!(NA_INTEGER); -/// assert_eq!(>::from_robj(&sna).is_err(), true); +/// assert_eq!(>::try_from(&sna).is_err(), true); /// /// // NA integer - option gives none. -/// assert_eq!(>>::from_robj(&sna)?, NotNull(None)); +/// assert_eq!(>>::try_from(&sna)?, NotNull(None)); /// /// // NULL object. /// let snull = r!(NULL); -/// let nnull = >::from_robj(&snull)?; +/// let nnull = >::try_from(&snull)?; /// assert_eq!(nnull, Null); /// /// assert_eq!(r!(Nullable::::Null), r!(NULL)); @@ -33,31 +33,6 @@ pub enum Nullable { Null, } -impl<'a, T> FromRobj<'a> for Nullable -where - T: FromRobj<'a>, -{ - /// Convert an object that may be null to a rust type. - /// ``` - /// use extendr_api::prelude::*; - /// test! { - /// let s1 = r!(1); - /// let n1 = >::from_robj(&s1)?; - /// assert_eq!(n1, Nullable::NotNull(1)); - /// let snull = r!(NULL); - /// let nnull = >::from_robj(&snull)?; - /// assert_eq!(nnull, Nullable::Null); - /// } - /// ``` - fn from_robj(robj: &'a Robj) -> std::result::Result { - if robj.is_null() { - Ok(Nullable::Null) - } else { - Ok(Nullable::NotNull(::from_robj(robj)?)) - } - } -} - impl TryFrom for Nullable where T: TryFrom, diff --git a/extendr-api/tests/altrep_tests.rs b/extendr-api/tests/altrep_tests.rs index af5dca7984..ad0d67ec02 100644 --- a/extendr-api/tests/altrep_tests.rs +++ b/extendr-api/tests/altrep_tests.rs @@ -257,7 +257,7 @@ mod tests { impl AltListImpl for VecUsize { fn elt(&self, index: usize) -> Robj { - Self(vec![self.0[index]]).into_robj() + Self(vec![self.0[index]]).try_into().unwrap() } } @@ -277,7 +277,8 @@ mod tests { assert_eq!(obj.len(), 3); // convert to a list and test the .elt() method - let l = List::try_from(obj.into_robj()).unwrap(); + let robj: Robj = obj.try_into().unwrap(); + let l = List::try_from(robj).unwrap(); let li = l.elt(1).unwrap(); assert!(li.inherits("VecUsize")); @@ -324,7 +325,8 @@ mod tests_use_try_from { assert_eq!(obj.len(), 3); // convert to a list and test the .elt() method - let l = List::try_from(obj.into_robj()).unwrap(); + let robj: Robj = obj.try_into().unwrap(); + let l = List::try_from(robj).unwrap(); let li = l.elt(1).unwrap(); assert!(li.inherits("VecUsize")); diff --git a/extendr-api/tests/from_iterator_tests.rs b/extendr-api/tests/from_iterator_tests.rs index c75508d59e..47f9d8bdd7 100644 --- a/extendr-api/tests/from_iterator_tests.rs +++ b/extendr-api/tests/from_iterator_tests.rs @@ -62,7 +62,7 @@ fn test_with_gc_torture_strings() { let qq_r_character_vec: Strings = question_quote.into_iter().collect(); R!("gctorture(on = FALSE)")?; // Strings::from_values is the same as `.collect`. - let qq_directly = Strings::from_robj(&Robj::from(question_quote)).unwrap(); + let qq_directly = Strings::try_from(&Robj::from(question_quote)).unwrap(); assert_eq!(qq_r_character_vec, qq_directly); ); } diff --git a/extendr-macros/src/extendr_impl.rs b/extendr-macros/src/extendr_impl.rs index 0d0a66c486..fc333ff270 100644 --- a/extendr-macros/src/extendr_impl.rs +++ b/extendr-macros/src/extendr_impl.rs @@ -115,6 +115,22 @@ pub fn extendr_impl(mut item_impl: ItemImpl, opts: &ExtendrOptions) -> TokenStre } } + impl TryFrom for &#self_ty { + type Error = Error; + + fn try_from(robj: Robj) -> Result { + Self::try_from(&robj) + } + } + + impl TryFrom for &mut #self_ty { + type Error = Error; + + fn try_from(mut robj: Robj) -> Result { + Self::try_from(&mut robj) + } + } + // Output conversion function for this type. impl TryFrom<&Robj> for &#self_ty { type Error = Error; diff --git a/extendr-macros/src/extendr_module.rs b/extendr-macros/src/extendr_module.rs index 3c929733cb..92259081ee 100644 --- a/extendr-macros/src/extendr_module.rs +++ b/extendr-macros/src/extendr_module.rs @@ -102,10 +102,10 @@ pub fn extendr_module(item: TokenStream) -> TokenStream { use extendr_api::robj::*; use extendr_api::GetSexp; let robj = Robj::from_sexp(use_symbols_sexp); - let use_symbols: bool = ::from_robj(&robj).unwrap(); + let use_symbols: bool = ::try_from(&robj).unwrap(); let robj = Robj::from_sexp(package_name_sexp); - let package_name: &str = <&str>::from_robj(&robj).unwrap(); + let package_name: &str = <&str>::try_from(&robj).unwrap(); extendr_api::Robj::from( #module_metadata_name() diff --git a/extendr-macros/src/lib.rs b/extendr-macros/src/lib.rs index 446f603aa5..31ee2dee74 100644 --- a/extendr-macros/src/lib.rs +++ b/extendr-macros/src/lib.rs @@ -95,6 +95,7 @@ pub fn extendr(attr: TokenStream, item: TokenStream) -> TokenStream { let extendr_opts_parser = syn::meta::parser(|meta| opts.parse(meta)); parse_macro_input!(attr with extendr_opts_parser); + opts.use_try_from = true; match item { Item::Fn(func) => extendr_function::extendr_function(func, &opts), Item::Impl(item_impl) => extendr_impl::extendr_impl(item_impl, &opts),