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/// The sym! macro install symbols.
48/// You should cache your symbols in variables
49/// as generating them is costly.
50/// ```
51/// use extendr_api::prelude::*;
52/// test! {
53///
54/// let wombat = sym!(wombat);
55/// assert_eq!(wombat, r!(Symbol::from_string("wombat")));
56/// }
57/// ```
58#[macro_export]
59macro_rules! sym {
60 ($($tokens: tt)*) => {
61 Robj::from(Symbol::from_string(stringify!($($tokens)*)))
62 };
63}
64
65/// Create a dataframe.
66///
67/// Example:
68/// ```
69/// use extendr_api::prelude::*;
70/// test! {
71/// let mydata = data_frame!(x=1, y=2);
72/// assert_eq!(mydata.inherits("data.frame"), true);
73/// //assert_eq!(mydata, r!(List::from_pairs(vec![("x", r!(1)), ("y", r!(2))])).set_class(&["data.frame"])?);
74/// }
75/// ```
76///
77/// Panics on error.
78#[macro_export]
79macro_rules! data_frame {
80 () => {
81 call!("data.frame").unwrap()
82 };
83 ($($rest: tt)*) => {
84 call!("data.frame", $($rest)*).unwrap()
85 };
86}
87
88/// Create a factor.
89///
90/// Example:
91/// ```
92/// use extendr_api::prelude::*;
93/// test! {
94/// let factor = factor!(vec!["abcd", "def", "fg", "fg"]);
95/// assert_eq!(factor.levels().unwrap().collect::<Vec<_>>(), vec!["abcd", "def", "fg"]);
96/// assert_eq!(factor.as_integer_vector().unwrap(), vec![1, 2, 3, 3]);
97/// assert_eq!(factor.as_str_iter().unwrap().collect::<Vec<_>>(), vec!["abcd", "def", "fg", "fg"]);
98/// }
99/// ```
100///
101/// Panics on error.
102#[macro_export]
103macro_rules! factor {
104 ($($rest: tt)*) => {
105 call!("factor", $($rest)*).unwrap()
106 };
107}
108
109/// Print via the R output stream.
110///
111/// Works like [`print!`] but integrates with R and respects
112/// redirection with functions like `sink()` and `capture.output()`
113#[macro_export]
114macro_rules! rprint {
115 () => {
116 };
117 ($($rest: tt)*) => {
118 print_r_output(format!($($rest)*));
119 };
120}
121
122/// Print with a newline via the R output stream.
123///
124/// Works like [`println!`] but integrates with R and respects
125/// redirection with functions like `sink()` and `capture.output()`
126#[macro_export]
127macro_rules! rprintln {
128 () => {
129 print_r_output("\n");
130 };
131 ($($rest: tt)*) => {
132 print_r_output(format!($($rest)*));
133 print_r_output("\n");
134 };
135}
136
137/// Print via the R error stream.
138#[macro_export]
139macro_rules! reprint {
140 () => {
141 };
142 ($($rest: tt)*) => {
143 print_r_error(format!($($rest)*));
144 };
145}
146
147/// Print with a newline via the R output stream.
148#[macro_export]
149macro_rules! reprintln {
150 () => {
151 print_r_error("\n");
152 };
153 ($($rest: tt)*) => {
154 print_r_error(format!($($rest)*));
155 print_r_error("\n");
156 };
157}
158
159/// Macro for running tests.
160///
161/// This starts up the underlying [`extendr_engine`] so that interactions with R will work.
162/// Additionally, this allows us to use `?` in example code instead of `unwrap()`.
163///
164/// **Note:** This macro is meant to be used in test code (annotated with
165/// `#[cfg(test)]`) or in doc strings. If it is used in library code that
166/// gets incorporated into an R package, R CMD check will complain about
167/// non-API calls.
168///
169/// [`extendr_engine`]: https://extendr.github.io/extendr/extendr_engine/
170#[macro_export]
171macro_rules! test {
172 () => {
173 test(|| Ok(()))
174 };
175 ($($rest: tt)*) => {
176 {
177 use extendr_engine;
178
179 // this helper function must reside in the macro so it doesn't get compiled
180 // unless the macro actually gets used (e.g., in testing code)
181 fn test<F: FnOnce() -> extendr_api::Result<()>>(f: F) {
182 extendr_engine::start_r();
183 f().unwrap();
184 }
185
186 test(|| {
187 $($rest)*
188 Ok(())
189 })
190 }
191 };
192}