Skip to main content

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