#![doc(
html_logo_url = "https://raw.githubusercontent.com/extendr/extendr/master/extendr-logo-256.png"
)]
pub mod error;
pub mod functions;
pub mod iter;
pub mod lang_macros;
pub mod logical;
pub mod matrix;
pub mod metadata;
pub mod ownership;
pub mod prelude;
pub mod rmacros;
pub mod robj;
pub mod thread_safety;
pub mod wrapper;
#[cfg(feature = "ndarray")]
pub mod robj_ndarray;
pub use error::*;
pub use functions::*;
pub use lang_macros::*;
pub use logical::*;
pub use matrix::*;
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::*;
pub const TRUE: Bool = Bool(1);
pub const FALSE: Bool = Bool(0);
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: Bool = Bool(std::i32::MIN);
#[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.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.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);
}
pub trait IsNA {
fn is_na(&self) -> bool;
}
impl IsNA for f64 {
fn is_na(&self) -> bool {
unsafe { R_IsNA(*self) != 0 }
}
}
impl IsNA for i32 {
fn is_na(&self) -> bool {
*self == std::i32::MIN
}
}
impl IsNA for Bool {
fn is_na(&self) -> bool {
self.0 == std::i32::MIN
}
}
impl IsNA for &str {
fn is_na(&self) -> bool {
self.as_ptr() == na_str().as_ptr()
}
}
#[derive(Debug, PartialEq)]
pub enum RType {
Null, Symbol, Pairlist, Function, Enviroment, Promise, Language, Special, Builtin, Character, Logical, Integer, Real, Complex, String, Dot, Any, List, Expression, Bytecode, ExternalPtr, WeakRef, Raw, 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 std::collections::HashMap;
use extendr_macros::extendr;
use extendr_macros::extendr_module;
#[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: &[Bool]) -> &[Bool] {
x
}
#[extendr]
pub fn f64_iter(x: Real) -> Real {
x
}
#[extendr]
pub fn i32_iter(x: Int) -> Int {
x
}
#[extendr]
pub fn bool_iter(x: Logical) -> Logical {
x
}
#[extendr]
pub fn symbol(x: Symbol) -> Symbol {
x
}
#[extendr]
pub fn matrix(x: RMatrix<&[f64]>) -> RMatrix<&[f64]> {
x
}
#[extendr]
pub fn hash_map(x: HashMap<&str, Robj>) -> HashMap<&str, Robj> {
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!(new_owned(wrap__return_u8()), Robj::from(123));
assert_eq!(new_owned(wrap__return_u16()), Robj::from(123));
assert_eq!(new_owned(wrap__return_u32()), Robj::from(123));
assert_eq!(new_owned(wrap__return_u64()), Robj::from(123));
assert_eq!(new_owned(wrap__return_i8()), Robj::from(123));
assert_eq!(new_owned(wrap__return_i16()), Robj::from(123));
assert_eq!(new_owned(wrap__return_i32()), Robj::from(123));
assert_eq!(new_owned(wrap__return_i64()), Robj::from(123));
assert_eq!(new_owned(wrap__return_f32()), Robj::from(123.));
assert_eq!(new_owned(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!(new_owned(wrap__f64_slice(robj.get())), robj);
let robj = r!([1, 2, 3]);
assert_eq!(new_owned(wrap__i32_slice(robj.get())), robj);
let robj = r!([TRUE, FALSE, TRUE]);
assert_eq!(new_owned(wrap__bool_slice(robj.get())), robj);
let robj = r!([1., 2., 3.]);
assert_eq!(new_owned(wrap__f64_iter(robj.get())), robj);
let robj = r!([1, 2, 3]);
assert_eq!(new_owned(wrap__i32_iter(robj.get())), robj);
let robj = r!([TRUE, FALSE, TRUE]);
assert_eq!(new_owned(wrap__bool_iter(robj.get())), robj);
let robj = sym!(fred);
assert_eq!(new_owned(wrap__symbol(robj.get())), robj);
let m = RMatrix::new([1., 2.], 1, 2);
let robj = r!(m);
assert_eq!(new_owned(wrap__matrix(robj.get())), robj);
let robj = r!(List(&[1, 2]));
robj.set_attrib(names_symbol(), r!(["a", "b"]))?;
let res = new_owned(wrap__hash_map(robj.get()));
assert_eq!(res.len(), 2);
}
}
}
#[test]
fn r_output_test() {
test! {
let txt_con = 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!(na_str().as_ptr() != "NA".as_ptr());
assert_eq!(na_str(), "NA");
assert_eq!("NA".is_na(), false);
assert_eq!(na_str().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].name, "aux_func");
assert_eq!(metadata.functions[0].args[0].name, "_person");
assert_eq!(metadata.functions[1].name, "get_my_module_metadata");
assert_eq!(metadata.impls[0].name, "Person");
assert_eq!(metadata.impls[0].methods.len(), 3);
let robj = unsafe { new_owned(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);
}
}
}