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); };
}