1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
//! # Savvy - A Simple R Interface
//!
//! **savvy** is a simple R extension interface using Rust, like the [extendr] framework.
//! The name "savvy" comes from the Japanese word "錆" (pronounced as `sàbí`), which means "Rust".
//!
//! With savvy, you can automatically generate R functions from Rust code. This is
//! an example of what savvy-powered function would look like.
//!
//! **Rust**:
//!
//! ```no_run
//! use savvy::savvy;
//!
//! /// Convert to Upper-case
//! ///
//! /// @param x A character vector.
//! /// @export
//! #[savvy]
//! fn to_upper(x: StringSexp) -> savvy::Result<savvy::Sexp> {
//! // Use `Owned{type}Sexp` to allocate an R vector for output.
//! let mut out = OwnedStringSexp::new(x.len())?;
//!
//! for (i, e) in x.iter().enumerate() {
//! // To Rust, missing value is an ordinary value. In `&str`'s case, it's just "NA".
//! // You have to use `.is_na()` method to distinguish the missing value.
//! if e.is_na() {
//! // Values need to be set by `set_elt()` one by one.
//! out.set_elt(i, <&str>::na())?;
//! continue;
//! }
//!
//! let e_upper = e.to_uppercase();
//! out.set_elt(i, e_upper.as_str())?;
//! }
//!
//! out.into()
//! }
//! ```
//!
//! **R**:
//!
//! ```text
//! to_upper(c("a", "b", "c"))
//! #> [1] "A" "B" "C"
//! ```
//!
//! [extendr]: https://extendr.github.io/
//!
//! ## User guide
//!
//! <https://yutannihilation.github.io/savvy/guide/>
pub mod error;
pub mod ffi;
pub mod io;
pub mod protect;
pub mod sexp;
pub mod unwind_protect;
use std::os::raw::c_char;
pub use error::{Error, Result};
pub use sexp::external_pointer::{get_external_pointer_addr, ExternalPointerSexp, IntoExtPtrSexp};
pub use sexp::function::{FunctionArgs, FunctionCallResult, FunctionSexp};
pub use sexp::integer::{IntegerSexp, OwnedIntegerSexp};
pub use sexp::list::{ListSexp, OwnedListSexp};
pub use sexp::logical::{LogicalSexp, OwnedLogicalSexp};
pub use sexp::na::NotAvailableValue;
pub use sexp::null::NullSexp;
pub use sexp::real::{OwnedRealSexp, RealSexp};
pub use sexp::string::{OwnedStringSexp, StringSexp};
pub use sexp::{Sexp, TypedSexp};
pub use unwind_protect::unwind_protect;
// re-export
pub use savvy_macro::savvy;
use ffi::SEXP;
use savvy_ffi::{cetype_t_CE_UTF8, Rf_allocVector, Rf_mkCharLenCE, SEXPTYPE};
fn alloc_vector(arg1: SEXPTYPE, arg2: usize) -> crate::error::Result<SEXP> {
unsafe { unwind_protect(|| Rf_allocVector(arg1, arg2 as _)) }
}
// This wrapper function handles Error and panicks, and flag it by setting the
// lowest bit to 1. The lowest bit is supposed to be detected (and then removed)
// on the corresponding C function.
//
// cf. https://en.wikipedia.org/wiki/Tagged_pointer
pub fn handle_error(e: crate::error::Error) -> SEXP {
match e {
// The token is already tagged, so pass it as it is.
error::Error::Aborted(token) => token,
// In other cases, return the error string with the tag
e => unsafe {
let msg = e.to_string();
let r_error = Rf_mkCharLenCE(
msg.as_ptr() as *const c_char,
msg.len() as i32,
cetype_t_CE_UTF8,
);
// set the error flag
(r_error as usize | 1) as SEXP
},
}
}
#[macro_export]
macro_rules! r_print {
() => {};
($($arg:tt)*) => { savvy::io::r_print(&format!($($arg)*), false); };
}
#[macro_export]
macro_rules! r_eprint {
() => {};
($($arg:tt)*) => { savvy::io::r_eprint(&format!($($arg)*), false); };
}
#[macro_export]
macro_rules! r_println {
() => {};
($($arg:tt)*) => { savvy::io::r_print(&format!($($arg)*), true); };
}
#[macro_export]
macro_rules! r_eprintln {
() => {};
($($arg:tt)*) => { savvy::io::r_eprint(&format!($($arg)*), true); };
}