use super::*;
use crate as extendr_api;
use crate::conversions::try_into_int::FloatToInt;
macro_rules! impl_try_from_scalar_integer {
($t:ty) => {
impl TryFrom<&Robj> for $t {
type Error = Error;
fn try_from(robj: &Robj) -> Result<Self> {
match robj.len() {
0 => return Err(Error::ExpectedNonZeroLength(robj.clone())),
1 => {}
_ => return Err(Error::ExpectedScalar(robj.clone())),
};
if robj.is_na() {
return Err(Error::MustNotBeNA(robj.clone()));
}
if let Some(v) = robj.as_integer() {
return Self::try_from(v).map_err(|_| Error::OutOfLimits(robj.clone()));
}
if let Some(v) = robj.as_real() {
return v
.try_into_int()
.map_err(|conv_err| Error::ExpectedWholeNumber(robj.clone(), conv_err));
}
Err(Error::ExpectedNumeric(robj.clone()))
}
}
};
}
macro_rules! impl_try_from_scalar_real {
($t:ty) => {
impl TryFrom<&Robj> for $t {
type Error = Error;
fn try_from(robj: &Robj) -> Result<Self> {
match robj.len() {
0 => return Err(Error::ExpectedNonZeroLength(robj.clone())),
1 => {}
_ => return Err(Error::ExpectedScalar(robj.clone())),
};
if robj.is_na() {
return Err(Error::MustNotBeNA(robj.clone()));
}
if let Some(v) = robj.as_real() {
return Ok(v as Self);
}
if let Some(v) = robj.as_integer() {
return Ok(v as Self);
}
Err(Error::ExpectedNumeric(robj.clone()))
}
}
};
}
macro_rules! impl_typed_slice_conversions {
($type:ty, $error:ident, $desc:expr) => {
impl_typed_slice_conversions!($type, $error, $error, $error, $desc);
};
($type:ty, $vec_error:ident, $slice_error:ident, $mut_error:ident, $desc:expr) => {
impl TryFrom<&Robj> for Vec<$type> {
type Error = Error;
#[doc = concat!("Convert ", $desc, " into `Vec<", stringify!($type), ">`.")]
#[doc = "Note: Unless you plan to store the result, use a slice instead."]
fn try_from(robj: &Robj) -> Result<Self> {
robj.as_typed_slice()
.map(<[_]>::to_vec)
.ok_or_else(|| Error::$vec_error(robj.clone()))
}
}
impl TryFrom<&Robj> for &[$type] {
type Error = Error;
#[doc = concat!("Convert ", $desc, " into `&[", stringify!($type), "]`.")]
fn try_from(robj: &Robj) -> Result<Self> {
robj.as_typed_slice()
.ok_or_else(|| Error::$slice_error(robj.clone()))
}
}
impl TryFrom<&mut Robj> for &mut [$type] {
type Error = Error;
#[doc = concat!("Convert ", $desc, " into `&mut [", stringify!($type), "]`.")]
fn try_from(robj: &mut Robj) -> Result<Self> {
robj.as_typed_slice_mut()
.ok_or_else(|| Error::$mut_error(robj.clone()))
}
}
impl TryFrom<&Robj> for Option<&[$type]> {
type Error = Error;
#[doc = concat!("Convert ", $desc, " into `Option<&[", stringify!($type), "]>`.")]
fn try_from(robj: &Robj) -> Result<Self> {
if robj.is_null() || robj.is_na() {
Ok(None)
} else {
Ok(Some(<&[$type]>::try_from(robj)?))
}
}
}
impl TryFrom<&mut Robj> for Option<&mut [$type]> {
type Error = Error;
#[doc = concat!("Convert ", $desc, " into `Option<&mut [", stringify!($type), "]>`.")]
fn try_from(robj: &mut Robj) -> Result<Self> {
if robj.is_null() || robj.is_na() {
Ok(None)
} else {
Ok(Some(<&mut [$type]>::try_from(robj)?))
}
}
}
impl TryFrom<&Robj> for &$type {
type Error = Error;
#[doc = concat!("Convert ", $desc, " into `&", stringify!($type), "`.")]
fn try_from(robj: &Robj) -> Result<Self> {
let slice: &[$type] = robj.try_into()?;
if slice.is_empty() {
return Err(Error::ExpectedNonZeroLength(robj.clone()));
}
if slice.len() != 1 {
return Err(Error::ExpectedScalar(robj.clone()));
}
let Some(value) = slice.get(0) else {
unreachable!()
};
if value.is_na() {
return Err(Error::MustNotBeNA(robj.clone()));
}
Ok(value)
}
}
impl TryFrom<&mut Robj> for &mut $type {
type Error = Error;
#[doc = concat!("Convert ", $desc, " into `&mut ", stringify!($type), "`.")]
fn try_from(robj: &mut Robj) -> Result<Self> {
let slice: &mut [$type] = robj.try_into()?;
if slice.is_empty() {
return Err(Error::ExpectedNonZeroLength(robj.clone()));
}
if slice.len() != 1 {
return Err(Error::ExpectedScalar(robj.clone()));
}
let Some(value) = slice.get_mut(0) else {
unreachable!()
};
if value.is_na() {
return Err(Error::MustNotBeNA(robj.clone()));
}
Ok(value)
}
}
};
}
impl_try_from_scalar_integer!(u8);
impl_try_from_scalar_integer!(u16);
impl_try_from_scalar_integer!(u32);
impl_try_from_scalar_integer!(u64);
impl_try_from_scalar_integer!(usize);
impl_try_from_scalar_integer!(i8);
impl_try_from_scalar_integer!(i16);
impl_try_from_scalar_integer!(i32);
impl_try_from_scalar_integer!(i64);
impl_try_from_scalar_integer!(isize);
impl_try_from_scalar_real!(f32);
impl_try_from_scalar_real!(f64);
impl TryFrom<&Robj> for bool {
type Error = Error;
fn try_from(robj: &Robj) -> Result<Self> {
if robj.is_na() {
Err(Error::MustNotBeNA(robj.clone()))
} else {
Ok(<Rbool>::try_from(robj)?.is_true())
}
}
}
impl TryFrom<&Robj> for &str {
type Error = Error;
fn try_from(robj: &Robj) -> Result<Self> {
if robj.is_na() {
return Err(Error::MustNotBeNA(robj.clone()));
}
match robj.len() {
0 => Err(Error::ExpectedNonZeroLength(robj.clone())),
1 => {
if let Some(s) = robj.as_str() {
Ok(s)
} else {
Err(Error::ExpectedString(robj.clone()))
}
}
_ => Err(Error::ExpectedScalar(robj.clone())),
}
}
}
impl TryFrom<&Robj> for Option<&str> {
type Error = Error;
fn try_from(robj: &Robj) -> Result<Self> {
if robj.is_null() || robj.is_na() {
Ok(None)
} else {
Ok(Some(<&str>::try_from(robj)?))
}
}
}
impl TryFrom<&Robj> for String {
type Error = Error;
fn try_from(robj: &Robj) -> Result<Self> {
<&str>::try_from(robj).map(|s| s.to_string())
}
}
impl_typed_slice_conversions!(i32, ExpectedInteger, "an INTSXP object");
impl_typed_slice_conversions!(Rint, ExpectedInteger, "an INTSXP object");
impl_typed_slice_conversions!(Rfloat, ExpectedReal, "a REALSXP object");
impl_typed_slice_conversions!(
Rbool,
ExpectedInteger,
ExpectedLogical,
ExpectedLogical,
"a LGLSXP object"
);
impl_typed_slice_conversions!(Rcplx, ExpectedComplex, "a complex object");
impl_typed_slice_conversions!(u8, ExpectedRaw, "a RAWSXP object");
impl_typed_slice_conversions!(f64, ExpectedReal, "a REALSXP object");
impl TryFrom<&Robj> for Vec<String> {
type Error = Error;
fn try_from(robj: &Robj) -> Result<Self> {
if let Some(iter) = robj.as_str_iter() {
if iter.clone().any(|s| s.is_na()) {
Err(Error::MustNotBeNA(robj.clone()))
} else {
Ok(iter.map(|s| s.to_string()).collect::<Vec<String>>())
}
} else {
Err(Error::ExpectedString(robj.clone()))
}
}
}
impl TryFrom<&Robj> for Rcplx {
type Error = Error;
fn try_from(robj: &Robj) -> Result<Self> {
match robj.len() {
0 => return Err(Error::ExpectedNonZeroLength(robj.clone())),
1 => {}
_ => return Err(Error::ExpectedScalar(robj.clone())),
};
if robj.is_na() {
return Ok(Rcplx::na());
}
if let Some(v) = robj.as_real() {
return Ok(Rcplx::from(v));
}
if let Some(v) = robj.as_integer() {
return Ok(Rcplx::from(v as f64));
}
if let Some(s) = robj.as_typed_slice() {
return Ok(s[0]);
}
Err(Error::ExpectedComplex(robj.clone()))
}
}
macro_rules! impl_try_from_robj {
($(@generics<$generics:tt>)? $type:ty $(where $($where_clause:tt)*)?) => {
impl$(<$generics>)? TryFrom<Robj> for $type $(where $($where_clause)*)? {
type Error = Error;
fn try_from(robj: Robj) -> Result<Self> {
Self::try_from(&robj)
}
}
impl$(<$generics>)? TryFrom<&Robj> for Option<$type> $(where $($where_clause)*)? {
type Error = Error;
fn try_from(robj: &Robj) -> Result<Self> {
if robj.is_null() || robj.is_na() {
Ok(None)
} else {
Ok(Some(<$type>::try_from(robj)?))
}
}
}
impl$(<$generics>)? TryFrom<Robj> for Option<$type> $(where $($where_clause)*)? {
type Error = Error;
fn try_from(robj: Robj) -> Result<Self> {
Self::try_from(&robj)
}
}
};
}
#[rustfmt::skip]
impl_try_from_robj!(u8);
impl_try_from_robj!(u16);
impl_try_from_robj!(u32);
impl_try_from_robj!(u64);
impl_try_from_robj!(usize);
impl_try_from_robj!(i8);
impl_try_from_robj!(i16);
impl_try_from_robj!(i32);
impl_try_from_robj!(i64);
impl_try_from_robj!(isize);
impl_try_from_robj!(bool);
impl_try_from_robj!(Rint);
impl_try_from_robj!(Rfloat);
impl_try_from_robj!(Rbool);
impl_try_from_robj!(Rcplx);
impl_try_from_robj!(f32);
impl_try_from_robj!(f64);
impl_try_from_robj!(Vec::<String>);
impl_try_from_robj!(Vec::<Rint>);
impl_try_from_robj!(Vec::<Rfloat>);
impl_try_from_robj!(Vec::<Rbool>);
impl_try_from_robj!(Vec::<Rcplx>);
impl_try_from_robj!(Vec::<u8>);
impl_try_from_robj!(Vec::<i32>);
impl_try_from_robj!(Vec::<f64>);
impl_try_from_robj!(String);
impl_try_from_robj!(@generics<T> HashMap::<&str, T> where T: TryFrom<Robj, Error = error::Error>);
impl_try_from_robj!(@generics<T> HashMap::<String,T> where T: TryFrom<Robj, Error = error::Error>);
impl_try_from_robj!(HashMap::<&str, Robj>);
impl_try_from_robj!(HashMap::<String, Robj>);
impl TryFrom<&Robj> for Option<()> {
type Error = Error;
fn try_from(value: &Robj) -> Result<Self> {
if value.is_null() {
Ok(Some(()))
} else {
Err(Error::ExpectedNull(value.clone()))
}
}
}
impl TryFrom<Robj> for Option<()> {
type Error = Error;
fn try_from(robj: Robj) -> Result<Self> {
Self::try_from(&robj)
}
}
impl<T> TryFrom<&Robj> for HashMap<&str, T>
where
T: TryFrom<Robj, Error = error::Error>,
{
type Error = Error;
fn try_from(value: &Robj) -> Result<Self> {
let value: List = value.try_into()?;
let value = value
.iter()
.map(|(name, value)| -> Result<(&str, T)> { value.try_into().map(|x| (name, x)) })
.collect::<Result<HashMap<_, _>>>()?;
Ok(value)
}
}
impl<T> TryFrom<&Robj> for HashMap<String, T>
where
T: TryFrom<Robj, Error = error::Error>,
{
type Error = Error;
fn try_from(value: &Robj) -> Result<Self> {
let value: HashMap<&str, _> = value.try_into()?;
Ok(value.into_iter().map(|(k, v)| (k.to_string(), v)).collect())
}
}
macro_rules! impl_try_from_robj_for_arrays {
($slice_type:ty) => {
impl<const N: usize> TryFrom<&Robj> for [$slice_type; N] {
type Error = Error;
fn try_from(value: &Robj) -> Result<Self> {
let value: &[$slice_type] = value.try_into()?;
if value.len() != N {
return Err(Error::ExpectedLength(N));
}
let value: Self = value
.try_into()
.map_err(|error| format!("{}", error).to_string())?;
Ok(value)
}
}
impl<const N: usize> TryFrom<Robj> for [$slice_type; N] {
type Error = Error;
fn try_from(robj: Robj) -> Result<Self> {
Self::try_from(&robj)
}
}
impl<const N: usize> TryFrom<&Robj> for Option<[$slice_type; N]> {
type Error = Error;
fn try_from(robj: &Robj) -> Result<Self> {
if robj.is_null() || robj.is_na() {
Ok(None)
} else {
Ok(Some(<[$slice_type; N]>::try_from(robj)?))
}
}
}
impl<const N: usize> TryFrom<Robj> for Option<[$slice_type; N]> {
type Error = Error;
fn try_from(robj: Robj) -> Result<Self> {
Self::try_from(&robj)
}
}
};
}
impl_try_from_robj_for_arrays!(Rint);
impl_try_from_robj_for_arrays!(Rfloat);
impl_try_from_robj_for_arrays!(Rbool);
impl_try_from_robj_for_arrays!(Rcplx);
impl_try_from_robj_for_arrays!(u8);
impl_try_from_robj_for_arrays!(i32);
impl_try_from_robj_for_arrays!(f64);
impl<T0> TryFrom<&Robj> for (T0,)
where
T0: for<'a> TryFrom<&'a Robj, Error = Error>,
{
type Error = Error;
fn try_from(robj: &Robj) -> Result<Self> {
let list: List = robj.try_into()?;
if list.len() != 1 {
return Err(Error::ExpectedLength(1));
}
Ok(((&list.elt(0)?).try_into()?,))
}
}
impl<T0> TryFrom<Robj> for (T0,)
where
T0: for<'a> TryFrom<&'a Robj, Error = Error>,
{
type Error = Error;
fn try_from(robj: Robj) -> Result<Self> {
Self::try_from(&robj)
}
}
impl<T0> TryFrom<&Robj> for Option<(T0,)>
where
T0: for<'a> TryFrom<&'a Robj, Error = Error>,
{
type Error = Error;
fn try_from(robj: &Robj) -> Result<Self> {
if robj.is_null() || robj.is_na() {
Ok(None)
} else {
<(T0,)>::try_from(robj).map(Some)
}
}
}
impl<T0> TryFrom<Robj> for Option<(T0,)>
where
T0: for<'a> TryFrom<&'a Robj, Error = Error>,
{
type Error = Error;
fn try_from(robj: Robj) -> Result<Self> {
Self::try_from(&robj)
}
}
impl_try_from_robj_tuples!((2, 12));
impl TryFrom<&Robj> for HashMap<&str, Robj> {
type Error = Error;
fn try_from(value: &Robj) -> Result<Self> {
let value: List = value.try_into()?;
Ok(value.into_iter().collect())
}
}
impl TryFrom<&Robj> for HashMap<String, Robj> {
type Error = Error;
fn try_from(value: &Robj) -> Result<Self> {
let value: HashMap<&str, _> = value.try_into()?;
Ok(value.into_iter().map(|(k, v)| (k.to_string(), v)).collect())
}
}