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}