use super::*;
use crate::single_threaded;
use extendr_ffi::{
cetype_t, R_BlankString, R_NaInt, R_NaReal, R_NaString, R_NilValue, Rcomplex, Rf_mkCharLenCE,
COMPLEX, INTEGER, LOGICAL, RAW, REAL, SET_STRING_ELT, SEXPTYPE,
};
mod repeat_into_robj;
pub(crate) fn str_to_character(s: &str) -> SEXP {
unsafe {
if s.is_na() {
R_NaString
} else if s.is_empty() {
R_BlankString
} else {
single_threaded(|| {
Rf_mkCharLenCE(s.as_ptr().cast(), s.len() as i32, cetype_t::CE_UTF8)
})
}
}
}
impl From<()> for Robj {
fn from(_: ()) -> Self {
unsafe { Robj::from_sexp(R_NilValue) }
}
}
#[cfg(not(any(feature = "result_list", feature = "result_condition")))]
impl<T, E> From<std::result::Result<T, E>> for Robj
where
T: Into<Robj>,
E: std::fmt::Debug + std::fmt::Display,
{
fn from(res: std::result::Result<T, E>) -> Self {
match res {
Ok(val) => val.into(),
Err(err) => panic!("{}", err),
}
}
}
#[cfg(all(feature = "result_condition", not(feature = "result_list")))]
impl<T, E> From<std::result::Result<T, E>> for Robj
where
T: Into<Robj>,
E: Into<Robj>,
{
fn from(res: std::result::Result<T, E>) -> Self {
use crate as extendr_api;
match res {
Ok(x) => x.into(),
Err(x) => {
let mut err = list!(message = "extendr_err", value = x.into());
err.set_class(["extendr_error", "error", "condition"])
.expect("internal error: failed to set class");
err.into()
}
}
}
}
#[cfg(feature = "result_list")]
impl<T, E> From<std::result::Result<T, E>> for Robj
where
T: Into<Robj>,
E: Into<Robj>,
{
fn from(res: std::result::Result<T, E>) -> Self {
use crate as extendr_api;
let mut result = match res {
Ok(x) => list!(ok = x.into(), err = NULL),
Err(x) => {
let err_robj = x.into();
if err_robj.is_null() {
panic!("Internal error: result_list not allowed to return NULL as err-value")
}
list!(ok = NULL, err = err_robj)
}
};
result
.set_class(&["extendr_result"])
.expect("Internal error: failed to set class");
result.into()
}
}
impl From<Error> for Robj {
fn from(res: Error) -> Self {
res.to_string().into()
}
}
impl From<Error> for String {
fn from(res: Error) -> Self {
res.to_string()
}
}
impl From<&Robj> for Robj {
fn from(val: &Robj) -> Self {
unsafe { Robj::from_sexp(val.get()) }
}
}
pub trait IntoRobj {
fn into_robj(self) -> Robj;
}
impl<T> IntoRobj for T
where
Robj: From<T>,
{
fn into_robj(self) -> Robj {
self.into()
}
}
pub trait ToVectorValue {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::NILSXP
}
fn to_real(&self) -> f64
where
Self: Sized,
{
0.
}
fn to_complex(&self) -> Rcomplex
where
Self: Sized,
{
Rcomplex { r: 0., i: 0. }
}
fn to_integer(&self) -> i32
where
Self: Sized,
{
i32::MIN
}
fn to_logical(&self) -> i32
where
Self: Sized,
{
i32::MIN
}
fn to_raw(&self) -> u8
where
Self: Sized,
{
0
}
fn to_sexp(&self) -> SEXP
where
Self: Sized,
{
unsafe { R_NilValue }
}
}
macro_rules! impl_real_tvv {
($t: ty) => {
impl ToVectorValue for $t {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::REALSXP
}
fn to_real(&self) -> f64 {
*self as f64
}
}
impl ToVectorValue for &$t {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::REALSXP
}
fn to_real(&self) -> f64 {
**self as f64
}
}
impl ToVectorValue for Option<$t> {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::REALSXP
}
fn to_real(&self) -> f64 {
if self.is_some() {
self.unwrap() as f64
} else {
unsafe { R_NaReal }
}
}
}
};
}
impl_real_tvv!(f64);
impl_real_tvv!(f32);
impl_real_tvv!(i64);
impl_real_tvv!(u32);
impl_real_tvv!(u64);
impl_real_tvv!(usize);
macro_rules! impl_complex_tvv {
($t: ty) => {
impl ToVectorValue for $t {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::CPLXSXP
}
fn to_complex(&self) -> Rcomplex {
unsafe { std::mem::transmute(*self) }
}
}
impl ToVectorValue for &$t {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::CPLXSXP
}
fn to_complex(&self) -> Rcomplex {
unsafe { std::mem::transmute(**self) }
}
}
};
}
impl_complex_tvv!(c64);
impl_complex_tvv!(Rcplx);
impl_complex_tvv!((f64, f64));
macro_rules! impl_integer_tvv {
($t: ty) => {
impl ToVectorValue for $t {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::INTSXP
}
fn to_integer(&self) -> i32 {
*self as i32
}
}
impl ToVectorValue for &$t {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::INTSXP
}
fn to_integer(&self) -> i32 {
**self as i32
}
}
impl ToVectorValue for Option<$t> {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::INTSXP
}
fn to_integer(&self) -> i32 {
if self.is_some() {
self.unwrap() as i32
} else {
unsafe { R_NaInt }
}
}
}
};
}
impl_integer_tvv!(i8);
impl_integer_tvv!(i16);
impl_integer_tvv!(i32);
impl_integer_tvv!(u16);
impl ToVectorValue for u8 {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::RAWSXP
}
fn to_raw(&self) -> u8 {
*self
}
}
impl ToVectorValue for &u8 {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::RAWSXP
}
fn to_raw(&self) -> u8 {
**self
}
}
macro_rules! impl_str_tvv {
($t: ty) => {
impl ToVectorValue for $t {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::STRSXP
}
fn to_sexp(&self) -> SEXP
where
Self: Sized,
{
str_to_character(self.as_ref())
}
}
impl ToVectorValue for &$t {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::STRSXP
}
fn to_sexp(&self) -> SEXP
where
Self: Sized,
{
str_to_character(self.as_ref())
}
}
impl ToVectorValue for Option<$t> {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::STRSXP
}
fn to_sexp(&self) -> SEXP
where
Self: Sized,
{
if let Some(s) = self {
str_to_character(s.as_ref())
} else {
unsafe { R_NaString }
}
}
}
};
}
impl_str_tvv! {&str}
impl_str_tvv! {String}
impl ToVectorValue for Rstr {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::STRSXP
}
fn to_sexp(&self) -> SEXP
where
Self: Sized,
{
unsafe { self.get() }
}
}
impl ToVectorValue for &Rstr {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::STRSXP
}
fn to_sexp(&self) -> SEXP
where
Self: Sized,
{
unsafe { self.get() }
}
}
impl ToVectorValue for Option<Rstr> {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::STRSXP
}
fn to_sexp(&self) -> SEXP
where
Self: Sized,
{
if let Some(s) = self {
unsafe { s.get() }
} else {
unsafe { R_NaString }
}
}
}
impl TryFrom<&Robj> for Rstr {
type Error = crate::Error;
fn try_from(robj: &Robj) -> Result<Self> {
let sexptype = robj.sexptype();
if let SEXPTYPE::STRSXP = sexptype {
if robj.len() == 1 {
let strs = Strings::try_from(robj)?;
Ok(strs.elt(0))
} else {
Err(Error::ExpectedRstr(robj.clone()))
}
} else if let SEXPTYPE::CHARSXP = sexptype {
Ok(Rstr { robj: robj.clone() })
} else {
Err(Error::ExpectedRstr(robj.clone()))
}
}
}
impl TryFrom<Robj> for Rstr {
type Error = crate::Error;
fn try_from(value: Robj) -> std::result::Result<Self, Self::Error> {
Self::try_from(&value)
}
}
impl GetSexp for Rstr {
unsafe fn get(&self) -> SEXP {
self.robj.get()
}
unsafe fn get_mut(&mut self) -> SEXP {
self.robj.get_mut()
}
fn as_robj(&self) -> &Robj {
&self.robj
}
fn as_robj_mut(&mut self) -> &mut Robj {
&mut self.robj
}
}
impl Length for Rstr {}
impl Types for Rstr {}
impl Conversions for Rstr {}
impl Rinternals for Rstr {}
impl Slices for Rstr {}
impl Operators for Rstr {}
impl ToVectorValue for bool {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::LGLSXP
}
fn to_logical(&self) -> i32
where
Self: Sized,
{
*self as i32
}
}
impl ToVectorValue for &bool {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::LGLSXP
}
fn to_logical(&self) -> i32
where
Self: Sized,
{
**self as i32
}
}
impl ToVectorValue for Rbool {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::LGLSXP
}
fn to_logical(&self) -> i32
where
Self: Sized,
{
self.0
}
}
impl ToVectorValue for &Rbool {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::LGLSXP
}
fn to_logical(&self) -> i32
where
Self: Sized,
{
self.0
}
}
impl ToVectorValue for Option<bool> {
fn sexptype() -> SEXPTYPE {
SEXPTYPE::LGLSXP
}
fn to_logical(&self) -> i32 {
if self.is_some() {
self.unwrap() as i32
} else {
unsafe { R_NaInt }
}
}
}
impl<T> From<&Option<T>> for Robj
where
Option<T>: ToVectorValue + Clone,
{
fn from(value: &Option<T>) -> Self {
value.clone().into()
}
}
fn fixed_size_collect<I>(iter: I, len: usize) -> Robj
where
I: Iterator,
I: Sized,
I::Item: ToVectorValue,
{
single_threaded(|| unsafe {
let sexptype = I::Item::sexptype();
if sexptype != SEXPTYPE::NILSXP {
let res = Robj::alloc_vector(sexptype, len);
let sexp = res.get();
match sexptype {
SEXPTYPE::REALSXP => {
let ptr = REAL(sexp);
for (i, v) in iter.enumerate() {
*ptr.add(i) = v.to_real();
}
}
SEXPTYPE::CPLXSXP => {
let ptr = COMPLEX(sexp);
for (i, v) in iter.enumerate() {
*ptr.add(i) = v.to_complex();
}
}
SEXPTYPE::INTSXP => {
let ptr = INTEGER(sexp);
for (i, v) in iter.enumerate() {
*ptr.add(i) = v.to_integer();
}
}
SEXPTYPE::LGLSXP => {
let ptr = LOGICAL(sexp);
for (i, v) in iter.enumerate() {
*ptr.add(i) = v.to_logical();
}
}
SEXPTYPE::STRSXP => {
for (i, v) in iter.enumerate() {
SET_STRING_ELT(sexp, i as isize, v.to_sexp());
}
}
SEXPTYPE::RAWSXP => {
let ptr = RAW(sexp);
for (i, v) in iter.enumerate() {
*ptr.add(i) = v.to_raw();
}
}
_ => {
panic!("unexpected SEXPTYPE in collect_robj");
}
}
res
} else {
Robj::from(())
}
})
}
pub trait RobjItertools: Iterator {
fn collect_robj(self) -> Robj
where
Self: Iterator,
Self: Sized,
Self::Item: ToVectorValue,
{
if let (len, Some(max)) = self.size_hint() {
if len == max {
return fixed_size_collect(self, len);
}
}
let vec: Vec<_> = self.collect();
assert!(vec.iter().size_hint() == (vec.len(), Some(vec.len())));
vec.into_iter().collect_robj()
}
fn collect_rarray<const LEN: usize>(self, dims: [usize; LEN]) -> Result<RArray<Self::Item, LEN>>
where
Self: Iterator,
Self: Sized,
Self::Item: ToVectorValue,
Robj: for<'a> AsTypedSlice<'a, Self::Item>,
{
let mut vector = self.collect_robj();
let prod = dims.iter().product::<usize>();
if prod != vector.len() {
return Err(Error::Other(format!(
"The vector length ({}) does not match the length implied by the dimensions ({})",
vector.len(),
prod
)));
}
vector.set_attrib(wrapper::symbol::dim_symbol(), dims.iter().collect_robj())?;
let _data = vector.as_typed_slice().ok_or(Error::Other(
"Unknown error in converting to slice".to_string(),
))?;
Ok(RArray::from_parts(vector))
}
}
impl<T> RobjItertools for T where T: Iterator {}
impl<T> From<T> for Robj
where
T: ToVectorValue,
{
fn from(scalar: T) -> Self {
Some(scalar).into_iter().collect_robj()
}
}
macro_rules! impl_from_as_iterator {
($t: ty) => {
impl<T> From<$t> for Robj
where
$t: RobjItertools,
<$t as Iterator>::Item: ToVectorValue,
T: ToVectorValue,
{
fn from(val: $t) -> Self {
val.collect_robj()
}
}
};
}
impl<T, const N: usize> From<[T; N]> for Robj
where
T: ToVectorValue,
{
fn from(val: [T; N]) -> Self {
fixed_size_collect(val.into_iter(), N)
}
}
impl<'a, T, const N: usize> From<&'a [T; N]> for Robj
where
Self: 'a,
&'a T: ToVectorValue + 'a,
{
fn from(val: &'a [T; N]) -> Self {
fixed_size_collect(val.iter(), N)
}
}
impl<'a, T, const N: usize> From<&'a mut [T; N]> for Robj
where
Self: 'a,
&'a mut T: ToVectorValue + 'a,
{
fn from(val: &'a mut [T; N]) -> Self {
fixed_size_collect(val.iter_mut(), N)
}
}
impl<T: ToVectorValue + Clone> From<&Vec<T>> for Robj {
fn from(value: &Vec<T>) -> Self {
let len = value.len();
fixed_size_collect(value.iter().cloned(), len)
}
}
impl<T: ToVectorValue> From<Vec<T>> for Robj {
fn from(value: Vec<T>) -> Self {
let len = value.len();
fixed_size_collect(value.into_iter(), len)
}
}
impl<'a, T> From<&'a [T]> for Robj
where
Self: 'a,
T: 'a,
&'a T: ToVectorValue,
{
fn from(val: &'a [T]) -> Self {
val.iter().collect_robj()
}
}
impl_from_as_iterator! {Range<T>}
impl_from_as_iterator! {RangeInclusive<T>}
impl From<Vec<Robj>> for Robj {
fn from(val: Vec<Robj>) -> Self {
Self::from(&val)
}
}
impl From<&Vec<Robj>> for Robj {
fn from(val: &Vec<Robj>) -> Self {
List::from_values(val.iter()).into()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate as extendr_api;
#[test]
fn test_vec_rint_to_robj() {
test! {
let int_vec = vec![3,4,0,-2];
let int_vec_robj: Robj = int_vec.clone().into();
assert_eq!(int_vec_robj.as_integer_slice().unwrap(), &int_vec);
let rint_vec = vec![Rint::from(3), Rint::from(4), Rint::from(0), Rint::from(-2)];
let rint_vec_robj: Robj = rint_vec.into();
assert_eq!(rint_vec_robj.as_integer_slice().unwrap(), &int_vec);
}
}
#[test]
fn test_collect_rarray_matrix() {
test! {
let rmat = (1i32..=16).collect_rarray([4, 4]);
assert!(rmat.is_ok());
assert_eq!(Robj::from(rmat), R!("matrix(1:16, nrow=4)").unwrap());
}
}
#[test]
fn test_collect_rarray_tensor() {
test! {
let rmat = (1i32..=16).collect_rarray([2, 4, 2]);
assert!(rmat.is_ok());
assert_eq!(Robj::from(rmat), R!("array(1:16, dim=c(2, 4, 2))").unwrap());
}
}
#[test]
fn test_collect_rarray_matrix_failure() {
test! {
let rmat = (1i32..=16).collect_rarray([3, 3]);
assert!(rmat.is_err());
let msg = rmat.unwrap_err().to_string();
assert!(msg.contains('9'));
assert!(msg.contains("dimension"));
}
}
#[test]
fn test_collect_tensor_failure() {
test! {
let rmat = (1i32..=16).collect_rarray([3, 3, 3]);
assert!(rmat.is_err());
let msg = rmat.unwrap_err().to_string();
assert!(msg.contains("27"));
assert!(msg.contains("dimension"));
}
}
#[test]
#[cfg(all(feature = "result_condition", not(feature = "result_list")))]
fn test_result_condition() {
use crate::prelude::*;
fn my_err_f() -> std::result::Result<f64, f64> {
Err(42.0) }
test! {
assert_eq!(
r!(my_err_f()),
R!(
"structure(list(message = 'extendr_err',
value = 42.0), class = c('extendr_error', 'error', 'condition'))"
).unwrap()
);
}
}
#[test]
#[cfg(feature = "result_list")]
fn test_result_list() {
use crate::prelude::*;
fn my_err_f() -> std::result::Result<f64, String> {
Err("We have water in the engine room!".to_string())
}
fn my_ok_f() -> std::result::Result<f64, String> {
Ok(123.123)
}
test! {
assert_eq!(
r!(my_err_f()),
R!("x=list(ok=NULL, err='We have water in the engine room!')
class(x)='extendr_result'
x"
).unwrap()
);
assert_eq!(
r!(my_ok_f()),
R!("x = list(ok=123.123, err=NULL)
class(x)='extendr_result'
x"
).unwrap()
);
}
}
}