miniextendr_api/lib.rs
1//! miniextendr-api: core runtime for Rust <-> R interop.
2//!
3//! This crate provides the FFI surface, safety wrappers, and macro re-exports
4//! used by most miniextendr users. It is the primary dependency for building
5//! Rust-powered R packages and exposing Rust types to R.
6//!
7//! At a glance:
8//! - FFI bindings + checked wrappers for R's C API (`sys`, `r_ffi_checked`).
9//! - Conversions between Rust and R types (`IntoR`, `TryFromSexp`, `Coerce`).
10//! - ALTREP traits, registration helpers, and iterator-backed ALTREP data types.
11//! - Wrapper generation from Rust signatures (`#[miniextendr]`, automatic registration via linkme).
12//! - Main-thread unwind protection plus optional worker dispatch (`worker`).
13//! - Class system support (S3, S4, S7, R6, env-style impl blocks).
14//! - Cross-package trait ABI for type-erased dispatch (`trait_abi`).
15//!
16//! Most users should depend on this crate directly. For embedding R in
17//! standalone binaries or integration tests, see `miniextendr-engine`.
18//!
19//! ## Quick start
20//!
21//! ```ignore
22//! use miniextendr_api::miniextendr;
23//!
24//! #[miniextendr]
25//! fn add(a: i32, b: i32) -> i32 {
26//! a + b
27//! }
28//! ```
29//!
30//! That's it — `#[miniextendr]` handles everything. Items self-register
31//! at link time; `miniextendr_init!` generates the `R_init_*` function
32//! that calls `package_init()` to register all routines with R.
33//! Wrapper R code is produced from Rust doc comments (roxygen tags are
34//! extracted) by loading the freshly linked package library and calling the
35//! registry writer. `R/miniextendr-wrappers.R` is regenerated during
36//! development installs, kept out of git, and shipped from disk in release
37//! tarballs; roxygen2 derives the tracked `NAMESPACE` and `man/*.Rd` files.
38//!
39//! ## Choosing the right API
40//!
41//! miniextendr has several places where two or more APIs reach the same
42//! goal with different tradeoffs — a stricter / safer / more validated
43//! option, and a looser / easier / less protective one. The most common
44//! pairs are:
45//!
46//! | I'm reaching for... | Consider also | Why |
47//! |---|---|---|
48//! | default `IntoR` for `i64` / `u64` / `isize` / `usize` (silently widens to `REALSXP` on overflow) | `#[miniextendr(strict)]` → [`crate::strict`] helpers (panic on overflow) | strict catches the truncation bugs caused by R having no native 64-bit integer type |
49//! | [`Coerce`] (infallible widening) | [`TryCoerce`] (fallible) | the source range can exceed the target type |
50//! | `Rf_*_unchecked` FFI | checked variants | unchecked is only safe inside ALTREP callbacks, `with_r_unwind_protect`, or `with_r_thread` — MXL301 lint enforces |
51//! | `panic!(msg)` | `miniextendr_api::error!("msg", class = "...")` | typed conditions let R-side `tryCatch` handlers route by class |
52//! | raw `_dots: &Dots` | `#[miniextendr(dots = typed_list!(...))]` | validation moves from runtime to macro call site |
53//! | `#[derive(AltrepInteger)]` field-based | `#[altrep(manual)]` + handwritten traits | when custom storage or computed-on-access can't fit the derive |
54//! | hand-rolled [`TryFromSexp`] + [`IntoR`] | `#[derive(RSerializeNative)]` (serde feature) | serde is ergonomic for nested structs; hand-rolled is zero-overhead and fully controlled |
55//!
56//! Project-wide defaults are controlled by mutually-exclusive cargo
57//! features — see the "Project-wide Defaults" feature table below.
58//!
59//! ### Default opinion
60//!
61//! When in doubt, pick the **stricter** path. The framework's default
62//! stance is "fail loudly, leave a trail." The looser variants exist for
63//! cases where the cost is measured or the looser semantics are correct
64//! for your data — they are not the default.
65//!
66//! ## GC protection and ownership
67//!
68//! R's garbage collector can reclaim any SEXP that isn't protected. miniextendr
69//! provides three complementary protection mechanisms:
70//!
71//! | Strategy | Module | Lifetime | Release Order | Use Case |
72//! |----------|--------|----------|---------------|----------|
73//! | **PROTECT stack** | [`gc_protect`] | Within `.Call` | LIFO (stack) | Temporary allocations |
74//! | **VECSXP pool** | [`protect_pool`] | Across `.Call`s | Any order | Long-lived R objects |
75//! | **R ownership** | [`ExternalPtr`](struct@ExternalPtr) | Until R GCs | R decides | Rust data owned by R |
76//!
77//! Quick guide:
78//!
79//! **Temporary allocations during computation** -> [`ProtectScope`]
80//! ```ignore
81//! unsafe fn compute(x: SEXP) -> SEXP {
82//! let scope = ProtectScope::new();
83//! let temp = scope.protect(Rf_allocVector(REALSXP, 100));
84//! // ... work with temp ...
85//! result.into_raw()
86//! } // UNPROTECT(n) called automatically
87//! ```
88//!
89//! **R objects surviving across `.Call`s** -> [`ProtectPool`] or `R_PreserveObject`
90//! ```ignore
91//! // ProtectPool: O(1) insert/release with generational keys
92//! let mut pool = unsafe { ProtectPool::new(16) };
93//! let key = unsafe { pool.insert(backing_vec) };
94//! // ... use across multiple .Calls ...
95//! unsafe { pool.release(key) };
96//! ```
97//!
98//! **Rust data owned by R** -> [`ExternalPtr`](struct@ExternalPtr)
99//! ```ignore
100//! #[miniextendr]
101//! fn create_model() -> ExternalPtr<MyModel> {
102//! ExternalPtr::new(MyModel::new())
103//! } // R owns it; Drop runs when R GCs
104//! ```
105//!
106//! Note: ALTREP trait methods receive raw SEXP pointers from R's runtime.
107//! These are safe to dereference because R guarantees valid SEXPs in ALTREP callbacks.
108//!
109//! ## Threading and safety
110//!
111//! R uses `longjmp` for errors, which can bypass Rust destructors. Generated
112//! wrappers run inline on R's main thread inside `R_UnwindProtect` by default.
113//! Opt-in `#[miniextendr(worker)]` functions (or crates using
114//! `worker-default`) run Rust logic on the worker and marshal R API calls back
115//! through `with_r_thread`. Most FFI wrappers are main-thread routed via
116//! `#[r_ffi_checked]`. Use unchecked variants only when you have arranged a
117//! safe context.
118//!
119//! The `nonapi` feature exposes legacy controls for R's process-global stack
120//! bounds. Disabling that check does **not** make R's API safe off the main
121//! thread; package code must keep R API work on the recorded main thread.
122//!
123//! ## Feature Flags
124//!
125//! ### Core Features
126//!
127//! | Feature | Description |
128//! |---------|-------------|
129//! | `nonapi` | Non-API R symbols (stack controls, mutable `DATAPTR`). May break with R updates. |
130//! | `rayon` | Parallel iterators via Rayon. Adds `RParallelIterator`, `RParallelExtend`. |
131//! | `connections` | Experimental R connection framework. **Unstable R API.** |
132//! | `indicatif` | Progress bars routed through R connections. Requires `nonapi` + `connections`. |
133//! | `vctrs` | vctrs class construction (`new_vctr`, `new_rcrd`, `new_list_of`) and `#[derive(Vctrs)]`. |
134//! | `worker-thread` | Worker dispatch infrastructure. It does not change proc-macro defaults by itself; without it, worker APIs are inline stubs. |
135//!
136//! ### Type Conversions (Scalars & Vectors)
137//!
138//! | Feature | Rust Type | R Type | Notes |
139//! |---------|-----------|--------|-------|
140//! | `either` | `Either<L, R>` | Tries L then R | Union-like dispatch |
141//! | `uuid` | `Uuid`, `Vec<Uuid>` | `character` | UUID ↔ string |
142//! | `regex` | `Regex` | `character(1)` | Compiles pattern from R |
143//! | `url` | `Url`, `Vec<Url>` | `character` | Validated URLs |
144//! | `time` | `OffsetDateTime`, `Date` | `POSIXct`, `Date` | Date/time conversions |
145//! | `jiff` | `Timestamp`, `Zoned`, civil types | R date/time classes | IANA timezone-aware conversions |
146//! | `ordered-float` | `OrderedFloat<f64>` | `numeric` | NaN-orderable floats |
147//! | `num-bigint` | `BigInt`, `BigUint` | `character` | Arbitrary precision via strings |
148//! | `rust_decimal` | `Decimal` | `character` | Fixed-point decimals |
149//! | `num-complex` | `Complex<f64>` | `complex` | Native R complex support |
150//! | `indexmap` | `IndexMap<String, T>` | named `list` | Preserves insertion order |
151//! | `bitflags` | `RFlags<T>` | `integer` | Bitflags ↔ integer |
152//! | `bitvec` | `RBitVec` | `logical` | Bit vectors ↔ logical |
153//! | `tinyvec` | `TinyVec<[T; N]>`, `ArrayVec<[T; N]>` | vectors | Small-vector optimization |
154//!
155//! ### Matrix & Array Libraries
156//!
157//! | Feature | Types | Conversions |
158//! |---------|-------|-------------|
159//! | `ndarray` | `Array1`–`Array6`, `ArrayD`, views | R vectors/matrices ↔ ndarray |
160//! | `nalgebra` | `DVector`, `DMatrix` | R vectors/matrices ↔ nalgebra |
161//!
162//! ### Serialization
163//!
164//! | Feature | Traits/Modules | Description |
165//! |---------|----------------|-------------|
166//! | `serde` | `RSerializeNative`, `RDeserializeNative` | Direct Rust ↔ R native serialization |
167//! | `serde_json` | `RSerialize`, `RDeserialize` | JSON string serialization (includes `serde`) |
168//! | `borsh` | `Borsh<T>` | Binary serialization ↔ raw vectors via Borsh |
169//!
170//! ### Adapter Traits (Generic Operations)
171//!
172//! | Feature | Traits | Use Case |
173//! |---------|--------|----------|
174//! | `num-traits` | `RNum`, `RSigned`, `RFloat` | Generic numeric operations |
175//! | `bytes` | `RBuf`, `RBufMut` | Byte buffer operations |
176//!
177//! ### Text & Data Processing
178//!
179//! | Feature | Types/Functions | Description |
180//! |---------|-----------------|-------------|
181//! | `aho-corasick` | `AhoCorasick`, `aho_compile` | Fast multi-pattern string search |
182//! | `toml` | `TomlValue`, `toml_from_str` | TOML parsing and serialization |
183//! | `tabled` | `table_to_string` | ASCII/Unicode table formatting |
184//! | `sha2` | `sha256_str`, `sha512_bytes` | Cryptographic hashing |
185//! | `blake3` | `blake3_str`, `blake3_bytes` | BLAKE3 hashing (fast, 32-byte digests) |
186//! | `md5` | `md5_str`, `md5_bytes` | MD5 hashing (interop only — broken crypto) |
187//! | `globset` | `GlobSet`, `build_globset` | Shell-style glob matching (path-aware) |
188//! | `zstd` | `zstd_compress`, `zstd_decompress` | zstd whole-buffer compression |
189//!
190//! ### Random Number Generation
191//!
192//! | Feature | Types | Description |
193//! |---------|-------|-------------|
194//! | `rand` | `RRng`, `RDistributions` | Wraps R's RNG with `rand` traits |
195//! | `rand_distr` | Re-exports `rand_distr` | Additional distributions (Normal, Exp, etc.) |
196//!
197//! ### Binary Data
198//!
199//! | Feature | Types | Description |
200//! |---------|-------|-------------|
201//! | `raw_conversions` | `Raw<T>`, `RawSlice<T>` | POD types ↔ raw vectors via bytemuck |
202//!
203//! ### Project-wide Defaults (mutually exclusive where noted)
204//!
205//! | Feature | Description |
206//! |---------|-------------|
207//! | `r6-default` | Default class system: R6 (mutually exclusive with `s7-default`) |
208//! | `s7-default` | Default class system: S7 (mutually exclusive with `r6-default`) |
209//! | `worker-default` | Default to worker thread dispatch (implies `worker-thread`) |
210//! | `strict-default` | Default to strict mode for lossy integer conversions |
211//! | `coerce-default` | Default to coerce mode for type conversions |
212//! | `fast-default` | Default to fast wrappers and unchecked conversion paths |
213//!
214//! ### Development / Diagnostics
215//!
216//! | Feature | Description |
217//! |---------|-------------|
218//! | `doc-lint` | Warn on roxygen doc comment mismatches (enabled by default) |
219//! | `macro-coverage` | Expose macro coverage test module for `cargo expand` auditing |
220//! | `growth-debug` | Track and report collection growth events (zero-cost when off) |
221//! | `full-integrations` | Every optional dependency integration (maintenance aggregate) |
222//! | `full-codegen` | Codegen/runtime selectors using `r6-default` (maintenance aggregate) |
223//! | `full-codegen-s7` | Codegen/runtime selectors using `s7-default` (maintenance aggregate) |
224//! | `full` | Integrations + `full-codegen` + diagnostics for local checks |
225// Re-export linkme for use by generated code (distributed_slice entries)
226#[doc(hidden)]
227pub use linkme;
228
229// Procedural macros (re-exported from miniextendr-macros)
230#[doc(hidden)]
231pub use miniextendr_macros::__mx_trait_impl_expand;
232#[doc(inline)]
233pub use miniextendr_macros::ExternalPtr;
234#[doc(inline)]
235pub use miniextendr_macros::RNativeType;
236#[doc(inline)]
237pub use miniextendr_macros::impl_typed_external;
238#[doc(inline)]
239pub use miniextendr_macros::list;
240#[doc(inline)]
241pub use miniextendr_macros::miniextendr;
242#[doc(inline)]
243pub use miniextendr_macros::miniextendr_init;
244#[doc(inline)]
245pub use miniextendr_macros::r_ffi_checked;
246#[doc(inline)]
247pub use miniextendr_macros::typed_dataframe;
248#[doc(inline)]
249pub use miniextendr_macros::typed_list;
250// Note: RFactor derive macro is re-exported - it shares the name with the RFactor trait
251// but they're in different namespaces (derive macros vs types/traits)
252#[cfg(feature = "vctrs")]
253#[doc(inline)]
254pub use miniextendr_macros::{PreferVctrs, Vctrs};
255// Note: MatchArg derive macro is re-exported - it shares the name with the MatchArg trait
256// but they're in different namespaces (derive macros vs types/traits), same as RFactor.
257// The same applies to the `IntoR` and `TryFromSexp` derive macros, which share names with
258// the `IntoR` / `TryFromSexp` traits — `#[derive(IntoR)]` (macro namespace) and the trait
259// (type namespace) coexist, exactly like serde's `Serialize`.
260#[doc(inline)]
261pub use miniextendr_macros::{
262 Altrep, AltrepComplex, AltrepInteger, AltrepList, AltrepLogical, AltrepRaw, AltrepReal,
263 AltrepString, DataFrameRow, IntoList, IntoR, MatchArg, PreferDataFrame, PreferExternalPtr,
264 PreferList, PreferRNativeType, RFactor, TryFromList, TryFromSexp,
265};
266
267pub mod altrep;
268pub mod altrep_bridge;
269pub mod altrep_data;
270pub mod altrep_ext;
271pub mod altrep_impl;
272pub mod altrep_sexp;
273pub mod altrep_traits;
274
275// Re-export for backward compatibility - RegisterAltrep was moved from altrep_registration to altrep
276#[doc(hidden)]
277pub mod altrep_registration {
278 pub use crate::altrep::RegisterAltrep;
279}
280/// Core type vocabulary for R values: `SEXPTYPE`, `R_xlen_t`, `Rcomplex`,
281/// `RLogical`, `Rboolean`, `RNativeType`, `cetype_t`.
282pub mod sexp_types;
283
284/// The `SEXP` newtype and inherent methods.
285pub mod sexp;
286
287/// `SexpExt` — the ergonomic extension trait on `SEXP`.
288pub mod sexp_ext;
289
290/// Raw R FFI bindings — `extern "C-unwind"` blocks for `Rf_*` / `R_*` /
291/// `INTEGER` / etc., plus the ALTREP method type aliases under
292/// [`sys::altrep`]. The escape hatch for code that genuinely needs the raw
293/// R C API.
294///
295/// **Almost no user code should reach into this module.** The framework
296/// expects you to use the crate-root re-exports for the type vocabulary
297/// ([`SEXP`], [`SexpExt`], [`SEXPTYPE`], [`R_xlen_t`], …) and the
298/// higher-level safe wrappers ([`IntoR`], [`TryFromSexp`],
299/// [`worker::with_r_thread`], [`unwind_protect::with_r_unwind_protect`],
300/// the `#[miniextendr]` proc-macro). `sys::` is only here so those
301/// wrappers — and the rare hand-rolled FFI in tests or benchmarks — have
302/// something to call.
303pub mod sys;
304
305// Crate-root re-exports for the ergonomic API surface.
306pub use sexp::{SEXP, SEXPREC};
307pub use sexp_ext::SexpExt;
308pub use sexp_types::{
309 R_CFinalizer_t, R_xlen_t, RLogical, RNativeType, Rboolean, Rbyte, Rcomplex, SEXPTYPE, cetype_t,
310};
311
312/// Automatic registration internals.
313///
314/// Items annotated with `#[miniextendr]` self-register at link time.
315/// The C entrypoint calls [`registry::miniextendr_register_routines`] to
316/// finalize registration with R. Users don't interact with this module.
317pub mod registry;
318
319/// Host-time generator of `wasm_registry.rs` — the WASM-side replacement for
320/// linkme. See the module for full rationale.
321///
322/// Host-only — the writer reads the live linkme distributed slices to format
323/// `wasm_registry.rs`, and linkme isn't available on wasm32 anyway.
324#[cfg(not(target_arch = "wasm32"))]
325pub mod wasm_registry_writer;
326
327// Re-export high-level ALTREP data traits
328pub use altrep_data::{
329 AltComplexData,
330 AltIntegerData,
331 AltListData,
332 AltLogicalData,
333 AltRawData,
334 AltRealData,
335 AltStringData,
336 AltrepDataptr,
337 AltrepExtract,
338 AltrepLen,
339 // Iterator-backed ALTREP types (R-native)
340 IterComplexData,
341 // Iterator-backed ALTREP types (with Coerce support)
342 IterIntCoerceData,
343 IterIntData,
344 IterIntFromBoolData,
345 IterListData,
346 IterLogicalData,
347 IterRawData,
348 IterRealCoerceData,
349 IterRealData,
350 IterState,
351 IterStringData,
352 Logical,
353 Sortedness,
354 // Sparse iterator-backed ALTREP types (compute-on-access)
355 SparseIterComplexData,
356 SparseIterIntData,
357 SparseIterLogicalData,
358 SparseIterRawData,
359 SparseIterRealData,
360 SparseIterState,
361 // Streaming ALTREP types (chunk-cached reader closures)
362 StreamingIntData,
363 StreamingRealData,
364 // Windowed iterator-backed ALTREP types
365 WindowedIterIntData,
366 WindowedIterRealData,
367 WindowedIterState,
368};
369// Re-export RBase enum, AltrepGuard, and AltrepSexp
370pub use altrep::RBase;
371pub use altrep_sexp::{AltrepSexp, ensure_materialized};
372pub use altrep_traits::AltrepGuard;
373
374// ALTREP package name global - set by C entrypoint before ALTREP registration
375// This is a pointer to a null-terminated C string provided by C code.
376// Default: c"unknown" for safety if not set.
377use std::sync::atomic::{AtomicPtr, Ordering};
378static ALTREP_PKG_NAME_PTR: AtomicPtr<std::ffi::c_char> =
379 AtomicPtr::new(c"unknown".as_ptr().cast_mut());
380
381/// Returns the current ALTREP package name as a C string pointer.
382/// This is set by the C entrypoint before ALTREP registration.
383#[doc(hidden)]
384pub struct AltrepPkgName;
385
386impl AltrepPkgName {
387 /// Get the package name pointer.
388 #[inline]
389 pub fn as_ptr() -> *const std::ffi::c_char {
390 ALTREP_PKG_NAME_PTR.load(Ordering::Acquire)
391 }
392}
393
394/// Opaque handle for ALTREP package name.
395/// Use `ALTREP_PKG_NAME.as_ptr()` to get the C string pointer.
396#[doc(hidden)]
397pub static ALTREP_PKG_NAME: AltrepPkgName = AltrepPkgName;
398
399/// Set the ALTREP package name. Called from C entrypoint.
400/// # Safety
401/// The provided pointer must point to a valid null-terminated C string
402/// that lives for the duration of the R session.
403///
404/// The strict requirement is narrower: R copies the bytes via `install()`
405/// inside each `R_make_alt*_class` call that consults this global (see
406/// `RegisterClass` in R's `src/main/altrep.c`), so the pointer only has to
407/// remain valid across those calls. We keep the session-lifetime contract
408/// because we don't track which registrations are still pending; a string
409/// literal passed from C satisfies both.
410#[doc(hidden)]
411#[unsafe(no_mangle)]
412pub unsafe extern "C" fn miniextendr_set_altrep_pkg_name(name: *const std::ffi::c_char) {
413 let name = if name.is_null() {
414 c"unknown".as_ptr()
415 } else {
416 name
417 };
418 ALTREP_PKG_NAME_PTR.store(name.cast_mut(), Ordering::Release);
419}
420
421// DllInfo global — stored during package_init, used by ALTREP class registration.
422// R needs DllInfo to associate ALTREP classes with their package for serialization.
423// Without it, readRDS in a fresh session can't find the class.
424static ALTREP_DLL_INFO: AtomicPtr<std::ffi::c_void> = AtomicPtr::new(std::ptr::null_mut());
425
426/// Get the stored DllInfo pointer for ALTREP class registration.
427#[doc(hidden)]
428pub fn altrep_dll_info() -> *mut sys::DllInfo {
429 ALTREP_DLL_INFO.load(Ordering::Acquire).cast()
430}
431
432/// Store the DllInfo pointer during package init.
433#[doc(hidden)]
434pub fn set_altrep_dll_info(dll: *mut sys::DllInfo) {
435 ALTREP_DLL_INFO.store(dll.cast(), Ordering::Release);
436}
437
438// Note: SexpExt is pub(crate), imported directly in modules that need it
439pub mod from_r;
440pub mod into_r;
441pub mod into_r_error;
442pub use into_r::{Altrep, IntoR, IntoRAltrep};
443/// Container conversions for forwarding newtypes (`#[derive(TryFromSexp)]` /
444/// `#[derive(IntoR)]`). See the module docs and issue #844.
445pub mod newtype;
446pub use into_r_error::IntoRError;
447pub use newtype::{FromRNewtype, IntoRNewtype, IntoRVecElement};
448pub mod into_r_as;
449pub use into_r_as::{IntoRAs, StorageCoerceError};
450pub mod pump;
451pub mod unwind_protect;
452pub mod worker;
453
454// Re-export commonly used worker items at root for convenience
455pub use worker::{Sendable, is_r_main_thread, with_r_thread};
456
457// Required exports for generated code and initialization
458pub use worker::miniextendr_runtime_init;
459
460// Advanced stack-configuration utilities; not permission for off-main R API use
461pub mod thread;
462
463// Collection growth debug instrumentation (diagnostics)
464#[cfg(feature = "growth-debug")]
465pub mod growth_debug;
466
467/// Track a collection growth (reallocation) event.
468///
469/// When the `growth-debug` feature is enabled, increments a thread-local counter
470/// for the named collection. When disabled, compiles to a no-op.
471///
472/// # Example
473///
474/// ```ignore
475/// let old_cap = vec.capacity();
476/// vec.push(item);
477/// if vec.capacity() != old_cap {
478/// track_growth!("my_vec");
479/// }
480/// ```
481#[cfg(feature = "growth-debug")]
482#[macro_export]
483macro_rules! track_growth {
484 ($name:expr) => {
485 $crate::growth_debug::record_growth($name)
486 };
487}
488
489/// Track a collection growth (reallocation) event.
490///
491/// No-op when `growth-debug` feature is disabled.
492#[cfg(not(feature = "growth-debug"))]
493#[macro_export]
494macro_rules! track_growth {
495 ($name:expr) => {};
496}
497
498/// Print and reset all growth counters.
499///
500/// When the `growth-debug` feature is enabled, prints all tracked growth events
501/// to stderr and resets the counters. When disabled, compiles to a no-op.
502#[cfg(feature = "growth-debug")]
503#[macro_export]
504macro_rules! report_growth {
505 () => {
506 $crate::growth_debug::report_and_reset()
507 };
508}
509
510/// Print and reset all growth counters.
511///
512/// No-op when `growth-debug` feature is disabled.
513#[cfg(not(feature = "growth-debug"))]
514#[macro_export]
515macro_rules! report_growth {
516 () => {};
517}
518
519/// Parse and evaluate **runtime** R source from Rust.
520///
521/// `r_str!` is sugar around [`expression::r_eval_str`]. It is the right tool
522/// when the R code is genuinely dynamic — built with `format!`, derived from
523/// user input, or otherwise not known at compile time. For code you can write
524/// literally, prefer [`r!`](crate::r), which gives a cheap compile-time
525/// sanity check on the token stream.
526///
527/// The argument is any expression evaluating to something `AsRef<str>`
528/// (`&str`, `String`, `&String`, …). It is parsed with `R_ParseVector` and
529/// evaluated with `Rf_eval`, with every intermediate SEXP protected and the
530/// parse status checked, so a syntax error becomes an `Err`, never a segfault
531/// or silent wrong answer.
532///
533/// # Forms
534///
535/// - `r_str!(code)` — evaluate in `R_GlobalEnv`.
536/// - `r_str!(code, env = e)` — evaluate in the environment SEXP `e`.
537///
538/// Both forms evaluate to `Result<SEXP, String>`; the `SEXP` is **unprotected**
539/// (protect it before further allocations).
540///
541/// # Safety
542///
543/// Expands to an `unsafe` block. Must be reached from (or routed to) the R
544/// main thread — the underlying FFI is `#[r_ffi_checked]`, so calls from a
545/// worker thread are serialized onto the R thread.
546///
547/// # Example
548///
549/// ```ignore
550/// let obj = "mtcars";
551/// let code = format!("summary({obj})");
552/// let summary = r_str!(&code)?; // in R_GlobalEnv
553/// let three = r_str!("1L + 2L")?;
554/// let in_env = r_str!("x + 1", env = my_env)?;
555/// ```
556#[macro_export]
557macro_rules! r_str {
558 ($code:expr $(,)?) => {
559 unsafe {
560 $crate::expression::r_eval_str(
561 ::core::convert::AsRef::<str>::as_ref(&$code),
562 $crate::sys::R_GlobalEnv,
563 )
564 }
565 };
566 ($code:expr, env = $env:expr $(,)?) => {
567 unsafe {
568 $crate::expression::r_eval_str(::core::convert::AsRef::<str>::as_ref(&$code), $env)
569 }
570 };
571}
572
573/// Evaluate R code written as **Rust tokens**, validated at compile time.
574///
575/// `r!` takes a single R expression as a token stream, `stringify!`s it into a
576/// static R source string at build time, and evaluates it via
577/// [`expression::r_eval_str`] (the same protect-safe parse + eval path as
578/// [`r_str!`](crate::r_str)).
579///
580/// # What you get today
581///
582/// Because the argument is a Rust token tree, the Rust front-end already
583/// rejects **unbalanced delimiters** (`r!(f(1, 2)` won't compile) and
584/// lexically invalid tokens before R ever sees the string — a cheap
585/// compile-time guard over the pure-runtime [`r_str!`](crate::r_str). The
586/// source is lowered to a `&'static str` (`stringify!`), so there is no
587/// `format!` allocation at the call site.
588///
589/// This proc-macro additionally validates a conservative subset of known-bad
590/// R syntax constructs (trailing binary operators, consecutive non-unary
591/// binary operators, bare `if`/`while`/`for` without a body, etc.) and emits
592/// a precise compile error pointing at the offending token. Empty (missing)
593/// call arguments — `f(, x)`, `matrix(, 2, 2)` — are valid R and pass.
594///
595/// # What is deferred
596///
597/// Direct `Rf_lang*` call-tree lowering (skipping the runtime parser entirely)
598/// is tracked as a follow-up — see issue #938 (item 2). Until then `r!` parses
599/// its static string at first evaluation, exactly like `r_str!`.
600///
601/// # Non-goals
602///
603/// A complete R grammar validator is not achievable over Rust tokens:
604/// - Single-quoted strings (`'hello'`) and backtick-quoted names (`` `foo` ``)
605/// already die at the Rust lexer — nothing to validate.
606/// - `%op%` tokenises as `%`, ident, `%` and is accepted without analysis.
607/// - Anything the validator cannot confidently classify as wrong passes through
608/// unvalidated (conservative reject-only-known-bad design).
609///
610/// # Forms
611///
612/// - `r!(R tokens…)` — evaluate in `R_GlobalEnv`.
613/// - `r!(env: e; R tokens…)` — evaluate in the environment SEXP `e`. The
614/// leading `env: <expr> ;` is consumed as Rust, the rest is R source. (The
615/// `;` separator is used instead of a trailing `, env =` because R source is
616/// a free token stream — a trailing keyword can't be reliably split off it.)
617///
618/// Both evaluate to `Result<SEXP, String>`; the `SEXP` is **unprotected**.
619///
620/// For genuinely dynamic code, use [`r_str!`](crate::r_str) instead.
621///
622/// # Note on `stringify!` spacing
623///
624/// `stringify!` normalises whitespace (`a+b` and `a + b` both stringify to
625/// `a + b`) but preserves token order and literal contents, which is all R's
626/// parser needs. String literals keep their quotes.
627///
628/// # Safety
629///
630/// Expands to an `unsafe` block; see [`r_str!`](crate::r_str).
631///
632/// # Example
633///
634/// ```ignore
635/// let three = r!(1L + 2L)?;
636/// let rows = r!(getFromNamespace(".theoph_rows", "dataframeflows")())?;
637/// let in_env = r!(env: my_env; x + 1)?;
638/// ```
639#[doc(inline)]
640pub use miniextendr_macros::r;
641
642// `indicatif` progress integration (R console)
643#[cfg(feature = "indicatif")]
644pub mod progress;
645#[cfg(feature = "indicatif")]
646pub use indicatif;
647
648// Legacy R-oriented stack sizing (always available; not an off-main R bridge)
649#[cfg(windows)]
650pub use thread::WINDOWS_R_STACK_SIZE;
651pub use thread::{DEFAULT_R_STACK_SIZE, RThreadBuilder};
652
653// Legacy process-global stack controls (requires nonapi; removal tracked in #1352)
654#[cfg(feature = "nonapi")]
655pub use thread::{StackCheckGuard, scope_with_r, spawn_with_r, with_stack_checking_disabled};
656
657// Panic telemetry hook for structured panic→R-error diagnostics
658pub mod panic_telemetry;
659
660// Unified FFI guard for catching panics at Rust-R boundaries
661pub mod ffi_guard;
662pub use ffi_guard::{GuardMode, guarded_ffi_call, guarded_ffi_call_with_fallback};
663
664// The unified owned data.frame type + conversion trait family
665pub mod dataframe;
666pub use dataframe::{
667 BuiltDataFrame, ColumnarFrame, DataFrame, DataFrameError, FromDataFrame, GroupKey,
668 GroupedDataFrame, IntoDataFrame, IntoDataFrameSplit, NamedDataFrameListBuilder, group_rows,
669};
670
671// Closure-per-column DataFrame builder (parallel fill with `rayon`, serial
672// otherwise). Available regardless of the `rayon` feature (#1055).
673pub mod dataframe_builder;
674pub use dataframe_builder::RDataFrameBuilder;
675
676// Strict conversion helpers for #[miniextendr(strict)]
677pub mod strict;
678
679// Cached R class attribute SEXPs (POSIXct, Date, data.frame, etc.)
680pub mod cached_class;
681
682// Tagged condition value transport for the Rust→R boundary
683pub mod error_value;
684
685// Error handling helpers (r_warning, r_print!, r_println! macros)
686pub mod error;
687pub use error::r_warning;
688
689// RNG (random number generation) utilities
690pub mod rng;
691pub use rng::{RngGuard, with_rng};
692
693// Re-export from_r
694pub use from_r::{SexpError, SexpLengthError, SexpNaError, SexpTypeError, TryFromSexp};
695
696// Encoding / locale probing (mainly for debugging). The module is always
697// compiled; the symbols that reference non-API locale state from R's `Defn.h`
698// (`utf8locale`, `mbcslocale`, `known_to_be_latin1`) are gated inside the
699// module behind `#[cfg(feature = "nonapi")]` so a default build never links
700// them. Only R-exported symbols may be declared there — hidden ones abort
701// dyn.load (see sys::nonapi_encoding).
702pub mod encoding;
703
704// Expression evaluation helpers (RSymbol, RCall, REnv)
705pub mod expression;
706pub use expression::{RCall, REnv, RSymbol, r_eval_str, r_eval_str_global};
707
708// S4 slot access and class checking helpers
709pub mod s4_helpers;
710
711// Note: RNativeType is pub(crate), imported directly in modules that need it
712
713pub mod backtrace;
714
715/// Shared truthiness parsing for boolean-ish environment-variable flags.
716pub(crate) mod env_flag;
717
718pub mod coerce;
719pub use coerce::{Coerce, CoerceError, Coerced, TryCoerce};
720
721/// Traits for R's `as.<class>()` coercion functions.
722///
723/// This module provides traits for implementing R's generic coercion methods
724/// (`as.data.frame()`, `as.list()`, `as.character()`, etc.) for Rust types
725/// wrapped in [`ExternalPtr`](struct@ExternalPtr).
726///
727/// See the [`r_coerce`] module documentation for usage examples.
728pub mod r_coerce;
729pub use r_coerce::{
730 // Core coercion traits (R's `as.<class>()` generics)
731 RCoerceCharacter,
732 RCoerceComplex,
733 RCoerceDataFrame,
734 RCoerceDate,
735 RCoerceEnvironment,
736 // Error type
737 RCoerceError,
738 RCoerceFactor,
739 RCoerceFunction,
740 RCoerceInteger,
741 RCoerceList,
742 RCoerceLogical,
743 RCoerceMatrix,
744 RCoerceNumeric,
745 RCoercePOSIXct,
746 RCoerceRaw,
747 RCoerceVector,
748 // Helpers
749 SUPPORTED_AS_GENERICS,
750 is_supported_as_generic,
751};
752
753pub mod condition;
754pub use condition::{AsRError, RCondition};
755pub mod convert;
756/// Support for R `...` arguments represented as a validated list.
757pub mod dots;
758pub mod list;
759pub mod missing;
760pub mod named_vector;
761pub mod rcow;
762pub mod rvalue;
763pub use rvalue::RValue;
764pub mod strvec;
765pub mod typed_list;
766pub use convert::{
767 AsDataFrame, AsDataFrameExt, AsDisplay, AsDisplayVec, AsExternalPtr, AsExternalPtrExt,
768 AsFromStr, AsFromStrVec, AsList, AsListExt, AsNamedList, AsNamedListExt, AsNamedVector,
769 AsNamedVectorExt, AsRNative, AsRNativeExt, Collect, CollectNA, CollectNAInt, CollectStrings,
770 ColumnSource,
771};
772#[cfg(feature = "vctrs")]
773pub use convert::{AsVctrs, AsVctrsExt};
774pub use into_r::Lazy;
775pub use list::{
776 IntoList, List, ListAccumulator, ListBuilder, ListMut, NamedList, TryFromList, collect_list,
777};
778pub use missing::{Missing, is_missing_arg};
779pub use named_vector::{AtomicElement, NamedVector};
780pub use rcow::{RBorrow, RCow};
781pub use strvec::{
782 ProtectedStrVec, ProtectedStrVecCowIter, ProtectedStrVecIter, StrVec, StrVecBuilder,
783 StrVecCowIter, StrVecIter,
784};
785pub use typed_list::{
786 TypeSpec, TypedEntry, TypedList, TypedListError, TypedListSpec, actual_type_string,
787 sexptype_name, validate_list,
788};
789
790// External pointer module - Box-like owned pointer wrapping R's EXTPTRSXP
791pub mod externalptr;
792
793// Connection framework (unstable R API - use with caution)
794#[cfg(feature = "connections")]
795pub mod connection;
796// txtProgressBar handle — drives utils::txtProgressBar from Rust
797#[cfg(feature = "connections")]
798pub mod txt_progress_bar;
799pub use externalptr::{
800 ErasedExternalPtr,
801 ExternalPtr,
802 ExternalSlice,
803 IntoExternalPtr,
804 TypedExternal,
805 // ALTREP helpers (checked)
806 altrep_data1_as,
807 // ALTREP helpers (unchecked - for performance-critical callbacks)
808 altrep_data1_as_unchecked,
809 altrep_data1_mut,
810 altrep_data1_mut_unchecked,
811 altrep_data2_as,
812 altrep_data2_as_unchecked,
813};
814
815// TypedExternal implementations for std types
816pub mod externalptr_std;
817
818// GC protection toolkit (PROTECT stack RAII wrappers)
819pub mod gc_protect;
820pub use gc_protect::{
821 OwnedProtect, ProtectIndex, ProtectScope, Protected, Protector, ReprotectSlot, Root,
822};
823
824// VECSXP pool with generational keys (slotmap-backed)
825pub mod protect_pool;
826pub use protect_pool::{ProtectKey, ProtectPool};
827
828// Reference-counted GC protection (BTreeMap + VECSXP backing)
829pub mod refcount_protect;
830pub use refcount_protect::{ArenaGuard, RefCountedArena, ThreadLocalArena};
831
832pub mod allocator;
833pub use allocator::RAllocator;
834
835pub mod r_memory;
836
837// region: Trait ABI Support
838//
839// Cross-package trait dispatch using a stable C ABI.
840// See `trait_abi` module docs for details.
841
842/// ABI types for cross-package trait dispatch.
843///
844/// This module defines the stable, C-compatible types used for runtime trait
845/// dispatch across R package boundaries.
846pub mod abi;
847
848/// C-callable mx_abi functions (mx_wrap, mx_get, mx_query, mx_abi_register).
849///
850/// These are registered via `R_RegisterCCallable` during package init and
851/// loaded by consumer packages via `R_GetCCallable`.
852pub mod mx_abi;
853
854/// Package initialization (`miniextendr_init!` support).
855///
856/// Consolidates all init steps into [`init::package_init`].
857pub mod init;
858
859/// Runtime support for trait ABI operations.
860///
861/// Provides C-callable loading and type conversion helpers for trait ABI support.
862pub mod trait_abi;
863
864/// vctrs class construction and trait support.
865///
866/// Provides helpers for building vctrs-compatible R objects and traits
867/// for describing vctrs class metadata from Rust types.
868///
869/// Enable with `features = ["vctrs"]`.
870#[cfg(feature = "vctrs")]
871pub mod vctrs;
872#[cfg(feature = "vctrs")]
873pub use vctrs::{
874 IntoVctrs, VctrsBuildError, VctrsClass, VctrsKind, VctrsListOf, VctrsRecord, new_list_of,
875 new_rcrd, new_vctr,
876};
877
878// Re-export key ABI types at crate root for convenience
879pub use abi::{mx_base_vtable, mx_erased, mx_meth, mx_tag};
880pub use trait_abi::TraitView;
881// endregion
882
883// region: Marker Traits
884//
885// Marker traits for types derived with proc-macros.
886// These enable compile-time identification and blanket implementations.
887
888/// Marker traits for proc-macro derived types.
889pub mod markers;
890// endregion
891
892// region: Adapter Traits
893//
894// Built-in adapter traits with blanket implementations for standard library traits.
895// These allow any Rust type implementing Debug, Display, Hash, Ord, etc. to be
896// exposed to R without boilerplate.
897
898/// Built-in adapter traits for std library traits.
899///
900/// Provides [`RDebug`], [`RDisplay`], [`RHash`], [`ROrd`], [`RPartialOrd`],
901/// [`RError`], [`RFromStr`], [`RClone`], [`RCopy`], [`RDefault`], [`RIterator`],
902/// [`RExtend`], and [`RFromIter`] with blanket implementations where possible.
903/// See module docs for usage.
904///
905/// [`RDebug`]: adapter_traits::RDebug
906/// [`RDisplay`]: adapter_traits::RDisplay
907/// [`RHash`]: adapter_traits::RHash
908/// [`ROrd`]: adapter_traits::ROrd
909/// [`RPartialOrd`]: adapter_traits::RPartialOrd
910/// [`RError`]: adapter_traits::RError
911/// [`RFromStr`]: adapter_traits::RFromStr
912/// [`RClone`]: adapter_traits::RClone
913/// [`RCopy`]: adapter_traits::RCopy
914/// [`RDefault`]: adapter_traits::RDefault
915/// [`RIterator`]: adapter_traits::RIterator
916/// [`RExtend`]: adapter_traits::RExtend
917/// [`RFromIter`]: adapter_traits::RFromIter
918pub mod adapter_traits;
919pub use adapter_traits::{
920 RClone, RCopy, RDebug, RDefault, RDisplay, RError, RExtend, RFromIter, RFromStr, RHash,
921 RIterator, RMakeIter, ROrd, RPartialOrd, RToVec,
922};
923
924/// This is used to ensure the macros of `miniextendr-macros` treat this crate as a "user crate"
925/// atleast in the `macro_coverage`
926#[doc(hidden)]
927extern crate self as miniextendr_api;
928
929#[cfg(feature = "macro-coverage")]
930#[doc(hidden)]
931pub mod macro_coverage;
932// endregion
933
934// region: Optional integrations with external crates (feature-gated)
935//
936// All optional feature integrations are organized in the `optionals` module.
937// Types are re-exported at crate root for backwards compatibility.
938
939/// Optional feature integrations with third-party crates.
940///
941/// This module contains all feature-gated integrations with external crates.
942/// Each submodule is only compiled when its corresponding feature is enabled.
943/// See the [module documentation][optionals] for a complete list of available features.
944pub mod optionals;
945
946// Re-export optional types at crate root for backwards compatibility
947#[cfg(feature = "rayon")]
948pub use optionals::parallel;
949#[cfg(feature = "rayon")]
950pub use optionals::rayon_bridge;
951#[cfg(feature = "rayon")]
952pub use optionals::{RParallelExtend, RParallelIterator};
953
954#[cfg(feature = "rand")]
955pub use optionals::rand;
956#[cfg(feature = "rand_distr")]
957pub use optionals::rand_distr;
958#[cfg(feature = "rand")]
959pub use optionals::rand_impl;
960#[cfg(feature = "rand")]
961pub use optionals::{RDistributionOps, RDistributions, RRng, RRngOps};
962
963#[cfg(feature = "either")]
964pub use optionals::either_impl;
965#[cfg(feature = "either")]
966pub use optionals::{Either, Left, Right};
967
968#[cfg(feature = "ndarray")]
969pub use optionals::ndarray_impl;
970#[cfg(feature = "ndarray")]
971pub use optionals::{
972 ArcArray1, ArcArray2, Array0, Array1, Array2, Array3, Array4, Array5, Array6, ArrayD,
973 ArrayView0, ArrayView1, ArrayView2, ArrayView3, ArrayView4, ArrayView5, ArrayView6, ArrayViewD,
974 ArrayViewMut0, ArrayViewMut1, ArrayViewMut2, ArrayViewMut3, ArrayViewMut4, ArrayViewMut5,
975 ArrayViewMut6, ArrayViewMutD, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, RNdArrayOps, RNdIndex,
976 RNdSlice, RNdSlice2D, ShapeBuilder,
977};
978
979#[cfg(feature = "nalgebra")]
980pub use optionals::nalgebra_impl;
981#[cfg(feature = "nalgebra")]
982pub use optionals::{DMatrix, DVector, RMatrixOps, RVectorOps, SMatrix, SVector};
983
984#[cfg(feature = "num-bigint")]
985pub use optionals::num_bigint_impl;
986#[cfg(feature = "num-bigint")]
987pub use optionals::{BigInt, BigUint, RBigIntBitOps, RBigIntOps, RBigUintBitOps, RBigUintOps};
988
989#[cfg(feature = "rust_decimal")]
990pub use optionals::rust_decimal_impl;
991#[cfg(feature = "rust_decimal")]
992pub use optionals::{Decimal, RDecimalOps};
993
994#[cfg(feature = "ordered-float")]
995pub use optionals::ordered_float_impl;
996#[cfg(feature = "ordered-float")]
997pub use optionals::{OrderedFloat, ROrderedFloatOps};
998
999#[cfg(feature = "num-complex")]
1000pub use optionals::num_complex_impl;
1001#[cfg(feature = "num-complex")]
1002pub use optionals::{Complex, RComplexOps};
1003
1004#[cfg(feature = "num-traits")]
1005pub use optionals::num_traits_impl;
1006#[cfg(feature = "num-traits")]
1007pub use optionals::{RFloat, RNum, RSigned};
1008
1009#[cfg(feature = "uuid")]
1010pub use optionals::uuid_impl;
1011#[cfg(feature = "uuid")]
1012pub use optionals::{RUuidOps, Uuid, uuid_helpers};
1013
1014#[cfg(feature = "regex")]
1015pub use optionals::regex_impl;
1016#[cfg(feature = "regex")]
1017pub use optionals::{CaptureGroups, RCaptureGroups, RRegexOps, Regex};
1018
1019#[cfg(feature = "url")]
1020pub use optionals::url_impl;
1021#[cfg(feature = "url")]
1022pub use optionals::{RUrlOps, Url, url_helpers};
1023
1024#[cfg(feature = "aho-corasick")]
1025pub use optionals::aho_corasick_impl;
1026#[cfg(feature = "aho-corasick")]
1027pub use optionals::{
1028 AhoCorasick, RAhoCorasickOps, aho_compile, aho_count_matches, aho_find_all, aho_find_all_flat,
1029 aho_find_first, aho_is_match, aho_replace_all,
1030};
1031
1032#[cfg(feature = "indexmap")]
1033pub use optionals::indexmap_impl;
1034#[cfg(feature = "indexmap")]
1035pub use optionals::{IndexMap, RIndexMapOps};
1036
1037#[cfg(feature = "time")]
1038pub use optionals::time_impl;
1039#[cfg(feature = "time")]
1040pub use optionals::{Date, Duration, OffsetDateTime, RDateTimeFormat, RDuration};
1041#[cfg(feature = "time")]
1042pub use time;
1043
1044#[cfg(feature = "jiff")]
1045pub use jiff;
1046#[cfg(feature = "jiff")]
1047pub use optionals::jiff_impl;
1048#[cfg(feature = "jiff")]
1049pub use optionals::{
1050 JiffDate, JiffDateTime, JiffTime, JiffTimestampVec, JiffZonedVec, RDate, RDateTime,
1051 RSignedDuration, RSpan, RTime, RTimestamp, RZoned, SignedDuration, Span, Timestamp, Zoned,
1052};
1053
1054#[cfg(feature = "serde_json")]
1055pub use optionals::serde_impl;
1056#[cfg(feature = "toml")]
1057pub use optionals::toml_impl;
1058#[cfg(feature = "serde_json")]
1059pub use optionals::{
1060 FactorHandling, JsonOptions, JsonValue, NaHandling, RDeserialize, RJsonBridge, RJsonValueOps,
1061 RSerialize, SpecialFloatHandling, json_from_sexp, json_from_sexp_permissive,
1062 json_from_sexp_strict, json_from_sexp_with, json_into_sexp,
1063};
1064#[cfg(feature = "toml")]
1065pub use optionals::{RTomlOps, TomlValue, toml_from_str, toml_to_string, toml_to_string_pretty};
1066
1067#[cfg(feature = "bytes")]
1068pub use optionals::bytes_impl;
1069#[cfg(feature = "bytes")]
1070pub use optionals::{Buf, BufMut, Bytes, BytesMut, RBuf, RBufMut};
1071
1072#[cfg(feature = "sha2")]
1073pub use optionals::sha2_impl;
1074#[cfg(feature = "sha2")]
1075pub use optionals::{sha256_bytes, sha256_str, sha512_bytes, sha512_str};
1076
1077#[cfg(feature = "blake3")]
1078pub use optionals::blake3_impl;
1079#[cfg(feature = "blake3")]
1080pub use optionals::{blake3_bytes, blake3_hex, blake3_str};
1081
1082#[cfg(feature = "md5")]
1083pub use optionals::md5_impl;
1084#[cfg(feature = "md5")]
1085pub use optionals::{md5_bytes, md5_hex, md5_str};
1086
1087#[cfg(feature = "globset")]
1088pub use optionals::globset_impl;
1089#[cfg(feature = "globset")]
1090pub use optionals::{
1091 Glob, GlobBuilder, GlobOptions, GlobSet, GlobSetBuilder, build_globset, globset_is_match,
1092 globset_matches,
1093};
1094
1095#[cfg(feature = "zstd")]
1096pub use optionals::zstd_impl;
1097#[cfg(feature = "zstd")]
1098pub use optionals::{zstd_compress, zstd_decompress};
1099
1100#[cfg(feature = "borsh")]
1101pub use optionals::borsh_impl;
1102#[cfg(feature = "borsh")]
1103pub use optionals::{Borsh, RBorshOps, borsh_from_raw, borsh_to_raw};
1104
1105#[cfg(feature = "bitflags")]
1106pub use bitflags;
1107#[cfg(feature = "bitflags")]
1108pub use optionals::bitflags_impl;
1109#[cfg(feature = "bitflags")]
1110pub use optionals::{Flags, RFlags};
1111
1112#[cfg(feature = "bitvec")]
1113pub use optionals::bitvec_impl;
1114#[cfg(feature = "bitvec")]
1115pub use optionals::{BitVec, Lsb0, Msb0, RBitVec};
1116
1117#[cfg(feature = "tabled")]
1118pub use optionals::tabled_impl;
1119#[cfg(feature = "tabled")]
1120pub use optionals::{
1121 Builder, Table, Tabled, builder_to_string, table_from_vecs, table_to_string,
1122 table_to_string_opts, table_to_string_styled,
1123};
1124
1125#[cfg(feature = "tinyvec")]
1126pub use optionals::tinyvec_impl;
1127#[cfg(feature = "tinyvec")]
1128pub use optionals::{Array, ArrayVec, TinyVec};
1129
1130#[cfg(feature = "arrow")]
1131pub use optionals::arrow_impl;
1132#[cfg(feature = "arrow")]
1133pub use optionals::{
1134 ArrayRef, ArrowArray, BooleanArray, DataType, Date32Array, DictionaryArray, Field,
1135 Float64Array, Int32Array, RecordBatch, Schema, StringArray, StringDictionaryArray,
1136 TimestampSecondArray, UInt8Array,
1137};
1138
1139#[cfg(feature = "datafusion")]
1140pub use optionals::RSessionContext;
1141#[cfg(feature = "datafusion")]
1142pub use optionals::datafusion_impl;
1143
1144/// N-dimensional R arrays with const generic dimension count.
1145pub mod rarray;
1146pub use rarray::{RArray, RArray3D, RMatrix, RVector};
1147
1148/// Direct R serialization via serde (no JSON intermediate).
1149///
1150/// Provides efficient type-preserving conversions between Rust types and native R objects:
1151/// - [`AsSerialize<T>`][serde::AsSerialize] - Wrapper for returning `Serialize` types from `#[miniextendr]` functions
1152/// - [`RSerializeNative`][serde::RSerializeNative] - Convert Rust → R (struct → named list)
1153/// - [`RDeserializeNative`][serde::RDeserializeNative] - Convert R → Rust (named list → struct)
1154///
1155/// Enable with `features = ["serde"]`.
1156///
1157/// See the [`serde`] module documentation for type mappings and examples.
1158#[cfg(feature = "serde")]
1159pub mod serde;
1160/// Re-export the upstream `serde` crate (aliased to avoid conflict with [`mod serde`]).
1161///
1162/// Downstream crates can use `miniextendr_api::serde_crate::{Serialize, Deserialize}`
1163/// and `#[serde(crate = "miniextendr_api::serde_crate")]` to avoid a direct `serde` dep.
1164#[cfg(feature = "serde")]
1165pub use ::serde as serde_crate;
1166
1167/// Integration with the `bytemuck` crate for POD type conversions.
1168///
1169/// Provides explicit, safe conversions between Rust POD (Plain Old Data) types
1170/// and R raw vectors:
1171/// - `Raw<T>` - Single POD value (headerless, native layout)
1172/// - `RawSlice<T>` - Sequence of POD values (headerless)
1173/// - `RawTagged<T>` / `RawSliceTagged<T>` - With header metadata
1174///
1175/// Enable with `features = ["raw_conversions"]`.
1176///
1177/// ```ignore
1178/// use bytemuck::{Pod, Zeroable};
1179/// use miniextendr_api::raw_conversions::{Raw, RawSlice};
1180///
1181/// #[derive(Copy, Clone, Pod, Zeroable)]
1182/// #[repr(C)]
1183/// struct Vec3 { x: f32, y: f32, z: f32 }
1184///
1185/// #[miniextendr]
1186/// fn encode(x: f64, y: f64, z: f64) -> Raw<Vec3> {
1187/// Raw(Vec3 { x: x as f32, y: y as f32, z: z as f32 })
1188/// }
1189/// ```
1190#[cfg(feature = "raw_conversions")]
1191pub mod raw_conversions;
1192#[cfg(feature = "raw_conversions")]
1193pub use raw_conversions::{
1194 Pod, Raw, RawError, RawHeader, RawSlice, RawSliceTagged, RawTagged, Zeroable, raw_from_bytes,
1195 raw_slice_from_bytes, raw_slice_to_bytes, raw_to_bytes,
1196};
1197
1198/// `match.arg`-style string conversion for enums.
1199///
1200/// Provides the [`MatchArg`] trait for converting Rust enums to/from R character
1201/// strings with partial matching, like R's `match.arg()`.
1202/// Use `#[derive(MatchArg)]` on C-style enums to auto-generate the implementation.
1203pub mod match_arg;
1204pub use match_arg::{
1205 MatchArg, MatchArgError, choices_sexp, match_arg_from_sexp, match_arg_vec_from_sexp,
1206 match_arg_vec_into_sexp,
1207};
1208
1209/// Factor support for enum ↔ R factor conversions.
1210///
1211/// Provides the [`RFactor`] trait for converting Rust enums to/from R factors.
1212/// Use `#[derive(RFactor)]` on C-style enums to auto-generate the implementation.
1213///
1214/// # Example
1215///
1216/// ```ignore
1217/// use miniextendr_api::RFactor;
1218///
1219/// #[derive(Copy, Clone, RFactor)]
1220/// enum Color { Red, Green, Blue }
1221///
1222/// #[miniextendr]
1223/// fn color_name(c: Color) -> &'static str {
1224/// match c {
1225/// Color::Red => "red",
1226/// Color::Green => "green",
1227/// Color::Blue => "blue",
1228/// }
1229/// }
1230/// ```
1231pub mod factor;
1232pub use factor::{
1233 Factor, FactorMut, FactorOptionVec, FactorVec, RFactor, UnitEnumFactor, build_factor,
1234 build_levels_sexp, build_levels_sexp_cached, factor_from_sexp,
1235};
1236
1237/// Convenience re-exports for common miniextendr items.
1238///
1239/// A single `use miniextendr_api::prelude::*;` brings into scope the most
1240/// commonly used macros, traits, types, and helpers.
1241pub mod prelude;
1242// endregion