use libR_sys::*;
use std::os::raw;
use crate::*;
use std::collections::HashMap;
use std::iter::IntoIterator;
use std::ops::{Range, RangeInclusive};
mod from_robj;
mod into_robj;
mod operators;
mod rinternals;
#[cfg(test)]
mod tests;
pub use from_robj::*;
pub use into_robj::*;
pub use iter::*;
pub use operators::*;
pub use rinternals::*;
pub enum Robj {
#[doc(hidden)]
Owned(SEXP),
#[doc(hidden)]
Sys(SEXP),
}
impl Clone for Robj {
fn clone(&self) -> Self {
unsafe {
match *self {
Robj::Owned(sexp) => new_owned(sexp),
Robj::Sys(sexp) => new_sys(sexp),
}
}
}
}
impl Default for Robj {
fn default() -> Self {
Robj::from(())
}
}
impl Robj {
#[doc(hidden)]
pub unsafe fn get(&self) -> SEXP {
match self {
Robj::Owned(sexp) => *sexp,
Robj::Sys(sexp) => *sexp,
}
}
#[doc(hidden)]
pub unsafe fn get_mut(&mut self) -> Option<SEXP> {
match self {
Robj::Owned(sexp) => Some(*sexp),
Robj::Sys(_) => None,
}
}
#[doc(hidden)]
pub fn sexptype(&self) -> u32 {
unsafe { TYPEOF(self.get()) as u32 }
}
pub fn rtype(&self) -> RType {
match self.sexptype() {
NILSXP => RType::Null,
SYMSXP => RType::Symbol,
LISTSXP => RType::Pairlist,
CLOSXP => RType::Function,
ENVSXP => RType::Enviroment,
PROMSXP => RType::Promise,
LANGSXP => RType::Language,
SPECIALSXP => RType::Special,
BUILTINSXP => RType::Builtin,
CHARSXP => RType::Character,
LGLSXP => RType::Logical,
INTSXP => RType::Integer,
REALSXP => RType::Real,
CPLXSXP => RType::Complex,
STRSXP => RType::String,
DOTSXP => RType::Dot,
ANYSXP => RType::Any,
VECSXP => RType::List,
EXPRSXP => RType::Expression,
BCODESXP => RType::Bytecode,
EXTPTRSXP => RType::ExternalPtr,
WEAKREFSXP => RType::WeakRef,
RAWSXP => RType::Raw,
S4SXP => RType::S4,
_ => RType::Unknown,
}
}
pub fn len(&self) -> usize {
unsafe { Rf_xlength(self.get()) as usize }
}
pub fn local<K: Into<Robj>>(&self, key: K) -> Option<Robj> {
let key = key.into();
if self.is_environment() && key.is_symbol() {
unsafe { Some(new_owned(Rf_findVarInFrame3(self.get(), key.get(), 1))) }
} else {
None
}
}
pub fn set_local<K: Into<Robj>, V: Into<Robj>>(&self, key: K, value: V) {
let key = key.into();
let value = value.into();
if self.is_environment() && key.is_symbol() {
single_threaded(|| unsafe {
Rf_defineVar(key.get(), value.get(), self.get());
})
}
}
pub fn parent(&self) -> Option<Robj> {
unsafe {
if self.is_environment() {
let parent = ENCLOS(self.get());
if Rf_isEnvironment(parent) != 0 && parent != R_EmptyEnv {
return Some(new_owned(parent));
}
}
None
}
}
pub fn is_na(&self) -> bool {
if self.len() != 1 {
false
} else {
unsafe {
let sexp = self.get();
match self.sexptype() {
STRSXP => STRING_ELT(sexp, 0) == libR_sys::R_NaString,
INTSXP => *(INTEGER(sexp)) == libR_sys::R_NaInt,
LGLSXP => *(LOGICAL(sexp)) == libR_sys::R_NaInt,
REALSXP => R_IsNA(*(REAL(sexp))) != 0,
_ => false,
}
}
}
}
pub fn as_integer_slice<'a>(&self) -> Option<&'a [i32]> {
self.as_typed_slice()
}
pub fn as_integer_iter(&self) -> Option<Int> {
if let Some(slice) = self.as_integer_slice() {
Some(Int::from_slice(self.to_owned(), slice))
} else {
None
}
}
pub fn as_integer_vector(&self) -> Option<Vec<i32>> {
if let Some(value) = self.as_integer_slice() {
Some(value.iter().cloned().collect::<Vec<_>>())
} else {
None
}
}
pub fn as_logical_slice(&self) -> Option<&[Bool]> {
self.as_typed_slice()
}
pub fn as_logical_vector(&self) -> Option<Vec<Bool>> {
if let Some(value) = self.as_logical_slice() {
Some(value.iter().cloned().collect::<Vec<_>>())
} else {
None
}
}
pub fn as_logical_iter(&self) -> Option<Logical> {
if let Some(slice) = self.as_logical_slice() {
Some(Logical::from_slice(self.to_owned(), slice))
} else {
None
}
}
pub fn as_real_slice(&self) -> Option<&[f64]> {
self.as_typed_slice()
}
pub fn as_real_iter(&self) -> Option<Real> {
if let Some(slice) = self.as_real_slice() {
Some(Real::from_slice(self.to_owned(), slice))
} else {
None
}
}
pub fn as_real_vector(&self) -> Option<Vec<f64>> {
if let Some(value) = self.as_real_slice() {
Some(value.iter().cloned().collect::<Vec<_>>())
} else {
None
}
}
pub fn as_raw_slice(&self) -> Option<&[u8]> {
self.as_typed_slice()
}
pub fn as_integer_slice_mut(&mut self) -> Option<&mut [i32]> {
self.as_typed_slice_mut()
}
pub fn as_real_slice_mut(&mut self) -> Option<&mut [f64]> {
self.as_typed_slice_mut()
}
pub fn as_raw_slice_mut(&mut self) -> Option<&mut [u8]> {
self.as_typed_slice_mut()
}
pub fn as_string_vector(&self) -> Option<Vec<String>> {
if let Some(iter) = self.as_str_iter() {
Some(iter.map(str::to_string).collect())
} else {
None
}
}
pub fn as_str_vector(&self) -> Option<Vec<&str>> {
if let Some(iter) = self.as_str_iter() {
Some(iter.collect())
} else {
None
}
}
pub fn as_str<'a>(&self) -> Option<&'a str> {
unsafe {
match self.sexptype() {
STRSXP => {
if self.len() != 1 {
None
} else {
Some(to_str(R_CHAR(STRING_ELT(self.get(), 0)) as *const u8))
}
}
_ => None,
}
}
}
pub fn as_integer(&self) -> Option<i32> {
match self.as_integer_slice() {
Some(slice) if slice.len() == 1 && !slice[0].is_na() => Some(slice[0]),
_ => None,
}
}
pub fn as_real(&self) -> Option<f64> {
match self.as_real_slice() {
Some(slice) if slice.len() == 1 && !slice[0].is_na() => Some(slice[0]),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self.as_logical_slice() {
Some(slice) if slice.len() == 1 && !slice[0].is_na() => Some(slice[0].into()),
_ => None,
}
}
pub fn as_logical(&self) -> Option<Bool> {
match self.as_logical_slice() {
Some(slice) if slice.len() == 1 => Some(slice[0]),
_ => None,
}
}
pub fn eval(&self) -> Result<Robj> {
single_threaded(|| 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(Error::EvalError {
code: r!(self),
error,
})
} else {
Ok(new_owned(res))
}
})
}
pub fn eval_blind(&self) -> Robj {
let res = self.eval();
if res.is_err() {
Robj::from(())
} else {
Robj::from(res.unwrap())
}
}
pub fn is_owned(&self) -> bool {
match self {
Robj::Owned(_) => true,
_ => false,
}
}
#[doc(hidden)]
pub fn to_owned(self) -> Robj {
match self {
Robj::Owned(_) => self,
_ => unsafe { new_owned(self.get()) },
}
}
}
pub trait AsTypedSlice<'a, T>
where
Self: 'a,
{
fn as_typed_slice(&self) -> Option<&'a [T]>
where
Self: 'a,
{
None
}
fn as_typed_slice_mut(&mut self) -> Option<&'a mut [T]>
where
Self: 'a,
{
None
}
}
macro_rules! make_typed_slice {
($type: ty, $fn: tt, $($sexp: tt),* ) => {
impl<'a> AsTypedSlice<'a, $type> for Robj
where
Self : 'a,
{
fn as_typed_slice(&self) -> Option<&'a [$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<&'a 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 get_attrib<'a, N>(&self, name: N) -> Option<Robj>
where
Self: 'a,
Robj: From<N> + 'a,
{
let name = Robj::from(name);
if self.sexptype() == CHARSXP {
None
} else {
let res = unsafe { new_owned(Rf_getAttrib(self.get(), name.get())) };
if res.is_null() {
None
} else {
Some(res)
}
}
}
pub fn set_attrib<N, V>(&self, name: N, value: V) -> Result<Robj>
where
N: Into<Robj>,
V: Into<Robj>,
{
let name = name.into();
let value = value.into();
unsafe {
single_threaded(|| {
catch_r_error(|| Rf_setAttrib(self.get(), name.get(), value.get()))
.map(|_| self.clone())
})
}
}
pub fn names(&self) -> Option<StrIter> {
if let Some(names) = self.get_attrib(names_symbol()) {
names.as_str_iter()
} else {
None
}
}
pub fn set_names<T>(&self, names: T) -> Result<Robj>
where
T: IntoIterator,
T::IntoIter: Iterator,
T::Item: ToVectorValue + AsRef<str>,
{
let iter = names.into_iter();
let robj = iter.collect_robj();
if robj.len() == self.len() {
self.set_attrib(names_symbol(), robj)
} else {
Err(Error::NamesLengthMismatch)
}
}
pub fn dim(&self) -> Option<Int> {
if let Some(dim) = self.get_attrib(dim_symbol()) {
dim.as_integer_iter()
} else {
None
}
}
pub fn dimnames(&self) -> Option<ListIter> {
if let Some(names) = self.get_attrib(dimnames_symbol()) {
names.as_list_iter()
} else {
None
}
}
pub fn as_named_list_iter(&self) -> Option<NamedListIter> {
if let Some(names) = self.names() {
if let Some(values) = self.as_list_iter() {
return Some(names.zip(values));
}
}
None
}
pub fn class(&self) -> Option<StrIter> {
if let Some(class) = self.get_attrib(class_symbol()) {
class.as_str_iter()
} else {
None
}
}
pub fn set_class<T>(&self, class: T) -> Result<Robj>
where
T: IntoIterator,
T::IntoIter: Iterator,
T::Item: ToVectorValue + AsRef<str>,
{
let iter = class.into_iter();
self.set_attrib(class_symbol(), iter.collect_robj())
}
pub fn inherits(&self, classname: &str) -> bool {
if let Some(mut iter) = self.class() {
iter.find(|&n| n == classname).is_some()
} else {
false
}
}
pub fn levels(&self) -> Option<StrIter> {
if let Some(levels) = self.get_attrib(levels_symbol()) {
levels.as_str_iter()
} else {
None
}
}
pub fn ls(&self) -> Option<Vec<&str>> {
self.as_env_iter()
.map(|iter| iter.map(|(k, _)| k).collect::<Vec<_>>())
}
}
#[doc(hidden)]
pub unsafe fn new_owned(sexp: SEXP) -> Robj {
single_threaded(|| ownership::protect(sexp));
Robj::Owned(sexp)
}
#[doc(hidden)]
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_integer_slice() == Some(rhs)
}
}
impl<'a> PartialEq<[f64]> for Robj {
fn eq(&self, rhs: &[f64]) -> bool {
self.as_real_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 {
unsafe {
if self.get() == rhs.get() {
return true;
}
if self.sexptype() == rhs.sexptype() && self.len() == rhs.len() {
let lsexp = self.get();
let rsexp = rhs.get();
match self.sexptype() {
NILSXP => true,
SYMSXP => PRINTNAME(lsexp) == PRINTNAME(rsexp),
LISTSXP | LANGSXP | DOTSXP => self
.as_pairlist_iter()
.unwrap()
.eq(rhs.as_pairlist_iter().unwrap()),
CLOSXP => false,
ENVSXP => false, PROMSXP => false,
SPECIALSXP => false,
BUILTINSXP => false,
CHARSXP => self.as_character() == rhs.as_character(),
LGLSXP => self.as_logical_slice() == rhs.as_logical_slice(),
INTSXP => self.as_integer_slice() == rhs.as_integer_slice(),
REALSXP => self.as_real_slice() == rhs.as_real_slice(),
CPLXSXP => false,
ANYSXP => false,
VECSXP | EXPRSXP | WEAKREFSXP => {
self.as_list_iter().unwrap().eq(rhs.as_list_iter().unwrap())
}
STRSXP => self.as_str_iter().unwrap().eq(rhs.as_str_iter().unwrap()),
BCODESXP => false,
EXTPTRSXP => false,
RAWSXP => self.as_raw_slice() == rhs.as_raw_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, "r!(NULL)"),
SYMSXP => {
if self.is_missing_arg() {
write!(f, "missing_arg()")
} else if self.is_unbound_value() {
write!(f, "unbound_value()")
} else {
write!(f, "sym!({})", self.as_symbol().unwrap().0)
}
}
LISTSXP => write!(f, "r!({:?})", self.as_pairlist().unwrap()),
CLOSXP => write!(f, "r!(Function())"),
ENVSXP => unsafe {
let sexp = self.get();
if sexp == R_GlobalEnv {
write!(f, "global_env()")
} else if sexp == R_BaseEnv {
write!(f, "base_env()")
} else if sexp == R_EmptyEnv {
write!(f, "empty_env()")
} else {
write!(f, "r!({:?})", self.as_environment().unwrap())
}
},
PROMSXP => write!(f, "r!(Promise())"),
LANGSXP => write!(f, "r!({:?})", self.as_lang().unwrap()),
SPECIALSXP => write!(f, "r!(Special())"),
BUILTINSXP => write!(f, "r!(Builtin())"),
CHARSXP => write!(f, "r!({:?})", self.as_character().unwrap()),
LGLSXP => {
let slice = self.as_logical_slice().unwrap();
if slice.len() == 1 {
write!(
f,
"{}",
if slice[0].0 == 0 {
"r!(FALSE)"
} else {
"r!(TRUE)"
}
)
} else {
write!(f, "r!({:?})", slice)
}
}
INTSXP => {
let slice = self.as_integer_slice().unwrap();
if slice.len() == 1 {
write!(f, "r!({:?})", slice[0])
} else {
write!(f, "r!({:?})", self.as_integer_slice().unwrap())
}
}
REALSXP => {
let slice = self.as_real_slice().unwrap();
if slice.len() == 1 {
write!(f, "r!({:?})", slice[0])
} else {
write!(f, "r!({:?})", slice)
}
}
VECSXP => write!(f, "r!({:?})", self.as_list().unwrap()),
EXPRSXP => write!(f, "r!({:?})", self.as_expr().unwrap()),
WEAKREFSXP => write!(
f,
"r!(Weakref({:?}))",
self.as_list_iter().unwrap().collect::<Vec<_>>()
),
STRSXP => {
write!(f, "r!([")?;
let mut sep = "";
for s in self.as_str_iter().unwrap() {
write!(f, "{}{:?}", sep, s)?;
sep = ", ";
}
write!(f, "])")
}
DOTSXP => write!(f, "r!(Dot())"),
ANYSXP => write!(f, "r!(Any())"),
BCODESXP => write!(f, "r!(Bcode())"),
EXTPTRSXP => write!(f, "r!(Extptr())"),
RAWSXP => {
write!(f, "r!({:?})", self.as_raw().unwrap())
}
S4SXP => write!(f, "r!(S4())"),
NEWSXP => write!(f, "r!(New())"),
FREESXP => write!(f, "r!(Free())"),
_ => write!(f, "??"),
}
}
}
pub(crate) 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 Drop for Robj {
fn drop(&mut self) {
unsafe {
match self {
Robj::Owned(sexp) => ownership::unprotect(*sexp),
Robj::Sys(_) => (),
}
}
}
}