#![doc(
html_logo_url = "https://raw.githubusercontent.com/extendr/extendr/master/extendr-logo-256.png"
)]
pub mod error;
pub mod functions;
pub mod io;
pub mod iter;
pub mod lang_macros;
pub mod metadata;
pub mod ownership;
pub mod prelude;
pub mod rmacros;
#[cfg(feature = "serde")]
pub mod serializer;
#[cfg(feature = "serde")]
pub mod deserializer;
#[cfg(feature = "graphics")]
pub mod graphics;
pub mod robj;
pub mod scalar;
pub mod thread_safety;
pub mod wrapper;
pub mod na;
#[cfg(feature = "ndarray")]
pub mod robj_ndarray;
pub use std::convert::{TryFrom, TryInto};
pub use std::ops::Deref;
pub use std::ops::DerefMut;
pub use robj::Robj;
pub use error::*;
pub use functions::*;
pub use lang_macros::*;
pub use na::*;
pub use rmacros::*;
pub use robj::*;
pub use thread_safety::{
catch_r_error, handle_panic, single_threaded, this_thread_id, throw_r_error,
};
pub use wrapper::*;
#[cfg(feature = "ndarray")]
pub use robj_ndarray::*;
pub use extendr_macros::*;
use scalar::Rbool;
pub const TRUE: Rbool = Rbool::true_value();
pub const FALSE: Rbool = Rbool::false_value();
pub const NULL: () = ();
pub const NA_INTEGER: Option<i32> = None;
pub const NA_REAL: Option<f64> = None;
pub const NA_STRING: Option<&str> = None;
pub const NA_LOGICAL: Rbool = Rbool::na_value();
#[doc(hidden)]
pub use std::collections::HashMap;
#[doc(hidden)]
pub use libR_sys::DllInfo;
#[doc(hidden)]
pub use libR_sys::SEXP;
#[doc(hidden)]
use libR_sys::*;
#[doc(hidden)]
use std::ffi::CString;
pub use metadata::Metadata;
#[doc(hidden)]
pub struct CallMethod {
pub call_symbol: std::ffi::CString,
pub func_ptr: *const u8,
pub num_args: i32,
}
unsafe fn make_method_def(
cstrings: &mut Vec<std::ffi::CString>,
rmethods: &mut Vec<libR_sys::R_CallMethodDef>,
func: &metadata::Func,
wrapped_name: &str,
) {
cstrings.push(std::ffi::CString::new(wrapped_name).unwrap());
rmethods.push(libR_sys::R_CallMethodDef {
name: cstrings.last().unwrap().as_ptr(),
fun: Some(std::mem::transmute(func.func_ptr)),
numArgs: func.args.len() as i32,
});
}
#[doc(hidden)]
pub unsafe fn register_call_methods(info: *mut libR_sys::DllInfo, metadata: Metadata) {
let mut rmethods = Vec::new();
let mut cstrings = Vec::new();
for func in metadata.functions {
let wrapped_name = format!("wrap__{}", func.mod_name);
make_method_def(&mut cstrings, &mut rmethods, &func, wrapped_name.as_str());
}
for imp in metadata.impls {
for func in imp.methods {
let wrapped_name = format!("wrap__{}__{}", imp.name, func.mod_name);
make_method_def(&mut cstrings, &mut rmethods, &func, wrapped_name.as_str());
}
}
rmethods.push(libR_sys::R_CallMethodDef {
name: std::ptr::null(),
fun: None,
numArgs: 0,
});
libR_sys::R_registerRoutines(
info,
std::ptr::null(),
rmethods.as_ptr(),
std::ptr::null(),
std::ptr::null(),
);
libR_sys::R_useDynamicSymbols(info, 0);
libR_sys::R_forceSymbols(info, 0);
}
#[derive(Debug, PartialEq)]
pub enum Rtype {
Null, Symbol, Pairlist, Function, Environment, Promise, Language, Special, Builtin, Rstr, Logicals, Integers, Doubles, Complexes, Strings, Dot, Any, List, Expressions, Bytecode, ExternalPtr, WeakRef, Raw, S4, Unknown,
}
#[derive(Debug, PartialEq)]
pub enum Rany<'a> {
Null(&'a Robj), Symbol(&'a Symbol), Pairlist(&'a Pairlist), Function(&'a Function), Environment(&'a Environment), Promise(&'a Promise), Language(&'a Language), Special(&'a Primitive), Builtin(&'a Primitive), Rstr(&'a Rstr), Logicals(&'a Logicals), Integers(&'a Integers), Doubles(&'a Doubles), Complexes(&'a Robj), Strings(&'a Strings), Dot(&'a Robj), Any(&'a Robj), List(&'a List), Expressions(&'a Expressions), Bytecode(&'a Robj), ExternalPtr(&'a Robj), WeakRef(&'a Robj), Raw(&'a Raw), S4(&'a S4), Unknown(&'a Robj),
}
pub fn rtype_to_sxp(rtype: Rtype) -> i32 {
use Rtype::*;
(match rtype {
Null => NILSXP,
Symbol => SYMSXP,
Pairlist => LISTSXP,
Function => CLOSXP,
Environment => ENVSXP,
Promise => PROMSXP,
Language => LANGSXP,
Special => SPECIALSXP,
Builtin => BUILTINSXP,
Rstr => CHARSXP,
Logicals => LGLSXP,
Integers => INTSXP,
Doubles => REALSXP,
Complexes => CPLXSXP,
Strings => STRSXP,
Dot => DOTSXP,
Any => ANYSXP,
List => VECSXP,
Expressions => EXPRSXP,
Bytecode => BCODESXP,
ExternalPtr => EXTPTRSXP,
WeakRef => WEAKREFSXP,
Raw => RAWSXP,
S4 => S4SXP,
Unknown => panic!("attempt to use Unknown Rtype"),
}) as i32
}
pub fn sxp_to_rtype(sxptype: i32) -> Rtype {
use Rtype::*;
match sxptype as u32 {
NILSXP => Null,
SYMSXP => Symbol,
LISTSXP => Pairlist,
CLOSXP => Function,
ENVSXP => Environment,
PROMSXP => Promise,
LANGSXP => Language,
SPECIALSXP => Special,
BUILTINSXP => Builtin,
CHARSXP => Rstr,
LGLSXP => Logicals,
INTSXP => Integers,
REALSXP => Doubles,
CPLXSXP => Complexes,
STRSXP => Strings,
DOTSXP => Dot,
ANYSXP => Any,
VECSXP => List,
EXPRSXP => Expressions,
BCODESXP => Bytecode,
EXTPTRSXP => ExternalPtr,
WEAKREFSXP => WeakRef,
RAWSXP => Raw,
S4SXP => S4,
_ => Unknown,
}
}
#[doc(hidden)]
pub fn print_r_output<T: Into<Vec<u8>>>(s: T) {
let cs = CString::new(s).expect("NulError");
unsafe {
Rprintf(cs.as_ptr());
}
}
#[doc(hidden)]
pub fn print_r_error<T: Into<Vec<u8>>>(s: T) {
let cs = CString::new(s).expect("NulError");
unsafe {
REprintf(cs.as_ptr());
}
}
#[cfg(test)]
mod tests {
use super::prelude::*;
use crate as extendr_api;
use extendr_macros::extendr;
use extendr_macros::extendr_module;
use extendr_macros::pairlist;
#[extendr]
pub fn inttypes(a: i8, b: u8, c: i16, d: u16, e: i32, f: u32, g: i64, h: u64) {
assert_eq!(a, 1);
assert_eq!(b, 2);
assert_eq!(c, 3);
assert_eq!(d, 4);
assert_eq!(e, 5);
assert_eq!(f, 6);
assert_eq!(g, 7);
assert_eq!(h, 8);
}
#[extendr]
pub fn floattypes(a: f32, b: f64) {
assert_eq!(a, 1.);
assert_eq!(b, 2.);
}
#[extendr]
pub fn strtypes(a: &str, b: String) {
assert_eq!(a, "abc");
assert_eq!(b, "def");
}
#[extendr]
pub fn vectortypes(a: Vec<i32>, b: Vec<f64>) {
assert_eq!(a, [1, 2, 3]);
assert_eq!(b, [4., 5., 6.]);
}
#[extendr]
pub fn robjtype(a: Robj) {
assert_eq!(a, Robj::from(1))
}
#[extendr]
pub fn return_u8() -> u8 {
123
}
#[extendr]
pub fn return_u16() -> u16 {
123
}
#[extendr]
pub fn return_u32() -> u32 {
123
}
#[extendr]
pub fn return_u64() -> u64 {
123
}
#[extendr]
pub fn return_i8() -> i8 {
123
}
#[extendr]
pub fn return_i16() -> i16 {
123
}
#[extendr]
pub fn return_i32() -> i32 {
123
}
#[extendr]
pub fn return_i64() -> i64 {
123
}
#[extendr]
pub fn return_f32() -> f32 {
123.
}
#[extendr]
pub fn return_f64() -> f64 {
123.
}
#[extendr]
pub fn f64_slice(x: &[f64]) -> &[f64] {
x
}
#[extendr]
pub fn i32_slice(x: &[i32]) -> &[i32] {
x
}
#[extendr]
pub fn bool_slice(x: &[Rbool]) -> &[Rbool] {
x
}
#[extendr]
pub fn f64_iter(x: Doubles) -> Doubles {
x
}
#[extendr]
pub fn i32_iter(x: Integers) -> Integers {
x
}
#[extendr]
pub fn symbol(x: Symbol) -> Symbol {
x
}
#[extendr]
pub fn matrix(x: RMatrix<f64>) -> RMatrix<f64> {
x
}
struct Person {
pub name: String,
}
#[extendr]
impl Person {
fn new() -> Self {
Self {
name: "".to_string(),
}
}
fn set_name(&mut self, name: &str) {
self.name = name.to_string();
}
fn name(&self) -> &str {
self.name.as_str()
}
}
#[extendr]
fn aux_func(_person: &Person) {}
extendr_module! {
mod my_module;
fn aux_func;
impl Person;
}
#[test]
fn export_test() {
test! {
use super::*;
unsafe {
wrap__inttypes(
Robj::from(1).get(),
Robj::from(2).get(),
Robj::from(3).get(),
Robj::from(4).get(),
Robj::from(5).get(),
Robj::from(6).get(),
Robj::from(7).get(),
Robj::from(8).get(),
);
wrap__inttypes(
Robj::from(1.).get(),
Robj::from(2.).get(),
Robj::from(3.).get(),
Robj::from(4.).get(),
Robj::from(5.).get(),
Robj::from(6.).get(),
Robj::from(7.).get(),
Robj::from(8.).get(),
);
wrap__floattypes(Robj::from(1.).get(), Robj::from(2.).get());
wrap__floattypes(Robj::from(1).get(), Robj::from(2).get());
wrap__strtypes(Robj::from("abc").get(), Robj::from("def").get());
wrap__vectortypes(
Robj::from(&[1, 2, 3] as &[i32]).get(),
Robj::from(&[4., 5., 6.] as &[f64]).get(),
);
wrap__robjtype(Robj::from(1).get());
assert_eq!(Robj::from_sexp(wrap__return_u8()), Robj::from(123_u8));
assert_eq!(Robj::from_sexp(wrap__return_u16()), Robj::from(123));
assert_eq!(Robj::from_sexp(wrap__return_u32()), Robj::from(123.));
assert_eq!(Robj::from_sexp(wrap__return_u64()), Robj::from(123.));
assert_eq!(Robj::from_sexp(wrap__return_i8()), Robj::from(123));
assert_eq!(Robj::from_sexp(wrap__return_i16()), Robj::from(123));
assert_eq!(Robj::from_sexp(wrap__return_i32()), Robj::from(123));
assert_eq!(Robj::from_sexp(wrap__return_i64()), Robj::from(123.));
assert_eq!(Robj::from_sexp(wrap__return_f32()), Robj::from(123.));
assert_eq!(Robj::from_sexp(wrap__return_f64()), Robj::from(123.));
}
}
}
#[test]
fn class_wrapper_test() {
test! {
let mut person = Person::new();
person.set_name("fred");
let robj = r!(person);
assert_eq!(robj.check_external_ptr("Person"), true);
let person2 = <&Person>::from_robj(&robj).unwrap();
assert_eq!(person2.name(), "fred");
}
}
#[test]
fn slice_test() {
test! {
unsafe {
let robj = r!([1., 2., 3.]);
assert_eq!(Robj::from_sexp(wrap__f64_slice(robj.get())), robj);
let robj = r!([1, 2, 3]);
assert_eq!(Robj::from_sexp(wrap__i32_slice(robj.get())), robj);
let robj = r!([TRUE, FALSE, TRUE]);
assert_eq!(Robj::from_sexp(wrap__bool_slice(robj.get())), robj);
let robj = r!([1., 2., 3.]);
assert_eq!(Robj::from_sexp(wrap__f64_iter(robj.get())), robj);
let robj = r!([1, 2, 3]);
assert_eq!(Robj::from_sexp(wrap__i32_iter(robj.get())), robj);
let robj = sym!(fred);
assert_eq!(Robj::from_sexp(wrap__symbol(robj.get())), robj);
let m = RMatrix::new_matrix(1, 2, |r, c| if r == c {1.0} else {0.});
let robj = r!(m);
assert_eq!(Robj::from_sexp(wrap__matrix(robj.get())), robj);
}
}
}
#[test]
fn r_output_test() {
test! {
let txt_con = R!(r#"textConnection("test_con", open = "w")"#).unwrap();
call!("sink", &txt_con).unwrap();
rprintln!("Hello world");
call!("sink").unwrap();
call!("close", &txt_con).unwrap();
let result = R!("test_con").unwrap();
assert_eq!(result, r!("Hello world"));
}
}
#[test]
fn test_na_str() {
assert_ne!(<&str>::na().as_ptr(), "NA".as_ptr());
assert_eq!(<&str>::na(), "NA");
assert_eq!("NA".is_na(), false);
assert_eq!(<&str>::na().is_na(), true);
}
#[test]
fn metadata_test() {
test! {
let metadata = get_my_module_metadata();
assert_eq!(metadata.functions[0].doc, " comment #1\n comment #2\n\n comment #3\n comment #4\n *\n aux_func doc comment.");
assert_eq!(metadata.functions[0].rust_name, "aux_func");
assert_eq!(metadata.functions[0].mod_name, "aux_func");
assert_eq!(metadata.functions[0].r_name, "aux_func");
assert_eq!(metadata.functions[0].args[0].name, "_person");
assert_eq!(metadata.functions[1].rust_name, "get_my_module_metadata");
assert_eq!(metadata.impls[0].name, "Person");
assert_eq!(metadata.impls[0].methods.len(), 3);
let robj = Robj::from_sexp(wrap__get_my_module_metadata());
let functions = robj.dollar("functions").unwrap();
let impls = robj.dollar("impls").unwrap();
assert_eq!(functions.len(), 3);
assert_eq!(impls.len(), 1);
}
}
#[test]
fn pairlist_macro_works() {
test! {
assert_eq!(pairlist!(1, 2, 3), Pairlist::from_pairs(&[("", 1), ("", 2), ("", 3)]));
assert_eq!(pairlist!(a=1, 2, 3), Pairlist::from_pairs(&[("a", 1), ("", 2), ("", 3)]));
assert_eq!(pairlist!(1, b=2, 3), Pairlist::from_pairs(&[("", 1), ("b", 2), ("", 3)]));
assert_eq!(pairlist!(a=1, b=2, c=3), Pairlist::from_pairs(&[("a", 1), ("b", 2), ("c", 3)]));
assert_eq!(pairlist!(a=NULL), Pairlist::from_pairs(&[("a", ())]));
assert_eq!(pairlist!(), Pairlist::from(()));
}
}
#[test]
fn big_r_macro_works() {
test! {
assert_eq!(R!("1")?, r!(1.0));
assert_eq!(R!(r"1")?, r!(1.0));
assert_eq!(R!(r"
x <- 1
x
")?, r!(1.0));
assert_eq!(R!(r"
x <- {{ 1.0 }}
x
")?, r!(1.0));
assert_eq!(R!(r"
x <- {{ (0..4).collect_robj() }}
x
")?, r!([0, 1, 2, 3]));
assert_eq!(R!(r#"
x <- "hello"
x
"#)?, r!("hello"));
assert_eq!(Rraw!(r"
x <- {{ 1 }}
x
")?, r!(1.0));
}
}
}