Skip to main content

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