extendr_api/rmacros.rs
1//!
2//! rmacros - a set of macros to call actual R functions in a rusty way.
3//!
4
5/// Convert a rust expression to an R object.
6///
7/// Shorthand for `Robj::from(x)`.
8///
9/// Example:
10/// ```
11/// use extendr_api::prelude::*;
12/// test! {
13/// let fred = r!(1);
14/// assert_eq!(fred, Robj::from(1));
15///
16/// let int_array = r!([1, 2, 3]);
17/// assert_eq!(int_array.len(), 3);
18///
19/// let numeric_array = r!([1., 2., 3.]);
20/// assert_eq!(numeric_array.len(), 3);
21///
22/// let logical_array = r!([true, false, true]);
23/// assert_eq!(logical_array.len(), 3);
24///
25/// let numeric_array_with_na = r!([Some(1.), None, Some(3.)]);
26/// assert_eq!(numeric_array_with_na.len(), 3);
27/// }
28/// ```
29#[macro_export]
30macro_rules! r {
31 ($e: expr) => {
32 extendr_api::Robj::from($e)
33 };
34}
35
36/// Get a local variable from the calling function
37/// or a global variable if no such variable exists.
38///
39/// Variables with embedded "." may not work.
40#[macro_export]
41macro_rules! var {
42 ($($tokens: tt)*) => {{
43 local_var(sym!($($tokens)*))
44 }};
45}
46
47/// Get a global variable.
48///
49/// Variables with embedded "." may not work.
50#[macro_export]
51macro_rules! global {
52 ($($tokens: tt)*) => {{
53 global_var(sym!($($tokens)*))
54 }};
55}
56
57/// The sym! macro install symbols.
58/// You should cache your symbols in variables
59/// as generating them is costly.
60/// ```
61/// use extendr_api::prelude::*;
62/// test! {
63///
64/// let wombat = sym!(wombat);
65/// assert_eq!(wombat, r!(Symbol::from_string("wombat")));
66/// }
67/// ```
68#[macro_export]
69macro_rules! sym {
70 ($($tokens: tt)*) => {
71 Robj::from(Symbol::from_string(stringify!($($tokens)*)))
72 };
73}
74
75/// Create a dataframe.
76///
77/// Example:
78/// ```
79/// use extendr_api::prelude::*;
80/// test! {
81/// let mydata = data_frame!(x=1, y=2);
82/// assert_eq!(mydata.inherits("data.frame"), true);
83/// //assert_eq!(mydata, r!(List::from_pairs(vec![("x", r!(1)), ("y", r!(2))])).set_class(&["data.frame"])?);
84/// }
85/// ```
86///
87/// Panics on error.
88#[macro_export]
89macro_rules! data_frame {
90 () => {
91 call!("data.frame").unwrap()
92 };
93 ($($rest: tt)*) => {
94 call!("data.frame", $($rest)*).unwrap()
95 };
96}
97
98/// Create a factor.
99///
100/// Example:
101/// ```
102/// use extendr_api::prelude::*;
103/// test! {
104/// let factor = factor!(vec!["abcd", "def", "fg", "fg"]);
105/// assert_eq!(factor.levels().unwrap().collect::<Vec<_>>(), vec!["abcd", "def", "fg"]);
106/// assert_eq!(factor.as_integer_vector().unwrap(), vec![1, 2, 3, 3]);
107/// assert_eq!(factor.as_str_iter().unwrap().collect::<Vec<_>>(), vec!["abcd", "def", "fg", "fg"]);
108/// }
109/// ```
110///
111/// Panics on error.
112#[macro_export]
113macro_rules! factor {
114 ($($rest: tt)*) => {
115 call!("factor", $($rest)*).unwrap()
116 };
117}
118
119/// Print via the R output stream.
120///
121/// Works like [`print!`] but integrates with R and respects
122/// redirection with functions like `sink()` and `capture.output()`
123#[macro_export]
124macro_rules! rprint {
125 () => {
126 };
127 ($($rest: tt)*) => {
128 print_r_output(format!($($rest)*));
129 };
130}
131
132/// Print with a newline via the R output stream.
133///
134/// Works like [`println!`] but integrates with R and respects
135/// redirection with functions like `sink()` and `capture.output()`
136#[macro_export]
137macro_rules! rprintln {
138 () => {
139 print_r_output("\n");
140 };
141 ($($rest: tt)*) => {
142 print_r_output(format!($($rest)*));
143 print_r_output("\n");
144 };
145}
146
147/// Print via the R error stream.
148#[macro_export]
149macro_rules! reprint {
150 () => {
151 };
152 ($($rest: tt)*) => {
153 print_r_error(format!($($rest)*));
154 };
155}
156
157/// Print with a newline via the R output stream.
158#[macro_export]
159macro_rules! reprintln {
160 () => {
161 print_r_error("\n");
162 };
163 ($($rest: tt)*) => {
164 print_r_error(format!($($rest)*));
165 print_r_error("\n");
166 };
167}
168
169/// Macro for running tests.
170///
171/// This starts up the underlying [`extendr_engine`] so that interactions with R will work.
172/// Additionally, this allows us to use `?` in example code instead of `unwrap()`.
173///
174/// **Note:** This macro is meant to be used in test code (annotated with
175/// `#[cfg(test)]`) or in doc strings. If it is used in library code that
176/// gets incorporated into an R package, R CMD check will complain about
177/// non-API calls.
178///
179/// [`extendr_engine`]: https://extendr.github.io/extendr/extendr_engine/
180#[macro_export]
181macro_rules! test {
182 () => {
183 test(|| Ok(()))
184 };
185 ($($rest: tt)*) => {
186 {
187 use extendr_engine;
188
189 // this helper function must reside in the macro so it doesn't get compiled
190 // unless the macro actually gets used (e.g., in testing code)
191 fn test<F: FnOnce() -> extendr_api::Result<()>>(f: F) {
192 extendr_engine::start_r();
193 f().unwrap();
194 }
195
196 test(|| {
197 $($rest)*
198 Ok(())
199 })
200 }
201 };
202}