use libR_sys::*;
use std::os::raw;
use crate::logical::*;
use crate::wrapper::*;
use crate::AnyError;
use ndarray::prelude::*;
pub enum Robj {
Owned(SEXP),
Borrowed(SEXP),
Sys(SEXP),
}
pub const TRUE: bool = true;
pub const FALSE: bool = false;
pub const NULL: () = ();
impl Clone for Robj {
fn clone(&self) -> Self {
self.duplicate()
}
}
impl Default for Robj {
fn default() -> Self {
Robj::from(())
}
}
pub trait FromRobj<'a>: Sized {
fn from_robj(_robj: &'a Robj) -> Result<Self, &'static str> {
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) -> Result<Self, &'static str> {
if let Some(v) = robj.as_i32_slice() {
if v.len() == 0 {
Err("zero length vector")
} else {
Ok(v[0] as Self)
}
} else if let Some(v) = robj.as_f64_slice() {
if v.len() == 0 {
Err("zero length vector")
} else {
Ok(v[0] as Self)
}
} 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 &'a str {
fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
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) -> Result<Self, &'static str> {
if let Some(s) = robj.as_str() {
Ok(s.to_string())
} else {
Err("not a string object")
}
}
}
impl<'a> FromRobj<'a> for Vec<i32> {
fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
if let Some(v) = robj.as_i32_slice() {
Ok(Vec::from(v))
} else {
Err("not an integer or logical vector")
}
}
}
impl<'a> FromRobj<'a> for Vec<f64> {
fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
if let Some(v) = robj.as_f64_slice() {
Ok(Vec::from(v))
} else {
Err("not a floating point vector")
}
}
}
impl<'a, T> FromRobj<'a> for ArrayView1<'a, T>
where
Robj: AsTypedSlice<T>,
{
fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
if let Some(v) = robj.as_typed_slice() {
Ok(ArrayView1::<'a, T>::from(v))
} else {
Err("not a floating point vector")
}
}
}
impl<'a> FromRobj<'a> for Robj {
fn from_robj(robj: &'a Robj) -> Result<Self, &'static str> {
Ok(unsafe { new_borrowed(robj.get()) })
}
}
impl Robj {
pub unsafe fn get(&self) -> SEXP {
match self {
Robj::Owned(sexp) => *sexp,
Robj::Borrowed(sexp) => *sexp,
Robj::Sys(sexp) => *sexp,
}
}
pub unsafe fn get_mut(&mut self) -> Option<SEXP> {
match self {
Robj::Owned(sexp) => Some(*sexp),
Robj::Borrowed(_) => None,
Robj::Sys(_) => None,
}
}
pub fn sexptype(&self) -> u32 {
unsafe { TYPEOF(self.get()) as u32 }
}
pub fn len(&self) -> usize {
unsafe { Rf_xlength(self.get()) as usize }
}
pub fn as_i32_slice(&self) -> Option<&[i32]> {
self.as_typed_slice()
}
pub fn as_logical_slice(&self) -> Option<&[Bool]> {
self.as_typed_slice()
}
pub fn as_f64_slice(&self) -> Option<&[f64]> {
self.as_typed_slice()
}
pub fn as_u8_slice(&self) -> Option<&[u8]> {
self.as_typed_slice()
}
pub fn as_i32_slice_mut(&mut self) -> Option<&mut [i32]> {
self.as_typed_slice_mut()
}
pub fn as_f64_slice_mut(&mut self) -> Option<&mut [f64]> {
self.as_typed_slice_mut()
}
pub fn as_u8_slice_mut(&mut self) -> Option<&mut [u8]> {
self.as_typed_slice_mut()
}
pub fn pairlist_iter(&self) -> Option<ListIter> {
match self.sexptype() {
LISTSXP | LANGSXP | DOTSXP => unsafe {
Some(ListIter {
list_elem: self.get(),
})
},
_ => None,
}
}
pub fn list_iter(&self) -> Option<VecIter> {
match self.sexptype() {
VECSXP | EXPRSXP | WEAKREFSXP => unsafe {
Some(VecIter {
vector: self.get(),
i: 0,
len: self.len(),
})
},
_ => None,
}
}
pub fn str_iter(&self) -> Option<StrIter> {
match self.sexptype() {
STRSXP => unsafe {
Some(StrIter {
vector: self.get(),
i: 0,
len: self.len(),
})
},
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
unsafe {
match self.sexptype() {
STRSXP => {
if self.len() == 0 {
None
} else {
Some(to_str(R_CHAR(STRING_ELT(self.get(), 0)) as *const u8))
}
}
CHARSXP => Some(to_str(R_CHAR(self.get()) as *const u8)),
SYMSXP => Some(to_str(R_CHAR(PRINTNAME(self.get())) as *const u8)),
_ => None,
}
}
}
pub fn eval(&self) -> Result<Robj, AnyError> {
unsafe {
let mut error: raw::c_int = 0;
let res = R_tryEval(self.get(), R_GlobalEnv, &mut error as *mut raw::c_int);
if error != 0 {
Err(AnyError::from("R eval error"))
} else {
Ok(Robj::from(res))
}
}
}
pub fn eval_blind(&self) -> Robj {
unsafe {
let mut error: raw::c_int = 0;
let res = R_tryEval(self.get(), R_GlobalEnv, &mut error as *mut raw::c_int);
if error != 0 {
Robj::from(())
} else {
Robj::from(res)
}
}
}
pub fn parse(code: &str) -> Result<Robj, AnyError> {
unsafe {
use libR_sys::*;
let mut status = 0_u32;
let status_ptr = &mut status as *mut u32;
let code: Robj = code.into();
let parsed = Robj::from(R_ParseVector(code.get(), -1, status_ptr, R_NilValue));
match status {
1 => Ok(parsed),
_ => Err(AnyError::from("parse_error")),
}
}
}
pub fn eval_string(code: &str) -> Result<Robj, AnyError> {
let expr = Robj::parse(code)?;
let mut res = Robj::from(());
if let Some(iter) = expr.list_iter() {
for lang in iter {
res = lang.eval()?;
}
}
Ok(res)
}
pub unsafe fn unprotected(self) -> Robj {
match self {
Robj::Owned(sexp) => {
R_ReleaseObject(sexp);
Robj::Borrowed(sexp)
}
_ => self,
}
}
pub fn is_owned(&self) -> bool {
match self {
Robj::Owned(_) => true,
_ => false,
}
}
}
pub trait AsTypedSlice<T> {
fn as_typed_slice(&self) -> Option<&[T]> {
None
}
fn as_typed_slice_mut(&mut self) -> Option<&mut [T]> {
None
}
}
macro_rules! make_typed_slice {
($type: ty, $fn: tt, $($sexp: tt),* ) => {
impl AsTypedSlice<$type> for Robj {
fn as_typed_slice(&self) -> Option<&[$type]> {
match self.sexptype() {
$( $sexp )|* => {
unsafe {
let ptr = $fn(self.get()) as *const $type;
Some(std::slice::from_raw_parts(ptr, self.len()))
}
}
_ => None
}
}
fn as_typed_slice_mut(&mut self) -> Option<&mut [$type]> {
match self.sexptype() {
$( $sexp )|* => {
unsafe {
let ptr = $fn(self.get()) as *mut $type;
Some(std::slice::from_raw_parts_mut(ptr, self.len()))
}
}
_ => None
}
}
}
}
}
make_typed_slice!(Bool, INTEGER, LGLSXP);
make_typed_slice!(i32, INTEGER, INTSXP);
make_typed_slice!(f64, REAL, REALSXP);
make_typed_slice!(u8, RAW, RAWSXP);
#[allow(non_snake_case)]
impl Robj {
pub fn globalEnv() -> Robj {
unsafe { new_sys(R_GlobalEnv) }
}
pub fn emptyEnv() -> Robj {
unsafe { new_sys(R_EmptyEnv) }
}
pub fn baseEnv() -> Robj {
unsafe { new_sys(R_BaseEnv) }
}
pub fn baseNamespace() -> Robj {
unsafe { new_sys(R_BaseNamespace) }
}
pub fn namespaceRegistry() -> Robj {
unsafe { new_sys(R_NamespaceRegistry) }
}
pub fn srcref() -> Robj {
unsafe { new_sys(R_Srcref) }
}
pub fn nilValue() -> Robj {
unsafe { new_sys(R_NilValue) }
}
pub fn unboundValue() -> Robj {
unsafe { new_sys(R_UnboundValue) }
}
pub fn missingArg() -> Robj {
unsafe { new_sys(R_MissingArg) }
}
pub fn baseSymbol() -> Robj {
unsafe { new_sys(R_BaseSymbol) }
}
pub fn braceSymbol() -> Robj {
unsafe { new_sys(R_BraceSymbol) }
}
pub fn bracket2Symbol() -> Robj {
unsafe { new_sys(R_Bracket2Symbol) }
}
pub fn bracketSymbol() -> Robj {
unsafe { new_sys(R_BracketSymbol) }
}
pub fn classSymbol() -> Robj {
unsafe { new_sys(R_ClassSymbol) }
}
pub fn deviceSymbol() -> Robj {
unsafe { new_sys(R_DeviceSymbol) }
}
pub fn dimNamesSymbol() -> Robj {
unsafe { new_sys(R_DimNamesSymbol) }
}
pub fn dimSymbol() -> Robj {
unsafe { new_sys(R_DimSymbol) }
}
pub fn dollarSymbol() -> Robj {
unsafe { new_sys(R_DollarSymbol) }
}
pub fn dotsSymbol() -> Robj {
unsafe { new_sys(R_DotsSymbol) }
}
pub fn doubleColonSymbol() -> Robj {
unsafe { new_sys(R_DoubleColonSymbol) }
}
pub fn lastvalueSymbol() -> Robj {
unsafe { new_sys(R_LastvalueSymbol) }
}
pub fn levelsSymbol() -> Robj {
unsafe { new_sys(R_LevelsSymbol) }
}
pub fn modeSymbol() -> Robj {
unsafe { new_sys(R_ModeSymbol) }
}
pub fn naRmSymbol() -> Robj {
unsafe { new_sys(R_NaRmSymbol) }
}
pub fn nameSymbol() -> Robj {
unsafe { new_sys(R_NameSymbol) }
}
pub fn namesSymbol() -> Robj {
unsafe { new_sys(R_NamesSymbol) }
}
pub fn namespaceEnvSymbol() -> Robj {
unsafe { new_sys(R_NamespaceEnvSymbol) }
}
pub fn packageSymbol() -> Robj {
unsafe { new_sys(R_PackageSymbol) }
}
pub fn previousSymbol() -> Robj {
unsafe { new_sys(R_PreviousSymbol) }
}
pub fn quoteSymbol() -> Robj {
unsafe { new_sys(R_QuoteSymbol) }
}
pub fn rowNamesSymbol() -> Robj {
unsafe { new_sys(R_RowNamesSymbol) }
}
pub fn seedsSymbol() -> Robj {
unsafe { new_sys(R_SeedsSymbol) }
}
pub fn sortListSymbol() -> Robj {
unsafe { new_sys(R_SortListSymbol) }
}
pub fn sourceSymbol() -> Robj {
unsafe { new_sys(R_SourceSymbol) }
}
pub fn specSymbol() -> Robj {
unsafe { new_sys(R_SpecSymbol) }
}
pub fn tspSymbol() -> Robj {
unsafe { new_sys(R_TspSymbol) }
}
pub fn tripleColonSymbol() -> Robj {
unsafe { new_sys(R_TripleColonSymbol) }
}
pub fn dot_defined() -> Robj {
unsafe { new_sys(R_dot_defined) }
}
pub fn dot_Method() -> Robj {
unsafe { new_sys(R_dot_Method) }
}
pub fn dot_packageName() -> Robj {
unsafe { new_sys(R_dot_packageName) }
}
pub fn dot_target() -> Robj {
unsafe { new_sys(R_dot_target) }
}
pub fn naString() -> Robj {
unsafe { new_sys(R_NaString) }
}
pub fn blankString() -> Robj {
unsafe { new_sys(R_BlankString) }
}
pub fn blankScalarString() -> Robj {
unsafe { new_sys(R_BlankScalarString) }
}
}
#[allow(non_snake_case)]
impl Robj {
pub fn isNull(&self) -> bool {
unsafe { Rf_isNull(self.get()) != 0 }
}
pub fn isSymbol(&self) -> bool {
unsafe { Rf_isSymbol(self.get()) != 0 }
}
pub fn isLogical(&self) -> bool {
unsafe { Rf_isLogical(self.get()) != 0 }
}
pub fn isReal(&self) -> bool {
unsafe { Rf_isReal(self.get()) != 0 }
}
pub fn isComplex(&self) -> bool {
unsafe { Rf_isComplex(self.get()) != 0 }
}
pub fn isExpression(&self) -> bool {
unsafe { Rf_isExpression(self.get()) != 0 }
}
pub fn isEnvironment(&self) -> bool {
unsafe { Rf_isEnvironment(self.get()) != 0 }
}
pub fn isString(&self) -> bool {
unsafe { Rf_isString(self.get()) != 0 }
}
pub fn isObject(&self) -> bool {
unsafe { Rf_isObject(self.get()) != 0 }
}
pub fn getCurrentSrcref(val: i32) -> Robj {
unsafe { new_owned(R_GetCurrentSrcref(val as raw::c_int)) }
}
pub fn getSrcFilename(&self) -> Robj {
unsafe { new_owned(R_GetSrcFilename(self.get())) }
}
pub fn asChar(&self) -> Robj {
unsafe { new_owned(Rf_asChar(self.get())) }
}
pub fn coerceVector(&self, sexptype: u32) -> Robj {
unsafe { new_owned(Rf_coerceVector(self.get(), sexptype as SEXPTYPE)) }
}
pub fn pairToVectorList(&self) -> Robj {
unsafe { new_owned(Rf_PairToVectorList(self.get())) }
}
pub fn vectorToPairList(&self) -> Robj {
unsafe { new_owned(Rf_VectorToPairList(self.get())) }
}
pub fn asCharacterFactor(&self) -> Robj {
unsafe { new_owned(Rf_asCharacterFactor(self.get())) }
}
pub fn asLogical(&self) -> bool {
unsafe { Rf_asLogical(self.get()) != 0 }
}
pub fn asInteger(&self) -> i32 {
unsafe { Rf_asInteger(self.get()) as i32 }
}
pub fn asReal(&self) -> f64 {
unsafe { Rf_asReal(self.get()) as f64 }
}
pub fn allocMatrix(sexptype: SEXPTYPE, rows: i32, cols: i32) -> Robj {
unsafe { new_owned(Rf_allocMatrix(sexptype, rows, cols)) }
}
pub fn duplicate(&self) -> Self {
unsafe { new_owned(Rf_duplicate(self.get())) }
}
pub fn ncols(&self) -> usize {
unsafe { Rf_ncols(self.get()) as usize }
}
pub fn nrows(&self) -> usize {
unsafe { Rf_nrows(self.get()) as usize }
}
pub unsafe fn makeExternalPtr<T>(p: *mut T, tag: Robj, prot: Robj) -> Self {
new_owned(R_MakeExternalPtr(
p as *mut ::std::os::raw::c_void,
tag.get(),
prot.get(),
))
}
pub unsafe fn externalPtrAddr<T>(&self) -> *mut T {
R_ExternalPtrAddr(self.get()) as *mut T
}
pub unsafe fn externalPtrTag(&self) -> Self {
new_borrowed(R_ExternalPtrTag(self.get()))
}
pub unsafe fn externalPtrProtected(&self) -> Self {
new_borrowed(R_ExternalPtrProtected(self.get()))
}
pub unsafe fn registerCFinalizer(&self, func: R_CFinalizer_t) {
R_RegisterCFinalizer(self.get(), func);
}
pub fn xlengthgets(&self, new_len: usize) -> Result<Robj, AnyError> {
unsafe {
if self.isVector() {
Ok(new_owned(Rf_xlengthgets(self.get(), new_len as R_xlen_t)))
} else {
Err(AnyError::from("xlengthgets: Not a vector type"))
}
}
}
pub fn allocVector(sexptype: u32, len: usize) -> Robj {
unsafe { new_owned(Rf_allocVector(sexptype, len as R_xlen_t)) }
}
pub fn conformable(a: &Robj, b: &Robj) -> bool {
unsafe { Rf_conformable(a.get(), b.get()) != 0 }
}
pub fn elt(&self, index: usize) -> Robj {
unsafe { Robj::from(Rf_elt(self.get(), index as raw::c_int)) }
}
pub fn isArray(&self) -> bool {
unsafe { Rf_isArray(self.get()) != 0 }
}
pub fn isFactor(&self) -> bool {
unsafe { Rf_isFactor(self.get()) != 0 }
}
pub fn isFrame(&self) -> bool {
unsafe { Rf_isFrame(self.get()) != 0 }
}
pub fn isFunction(&self) -> bool {
unsafe { Rf_isFunction(self.get()) != 0 }
}
pub fn isInteger(&self) -> bool {
unsafe { Rf_isInteger(self.get()) != 0 }
}
pub fn isLanguage(&self) -> bool {
unsafe { Rf_isLanguage(self.get()) != 0 }
}
pub fn isList(&self) -> bool {
unsafe { Rf_isList(self.get()) != 0 }
}
pub fn isMatrix(&self) -> bool {
unsafe { Rf_isMatrix(self.get()) != 0 }
}
pub fn isNewList(&self) -> bool {
unsafe { Rf_isNewList(self.get()) != 0 }
}
pub fn isNumber(&self) -> bool {
unsafe { Rf_isNumber(self.get()) != 0 }
}
pub fn isNumeric(&self) -> bool {
unsafe { Rf_isNumeric(self.get()) != 0 }
}
pub fn isPairList(&self) -> bool {
unsafe { Rf_isPairList(self.get()) != 0 }
}
pub fn isPrimitive(&self) -> bool {
unsafe { Rf_isPrimitive(self.get()) != 0 }
}
pub fn isTs(&self) -> bool {
unsafe { Rf_isTs(self.get()) != 0 }
}
pub fn isUserBinop(&self) -> bool {
unsafe { Rf_isUserBinop(self.get()) != 0 }
}
pub fn isValidString(&self) -> bool {
unsafe { Rf_isValidString(self.get()) != 0 }
}
pub fn isValidStringF(&self) -> bool {
unsafe { Rf_isValidStringF(self.get()) != 0 }
}
pub fn isVector(&self) -> bool {
unsafe { Rf_isVector(self.get()) != 0 }
}
pub fn isVectorAtomic(&self) -> bool {
unsafe { Rf_isVectorAtomic(self.get()) != 0 }
}
pub fn isVectorList(&self) -> bool {
unsafe { Rf_isVectorList(self.get()) != 0 }
}
pub fn isVectorizable(&self) -> bool {
unsafe { Rf_isVectorizable(self.get()) != 0 }
}
pub fn check_external_ptr(&self, expected_tag: &str) -> bool {
if self.sexptype() == libR_sys::EXTPTRSXP {
let tag = unsafe { self.externalPtrTag() };
if tag.as_str() == Some(expected_tag) {
return true;
}
}
false
}
}
pub unsafe fn new_owned(sexp: SEXP) -> Robj {
R_PreserveObject(sexp);
Robj::Owned(sexp)
}
pub unsafe fn new_borrowed(sexp: SEXP) -> Robj {
Robj::Borrowed(sexp)
}
pub unsafe fn new_sys(sexp: SEXP) -> Robj {
Robj::Sys(sexp)
}
impl<'a> PartialEq<[i32]> for Robj {
fn eq(&self, rhs: &[i32]) -> bool {
self.as_i32_slice() == Some(rhs)
}
}
impl<'a> PartialEq<[f64]> for Robj {
fn eq(&self, rhs: &[f64]) -> bool {
self.as_f64_slice() == Some(rhs)
}
}
impl PartialEq<str> for Robj {
fn eq(&self, rhs: &str) -> bool {
self.as_str() == Some(rhs)
}
}
impl PartialEq<Robj> for Robj {
fn eq(&self, rhs: &Robj) -> bool {
if self.sexptype() == rhs.sexptype() && self.len() == rhs.len() {
unsafe {
let lsexp = self.get();
let rsexp = rhs.get();
match self.sexptype() {
NILSXP => true,
SYMSXP => PRINTNAME(lsexp) == PRINTNAME(rsexp),
LISTSXP | LANGSXP | DOTSXP => self
.pairlist_iter()
.unwrap()
.eq(rhs.pairlist_iter().unwrap()),
CLOSXP => false,
ENVSXP => false,
PROMSXP => false,
SPECIALSXP => false,
BUILTINSXP => false,
CHARSXP => self.as_str() == rhs.as_str(),
LGLSXP => self.as_logical_slice() == rhs.as_logical_slice(),
INTSXP => self.as_i32_slice() == rhs.as_i32_slice(),
REALSXP => self.as_f64_slice() == rhs.as_f64_slice(),
CPLXSXP => false,
ANYSXP => false,
VECSXP | EXPRSXP => self.list_iter().unwrap().eq(rhs.list_iter().unwrap()),
STRSXP => self.str_iter().unwrap().eq(rhs.str_iter().unwrap()),
BCODESXP => false,
EXTPTRSXP => false,
WEAKREFSXP => false,
RAWSXP => self.as_u8_slice() == rhs.as_u8_slice(),
S4SXP => false,
NEWSXP => false,
FREESXP => false,
_ => false,
}
}
} else {
false
}
}
}
impl std::fmt::Debug for Robj {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.sexptype() {
NILSXP => write!(f, "NULL"),
SYMSXP => write!(f, "Symbol({:?})", self.as_str().unwrap()),
LANGSXP => write!(
f,
"Lang({:?})",
self.pairlist_iter().unwrap().collect::<Vec<Robj>>()
),
CHARSXP => write!(f, "Character({:?})", self.as_str().unwrap()),
LGLSXP => {
let slice = self.as_logical_slice().unwrap();
if slice.len() == 1 {
write!(f, "{}", if slice[0].0 == 0 { "FALSE" } else { "TRUE" })
} else {
write!(f, "&{:?}", slice)
}
}
INTSXP => {
let slice = self.as_i32_slice().unwrap();
if slice.len() == 1 {
write!(f, "{:?}", slice[0])
} else {
write!(f, "{:?}", self.as_i32_slice().unwrap())
}
}
REALSXP => {
let slice = self.as_f64_slice().unwrap();
if slice.len() == 1 {
write!(f, "{:?}", slice[0])
} else {
write!(f, "{:?}", slice)
}
}
VECSXP => write!(f, "{:?}", self.list_iter().unwrap().collect::<Vec<_>>()),
EXPRSXP => write!(
f,
"Expr({:?})",
self.list_iter().unwrap().collect::<Vec<_>>()
),
WEAKREFSXP => write!(
f,
"Weakref({:?})",
self.list_iter().unwrap().collect::<Vec<_>>()
),
STRSXP => {
write!(f, "[")?;
let mut sep = "";
for obj in self.str_iter().unwrap() {
write!(f, "{}{:?}", sep, obj)?;
sep = ", ";
}
write!(f, "]")
}
RAWSXP => {
let slice = self.as_u8_slice().unwrap();
if slice.len() == 1 {
write!(f, "{}", slice[0])
} else {
write!(f, "{:?}", slice)
}
}
_ => write!(f, "??"),
}
}
}
unsafe fn to_str<'a>(ptr: *const u8) -> &'a str {
let mut len = 0;
loop {
if *ptr.offset(len) == 0 {
break;
}
len += 1;
}
let slice = std::slice::from_raw_parts(ptr, len as usize);
std::str::from_utf8_unchecked(slice)
}
impl From<SEXP> for Robj {
fn from(sexp: SEXP) -> Self {
unsafe { new_borrowed(sexp) }
}
}
impl Drop for Robj {
fn drop(&mut self) {
unsafe {
match self {
Robj::Owned(sexp) => R_ReleaseObject(*sexp),
Robj::Borrowed(_) => (),
Robj::Sys(_) => (),
}
}
}
}
impl From<()> for Robj {
fn from(_: ()) -> Self {
unsafe { Robj::Sys(R_NilValue) }
}
}
impl From<bool> for Robj {
fn from(val: bool) -> Self {
unsafe { new_owned(Rf_ScalarLogical(val as raw::c_int)) }
}
}
macro_rules! impl_from_int_prim {
($t : ty) => {
impl From<$t> for Robj {
fn from(val: $t) -> Self {
unsafe { new_owned(Rf_ScalarInteger(val as raw::c_int)) }
}
}
};
}
impl_from_int_prim!(u8);
impl_from_int_prim!(u16);
impl_from_int_prim!(u32);
impl_from_int_prim!(u64);
impl_from_int_prim!(i8);
impl_from_int_prim!(i16);
impl_from_int_prim!(i32);
impl_from_int_prim!(i64);
macro_rules! impl_from_float_prim {
($t : ty) => {
impl From<$t> for Robj {
fn from(val: $t) -> Self {
unsafe { new_owned(Rf_ScalarReal(val as raw::c_double)) }
}
}
};
}
impl_from_float_prim!(f32);
impl_from_float_prim!(f64);
impl From<usize> for Robj {
fn from(val: usize) -> Self {
unsafe {
new_owned(if val >= 0x80000000 {
Rf_ScalarReal(val as raw::c_double)
} else {
Rf_ScalarInteger(val as raw::c_int)
})
}
}
}
impl<'a> From<Character<'a>> for Robj {
fn from(val: Character) -> Self {
unsafe {
new_owned(Rf_mkCharLen(
val.0.as_ptr() as *const raw::c_char,
val.0.len() as i32,
))
}
}
}
impl<'a> From<Lang<'a>> for Robj {
fn from(val: Lang<'a>) -> Self {
unsafe {
let mut name = Vec::from(val.0.as_bytes());
name.push(0);
new_owned(Rf_lang1(Rf_install(name.as_ptr() as *const raw::c_char)))
}
}
}
impl<'a> From<&'a str> for Robj {
fn from(val: &str) -> Self {
unsafe {
let sexp = Rf_allocVector(STRSXP, 1);
R_PreserveObject(sexp);
let ssexp = Rf_mkCharLen(val.as_ptr() as *const raw::c_char, val.len() as i32);
let ptr = STRING_PTR(sexp);
let slice = std::slice::from_raw_parts_mut(ptr, 1);
slice[0] = ssexp;
Robj::Owned(sexp)
}
}
}
impl<'a> From<&'a [&str]> for Robj {
fn from(vals: &'a [&str]) -> Self {
unsafe {
let len = vals.len();
let sexp = Rf_allocVector(STRSXP, len as R_xlen_t);
R_PreserveObject(sexp);
for (idx, &v) in vals.iter().enumerate() {
SET_STRING_ELT(
sexp,
idx as isize,
Rf_mkCharLen(v.as_ptr() as *const raw::c_char, v.len() as i32),
);
}
Robj::Owned(sexp)
}
}
}
impl<'a> From<&'a [i32]> for Robj {
fn from(vals: &[i32]) -> Self {
unsafe {
let len = vals.len();
let sexp = Rf_allocVector(INTSXP, len as R_xlen_t);
R_PreserveObject(sexp);
let ptr = INTEGER(sexp);
let slice = std::slice::from_raw_parts_mut(ptr, len);
for (i, &v) in vals.iter().enumerate() {
slice[i] = v;
}
Robj::Owned(sexp)
}
}
}
impl From<&[bool]> for Robj {
fn from(vals: &[bool]) -> Self {
unsafe {
let len = vals.len();
let sexp = Rf_allocVector(LGLSXP, len as R_xlen_t);
R_PreserveObject(sexp);
let ptr = LOGICAL(sexp);
let slice = std::slice::from_raw_parts_mut(ptr, len);
for (i, &v) in vals.iter().enumerate() {
slice[i] = v as i32;
}
Robj::Owned(sexp)
}
}
}
impl From<&[f64]> for Robj {
fn from(vals: &[f64]) -> Self {
unsafe {
let len = vals.len();
let sexp = Rf_allocVector(REALSXP, len as R_xlen_t);
R_PreserveObject(sexp);
let ptr = REAL(sexp);
let slice = std::slice::from_raw_parts_mut(ptr, len);
for (i, &v) in vals.iter().enumerate() {
slice[i] = v;
}
Robj::Owned(sexp)
}
}
}
impl From<&[u8]> for Robj {
fn from(vals: &[u8]) -> Self {
unsafe {
let len = vals.len();
let sexp = Rf_allocVector(RAWSXP, len as R_xlen_t);
R_PreserveObject(sexp);
let ptr = RAW(sexp);
let slice = std::slice::from_raw_parts_mut(ptr, len);
for (i, &v) in vals.iter().enumerate() {
slice[i] = v;
}
Robj::Owned(sexp)
}
}
}
impl<T: AsRef<str>> From<Vec<T>> for Robj {
fn from(vals: Vec<T>) -> Self {
unsafe {
let sexp = Rf_allocVector(STRSXP, vals.len() as R_xlen_t);
R_PreserveObject(sexp);
for (i, s) in vals.iter().enumerate() {
SET_STRING_ELT(sexp, i as R_xlen_t, Rf_mkCharLen(
s.as_ref().as_ptr() as *const raw::c_char,
s.as_ref().len() as i32,
));
}
Robj::Owned(sexp)
}
}
}
#[derive(Clone)]
pub struct VecIter {
vector: SEXP,
i: usize,
len: usize,
}
impl Iterator for VecIter {
type Item = Robj;
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
fn next(&mut self) -> Option<Self::Item> {
let i = self.i;
self.i += 1;
if i >= self.len {
return None;
} else {
Some(Robj::from(unsafe { VECTOR_ELT(self.vector, i as isize) }))
}
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.i += n;
self.next()
}
}
#[derive(Clone)]
pub struct ListIter {
list_elem: SEXP,
}
impl Iterator for ListIter {
type Item = Robj;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let sexp = self.list_elem;
if sexp == R_NilValue {
None
} else {
self.list_elem = CDR(sexp);
Some(new_borrowed(CAR(sexp)))
}
}
}
}
#[derive(Clone)]
pub struct StrIter {
vector: SEXP,
i: usize,
len: usize,
}
impl Iterator for StrIter {
type Item = &'static str;
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
fn next(&mut self) -> Option<Self::Item> {
let i = self.i;
self.i += 1;
if i >= self.len {
return None;
} else {
unsafe {
let sexp = STRING_ELT(self.vector, i as isize);
let ptr = R_CHAR(sexp) as *const u8;
let slice = std::slice::from_raw_parts(ptr, Rf_xlength(sexp) as usize);
Some(std::str::from_utf8_unchecked(slice))
}
}
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.i += n;
self.next()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::engine::*;
#[test]
fn test_debug() {
assert_eq!(format!("{:?}", Robj::from(NULL)), "NULL");
assert_eq!(format!("{:?}", Robj::from(TRUE)), "TRUE");
assert_eq!(format!("{:?}", Robj::from(FALSE)), "FALSE");
assert_eq!(format!("{:?}", Robj::from(1)), "1");
assert_eq!(format!("{:?}", Robj::from(1.)), "1.0");
assert_eq!(format!("{:?}", Robj::from("hello")), "[\"hello\"]");
assert_eq!(format!("{:?}", Robj::from(&[1, 2, 3][..])), "[1, 2, 3]");
assert_eq!(
format!("{:?}", Robj::from(&[1., 2., 3.][..])),
"[1.0, 2.0, 3.0]"
);
assert_eq!(
format!("{:?}", Robj::from(&[1_u8, 2_u8, 3_u8][..])),
"[1, 2, 3]"
);
assert_eq!(format!("{:?}", Robj::from(Symbol("x"))), "Symbol(\"x\")");
assert_eq!(
format!("{:?}", Robj::from(Character("x"))),
"Character(\"x\")"
);
assert_eq!(
format!("{:?}", Robj::from(Lang("x"))),
"Lang([Symbol(\"x\")])"
);
assert_eq!(
format!("{:?}", Robj::from(&[Bool(1), Bool(0)][..])),
"&[Bool(1), Bool(0)]"
);
}
#[test]
fn test_from_robj() {
assert_eq!(<u8>::from_robj(&Robj::from(1)), Ok(1));
assert_eq!(<u16>::from_robj(&Robj::from(1)), Ok(1));
assert_eq!(<u32>::from_robj(&Robj::from(1)), Ok(1));
assert_eq!(<u64>::from_robj(&Robj::from(1)), Ok(1));
assert_eq!(<i8>::from_robj(&Robj::from(1)), Ok(1));
assert_eq!(<i16>::from_robj(&Robj::from(1)), Ok(1));
assert_eq!(<i32>::from_robj(&Robj::from(1)), Ok(1));
assert_eq!(<i64>::from_robj(&Robj::from(1)), Ok(1));
assert_eq!(<f32>::from_robj(&Robj::from(1)), Ok(1.));
assert_eq!(<f64>::from_robj(&Robj::from(1)), Ok(1.));
assert_eq!(<Vec::<i32>>::from_robj(&Robj::from(1)), Ok(vec![1]));
assert_eq!(<Vec::<f64>>::from_robj(&Robj::from(1.)), Ok(vec![1.]));
assert_eq!(
<ArrayView1<f64>>::from_robj(&Robj::from(1.)),
Ok(ArrayView1::<f64>::from(&[1.][..]))
);
assert_eq!(
<ArrayView1<i32>>::from_robj(&Robj::from(1)),
Ok(ArrayView1::<i32>::from(&[1][..]))
);
assert_eq!(
<ArrayView1<Bool>>::from_robj(&Robj::from(true)),
Ok(ArrayView1::<Bool>::from(&[Bool(1)][..]))
);
let hello = Robj::from("hello");
assert_eq!(<&str>::from_robj(&hello), Ok("hello"));
}
#[test]
fn test_to_robj() {
assert_eq!(Robj::from(1_u8), Robj::from(1));
assert_eq!(Robj::from(1_u16), Robj::from(1));
assert_eq!(Robj::from(1_u32), Robj::from(1));
assert_eq!(Robj::from(1_u64), Robj::from(1));
assert_eq!(Robj::from(1_i8), Robj::from(1));
assert_eq!(Robj::from(1_i16), Robj::from(1));
assert_eq!(Robj::from(1_i32), Robj::from(1));
assert_eq!(Robj::from(1_i64), Robj::from(1));
assert_eq!(Robj::from(1.0_f32), Robj::from(1.));
assert_eq!(Robj::from(1.0_f64), Robj::from(1.));
let ab = Robj::from(vec!["a", "b"]);
let ab2 = Robj::from(vec!["a".to_string(), "b".to_string()]);
assert_eq!(ab, ab2);
assert_eq!(format!("{:?}", ab), "[\"a\", \"b\"]");
assert_eq!(format!("{:?}", ab2), "[\"a\", \"b\"]");
}
#[test]
fn parse_test() -> Result<(), AnyError> {
start_r();
let p = Robj::parse("print(1L);print(1L);")?;
assert_eq!(
format!("{:?}", p),
"Expr([Lang([Symbol(\"print\"), 1]), Lang([Symbol(\"print\"), 1])])"
);
let p = Robj::eval_string("1L + 1L")?;
assert_eq!(p, Robj::from(2));
Ok(())
}
}