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 (`ffi`, `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//! ## GC protection and ownership
38//!
39//! R's garbage collector can reclaim any SEXP that isn't protected. miniextendr
40//! provides three complementary protection mechanisms:
41//!
42//! | Strategy | Module | Lifetime | Release Order | Use Case |
43//! |----------|--------|----------|---------------|----------|
44//! | **PROTECT stack** | [`gc_protect`] | Within `.Call` | LIFO (stack) | Temporary allocations |
45//! | **Preserve list** | [`preserve`] | Across `.Call`s | Any order | Long-lived R objects |
46//! | **R ownership** | [`ExternalPtr`](struct@ExternalPtr) | Until R GCs | R decides | Rust data owned by R |
47//!
48//! Quick guide:
49//!
50//! **Temporary allocations during computation** -> [`ProtectScope`]
51//! ```ignore
52//! unsafe fn compute(x: SEXP) -> SEXP {
53//!     let scope = ProtectScope::new();
54//!     let temp = scope.protect(Rf_allocVector(REALSXP, 100));
55//!     // ... work with temp ...
56//!     result.into_raw()
57//! } // UNPROTECT(n) called automatically
58//! ```
59//!
60//! **R objects surviving across `.Call`s** -> [`preserve`]
61//! ```ignore
62//! // In RAllocator or similar long-lived context
63//! let cell = unsafe { preserve::insert(backing_vec) };
64//! // ... use across multiple .Calls ...
65//! unsafe { preserve::release(cell) };
66//! ```
67//!
68//! **Rust data owned by R** -> [`ExternalPtr`](struct@ExternalPtr)
69//! ```ignore
70//! #[miniextendr]
71//! fn create_model() -> ExternalPtr<MyModel> {
72//!     ExternalPtr::new(MyModel::new())
73//! } // R owns it; Drop runs when R GCs
74//! ```
75//!
76//! Note: ALTREP trait methods receive raw SEXP pointers from R's runtime.
77//! These are safe to dereference because R guarantees valid SEXPs in ALTREP callbacks.
78//!
79//! ## Threading and safety
80//!
81//! R uses `longjmp` for errors, which can bypass Rust destructors. The default
82//! pattern is to run Rust logic on a worker thread and marshal R API calls back
83//! to the main R thread via `with_r_thread`. Most FFI wrappers are
84//! main-thread routed via `#[r_ffi_checked]`. Use unchecked variants only when
85//! you have arranged a safe context.
86//!
87//! With the `nonapi` feature, miniextendr can disable R's stack checking to allow
88//! calls from other threads. R is still not thread-safe; serialize all R API use.
89//!
90//! ## Feature Flags
91//!
92//! ### Core Features
93//!
94//! | Feature | Description |
95//! |---------|-------------|
96//! | `nonapi` | Non-API R symbols (stack controls, mutable `DATAPTR`). May break with R updates. |
97//! | `rayon` | Parallel iterators via Rayon. Adds `RParallelIterator`, `RParallelExtend`. |
98//! | `connections` | Experimental R connection framework. **Unstable R API.** |
99//! | `indicatif` | Progress bars via R console. Requires `nonapi`. |
100//! | `vctrs` | vctrs class construction (`new_vctr`, `new_rcrd`, `new_list_of`) and `#[derive(Vctrs)]`. |
101//! | `worker-thread` | Worker thread for panic isolation and `Drop` safety. Without it, stubs run inline. |
102//!
103//! ### Type Conversions (Scalars & Vectors)
104//!
105//! | Feature | Rust Type | R Type | Notes |
106//! |---------|-----------|--------|-------|
107//! | `either` | `Either<L, R>` | Tries L then R | Union-like dispatch |
108//! | `uuid` | `Uuid`, `Vec<Uuid>` | `character` | UUID ↔ string |
109//! | `regex` | `Regex` | `character(1)` | Compiles pattern from R |
110//! | `url` | `Url`, `Vec<Url>` | `character` | Validated URLs |
111//! | `time` | `OffsetDateTime`, `Date` | `POSIXct`, `Date` | Date/time conversions |
112//! | `ordered-float` | `OrderedFloat<f64>` | `numeric` | NaN-orderable floats |
113//! | `num-bigint` | `BigInt`, `BigUint` | `character` | Arbitrary precision via strings |
114//! | `rust_decimal` | `Decimal` | `character` | Fixed-point decimals |
115//! | `num-complex` | `Complex<f64>` | `complex` | Native R complex support |
116//! | `indexmap` | `IndexMap<String, T>` | named `list` | Preserves insertion order |
117//! | `bitflags` | `RFlags<T>` | `integer` | Bitflags ↔ integer |
118//! | `bitvec` | `RBitVec` | `logical` | Bit vectors ↔ logical |
119//! | `tinyvec` | `TinyVec<[T; N]>`, `ArrayVec<[T; N]>` | vectors | Small-vector optimization |
120//!
121//! ### Matrix & Array Libraries
122//!
123//! | Feature | Types | Conversions |
124//! |---------|-------|-------------|
125//! | `ndarray` | `Array1`–`Array6`, `ArrayD`, views | R vectors/matrices ↔ ndarray |
126//! | `nalgebra` | `DVector`, `DMatrix` | R vectors/matrices ↔ nalgebra |
127//!
128//! ### Serialization
129//!
130//! | Feature | Traits/Modules | Description |
131//! |---------|----------------|-------------|
132//! | `serde` | `RSerializeNative`, `RDeserializeNative` | Direct Rust ↔ R native serialization |
133//! | `serde_json` | `RSerialize`, `RDeserialize` | JSON string serialization (includes `serde`) |
134//! | `borsh` | `Borsh<T>` | Binary serialization ↔ raw vectors via Borsh |
135//!
136//! ### Adapter Traits (Generic Operations)
137//!
138//! | Feature | Traits | Use Case |
139//! |---------|--------|----------|
140//! | `num-traits` | `RNum`, `RSigned`, `RFloat` | Generic numeric operations |
141//! | `bytes` | `RBuf`, `RBufMut` | Byte buffer operations |
142//!
143//! ### Text & Data Processing
144//!
145//! | Feature | Types/Functions | Description |
146//! |---------|-----------------|-------------|
147//! | `aho-corasick` | `AhoCorasick`, `aho_compile` | Fast multi-pattern string search |
148//! | `toml` | `TomlValue`, `toml_from_str` | TOML parsing and serialization |
149//! | `tabled` | `table_to_string` | ASCII/Unicode table formatting |
150//! | `sha2` | `sha256_str`, `sha512_bytes` | Cryptographic hashing |
151//!
152//! ### Random Number Generation
153//!
154//! | Feature | Types | Description |
155//! |---------|-------|-------------|
156//! | `rand` | `RRng`, `RDistributions` | Wraps R's RNG with `rand` traits |
157//! | `rand_distr` | Re-exports `rand_distr` | Additional distributions (Normal, Exp, etc.) |
158//!
159//! ### Binary Data
160//!
161//! | Feature | Types | Description |
162//! |---------|-------|-------------|
163//! | `raw_conversions` | `Raw<T>`, `RawSlice<T>` | POD types ↔ raw vectors via bytemuck |
164//!
165//! ### Project-wide Defaults (mutually exclusive where noted)
166//!
167//! | Feature | Description |
168//! |---------|-------------|
169//! | `default-r6` | Default class system: R6 (mutually exclusive with `default-s7`) |
170//! | `default-s7` | Default class system: S7 (mutually exclusive with `default-r6`) |
171//! | `default-worker` | Default to worker thread dispatch (implies `worker-thread`) |
172//! | `default-strict` | Default to strict mode for lossy integer conversions |
173//! | `default-coerce` | Default to coerce mode for type conversions |
174//!
175//! ### Development / Diagnostics
176//!
177//! | Feature | Description |
178//! |---------|-------------|
179//! | `doc-lint` | Warn on roxygen doc comment mismatches (enabled by default) |
180//! | `macro-coverage` | Expose macro coverage test module for `cargo expand` auditing |
181//! | `debug-preserve` | Enable `preserve::count()` diagnostic helpers (tests/benchmarks only) |
182//! | `growth-debug` | Track and report collection growth events (zero-cost when off) |
183//! | `refcount-fast-hash` | Use ahash for refcount arenas (enabled by default, not DOS-resistant) |
184// Re-export linkme for use by generated code (distributed_slice entries)
185#[doc(hidden)]
186pub use linkme;
187
188// Procedural macros (re-exported from miniextendr-macros)
189#[doc(hidden)]
190pub use miniextendr_macros::__mx_trait_impl_expand;
191#[doc(inline)]
192pub use miniextendr_macros::ExternalPtr;
193#[doc(inline)]
194pub use miniextendr_macros::RNativeType;
195#[doc(inline)]
196pub use miniextendr_macros::impl_typed_external;
197#[doc(inline)]
198pub use miniextendr_macros::list;
199#[doc(inline)]
200pub use miniextendr_macros::miniextendr;
201#[doc(inline)]
202pub use miniextendr_macros::miniextendr_init;
203#[doc(inline)]
204pub use miniextendr_macros::r_ffi_checked;
205#[doc(inline)]
206pub use miniextendr_macros::typed_list;
207// Note: RFactor derive macro is re-exported - it shares the name with the RFactor trait
208// but they're in different namespaces (derive macros vs types/traits)
209#[cfg(feature = "vctrs")]
210#[doc(inline)]
211pub use miniextendr_macros::Vctrs;
212// Note: MatchArg derive macro is re-exported - it shares the name with the MatchArg trait
213// but they're in different namespaces (derive macros vs types/traits), same as RFactor.
214#[doc(inline)]
215pub use miniextendr_macros::{
216    Altrep, AltrepComplex, AltrepInteger, AltrepList, AltrepLogical, AltrepRaw, AltrepReal,
217    AltrepString, DataFrameRow, IntoList, MatchArg, PreferDataFrame, PreferExternalPtr, PreferList,
218    PreferRNativeType, RFactor, TryFromList,
219};
220
221pub mod altrep;
222pub mod altrep_bridge;
223pub mod altrep_data;
224pub mod altrep_ext;
225pub mod altrep_impl;
226pub mod altrep_sexp;
227pub mod altrep_traits;
228
229// Re-export for backward compatibility - RegisterAltrep was moved from altrep_registration to altrep
230#[doc(hidden)]
231pub mod altrep_registration {
232    pub use crate::altrep::RegisterAltrep;
233}
234/// Raw R FFI bindings and low-level SEXP utilities.
235///
236/// Most users should prefer safe wrappers from higher-level modules.
237pub mod ffi;
238
239/// Automatic registration internals.
240///
241/// Items annotated with `#[miniextendr]` self-register at link time.
242/// The C entrypoint calls [`registry::miniextendr_register_routines`] to
243/// finalize registration with R. Users don't interact with this module.
244pub mod registry;
245
246/// Host-time generator of `wasm_registry.rs` — the WASM-side replacement for
247/// linkme. See the module for full rationale.
248///
249/// Host-only — the writer reads the live linkme distributed slices to format
250/// `wasm_registry.rs`, and linkme isn't available on wasm32 anyway.
251#[cfg(not(target_arch = "wasm32"))]
252pub mod wasm_registry_writer;
253
254// Re-export high-level ALTREP data traits
255pub use altrep_data::{
256    AltComplexData,
257    AltIntegerData,
258    AltListData,
259    AltLogicalData,
260    AltRawData,
261    AltRealData,
262    AltStringData,
263    AltrepDataptr,
264    AltrepExtract,
265    AltrepLen,
266    // Iterator-backed ALTREP types (R-native)
267    IterComplexData,
268    // Iterator-backed ALTREP types (with Coerce support)
269    IterIntCoerceData,
270    IterIntData,
271    IterIntFromBoolData,
272    IterListData,
273    IterLogicalData,
274    IterRawData,
275    IterRealCoerceData,
276    IterRealData,
277    IterState,
278    IterStringData,
279    Logical,
280    Sortedness,
281    // Sparse iterator-backed ALTREP types (compute-on-access)
282    SparseIterComplexData,
283    SparseIterIntData,
284    SparseIterLogicalData,
285    SparseIterRawData,
286    SparseIterRealData,
287    SparseIterState,
288    // Streaming ALTREP types (chunk-cached reader closures)
289    StreamingIntData,
290    StreamingRealData,
291    // Windowed iterator-backed ALTREP types
292    WindowedIterIntData,
293    WindowedIterRealData,
294    WindowedIterState,
295};
296// Re-export RBase enum, AltrepGuard, and AltrepSexp
297pub use altrep::RBase;
298pub use altrep_sexp::{AltrepSexp, ensure_materialized};
299pub use altrep_traits::AltrepGuard;
300
301// ALTREP package name global - set by C entrypoint before ALTREP registration
302// This is a pointer to a null-terminated C string provided by C code.
303// Default: c"unknown" for safety if not set.
304use std::sync::atomic::{AtomicPtr, Ordering};
305static ALTREP_PKG_NAME_PTR: AtomicPtr<std::ffi::c_char> =
306    AtomicPtr::new(c"unknown".as_ptr().cast_mut());
307
308/// Returns the current ALTREP package name as a C string pointer.
309/// This is set by the C entrypoint before ALTREP registration.
310#[doc(hidden)]
311pub struct AltrepPkgName;
312
313impl AltrepPkgName {
314    /// Get the package name pointer.
315    #[inline]
316    pub fn as_ptr() -> *const std::ffi::c_char {
317        ALTREP_PKG_NAME_PTR.load(Ordering::Acquire)
318    }
319}
320
321/// Opaque handle for ALTREP package name.
322/// Use `ALTREP_PKG_NAME.as_ptr()` to get the C string pointer.
323#[doc(hidden)]
324pub static ALTREP_PKG_NAME: AltrepPkgName = AltrepPkgName;
325
326/// Set the ALTREP package name. Called from C entrypoint.
327/// # Safety
328/// The provided pointer must point to a valid null-terminated C string
329/// that lives for the duration of the R session.
330///
331/// The strict requirement is narrower: R copies the bytes via `install()`
332/// inside each `R_make_alt*_class` call that consults this global (see
333/// `RegisterClass` in R's `src/main/altrep.c`), so the pointer only has to
334/// remain valid across those calls. We keep the session-lifetime contract
335/// because we don't track which registrations are still pending; a string
336/// literal passed from C satisfies both.
337#[doc(hidden)]
338#[unsafe(no_mangle)]
339pub unsafe extern "C" fn miniextendr_set_altrep_pkg_name(name: *const std::ffi::c_char) {
340    let name = if name.is_null() {
341        c"unknown".as_ptr()
342    } else {
343        name
344    };
345    ALTREP_PKG_NAME_PTR.store(name.cast_mut(), Ordering::Release);
346}
347
348// DllInfo global — stored during package_init, used by ALTREP class registration.
349// R needs DllInfo to associate ALTREP classes with their package for serialization.
350// Without it, readRDS in a fresh session can't find the class.
351static ALTREP_DLL_INFO: AtomicPtr<std::ffi::c_void> = AtomicPtr::new(std::ptr::null_mut());
352
353/// Get the stored DllInfo pointer for ALTREP class registration.
354#[doc(hidden)]
355pub fn altrep_dll_info() -> *mut ffi::DllInfo {
356    ALTREP_DLL_INFO.load(Ordering::Acquire).cast()
357}
358
359/// Store the DllInfo pointer during package init.
360#[doc(hidden)]
361pub fn set_altrep_dll_info(dll: *mut ffi::DllInfo) {
362    ALTREP_DLL_INFO.store(dll.cast(), Ordering::Release);
363}
364
365// Note: SexpExt is pub(crate), imported directly in modules that need it
366pub mod from_r;
367pub mod into_r;
368pub mod into_r_error;
369pub use into_r::{Altrep, IntoR, IntoRAltrep};
370pub use into_r_error::IntoRError;
371pub mod into_r_as;
372pub use into_r_as::{IntoRAs, StorageCoerceError};
373pub mod pump;
374pub mod unwind_protect;
375pub mod worker;
376
377// Re-export commonly used worker items at root for convenience
378pub use worker::{Sendable, is_r_main_thread, with_r_thread};
379
380// Required exports for generated code and initialization
381pub use worker::miniextendr_runtime_init;
382
383// Thread safety utilities for calling R from non-main threads
384pub mod thread;
385
386// Collection growth debug instrumentation (diagnostics)
387#[cfg(feature = "growth-debug")]
388pub mod growth_debug;
389
390/// Track a collection growth (reallocation) event.
391///
392/// When the `growth-debug` feature is enabled, increments a thread-local counter
393/// for the named collection. When disabled, compiles to a no-op.
394///
395/// # Example
396///
397/// ```ignore
398/// let old_cap = vec.capacity();
399/// vec.push(item);
400/// if vec.capacity() != old_cap {
401///     track_growth!("my_vec");
402/// }
403/// ```
404#[cfg(feature = "growth-debug")]
405#[macro_export]
406macro_rules! track_growth {
407    ($name:expr) => {
408        $crate::growth_debug::record_growth($name)
409    };
410}
411
412/// Track a collection growth (reallocation) event.
413///
414/// No-op when `growth-debug` feature is disabled.
415#[cfg(not(feature = "growth-debug"))]
416#[macro_export]
417macro_rules! track_growth {
418    ($name:expr) => {};
419}
420
421/// Print and reset all growth counters.
422///
423/// When the `growth-debug` feature is enabled, prints all tracked growth events
424/// to stderr and resets the counters. When disabled, compiles to a no-op.
425#[cfg(feature = "growth-debug")]
426#[macro_export]
427macro_rules! report_growth {
428    () => {
429        $crate::growth_debug::report_and_reset()
430    };
431}
432
433/// Print and reset all growth counters.
434///
435/// No-op when `growth-debug` feature is disabled.
436#[cfg(not(feature = "growth-debug"))]
437#[macro_export]
438macro_rules! report_growth {
439    () => {};
440}
441
442// `indicatif` progress integration (R console)
443#[cfg(feature = "indicatif")]
444pub mod progress;
445#[cfg(feature = "indicatif")]
446pub use indicatif;
447
448// Stack size constants and builder (always available)
449#[cfg(windows)]
450pub use thread::WINDOWS_R_STACK_SIZE;
451pub use thread::{DEFAULT_R_STACK_SIZE, RThreadBuilder};
452
453// Stack checking control (requires nonapi feature)
454#[cfg(feature = "nonapi")]
455pub use thread::{StackCheckGuard, scope_with_r, spawn_with_r, with_stack_checking_disabled};
456
457// Panic telemetry hook for structured panic→R-error diagnostics
458pub mod panic_telemetry;
459
460// Unified FFI guard for catching panics at Rust-R boundaries
461pub mod ffi_guard;
462pub use ffi_guard::{GuardMode, guarded_ffi_call, guarded_ffi_call_with_fallback};
463
464// Runtime wrapper for R data.frame objects
465pub mod dataframe;
466pub use dataframe::{DataFrameError, DataFrameView};
467
468// Strict conversion helpers for #[miniextendr(strict)]
469pub mod strict;
470
471// Cached R class attribute SEXPs (POSIXct, Date, data.frame, etc.)
472pub mod cached_class;
473
474// Error value transport for #[miniextendr(error_in_r)]
475pub mod error_value;
476
477// Error handling helpers (r_warning, r_print!, r_println! macros)
478pub mod error;
479pub use error::r_warning;
480
481// RNG (random number generation) utilities
482pub mod rng;
483pub use rng::{RngGuard, with_rng};
484
485// Re-export from_r
486pub use from_r::{SexpError, SexpLengthError, SexpNaError, SexpTypeError, TryFromSexp};
487
488// Encoding / locale probing (mainly for debugging). The module is always
489// compiled; the symbols that reference non-exported state from R's `Defn.h`
490// (`known_to_be_utf8`, `utf8locale`, ...) are gated inside the module behind
491// `#[cfg(feature = "nonapi")]` so a default build never links them.
492pub mod encoding;
493
494// Expression evaluation helpers (RSymbol, RCall, REnv)
495pub mod expression;
496pub use expression::{RCall, REnv, RSymbol};
497
498// S4 slot access and class checking helpers
499pub mod s4_helpers;
500
501// Note: RNativeType is pub(crate), imported directly in modules that need it
502
503pub mod backtrace;
504
505pub mod coerce;
506pub use coerce::{Coerce, CoerceError, Coerced, TryCoerce};
507
508/// Traits for R's `as.<class>()` coercion functions.
509///
510/// This module provides traits for implementing R's generic coercion methods
511/// (`as.data.frame()`, `as.list()`, `as.character()`, etc.) for Rust types
512/// wrapped in [`ExternalPtr`](struct@ExternalPtr).
513///
514/// See the [`as_coerce`] module documentation for usage examples.
515pub mod as_coerce;
516pub use as_coerce::{
517    // Core coercion traits
518    AsCharacter,
519    // Error type
520    AsCoerceError,
521    AsComplex,
522    AsDataFrame,
523    AsDate,
524    AsEnvironment,
525    AsFactor,
526    AsFunction,
527    AsInteger,
528    AsList as AsListCoerce,
529    AsLogical,
530    AsMatrix,
531    AsNumeric,
532    AsPOSIXct,
533    AsRaw,
534    AsVector,
535    // Helpers
536    SUPPORTED_AS_GENERICS,
537    is_supported_as_generic,
538};
539
540pub mod condition;
541pub use condition::{AsRError, RCondition};
542pub mod convert;
543/// Support for R `...` arguments represented as a validated list.
544pub mod dots;
545pub mod list;
546pub mod missing;
547pub mod named_vector;
548pub mod strvec;
549pub mod typed_list;
550pub use convert::{
551    AsDisplay, AsDisplayVec, AsExternalPtr, AsExternalPtrExt, AsFromStr, AsFromStrVec, AsList,
552    AsListExt, AsNamedList, AsNamedListExt, AsNamedVector, AsNamedVectorExt, AsRNative,
553    AsRNativeExt, Collect, CollectNA, CollectNAInt, CollectStrings, DataFrame, IntoDataFrame,
554    ToDataFrame, ToDataFrameExt,
555};
556#[cfg(feature = "serde")]
557pub use convert::{AsSerializeRow, SerializeDataFrame};
558pub use into_r::Lazy;
559pub use list::{
560    IntoList, List, ListAccumulator, ListBuilder, ListMut, NamedList, TryFromList, collect_list,
561};
562pub use missing::{Missing, is_missing_arg};
563pub use named_vector::{AtomicElement, NamedVector};
564pub use strvec::{
565    ProtectedStrVec, ProtectedStrVecCowIter, ProtectedStrVecIter, StrVec, StrVecBuilder,
566    StrVecCowIter, StrVecIter,
567};
568pub use typed_list::{
569    TypeSpec, TypedEntry, TypedList, TypedListError, TypedListSpec, actual_type_string,
570    sexptype_name, validate_list,
571};
572
573// External pointer module - Box-like owned pointer wrapping R's EXTPTRSXP
574pub mod externalptr;
575
576// Connection framework (unstable R API - use with caution)
577#[cfg(feature = "connections")]
578pub mod connection;
579// txtProgressBar handle — drives utils::txtProgressBar from Rust
580#[cfg(feature = "connections")]
581pub mod txt_progress_bar;
582pub use externalptr::{
583    ErasedExternalPtr,
584    ExternalPtr,
585    ExternalSlice,
586    IntoExternalPtr,
587    TypedExternal,
588    // ALTREP helpers (checked)
589    altrep_data1_as,
590    // ALTREP helpers (unchecked - for performance-critical callbacks)
591    altrep_data1_as_unchecked,
592    altrep_data1_mut,
593    altrep_data1_mut_unchecked,
594    altrep_data2_as,
595    altrep_data2_as_unchecked,
596};
597
598// TypedExternal implementations for std types
599pub mod externalptr_std;
600
601// Deprecated: DLL preserve list. Use ProtectPool or R_PreserveObject instead.
602// Kept for benchmark comparisons.
603pub mod preserve;
604
605// GC protection toolkit (PROTECT stack RAII wrappers)
606pub mod gc_protect;
607pub use gc_protect::{OwnedProtect, ProtectIndex, ProtectScope, Protector, ReprotectSlot, Root};
608
609// VECSXP pool with generational keys (slotmap-backed)
610pub mod protect_pool;
611pub use protect_pool::{ProtectKey, ProtectPool};
612
613// Reference-counted GC protection (BTreeMap + VECSXP backing)
614pub mod refcount_protect;
615pub use refcount_protect::{
616    Arena, ArenaGuard, HashMapArena, MapStorage, RefCountedArena, RefCountedGuard,
617    ThreadLocalArena, ThreadLocalArenaOps, ThreadLocalHashArena,
618};
619
620pub mod allocator;
621pub use allocator::RAllocator;
622
623pub mod r_memory;
624
625// region: Trait ABI Support
626//
627// Cross-package trait dispatch using a stable C ABI.
628// See `trait_abi` module docs for details.
629
630/// ABI types for cross-package trait dispatch.
631///
632/// This module defines the stable, C-compatible types used for runtime trait
633/// dispatch across R package boundaries.
634pub mod abi;
635
636/// C-callable mx_abi functions (mx_wrap, mx_get, mx_query, mx_abi_register).
637///
638/// These are registered via `R_RegisterCCallable` during package init and
639/// loaded by consumer packages via `R_GetCCallable`.
640pub mod mx_abi;
641
642/// Package initialization (`miniextendr_init!` support).
643///
644/// Consolidates all init steps into [`init::package_init`].
645pub mod init;
646
647/// Runtime support for trait ABI operations.
648///
649/// Provides C-callable loading and type conversion helpers for trait ABI support.
650pub mod trait_abi;
651
652/// vctrs class construction and trait support.
653///
654/// Provides helpers for building vctrs-compatible R objects and traits
655/// for describing vctrs class metadata from Rust types.
656///
657/// Enable with `features = ["vctrs"]`.
658#[cfg(feature = "vctrs")]
659pub mod vctrs;
660#[cfg(feature = "vctrs")]
661pub use vctrs::{
662    IntoVctrs, VctrsBuildError, VctrsClass, VctrsKind, VctrsListOf, VctrsRecord, new_list_of,
663    new_rcrd, new_vctr,
664};
665
666// Re-export key ABI types at crate root for convenience
667pub use abi::{mx_base_vtable, mx_erased, mx_meth, mx_tag};
668pub use trait_abi::TraitView;
669// endregion
670
671// region: Marker Traits
672//
673// Marker traits for types derived with proc-macros.
674// These enable compile-time identification and blanket implementations.
675
676/// Marker traits for proc-macro derived types.
677pub mod markers;
678pub use markers::{PrefersDataFrame, PrefersExternalPtr, PrefersList, PrefersRNativeType};
679// endregion
680
681// region: Adapter Traits
682//
683// Built-in adapter traits with blanket implementations for standard library traits.
684// These allow any Rust type implementing Debug, Display, Hash, Ord, etc. to be
685// exposed to R without boilerplate.
686
687/// Built-in adapter traits for std library traits.
688///
689/// Provides [`RDebug`], [`RDisplay`], [`RHash`], [`ROrd`], [`RPartialOrd`],
690/// [`RError`], [`RFromStr`], [`RClone`], [`RCopy`], [`RDefault`], [`RIterator`],
691/// [`RExtend`], and [`RFromIter`] with blanket implementations where possible.
692/// See module docs for usage.
693///
694/// [`RDebug`]: adapter_traits::RDebug
695/// [`RDisplay`]: adapter_traits::RDisplay
696/// [`RHash`]: adapter_traits::RHash
697/// [`ROrd`]: adapter_traits::ROrd
698/// [`RPartialOrd`]: adapter_traits::RPartialOrd
699/// [`RError`]: adapter_traits::RError
700/// [`RFromStr`]: adapter_traits::RFromStr
701/// [`RClone`]: adapter_traits::RClone
702/// [`RCopy`]: adapter_traits::RCopy
703/// [`RDefault`]: adapter_traits::RDefault
704/// [`RIterator`]: adapter_traits::RIterator
705/// [`RExtend`]: adapter_traits::RExtend
706/// [`RFromIter`]: adapter_traits::RFromIter
707pub mod adapter_traits;
708pub use adapter_traits::{
709    RClone, RCopy, RDebug, RDefault, RDisplay, RError, RExtend, RFromIter, RFromStr, RHash,
710    RIterator, RMakeIter, ROrd, RPartialOrd, RToVec,
711};
712
713/// This is used to ensure the macros of `miniextendr-macros` treat this crate as a "user crate"
714/// atleast in the `macro_coverage`
715#[doc(hidden)]
716extern crate self as miniextendr_api;
717
718#[cfg(feature = "macro-coverage")]
719#[doc(hidden)]
720pub mod macro_coverage;
721// endregion
722
723// region: Optional integrations with external crates (feature-gated)
724//
725// All optional feature integrations are organized in the `optionals` module.
726// Types are re-exported at crate root for backwards compatibility.
727
728/// Optional feature integrations with third-party crates.
729///
730/// This module contains all feature-gated integrations with external crates.
731/// Each submodule is only compiled when its corresponding feature is enabled.
732/// See the [module documentation][optionals] for a complete list of available features.
733pub mod optionals;
734
735// Re-export optional types at crate root for backwards compatibility
736#[cfg(feature = "rayon")]
737pub use optionals::rayon_bridge;
738#[cfg(feature = "rayon")]
739pub use optionals::{RParallelExtend, RParallelIterator};
740
741#[cfg(feature = "rand_distr")]
742pub use optionals::rand_distr;
743#[cfg(feature = "rand")]
744pub use optionals::rand_impl;
745#[cfg(feature = "rand")]
746pub use optionals::{RDistributionOps, RDistributions, RRng, RRngOps};
747
748#[cfg(feature = "either")]
749pub use optionals::either_impl;
750#[cfg(feature = "either")]
751pub use optionals::{Either, Left, Right};
752
753#[cfg(feature = "ndarray")]
754pub use optionals::ndarray_impl;
755#[cfg(feature = "ndarray")]
756pub use optionals::{
757    ArcArray1, ArcArray2, Array0, Array1, Array2, Array3, Array4, Array5, Array6, ArrayD,
758    ArrayView0, ArrayView1, ArrayView2, ArrayView3, ArrayView4, ArrayView5, ArrayView6, ArrayViewD,
759    ArrayViewMut0, ArrayViewMut1, ArrayViewMut2, ArrayViewMut3, ArrayViewMut4, ArrayViewMut5,
760    ArrayViewMut6, ArrayViewMutD, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, RNdArrayOps, RNdIndex,
761    RNdSlice, RNdSlice2D, ShapeBuilder,
762};
763
764#[cfg(feature = "nalgebra")]
765pub use optionals::nalgebra_impl;
766#[cfg(feature = "nalgebra")]
767pub use optionals::{DMatrix, DVector, RMatrixOps, RVectorOps, SMatrix, SVector};
768
769#[cfg(feature = "num-bigint")]
770pub use optionals::num_bigint_impl;
771#[cfg(feature = "num-bigint")]
772pub use optionals::{BigInt, BigUint, RBigIntBitOps, RBigIntOps, RBigUintBitOps, RBigUintOps};
773
774#[cfg(feature = "rust_decimal")]
775pub use optionals::rust_decimal_impl;
776#[cfg(feature = "rust_decimal")]
777pub use optionals::{Decimal, RDecimalOps};
778
779#[cfg(feature = "ordered-float")]
780pub use optionals::ordered_float_impl;
781#[cfg(feature = "ordered-float")]
782pub use optionals::{OrderedFloat, ROrderedFloatOps};
783
784#[cfg(feature = "num-complex")]
785pub use optionals::num_complex_impl;
786#[cfg(feature = "num-complex")]
787pub use optionals::{Complex, RComplexOps};
788
789#[cfg(feature = "num-traits")]
790pub use optionals::num_traits_impl;
791#[cfg(feature = "num-traits")]
792pub use optionals::{RFloat, RNum, RSigned};
793
794#[cfg(feature = "uuid")]
795pub use optionals::uuid_impl;
796#[cfg(feature = "uuid")]
797pub use optionals::{RUuidOps, Uuid, uuid_helpers};
798
799#[cfg(feature = "regex")]
800pub use optionals::regex_impl;
801#[cfg(feature = "regex")]
802pub use optionals::{CaptureGroups, RCaptureGroups, RRegexOps, Regex};
803
804#[cfg(feature = "url")]
805pub use optionals::url_impl;
806#[cfg(feature = "url")]
807pub use optionals::{RUrlOps, Url, url_helpers};
808
809#[cfg(feature = "aho-corasick")]
810pub use optionals::aho_corasick_impl;
811#[cfg(feature = "aho-corasick")]
812pub use optionals::{
813    AhoCorasick, RAhoCorasickOps, aho_compile, aho_count_matches, aho_find_all, aho_find_all_flat,
814    aho_find_first, aho_is_match, aho_replace_all,
815};
816
817#[cfg(feature = "indexmap")]
818pub use optionals::indexmap_impl;
819#[cfg(feature = "indexmap")]
820pub use optionals::{IndexMap, RIndexMapOps};
821
822#[cfg(feature = "time")]
823pub use optionals::time_impl;
824#[cfg(feature = "time")]
825pub use optionals::{Date, Duration, OffsetDateTime, RDateTimeFormat, RDuration};
826#[cfg(feature = "time")]
827pub use time;
828
829#[cfg(feature = "jiff")]
830pub use jiff;
831#[cfg(feature = "jiff")]
832pub use optionals::jiff_impl;
833#[cfg(feature = "jiff")]
834pub use optionals::{
835    JiffDate, JiffDateTime, JiffTime, JiffTimestampVec, JiffZonedVec, RDate, RDateTime,
836    RSignedDuration, RSpan, RTime, RTimestamp, RZoned, SignedDuration, Span, Timestamp, Zoned,
837};
838
839#[cfg(feature = "serde_json")]
840pub use optionals::serde_impl;
841#[cfg(feature = "toml")]
842pub use optionals::toml_impl;
843#[cfg(feature = "serde_json")]
844pub use optionals::{
845    FactorHandling, JsonOptions, JsonValue, NaHandling, RDeserialize, RJsonBridge, RJsonValueOps,
846    RSerialize, SpecialFloatHandling, json_from_sexp, json_from_sexp_permissive,
847    json_from_sexp_strict, json_from_sexp_with, json_into_sexp,
848};
849#[cfg(feature = "toml")]
850pub use optionals::{RTomlOps, TomlValue, toml_from_str, toml_to_string, toml_to_string_pretty};
851
852#[cfg(feature = "bytes")]
853pub use optionals::bytes_impl;
854#[cfg(feature = "bytes")]
855pub use optionals::{Buf, BufMut, Bytes, BytesMut, RBuf, RBufMut};
856
857#[cfg(feature = "sha2")]
858pub use optionals::sha2_impl;
859#[cfg(feature = "sha2")]
860pub use optionals::{sha256_bytes, sha256_str, sha512_bytes, sha512_str};
861
862#[cfg(feature = "borsh")]
863pub use optionals::borsh_impl;
864#[cfg(feature = "borsh")]
865pub use optionals::{Borsh, RBorshOps, borsh_from_raw, borsh_to_raw};
866
867#[cfg(feature = "bitflags")]
868pub use bitflags;
869#[cfg(feature = "bitflags")]
870pub use optionals::bitflags_impl;
871#[cfg(feature = "bitflags")]
872pub use optionals::{Flags, RFlags};
873
874#[cfg(feature = "bitvec")]
875pub use optionals::bitvec_impl;
876#[cfg(feature = "bitvec")]
877pub use optionals::{BitVec, Lsb0, Msb0, RBitVec};
878
879#[cfg(feature = "tabled")]
880pub use optionals::tabled_impl;
881#[cfg(feature = "tabled")]
882pub use optionals::{
883    Builder, Table, Tabled, builder_to_string, table_from_vecs, table_to_string,
884    table_to_string_opts, table_to_string_styled,
885};
886
887#[cfg(feature = "tinyvec")]
888pub use optionals::tinyvec_impl;
889#[cfg(feature = "tinyvec")]
890pub use optionals::{Array, ArrayVec, TinyVec};
891
892#[cfg(feature = "arrow")]
893pub use optionals::arrow_impl;
894#[cfg(feature = "arrow")]
895pub use optionals::{
896    ArrayRef, ArrowArray, BooleanArray, DataType, Date32Array, DictionaryArray, Field,
897    Float64Array, Int32Array, RecordBatch, Schema, StringArray, StringDictionaryArray,
898    TimestampSecondArray, UInt8Array,
899};
900
901#[cfg(feature = "datafusion")]
902pub use optionals::RSessionContext;
903#[cfg(feature = "datafusion")]
904pub use optionals::datafusion_impl;
905
906/// N-dimensional R arrays with const generic dimension count.
907pub mod rarray;
908pub use rarray::{RArray, RArray3D, RMatrix, RVector};
909
910/// Direct R serialization via serde (no JSON intermediate).
911///
912/// Provides efficient type-preserving conversions between Rust types and native R objects:
913/// - [`AsSerialize<T>`][serde::AsSerialize] - Wrapper for returning `Serialize` types from `#[miniextendr]` functions
914/// - [`RSerializeNative`][serde::RSerializeNative] - Convert Rust → R (struct → named list)
915/// - [`RDeserializeNative`][serde::RDeserializeNative] - Convert R → Rust (named list → struct)
916///
917/// Enable with `features = ["serde"]`.
918///
919/// See the [`serde`] module documentation for type mappings and examples.
920#[cfg(feature = "serde")]
921pub mod serde;
922/// Re-export the upstream `serde` crate (aliased to avoid conflict with [`mod serde`]).
923///
924/// Downstream crates can use `miniextendr_api::serde_crate::{Serialize, Deserialize}`
925/// and `#[serde(crate = "miniextendr_api::serde_crate")]` to avoid a direct `serde` dep.
926#[cfg(feature = "serde")]
927pub use ::serde as serde_crate;
928
929/// Integration with the `bytemuck` crate for POD type conversions.
930///
931/// Provides explicit, safe conversions between Rust POD (Plain Old Data) types
932/// and R raw vectors:
933/// - `Raw<T>` - Single POD value (headerless, native layout)
934/// - `RawSlice<T>` - Sequence of POD values (headerless)
935/// - `RawTagged<T>` / `RawSliceTagged<T>` - With header metadata
936///
937/// Enable with `features = ["raw_conversions"]`.
938///
939/// ```ignore
940/// use bytemuck::{Pod, Zeroable};
941/// use miniextendr_api::raw_conversions::{Raw, RawSlice};
942///
943/// #[derive(Copy, Clone, Pod, Zeroable)]
944/// #[repr(C)]
945/// struct Vec3 { x: f32, y: f32, z: f32 }
946///
947/// #[miniextendr]
948/// fn encode(x: f64, y: f64, z: f64) -> Raw<Vec3> {
949///     Raw(Vec3 { x: x as f32, y: y as f32, z: z as f32 })
950/// }
951/// ```
952#[cfg(feature = "raw_conversions")]
953pub mod raw_conversions;
954#[cfg(feature = "raw_conversions")]
955pub use raw_conversions::{
956    Pod, Raw, RawError, RawHeader, RawSlice, RawSliceTagged, RawTagged, Zeroable, raw_from_bytes,
957    raw_slice_from_bytes, raw_slice_to_bytes, raw_to_bytes,
958};
959
960/// `match.arg`-style string conversion for enums.
961///
962/// Provides the [`MatchArg`] trait for converting Rust enums to/from R character
963/// strings with partial matching, like R's `match.arg()`.
964/// Use `#[derive(MatchArg)]` on C-style enums to auto-generate the implementation.
965pub mod match_arg;
966pub use match_arg::{
967    MatchArg, MatchArgError, choices_sexp, match_arg_from_sexp, match_arg_vec_from_sexp,
968    match_arg_vec_into_sexp,
969};
970
971/// Factor support for enum ↔ R factor conversions.
972///
973/// Provides the [`RFactor`] trait for converting Rust enums to/from R factors.
974/// Use `#[derive(RFactor)]` on C-style enums to auto-generate the implementation.
975///
976/// # Example
977///
978/// ```ignore
979/// use miniextendr_api::RFactor;
980///
981/// #[derive(Copy, Clone, RFactor)]
982/// enum Color { Red, Green, Blue }
983///
984/// #[miniextendr]
985/// fn color_name(c: Color) -> &'static str {
986///     match c {
987///         Color::Red => "red",
988///         Color::Green => "green",
989///         Color::Blue => "blue",
990///     }
991/// }
992/// ```
993pub mod factor;
994pub use factor::{
995    Factor, FactorMut, FactorOptionVec, FactorVec, RFactor, UnitEnumFactor, build_factor,
996    build_levels_sexp, build_levels_sexp_cached, factor_from_sexp,
997};
998
999/// Convenience re-exports for common miniextendr items.
1000///
1001/// A single `use miniextendr_api::prelude::*;` brings into scope the most
1002/// commonly used macros, traits, types, and helpers.
1003pub mod prelude;
1004// endregion