aorist_extendr_api/lib.rs
1//!
2//! A safe and user friendly R extension interface.
3//!
4//! * Build rust extensions to R.
5//! * Convert R packages to Rust crates.
6//!
7//! This library aims to provide an interface that will be familiar to
8//! first-time users of Rust or indeed any compiled language.
9//!
10//! See [Robj] for much of the content of this crate.
11//! [Robj] provides a safe wrapper for the R object type.
12//!
13//! Use attributes and macros to export to R.
14//! ```ignore
15//! use extendr_api::prelude::*;
16//! // Export a function or impl to R.
17//! #[extendr]
18//! fn fred(a: i32) -> i32 {
19//! a + 1
20//! }
21//!
22//! // define exports using extendr_module
23//! extendr_module! {
24//! mod mymodule;
25//! fn fred;
26//! }
27//!
28//! ```
29//!
30//! In R:
31//!
32//! ```ignore
33//! result <- fred(1)
34//! ```
35//!
36//! [Robj] is a wrapper for R objects.
37//! The r!() and R!() macros let you build R objects
38//! using Rust and R syntax respectively.
39//! ```
40//! use extendr_api::prelude::*;
41//! test! {
42//! // An R object with a single string "hello"
43//! let character = r!("hello");
44//! let character = r!(["hello", "goodbye"]);
45//!
46//! // An R integer object with a single number 1L.
47//! // Note that in Rust, 1 is an integer and 1.0 is a real.
48//! let integer = r!(1);
49//!
50//! // An R real object with a single number 1.
51//! // Note that in R, 1 is a real and 1L is an integer.
52//! let real = r!(1.0);
53//!
54//! // An R real vector.
55//! let real_vector = r!([1.0, 2.0]);
56//! let real_vector = &[1.0, 2.0].iter().collect_robj();
57//! let real_vector = r!(vec![1.0, 2.0]);
58//!
59//! // An R function object.
60//! let function = R!("function(x, y) { x + y }")?;
61//!
62//! // A named list using the list! macro.
63//! let list = list!(a = 1, b = 2);
64//!
65//! // An unnamed list (of R objects) using the List wrapper.
66//! let list = r!(List::from_values(vec![1, 2, 3]));
67//! let list = r!(List::from_values(vec!["a", "b", "c"]));
68//! let list = r!(List::from_values(&[r!("a"), r!(1), r!(2.0)]));
69//!
70//! // A symbol
71//! let sym = sym!(wombat);
72//!
73//! // A R vector using collect_robj()
74//! let vector = (0..3).map(|x| x * 10).collect_robj();
75//! }
76//! ```
77//!
78//! In Rust, we prefer to use iterators rather than loops.
79//!
80//! ```
81//! use extendr_api::prelude::*;
82//! test! {
83//! // 1 ..= 100 is the same as 1:100
84//! let res = r!(1 ..= 100);
85//! assert_eq!(res, R!("1:100")?);
86//!
87//! // Rust arrays are zero-indexed so it is more common to use 0 .. 100.
88//! let res = r!(0 .. 100);
89//! assert_eq!(res.len(), 100);
90//!
91//! // Using map is a super fast way to generate vectors.
92//! let iter = (0..3).map(|i| format!("fred{}", i));
93//! let character = iter.collect_robj();
94//! assert_eq!(character, r!(["fred0", "fred1", "fred2"]));
95//! }
96//! ```
97//!
98//! To index a vector, first convert it to a slice and then
99//! remember to use 0-based indexing. In Rust, going out of bounds
100//! will cause and error (a panic) unlike C++ which may crash.
101//! ```
102//! use extendr_api::prelude::*;
103//! test! {
104//! let vals = r!([1.0, 2.0]);
105//! let slice = vals.as_real_slice().ok_or("expected slice")?;
106//! let one = slice[0];
107//! let two = slice[1];
108//! // let error = slice[2];
109//! assert_eq!(one, 1.0);
110//! assert_eq!(two, 2.0);
111//! }
112//! ```
113//!
114//! Much slower, but more general are these methods:
115//! ```
116//! use extendr_api::prelude::*;
117//! test! {
118//! let vals = r!([1.0, 2.0, 3.0]);
119//!
120//! // one-based indexing [[i]], returns an object.
121//! assert_eq!(vals.index(1)?, r!(1.0));
122//!
123//! // one-based slicing [x], returns an object.
124//! assert_eq!(vals.slice(1..=2)?, r!([1.0, 2.0]));
125//!
126//! // $ operator, returns an object
127//! let list = list!(a = 1.0, b = "xyz");
128//! assert_eq!(list.dollar("a")?, r!(1.0));
129//! }
130//! ```
131//!
132//! The [R!] macro lets you embed R code in Rust
133//! and takes Rust expressions in {{ }} pairs.
134//!
135//! The [Rraw!] macro will not expand the {{ }} pairs.
136//! ```
137//! use extendr_api::prelude::*;
138//! test! {
139//! // The text "1 + 1" is parsed as R source code.
140//! // The result is 1.0 + 1.0 in Rust.
141//! assert_eq!(R!("1 + 1")?, r!(2.0));
142//!
143//! let a = 1.0;
144//! assert_eq!(R!("1 + {{a}}")?, r!(2.0));
145//!
146//! assert_eq!(R!(r"
147//! x <- {{ a }}
148//! x + 1
149//! ")?, r!(2.0));
150//!
151//! assert_eq!(R!(r#"
152//! x <- "hello"
153//! x
154//! "#)?, r!("hello"));
155//!
156//! // Use the R meaning of {{ }} and do not expand.
157//! assert_eq!(Rraw!(r"
158//! x <- {{ 1 }}
159//! x + 1
160//! ")?, r!(2.0));
161//! }
162//! ```
163//!
164//! The [r!] macro converts a rust object to an R object
165//! and takes parameters.
166//! ```
167//! use extendr_api::prelude::*;
168//! test! {
169//! // The text "1.0+1.0" is parsed as Rust source code.
170//! let one = 1.0;
171//! assert_eq!(r!(one+1.0), r!(2.0));
172//! }
173//! ```
174//!
175//! You can call R functions and primitives using the [call!] macro.
176//! ```
177//! use extendr_api::prelude::*;
178//! test! {
179//!
180//! // As one R! macro call
181//! let confint1 = R!(confint(lm(weight ~ group - 1, PlantGrowth)))?;
182//!
183//! // As many parameterized calls.
184//! let formula = call!("~", sym!(weight), lang!("-", sym!(group), 1))?;
185//! let plant_growth = global!(PlantGrowth)?;
186//! let model = call!("lm", formula, plant_growth)?;
187//! let confint2 = call!("confint", model)?;
188//!
189//! assert_eq!(confint1.as_real_vector(), confint2.as_real_vector());
190//! }
191//! ```
192//!
193//! Rust has a concept of "Owned" and "Borrowed" objects.
194//!
195//! Owned objects, such as [Vec] and [String] allocate memory
196//! which is released when the object lifetime ends.
197//!
198//! Borrowed objects such as &[i32] and &str are just pointers
199//! to annother object's memory and can't live longer than the
200//! object they reference.
201//!
202//! Borrowed objects are much faster than owned objects and use less
203//! memory but are used only for temporary access.
204//!
205//! When we take a slice of an R vector, for example, we need the
206//! original R object to be alive or the data will be corrupted.
207//!
208//! ```
209//! use extendr_api::prelude::*;
210//! test! {
211//! // robj is an "Owned" object that controls the memory allocated.
212//! let robj = r!([1, 2, 3]);
213//!
214//! // Here slice is a "borrowed" reference to the bytes in robj.
215//! // and cannot live longer than robj.
216//! let slice = robj.as_integer_slice().ok_or("expected slice")?;
217//! assert_eq!(slice.len(), 3);
218//! }
219//! ```
220
221#![doc(
222 html_logo_url = "https://raw.githubusercontent.com/extendr/extendr/master/extendr-logo-256.png"
223)]
224
225pub mod error;
226pub mod functions;
227pub mod iter;
228pub mod lang_macros;
229pub mod logical;
230pub mod metadata;
231pub mod ownership;
232pub mod prelude;
233pub mod rmacros;
234
235pub mod robj;
236pub mod thread_safety;
237pub mod wrapper;
238
239#[cfg(feature = "ndarray")]
240pub mod robj_ndarray;
241
242pub use std::convert::{TryFrom, TryInto};
243pub use std::ops::Deref;
244
245//////////////////////////////////////////////////
246// Note these pub use statements are deprectaed
247//
248// `use extendr_api::prelude::*;`
249//
250// instead.
251
252pub use error::*;
253pub use functions::*;
254pub use lang_macros::*;
255pub use logical::*;
256pub use rmacros::*;
257pub use robj::*;
258pub use thread_safety::{
259 catch_r_error, handle_panic, single_threaded, this_thread_id, throw_r_error,
260};
261pub use wrapper::*;
262
263#[cfg(feature = "ndarray")]
264pub use robj_ndarray::*;
265
266pub use aorist_extendr_macros::*;
267//////////////////////////////////////////////////
268
269/// TRUE value eg. `r!(TRUE)`
270pub const TRUE: Bool = Bool(1);
271
272/// FALSE value eg. `r!(FALSE)`
273pub const FALSE: Bool = Bool(0);
274
275/// NULL value eg. `r!(NULL)`
276pub const NULL: () = ();
277
278/// NA value for integers eg. `r!(NA_INTEGER)`
279pub const NA_INTEGER: Option<i32> = None;
280
281/// NA value for real values eg. `r!(NA_REAL)`
282pub const NA_REAL: Option<f64> = None;
283
284/// NA value for strings. `r!(NA_STRING)`
285pub const NA_STRING: Option<&str> = None;
286
287/// NA value for logical. `r!(NA_LOGICAL)`
288pub const NA_LOGICAL: Bool = Bool(std::i32::MIN);
289
290#[doc(hidden)]
291pub use std::collections::HashMap;
292
293#[doc(hidden)]
294pub use libR_sys::DllInfo;
295
296#[doc(hidden)]
297pub use libR_sys::SEXP;
298
299#[doc(hidden)]
300use libR_sys::*;
301
302#[doc(hidden)]
303use std::ffi::CString;
304
305pub use metadata::Metadata;
306
307#[doc(hidden)]
308pub struct CallMethod {
309 pub call_symbol: std::ffi::CString,
310 pub func_ptr: *const u8,
311 pub num_args: i32,
312}
313
314unsafe fn make_method_def(
315 cstrings: &mut Vec<std::ffi::CString>,
316 rmethods: &mut Vec<libR_sys::R_CallMethodDef>,
317 func: &metadata::Func,
318 wrapped_name: &str,
319) {
320 cstrings.push(std::ffi::CString::new(wrapped_name).unwrap());
321 rmethods.push(libR_sys::R_CallMethodDef {
322 name: cstrings.last().unwrap().as_ptr(),
323 fun: Some(std::mem::transmute(func.func_ptr)),
324 numArgs: func.args.len() as i32,
325 });
326}
327
328// Internal function used to implement the .Call interface.
329// This is called from the code generated by the #[extendr] attribute.
330#[doc(hidden)]
331pub unsafe fn register_call_methods(info: *mut libR_sys::DllInfo, metadata: Metadata) {
332 let mut rmethods = Vec::new();
333 let mut cstrings = Vec::new();
334 for func in metadata.functions {
335 let wrapped_name = format!("wrap__{}", func.name);
336 make_method_def(&mut cstrings, &mut rmethods, &func, wrapped_name.as_str());
337 }
338
339 for imp in metadata.impls {
340 for func in imp.methods {
341 let wrapped_name = format!("wrap__{}__{}", imp.name, func.name);
342 make_method_def(&mut cstrings, &mut rmethods, &func, wrapped_name.as_str());
343 }
344 }
345
346 rmethods.push(libR_sys::R_CallMethodDef {
347 name: std::ptr::null(),
348 fun: None,
349 numArgs: 0,
350 });
351
352 libR_sys::R_registerRoutines(
353 info,
354 std::ptr::null(),
355 rmethods.as_ptr(),
356 std::ptr::null(),
357 std::ptr::null(),
358 );
359
360 // This seems to allow both symbols and strings,
361 libR_sys::R_useDynamicSymbols(info, 0);
362 libR_sys::R_forceSymbols(info, 0);
363}
364
365/// Return true if this primitive is NA.
366pub trait IsNA {
367 fn is_na(&self) -> bool;
368}
369
370impl IsNA for f64 {
371 fn is_na(&self) -> bool {
372 unsafe { R_IsNA(*self) != 0 }
373 }
374}
375
376impl IsNA for i32 {
377 fn is_na(&self) -> bool {
378 *self == std::i32::MIN
379 }
380}
381
382impl IsNA for Bool {
383 fn is_na(&self) -> bool {
384 self.0 == std::i32::MIN
385 }
386}
387
388impl IsNA for &str {
389 /// Check for NA in a string by address.
390 fn is_na(&self) -> bool {
391 self.as_ptr() == na_str().as_ptr()
392 }
393}
394
395/// Type of R objects used by [Robj::rtype].
396#[derive(Debug, PartialEq)]
397pub enum RType {
398 Null, // NILSXP
399 Symbol, // SYMSXP
400 Pairlist, // LISTSXP
401 Function, // CLOSXP
402 Environment, // ENVSXP
403 Promise, // PROMSXP
404 Language, // LANGSXP
405 Special, // SPECIALSXP
406 Builtin, // BUILTINSXP
407 Character, // CHARSXP
408 Logical, // LGLSXP
409 Integer, // INTSXP
410 Real, // REALSXP
411 Complex, // CPLXSXP
412 String, // STRSXP
413 Dot, // DOTSXP
414 Any, // ANYSXP
415 List, // VECSXP
416 Expression, // EXPRSXP
417 Bytecode, // BCODESXP
418 ExternalPtr, // EXTPTRSXP
419 WeakRef, // WEAKREFSXP
420 Raw, // RAWSXP
421 S4, // S4SXP
422 Unknown,
423}
424
425#[doc(hidden)]
426pub fn print_r_output<T: Into<Vec<u8>>>(s: T) {
427 let cs = CString::new(s).expect("NulError");
428 unsafe {
429 Rprintf(cs.as_ptr());
430 }
431}
432
433#[doc(hidden)]
434pub fn print_r_error<T: Into<Vec<u8>>>(s: T) {
435 let cs = CString::new(s).expect("NulError");
436 unsafe {
437 REprintf(cs.as_ptr());
438 }
439}
440
441#[cfg(test)]
442mod tests {
443 use super::prelude::*;
444 use crate as extendr_api;
445 use std::collections::HashMap;
446
447 use extendr_macros::extendr;
448 use extendr_macros::extendr_module;
449 use extendr_macros::pairlist;
450
451 #[extendr]
452 pub fn inttypes(a: i8, b: u8, c: i16, d: u16, e: i32, f: u32, g: i64, h: u64) {
453 assert_eq!(a, 1);
454 assert_eq!(b, 2);
455 assert_eq!(c, 3);
456 assert_eq!(d, 4);
457 assert_eq!(e, 5);
458 assert_eq!(f, 6);
459 assert_eq!(g, 7);
460 assert_eq!(h, 8);
461 }
462
463 #[extendr]
464 pub fn floattypes(a: f32, b: f64) {
465 assert_eq!(a, 1.);
466 assert_eq!(b, 2.);
467 }
468
469 #[extendr]
470 pub fn strtypes(a: &str, b: String) {
471 assert_eq!(a, "abc");
472 assert_eq!(b, "def");
473 }
474
475 #[extendr]
476 pub fn vectortypes(a: Vec<i32>, b: Vec<f64>) {
477 assert_eq!(a, [1, 2, 3]);
478 assert_eq!(b, [4., 5., 6.]);
479 }
480
481 #[extendr]
482 pub fn robjtype(a: Robj) {
483 assert_eq!(a, Robj::from(1))
484 }
485
486 #[extendr]
487 pub fn return_u8() -> u8 {
488 123
489 }
490
491 #[extendr]
492 pub fn return_u16() -> u16 {
493 123
494 }
495
496 #[extendr]
497 pub fn return_u32() -> u32 {
498 123
499 }
500
501 #[extendr]
502 pub fn return_u64() -> u64 {
503 123
504 }
505
506 #[extendr]
507 pub fn return_i8() -> i8 {
508 123
509 }
510
511 #[extendr]
512 pub fn return_i16() -> i16 {
513 123
514 }
515
516 #[extendr]
517 pub fn return_i32() -> i32 {
518 123
519 }
520
521 #[extendr]
522 pub fn return_i64() -> i64 {
523 123
524 }
525
526 #[extendr]
527 pub fn return_f32() -> f32 {
528 123.
529 }
530
531 #[extendr]
532 pub fn return_f64() -> f64 {
533 123.
534 }
535
536 #[extendr]
537 pub fn f64_slice(x: &[f64]) -> &[f64] {
538 x
539 }
540
541 #[extendr]
542 pub fn i32_slice(x: &[i32]) -> &[i32] {
543 x
544 }
545
546 #[extendr]
547 pub fn bool_slice(x: &[Bool]) -> &[Bool] {
548 x
549 }
550
551 #[extendr]
552 pub fn f64_iter(x: Real) -> Real {
553 x
554 }
555
556 #[extendr]
557 pub fn i32_iter(x: Int) -> Int {
558 x
559 }
560
561 #[extendr]
562 pub fn bool_iter(x: Logical) -> Logical {
563 x
564 }
565
566 #[extendr]
567 pub fn symbol(x: Symbol) -> Symbol {
568 x
569 }
570
571 #[extendr]
572 pub fn matrix(x: RMatrix<f64>) -> RMatrix<f64> {
573 x
574 }
575
576 #[extendr]
577 pub fn hash_map(x: HashMap<&str, Robj>) -> HashMap<&str, Robj> {
578 x
579 }
580
581 struct Person {
582 pub name: String,
583 }
584
585 #[extendr]
586 /// impl comment.
587 impl Person {
588 fn new() -> Self {
589 Self {
590 name: "".to_string(),
591 }
592 }
593
594 fn set_name(&mut self, name: &str) {
595 self.name = name.to_string();
596 }
597
598 fn name(&self) -> &str {
599 self.name.as_str()
600 }
601 }
602
603 // see metadata_test for the following comments.
604
605 /// comment #1
606 /// comment #2
607 /**
608 comment #3
609 comment #4
610 **/
611 #[extendr]
612 /// aux_func doc comment.
613 fn aux_func(_person: &Person) {}
614
615 // Macro to generate exports
616 extendr_module! {
617 mod my_module;
618 fn aux_func;
619 impl Person;
620 }
621
622 #[test]
623 fn export_test() {
624 test! {
625 use super::*;
626 // Call the exported functions through their generated C wrappers.
627 unsafe {
628 wrap__inttypes(
629 Robj::from(1).get(),
630 Robj::from(2).get(),
631 Robj::from(3).get(),
632 Robj::from(4).get(),
633 Robj::from(5).get(),
634 Robj::from(6).get(),
635 Robj::from(7).get(),
636 Robj::from(8).get(),
637 );
638 wrap__inttypes(
639 Robj::from(1.).get(),
640 Robj::from(2.).get(),
641 Robj::from(3.).get(),
642 Robj::from(4.).get(),
643 Robj::from(5.).get(),
644 Robj::from(6.).get(),
645 Robj::from(7.).get(),
646 Robj::from(8.).get(),
647 );
648 wrap__floattypes(Robj::from(1.).get(), Robj::from(2.).get());
649 wrap__floattypes(Robj::from(1).get(), Robj::from(2).get());
650 wrap__strtypes(Robj::from("abc").get(), Robj::from("def").get());
651 wrap__vectortypes(
652 Robj::from(&[1, 2, 3] as &[i32]).get(),
653 Robj::from(&[4., 5., 6.] as &[f64]).get(),
654 );
655 wrap__robjtype(Robj::from(1).get());
656
657 // General integer types.
658 assert_eq!(new_owned(wrap__return_u8()), Robj::from(123));
659 assert_eq!(new_owned(wrap__return_u16()), Robj::from(123));
660 assert_eq!(new_owned(wrap__return_u32()), Robj::from(123));
661 assert_eq!(new_owned(wrap__return_u64()), Robj::from(123));
662 assert_eq!(new_owned(wrap__return_i8()), Robj::from(123));
663 assert_eq!(new_owned(wrap__return_i16()), Robj::from(123));
664 assert_eq!(new_owned(wrap__return_i32()), Robj::from(123));
665 assert_eq!(new_owned(wrap__return_i64()), Robj::from(123));
666
667 // Floating point types.
668 assert_eq!(new_owned(wrap__return_f32()), Robj::from(123.));
669 assert_eq!(new_owned(wrap__return_f64()), Robj::from(123.));
670 }
671 }
672 }
673
674 #[test]
675 fn class_wrapper_test() {
676 test! {
677 let mut person = Person::new();
678 person.set_name("fred");
679 let robj = r!(person);
680 assert_eq!(robj.check_external_ptr("Person"), true);
681 let person2 = <&Person>::from_robj(&robj).unwrap();
682 assert_eq!(person2.name(), "fred");
683 }
684 }
685
686 #[test]
687 fn slice_test() {
688 test! {
689 unsafe {
690 // #[extendr]
691 // pub fn f64_slice(x: &[f64]) -> &[f64] { x }
692
693 let robj = r!([1., 2., 3.]);
694 assert_eq!(new_owned(wrap__f64_slice(robj.get())), robj);
695
696 // #[extendr]
697 // pub fn i32_slice(x: &[i32]) -> &[i32] { x }
698
699 let robj = r!([1, 2, 3]);
700 assert_eq!(new_owned(wrap__i32_slice(robj.get())), robj);
701
702 // #[extendr]
703 // pub fn bool_slice(x: &[Bool]) -> &[Bool] { x }
704
705 let robj = r!([TRUE, FALSE, TRUE]);
706 assert_eq!(new_owned(wrap__bool_slice(robj.get())), robj);
707
708 // #[extendr]
709 // pub fn f64_iter(x: Real) -> Real { x }
710
711 let robj = r!([1., 2., 3.]);
712 assert_eq!(new_owned(wrap__f64_iter(robj.get())), robj);
713
714 // #[extendr]
715 // pub fn i32_iter(x: Int) -> Int { x }
716
717 let robj = r!([1, 2, 3]);
718 assert_eq!(new_owned(wrap__i32_iter(robj.get())), robj);
719
720 // #[extendr]
721 // pub fn bool_iter(x: Logical) -> Logical { x }
722
723 let robj = r!([TRUE, FALSE, TRUE]);
724 assert_eq!(new_owned(wrap__bool_iter(robj.get())), robj);
725
726 // #[extendr]
727 // pub fn symbol(x: Symbol) -> Symbol { x }
728
729 let robj = sym!(fred);
730 assert_eq!(new_owned(wrap__symbol(robj.get())), robj);
731
732 // #[extendr]
733 // pub fn matrix(x: Matrix<&[f64]>) -> Matrix<&[f64]> { x }
734
735 let m = RMatrix::new_matrix(1, 2, |r, c| if r == c {1.0} else {0.});
736 let robj = r!(m);
737 assert_eq!(new_owned(wrap__matrix(robj.get())), robj);
738
739 // #[extendr]
740 // pub fn hash_map(x: HashMap<&str, Robj>) -> HashMap<&str, Robj> { x }
741 let robj = r!(List::from_values(&[1, 2]));
742 robj.set_attrib(names_symbol(), r!(["a", "b"]))?;
743 let res = new_owned(wrap__hash_map(robj.get()));
744 assert_eq!(res.len(), 2);
745 }
746 }
747 }
748
749 #[test]
750 fn r_output_test() {
751 // R equivalent
752 // > txt_con <- textConnection("test_con", open = "w")
753 // > sink(txt_con)
754 // > cat("Hello world")
755 // > sink()
756 // > close(txt_con)
757 // > expect_equal(test_con, "Hello world")
758 //
759
760 test! {
761 let txt_con = R!(textConnection("test_con", open = "w")).unwrap();
762 call!("sink", &txt_con).unwrap();
763 rprintln!("Hello world");
764 call!("sink").unwrap();
765 call!("close", &txt_con).unwrap();
766 let result = R!(test_con).unwrap();
767 assert_eq!(result, r!("Hello world"));
768 }
769 }
770
771 #[test]
772 fn test_na_str() {
773 assert!(na_str().as_ptr() != "NA".as_ptr());
774 assert_eq!(na_str(), "NA");
775 assert_eq!("NA".is_na(), false);
776 assert_eq!(na_str().is_na(), true);
777 }
778
779 #[test]
780 fn metadata_test() {
781 test! {
782 // Rust interface.
783 let metadata = get_my_module_metadata();
784 assert_eq!(metadata.functions[0].doc, " comment #1\n comment #2\n\n comment #3\n comment #4\n *\n aux_func doc comment.");
785 assert_eq!(metadata.functions[0].name, "aux_func");
786 assert_eq!(metadata.functions[0].args[0].name, "_person");
787 assert_eq!(metadata.functions[1].name, "get_my_module_metadata");
788 assert_eq!(metadata.impls[0].name, "Person");
789 assert_eq!(metadata.impls[0].methods.len(), 3);
790
791 // R interface
792 let robj = unsafe { new_owned(wrap__get_my_module_metadata()) };
793 let functions = robj.dollar("functions").unwrap();
794 let impls = robj.dollar("impls").unwrap();
795 assert_eq!(functions.len(), 3);
796 assert_eq!(impls.len(), 1);
797 }
798 }
799
800 #[test]
801 fn pairlist_macro_works() {
802 test! {
803 assert_eq!(pairlist!(1, 2, 3), Pairlist::from_pairs(&[("", 1), ("", 2), ("", 3)]));
804 assert_eq!(pairlist!(a=1, 2, 3), Pairlist::from_pairs(&[("a", 1), ("", 2), ("", 3)]));
805 assert_eq!(pairlist!(1, b=2, 3), Pairlist::from_pairs(&[("", 1), ("b", 2), ("", 3)]));
806 assert_eq!(pairlist!(a=1, b=2, c=3), Pairlist::from_pairs(&[("a", 1), ("b", 2), ("c", 3)]));
807 assert_eq!(pairlist!(a=NULL), Pairlist::from_pairs(&[("a", ())]));
808 assert_eq!(pairlist!(), Pairlist::from(()));
809 }
810 }
811
812 #[test]
813 fn big_r_macro_works() {
814 test! {
815 assert_eq!(R!("1")?, r!(1.0));
816 assert_eq!(R!(r"1")?, r!(1.0));
817 assert_eq!(R!(r"
818 x <- 1
819 x
820 ")?, r!(1.0));
821 assert_eq!(R!(r"
822 x <- {{ 1.0 }}
823 x
824 ")?, r!(1.0));
825 assert_eq!(R!(r"
826 x <- {{ (0..4).collect_robj() }}
827 x
828 ")?, r!([0, 1, 2, 3]));
829 assert_eq!(R!(r#"
830 x <- "hello"
831 x
832 "#)?, r!("hello"));
833 assert_eq!(Rraw!(r"
834 x <- {{ 1 }}
835 x
836 ")?, r!(1.0));
837 }
838 }
839}