Skip to main content

miniextendr_api/
convert.rs

1//! Wrapper helpers to force specific `IntoR` representations.
2//!
3//! This module provides two approaches for controlling how Rust types are converted to R:
4//!
5//! ## 1. `As*` Wrappers (Call-site Control)
6//!
7//! Use these wrappers when you want to override the conversion for a single return value:
8//!
9//! - [`AsList<T>`]: Convert `T` to an R list via [`IntoList`]
10//! - [`AsExternalPtr<T>`]: Convert `T` to an R external pointer
11//! - [`AsRNative<T>`]: Convert scalar `T` to a length-1 R vector
12//!
13//! ```ignore
14//! #[miniextendr]
15//! fn get_data() -> AsList<MyStruct> {
16//!     AsList(MyStruct { x: 1, y: 2 })
17//! }
18//! ```
19//!
20//! ## 2. `Prefer*` Derive Macros (Type-level Control)
21//!
22//! Use these derives when a type should *always* use a specific conversion:
23//!
24//! - `#[derive(IntoList, PreferList)]`: Type always converts to R list
25//! - `#[derive(ExternalPtr)]`: Type always converts to external pointer
26//!   (`PreferExternalPtr` is for a hand-written `TypedExternal` impl)
27//! - `#[derive(RNativeType, PreferRNativeType)]`: Newtype always converts to native R scalar
28//!
29//! ```ignore
30//! #[derive(IntoList, PreferList)]
31//! struct Point { x: f64, y: f64 }
32//!
33//! #[miniextendr]
34//! fn make_point() -> Point {  // Automatically becomes R list
35//!     Point { x: 1.0, y: 2.0 }
36//! }
37//! ```
38//!
39//! ## 3. `#[miniextendr(return = "...")]` Attribute
40//!
41//! Use this when you want to control conversion for a specific `#[miniextendr]` function
42//! without modifying the type itself:
43//!
44//! - `return = "list"`: Wrap result in `AsList`
45//! - `return = "externalptr"`: Wrap result in `AsExternalPtr`
46//! - `return = "native"`: Wrap result in `AsRNative`
47//!
48//! ```ignore
49//! #[miniextendr(return = "list")]
50//! fn get_as_list() -> MyStruct {
51//!     MyStruct { x: 1 }
52//! }
53//! ```
54//!
55//! ## Choosing the Right Approach
56//!
57//! | Situation | Recommended Approach |
58//! |-----------|---------------------|
59//! | Type should *always* convert one way | `Prefer*` derive |
60//! | Override conversion for one function | `As*` wrapper or `return` attribute |
61//! | Type has multiple valid representations | Don't use `Prefer*`; use `As*` or `return` |
62//!
63//! ## A note on the `Collect*` adapters (naming-convention boundary)
64//!
65//! This module also exposes [`Collect`], [`CollectStrings`], [`CollectNA`], and
66//! [`CollectNAInt`]. These are representation-forcing `IntoR` wrappers in the same
67//! spirit as the `As*` family, but they deliberately **do not** carry the `As*`
68//! prefix. The distinction is structural: the `As*` wrappers wrap a *finished value*
69//! `T` and re-route its conversion (`AsList<T>`, `AsExternalPtr<T>`, `AsRNative<T>`),
70//! whereas the `Collect*` wrappers wrap an *iterator* `I: ExactSizeIterator` and
71//! materialize it directly into a freshly allocated R vector. The `As*`-of-`T` shape
72//! ("convert this value *as* a list / pointer / native scalar") does not describe what
73//! these do — `Collect` names the operation (drain an iterator into an R vector),
74//! which is the more accurate verb. This divergence is intentional and tracked; see
75//! the conversion control-surface analysis (`analysis/conversion-control-surface-2026-06-07.md`,
76//! §3.4 / §4.5) and issue #871.
77
78/// `IntoR` for `Option<marker>` of a reading marker: `Some` converts the
79/// marker, `None` gives `NULL`, as for the other containers. The marker's
80/// `Option` `TryFromSexp` reads `NULL` back as `None`.
81macro_rules! impl_option_into_r_null {
82    ($t:ty) => {
83        impl $crate::into_r::IntoR for Option<$t> {
84            type Error = <$t as $crate::into_r::IntoR>::Error;
85            #[inline]
86            fn try_into_sexp(self) -> Result<$crate::SEXP, Self::Error> {
87                match self {
88                    Some(v) => v.try_into_sexp(),
89                    None => Ok($crate::SEXP::nil()),
90                }
91            }
92            #[inline]
93            unsafe fn try_into_sexp_unchecked(self) -> Result<$crate::SEXP, Self::Error> {
94                match self {
95                    Some(v) => unsafe { v.try_into_sexp_unchecked() },
96                    None => Ok($crate::SEXP::nil()),
97                }
98            }
99            #[inline]
100            fn into_sexp(self) -> $crate::SEXP {
101                match self {
102                    Some(v) => v.into_sexp(),
103                    None => $crate::SEXP::nil(),
104                }
105            }
106            #[inline]
107            unsafe fn into_sexp_unchecked(self) -> $crate::SEXP {
108                match self {
109                    Some(v) => unsafe { v.into_sexp_unchecked() },
110                    None => $crate::SEXP::nil(),
111                }
112            }
113        }
114    };
115}
116
117mod as_character;
118mod as_numeric;
119pub(crate) use as_character::read_character;
120pub use as_character::{AsCharacter, AsCharacterVec};
121pub use as_numeric::{AsNumeric, AsNumericVec};
122
123use crate::RNativeType;
124use crate::externalptr::{ExternalPtr, IntoExternalPtr};
125use crate::into_r::IntoR;
126use crate::list::{IntoList, List};
127use crate::named_vector::AtomicElement;
128
129/// Wrap a value and convert it to an R list via [`IntoList`] when returned from Rust.
130///
131/// Use this wrapper when you want to convert a single value to an R list without
132/// making that the default behavior for the type.
133///
134/// # Example
135///
136/// ```ignore
137/// #[derive(IntoList)]
138/// struct Point { x: f64, y: f64 }
139///
140/// #[miniextendr]
141/// fn make_point() -> AsList<Point> {
142///     AsList(Point { x: 1.0, y: 2.0 })
143/// }
144/// // In R: make_point() returns list(x = 1.0, y = 2.0)
145/// ```
146#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug + IntoList> ::core::fmt::Debug for AsList<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "AsList",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone + IntoList> ::core::clone::Clone for AsList<T> {
    #[inline]
    fn clone(&self) -> Self { AsList(::core::clone::Clone::clone(&self.0)) }
}Clone, #[automatically_derived]
impl<T: ::core::marker::Copy + IntoList> ::core::marker::Copy for AsList<T> {
}Copy)]
147pub struct AsList<T: IntoList>(pub T);
148
149impl<T: IntoList> From<T> for AsList<T> {
150    fn from(value: T) -> Self {
151        AsList(value)
152    }
153}
154
155impl<T: IntoList> IntoR for AsList<T> {
156    type Error = std::convert::Infallible;
157
158    #[inline]
159    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
160        Ok(self.into_sexp())
161    }
162
163    #[inline]
164    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
165        self.try_into_sexp()
166    }
167
168    #[inline]
169    fn into_sexp(self) -> crate::SEXP {
170        self.0.into_list().into_sexp()
171    }
172}
173
174// The historical public `convert::IntoDataFrame` (`-> List`) is retired. Its row→List engine
175// now lives in `crate::dataframe::ColumnSource` (an internal `#[doc(hidden)]` trait that the
176// enum-flatten codegen and the new public `dataframe::IntoDataFrame` both delegate to). The
177// public verb surface is `dataframe::{IntoDataFrame, FromDataFrame}` (returning
178// `Result<BuiltDataFrame, DataFrameError>`), which mirrors `IntoR` / `TryFromSexp`.
179pub use crate::dataframe::ColumnSource;
180
181// region: Column gather/scatter helpers
182
183/// Scatter a typed column SEXP from a dense inner data frame into a new
184/// SEXP of length `n_rows`, placing `NA`/`NULL` at rows not in `present_idx`.
185///
186/// This is called by `DataFrameRow`-derived enum code to flatten struct-typed
187/// variant fields into prefixed columns of the parent data frame.
188///
189/// The output type mirrors the input:
190/// - REALSXP → REALSXP (NA_real_ fill)
191/// - INTSXP  → INTSXP  (NA_integer_ fill)
192/// - LGLSXP  → LGLSXP  (NA_logical fill)
193/// - RAWSXP  → RAWSXP  (`0x00` fill — R raw has no NA)
194/// - CPLXSXP → CPLXSXP (NA complex fill: both parts NA_real_)
195/// - STRSXP  → STRSXP  (NA_character_ fill)
196/// - VECSXP  → VECSXP  (R_NilValue fill)
197/// - anything else → VECSXP (R_NilValue fill)
198///
199/// Contiguous primitive columns (real/integer/logical/raw/complex) scatter as a
200/// flat slice write via [`scatter_native`] — the NA-filled inverse of
201/// [`gather_native`]. String and list columns stay element-by-element because
202/// they are write-barriered arrays of `SEXP` pointers, not flat buffers.
203///
204/// When the output type mirrors the input, the column keeps every attribute
205/// except names/dim/dimnames, as `DataFrame::select_rows` does on the way back:
206/// a factor keeps its `levels` and `class` (absent rows are `NA`), a `Date` or
207/// `POSIXct` column its `class` and `tzone`.
208///
209/// # Safety
210///
211/// Must be called on the R main thread. `src` must be a valid SEXP of length
212/// `>= present_idx.len()`, kept reachable by the caller across this call.
213/// `n_rows` must equal the total row count of the parent data frame.
214#[doc(hidden)]
215pub unsafe fn scatter_column(
216    src: crate::SEXP,
217    present_idx: &[usize],
218    n_rows: usize,
219) -> crate::SEXP {
220    // SAFETY: caller guarantees R main thread; src is valid; n_rows is correct.
221    #[allow(unused_unsafe)]
222    unsafe {
223        use crate::{RLogical, Rcomplex, SEXPTYPE, SexpExt as _};
224
225        let out = match src.type_of() {
226            SEXPTYPE::REALSXP => scatter_native::<f64>(src, present_idx, n_rows),
227            SEXPTYPE::INTSXP => scatter_native::<i32>(src, present_idx, n_rows),
228            SEXPTYPE::LGLSXP => scatter_native::<RLogical>(src, present_idx, n_rows),
229            SEXPTYPE::RAWSXP => scatter_native::<u8>(src, present_idx, n_rows),
230            SEXPTYPE::CPLXSXP => scatter_native::<Rcomplex>(src, present_idx, n_rows),
231            SEXPTYPE::STRSXP => {
232                let out = crate::sys::Rf_allocVector(SEXPTYPE::STRSXP, n_rows as isize);
233                // Write-barriered CHARSXP-pointer array — fill NA_character_, then
234                // scatter present cells element-by-element.
235                for i in 0..(n_rows as isize) {
236                    out.set_string_elt(i, crate::SEXP::na_string());
237                }
238                for (pi, &row_i) in present_idx.iter().enumerate() {
239                    out.set_string_elt(row_i as isize, src.string_elt(pi as isize));
240                }
241                out
242            }
243            SEXPTYPE::VECSXP => {
244                let out = crate::sys::Rf_allocVector(SEXPTYPE::VECSXP, n_rows as isize);
245                // Write-barriered SEXP-pointer array. R_NilValue fill is automatic
246                // (Rf_allocVector zero-initialises VECSXP slots).
247                for (pi, &row_i) in present_idx.iter().enumerate() {
248                    out.set_vector_elt(row_i as isize, src.vector_elt(pi as isize));
249                }
250                out
251            }
252            _ => {
253                // Unknown/unsupported type — produce a VECSXP list-column.
254                // Cells for absent rows remain R_NilValue.
255                let out = crate::sys::Rf_allocVector(SEXPTYPE::VECSXP, n_rows as isize);
256                for (pi, &row_i) in present_idx.iter().enumerate() {
257                    out.set_vector_elt(row_i as isize, src.vector_elt(pi as isize));
258                }
259                return out;
260            }
261        };
262        // Copying the attributes allocates, so root `out` until it returns.
263        let out = crate::OwnedProtect::new(out);
264        out.copy_most_attrib(src);
265        *out
266    }
267}
268
269/// Scatter a dense typed column into a fresh SEXP of length `n_rows`, placing the
270/// `j`-th source value at row `present_idx[j]` and the per-type NA sentinel
271/// ([`RNativeType::R_NA`]) at every absent row.
272///
273/// The sparse-placing inverse of [`gather_native`]. Generic over the R element
274/// type: `f64`/`i32`/`RLogical`/`u8`/`Rcomplex` each resolve to the correct
275/// storage via their `RNativeType` impl, so the whole copy is a flat slice write
276/// with no per-element FFI call. Raw (`u8`) has no R NA, so absent positions
277/// become `0x00`.
278///
279/// # Safety
280///
281/// R main thread; `src` must be a `T::SEXP_TYPE` vector with at least
282/// `present_idx.len()` elements, and every index in `present_idx` must be
283/// `< n_rows`. The returned SEXP is unprotected (see [`scatter_column`]). No
284/// allocation occurs between allocating `out` and filling it, so `out` cannot be
285/// reaped mid-scatter.
286#[inline]
287unsafe fn scatter_native<T: RNativeType + Copy>(
288    src: crate::SEXP,
289    present_idx: &[usize],
290    n_rows: usize,
291) -> crate::SEXP {
292    unsafe {
293        use crate::SexpExt as _;
294        let src_vals: &[T] = src.as_slice::<T>();
295        let out = crate::sys::Rf_allocVector(T::SEXP_TYPE, n_rows as isize);
296        let out_vals: &mut [T] = out.as_mut_slice::<T>();
297        // NA-fill the whole output, then place present values. No allocation
298        // between alloc and fill — `out` is GC-safe.
299        out_vals.fill(T::R_NA);
300        for (pi, &row_i) in present_idx.iter().enumerate() {
301            out_vals[row_i] = src_vals[pi];
302        }
303        out
304    }
305}
306
307/// Gather the rows at `idx` (0-based, in order) from a contiguous primitive
308/// column into a fresh dense vector, where `out[j] = src[idx[j]]`.
309///
310/// Generic over the R element type: `f64`/`i32`/`RLogical`/`u8`/`Rcomplex` each
311/// resolve to the correct `REAL`/`INTEGER`/`LOGICAL`/`RAW`/`COMPLEX` storage via
312/// their `RNativeType` impl, so the copy is a plain slice gather with no
313/// per-element FFI call.
314///
315/// # Safety
316///
317/// R main thread; `src` must be a `T::SEXP_TYPE` vector and every index in `idx`
318/// must be `< xlength(src)`. The returned SEXP is unprotected (see
319/// [`gather_column`]). No allocation occurs between allocating `out` and filling
320/// it, so `out` cannot be reaped mid-gather.
321#[inline]
322unsafe fn gather_native<T: RNativeType + Copy>(src: crate::SEXP, idx: &[usize]) -> crate::SEXP {
323    unsafe {
324        use crate::SexpExt as _;
325        let src_vals: &[T] = src.as_slice::<T>();
326        let out = crate::sys::Rf_allocVector(T::SEXP_TYPE, idx.len() as isize);
327        let out_vals: &mut [T] = out.as_mut_slice::<T>();
328        for (dst, &row_i) in out_vals.iter_mut().zip(idx) {
329            *dst = src_vals[row_i];
330        }
331        out
332    }
333}
334
335/// Gather the elements at `idx` (0-based, in order) out of a typed vector SEXP
336/// into a new dense SEXP of length `idx.len()`, where `out[j] = src[idx[j]]`.
337///
338/// The selecting inverse of [`scatter_column`]: where `scatter_column` places a
339/// dense column's values at sparse positions, `gather_column` pulls a dense
340/// subset out of a vector by element index. An element is a row only for a
341/// plain vector or list column: `DataFrame::select_rows` expands a row index to
342/// the element indices of every matrix column and recurses into data.frame
343/// columns before calling this.
344///
345/// Contiguous primitive columns (real/integer/logical/raw/complex) are copied as
346/// a slice gather via [`gather_native`]; string and list columns are copied
347/// element-by-element because they are write-barriered arrays of `SEXP` pointers,
348/// not flat buffers. The output type mirrors the input; any other type falls back
349/// to a logical `NA` column (normal `data.frame` columns never reach it).
350///
351/// Column attributes (`class`, `levels`, `tzone`, element `names`, …) are **not**
352/// copied — the caller restores them after rooting the gathered column
353/// (`DataFrame::select_rows` uses [`SexpExt::copy_most_attrib`](crate::SexpExt::copy_most_attrib)
354/// plus a names gather).
355///
356/// # Safety
357///
358/// Must be called on the R main thread. `src` must be a valid SEXP and every
359/// index in `idx` must be `< xlength(src)`. The returned SEXP is unprotected;
360/// the caller must root it (e.g. via `SET_VECTOR_ELT` into a protected list)
361/// before performing any allocation.
362#[doc(hidden)]
363pub unsafe fn gather_column(src: crate::SEXP, idx: &[usize]) -> crate::SEXP {
364    // SAFETY: caller guarantees R main thread, a valid `src`, and in-range indices.
365    #[allow(unused_unsafe)]
366    unsafe {
367        use crate::{RLogical, Rcomplex, SEXPTYPE, SexpExt as _};
368
369        match src.type_of() {
370            SEXPTYPE::REALSXP => gather_native::<f64>(src, idx),
371            SEXPTYPE::INTSXP => gather_native::<i32>(src, idx),
372            SEXPTYPE::LGLSXP => gather_native::<RLogical>(src, idx),
373            SEXPTYPE::RAWSXP => gather_native::<u8>(src, idx),
374            SEXPTYPE::CPLXSXP => gather_native::<Rcomplex>(src, idx),
375            SEXPTYPE::STRSXP => {
376                // Write-barriered CHARSXP-pointer array — copy element-by-element.
377                let out = crate::sys::Rf_allocVector(SEXPTYPE::STRSXP, idx.len() as isize);
378                for (j, &row_i) in idx.iter().enumerate() {
379                    out.set_string_elt(j as isize, src.string_elt(row_i as isize));
380                }
381                out
382            }
383            SEXPTYPE::VECSXP => {
384                // Write-barriered SEXP-pointer array (list-column) — element-by-element.
385                let out = crate::sys::Rf_allocVector(SEXPTYPE::VECSXP, idx.len() as isize);
386                for (j, &row_i) in idx.iter().enumerate() {
387                    out.set_vector_elt(j as isize, src.vector_elt(row_i as isize));
388                }
389                out
390            }
391            _ => {
392                // Unknown SEXPTYPE: fall back to a logical NA column of the right length.
393                let out = crate::sys::Rf_allocVector(SEXPTYPE::LGLSXP, idx.len() as isize);
394                let na_vals: &mut [RLogical] = out.as_mut_slice::<RLogical>();
395                na_vals.fill(RLogical::NA);
396                out
397            }
398        }
399    }
400}
401// endregion
402
403/// Wrap a value and convert it to an R external pointer when returned from Rust.
404///
405/// Use this wrapper when you want to return a Rust value as an opaque pointer
406/// that R code can pass back to Rust functions later.
407///
408/// # Example
409///
410/// ```ignore
411/// struct Connection { handle: u64 }
412///
413/// impl IntoExternalPtr for Connection { /* ... */ }
414///
415/// #[miniextendr]
416/// fn open_connection(path: &str) -> AsExternalPtr<Connection> {
417///     AsExternalPtr(Connection { handle: 42 })
418/// }
419/// // In R: open_connection("foo") returns an external pointer
420/// ```
421#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug + IntoExternalPtr> ::core::fmt::Debug for
    AsExternalPtr<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "AsExternalPtr",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone + IntoExternalPtr> ::core::clone::Clone for
    AsExternalPtr<T> {
    #[inline]
    fn clone(&self) -> Self {
        AsExternalPtr(::core::clone::Clone::clone(&self.0))
    }
}Clone, #[automatically_derived]
impl<T: ::core::marker::Copy + IntoExternalPtr> ::core::marker::Copy for
    AsExternalPtr<T> {
}Copy)]
422pub struct AsExternalPtr<T: IntoExternalPtr>(pub T);
423
424impl<T: IntoExternalPtr> From<T> for AsExternalPtr<T> {
425    fn from(value: T) -> Self {
426        AsExternalPtr(value)
427    }
428}
429
430impl<T: IntoExternalPtr> IntoR for AsExternalPtr<T> {
431    type Error = std::convert::Infallible;
432
433    #[inline]
434    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
435        Ok(self.into_sexp())
436    }
437
438    #[inline]
439    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
440        self.try_into_sexp()
441    }
442
443    #[inline]
444    fn into_sexp(self) -> crate::SEXP {
445        ExternalPtr::new(self.0).into_sexp()
446    }
447}
448
449/// Wrap a scalar [`RNativeType`] and force native R vector conversion.
450///
451/// This creates a length-1 R vector containing the scalar value. Use this when
452/// you want to ensure a value is converted to its native R representation (e.g.,
453/// `i32` → integer vector, `f64` → numeric vector) rather than another path
454/// like `IntoExternalPtr`.
455///
456/// # Example
457///
458/// ```ignore
459/// #[repr(transparent)]
460/// #[derive(Clone, Copy, RNativeType)]
461/// struct Meters(f64);
462///
463/// #[miniextendr]
464/// fn distance() -> AsRNative<Meters> {
465///     AsRNative(Meters(42.5))
466/// }
467/// // In R: distance() returns 42.5 (numeric vector of length 1)
468/// ```
469///
470/// # Performance
471///
472/// This wrapper directly allocates an R vector and writes the value,
473/// avoiding intermediate Rust allocations.
474#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug + RNativeType> ::core::fmt::Debug for AsRNative<T>
    {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "AsRNative",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone + RNativeType> ::core::clone::Clone for
    AsRNative<T> {
    #[inline]
    fn clone(&self) -> Self {
        AsRNative(::core::clone::Clone::clone(&self.0))
    }
}Clone, #[automatically_derived]
impl<T: ::core::marker::Copy + RNativeType> ::core::marker::Copy for
    AsRNative<T> {
}Copy)]
475pub struct AsRNative<T: RNativeType>(pub T);
476
477impl<T: RNativeType> From<T> for AsRNative<T> {
478    fn from(value: T) -> Self {
479        AsRNative(value)
480    }
481}
482
483impl<T: RNativeType> IntoR for AsRNative<T> {
484    type Error = std::convert::Infallible;
485
486    #[inline]
487    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
488        Ok(self.into_sexp())
489    }
490
491    #[inline]
492    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
493        Ok(unsafe { self.into_sexp_unchecked() })
494    }
495
496    #[inline]
497    fn into_sexp(self) -> crate::SEXP {
498        // Directly allocate a length-1 R vector and write the scalar value.
499        // This avoids the intermediate Rust Vec allocation.
500        unsafe {
501            let sexp = crate::sys::Rf_allocVector(T::SEXP_TYPE, 1);
502            let ptr = T::dataptr_mut(sexp);
503            std::ptr::write(ptr, self.0);
504            sexp
505        }
506    }
507
508    #[inline]
509    unsafe fn into_sexp_unchecked(self) -> crate::SEXP {
510        unsafe {
511            let sexp = crate::sys::Rf_allocVector_unchecked(T::SEXP_TYPE, 1);
512            let ptr = T::dataptr_mut(sexp);
513            std::ptr::write(ptr, self.0);
514            sexp
515        }
516    }
517}
518// endregion
519
520// region: DataFrame / Vctrs representation wrappers
521
522/// Wrap a value and convert it to an R `data.frame` via [`IntoDataFrame`](crate::dataframe::IntoDataFrame) when returned.
523///
524/// The inner `T` is typically a `Vec<Row>` where `Row` derives
525/// [`DataFrameRow`](crate::markers::DataFrameRow). To return a frame directly,
526/// return the generated `{Row}DataFrame` companion or build a
527/// [`BuiltDataFrame`](crate::dataframe::BuiltDataFrame) with `IntoDataFrame`.
528///
529/// A failed conversion ([`DataFrameError`](crate::dataframe::DataFrameError)) surfaces in R as
530/// an error condition.
531///
532/// # Example
533///
534/// ```ignore
535/// #[derive(DataFrameRow)]
536/// struct Point { x: f64, y: f64 }
537///
538/// #[miniextendr]
539/// fn grid() -> AsDataFrame<Vec<Point>> {
540///     AsDataFrame(vec![Point { x: 0.0, y: 0.0 }, Point { x: 1.0, y: 1.0 }])
541/// }
542/// // In R: grid() returns a data.frame with columns x, y
543/// ```
544#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug + crate::dataframe::IntoDataFrame>
    ::core::fmt::Debug for AsDataFrame<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "AsDataFrame",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone + crate::dataframe::IntoDataFrame>
    ::core::clone::Clone for AsDataFrame<T> {
    #[inline]
    fn clone(&self) -> Self {
        AsDataFrame(::core::clone::Clone::clone(&self.0))
    }
}Clone)]
545pub struct AsDataFrame<T: crate::dataframe::IntoDataFrame>(pub T);
546
547impl<T: crate::dataframe::IntoDataFrame> From<T> for AsDataFrame<T> {
548    fn from(value: T) -> Self {
549        AsDataFrame(value)
550    }
551}
552
553impl<T: crate::dataframe::IntoDataFrame> IntoR for AsDataFrame<T> {
554    type Error = crate::dataframe::DataFrameError;
555
556    #[inline]
557    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
558        Ok(self.0.into_dataframe()?.into_sexp())
559    }
560
561    #[inline]
562    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
563        self.try_into_sexp()
564    }
565}
566
567/// Wrap a value and convert it to a **vctrs** S3 vector via [`IntoVctrs`](crate::vctrs::IntoVctrs)
568/// when returned.
569///
570/// Use this at a call site to return a `#[derive(Vctrs)]` type as its R vctrs object without the
571/// manual `value.into_vctrs().map_err(...)` boilerplate. For a type that should *always* convert
572/// this way, use `#[derive(Vctrs, PreferVctrs)]` instead.
573///
574/// A failed build ([`VctrsBuildError`](crate::vctrs::VctrsBuildError)) surfaces in R as an error
575/// condition.
576///
577/// # Example
578///
579/// ```ignore
580/// #[derive(Vctrs)]
581/// #[vctrs(class = "percent", base = "double")]
582/// struct Percent { #[vctrs(data)] values: Vec<f64> }
583///
584/// #[miniextendr]
585/// fn percent(x: Vec<f64>) -> AsVctrs<Percent> {
586///     AsVctrs(Percent { values: x })
587/// }
588/// ```
589#[cfg(feature = "vctrs")]
590#[derive(Debug, Clone)]
591pub struct AsVctrs<T: crate::vctrs::IntoVctrs>(pub T);
592
593#[cfg(feature = "vctrs")]
594impl<T: crate::vctrs::IntoVctrs> From<T> for AsVctrs<T> {
595    fn from(value: T) -> Self {
596        AsVctrs(value)
597    }
598}
599
600#[cfg(feature = "vctrs")]
601impl<T: crate::vctrs::IntoVctrs> IntoR for AsVctrs<T> {
602    type Error = crate::vctrs::VctrsBuildError;
603
604    #[inline]
605    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
606        self.0.into_vctrs()
607    }
608
609    #[inline]
610    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
611        self.try_into_sexp()
612    }
613}
614// endregion
615
616// region: Named pair wrappers
617
618/// Wrap a tuple pair collection and convert it to a **named R list** (VECSXP).
619///
620/// Preserves insertion order and allows duplicate names (sequence semantics).
621///
622/// # Supported input types
623///
624/// | Input | Bounds |
625/// |-------|--------|
626/// | `Vec<(K, V)>` | `K: AsRef<str>`, `V: IntoR` |
627/// | `[(K, V); N]` | `K: AsRef<str>`, `V: IntoR` |
628/// | `&[(K, V)]` | `K: AsRef<str>`, `V: Clone + IntoR` |
629///
630/// # GC safety — use *typed* values, never raw `SEXP` (#1030)
631///
632/// Prefer typed value types (`i32`, `String`, `Vec<T>`, …) over raw
633/// [`crate::SEXP`]. With a typed `V`, each value's `into_sexp()` runs *inside*
634/// [`AsNamedList::into_sexp`][IntoR::into_sexp], immediately before the value is
635/// protected and placed into the list — no allocation ever fires while a sibling
636/// value sits unprotected.
637///
638/// A raw `SEXP` value (`V = SEXP`) is **GC-fragile and must not be used**. The
639/// caller has to build the `SEXP` values *in an earlier frame* (each `into_sexp()`
640/// allocates) and then hand them to `AsNamedList`; the deferred `from_raw_pairs`
641/// runs later, in the generated wrapper, so there is **no call site at which a
642/// `ProtectScope` can span both value construction and the deferred list build**.
643/// The internal protection added in `into_sexp` (below) closes the *receipt →
644/// `from_raw_pairs`* window but cannot retroactively protect values that were
645/// already built and unrooted in the caller — that *construction* window is the
646/// latent use-after-free. Stable Rust cannot exclude one concrete type from a
647/// `V: IntoR` blanket, so this is enforced by convention (and the MXL302 lint on
648/// the `into_sexp()`-in-`vec!` idiom) rather than the type system; passing typed
649/// values sidesteps the hazard entirely.
650///
651/// # Example
652///
653/// ```ignore
654/// #[miniextendr]
655/// fn make_config() -> AsNamedList<Vec<(String, i32)>> {
656///     AsNamedList(vec![
657///         ("width".into(), 100),
658///         ("height".into(), 200),
659///     ])
660/// }
661/// // In R: make_config() returns list(width = 100L, height = 200L)
662/// ```
663#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for AsNamedList<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "AsNamedList",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for AsNamedList<T> {
    #[inline]
    fn clone(&self) -> Self {
        AsNamedList(::core::clone::Clone::clone(&self.0))
    }
}Clone)]
664pub struct AsNamedList<T>(pub T);
665
666impl<T> From<T> for AsNamedList<T> {
667    fn from(value: T) -> Self {
668        AsNamedList(value)
669    }
670}
671
672impl<K: AsRef<str>, V: IntoR> IntoR for AsNamedList<Vec<(K, V)>> {
673    type Error = std::convert::Infallible;
674
675    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
676        Ok(self.into_sexp())
677    }
678
679    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
680        self.try_into_sexp()
681    }
682
683    fn into_sexp(self) -> crate::SEXP {
684        // SAFETY: `into_sexp` for `#[miniextendr]` return values runs on the R
685        // main thread. Each value's `into_sexp()` is built and immediately
686        // `protect_raw`-rooted before the next value's allocation, mirroring the
687        // `#[derive(IntoList)]` codegen — no sibling sits unprotected across an
688        // allocation. The scope drops after `from_raw_pairs` returns. (#1030)
689        unsafe {
690            let scope = crate::gc_protect::ProtectScope::new();
691            let pairs: Vec<(K, crate::SEXP)> = self
692                .0
693                .into_iter()
694                .map(|(k, v)| (k, scope.protect_raw(v.into_sexp())))
695                .collect();
696            List::from_raw_pairs(pairs).into_sexp()
697        }
698    }
699}
700
701impl<K: AsRef<str>, V: IntoR, const N: usize> IntoR for AsNamedList<[(K, V); N]> {
702    type Error = std::convert::Infallible;
703
704    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
705        Ok(self.into_sexp())
706    }
707
708    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
709        self.try_into_sexp()
710    }
711
712    fn into_sexp(self) -> crate::SEXP {
713        // SAFETY: see the `Vec<(K, V)>` impl above — main thread, each value
714        // rooted before the next allocates. (#1030)
715        unsafe {
716            let scope = crate::gc_protect::ProtectScope::new();
717            let pairs: Vec<(K, crate::SEXP)> = self
718                .0
719                .into_iter()
720                .map(|(k, v)| (k, scope.protect_raw(v.into_sexp())))
721                .collect();
722            List::from_raw_pairs(pairs).into_sexp()
723        }
724    }
725}
726
727impl<K: AsRef<str>, V: Clone + IntoR> IntoR for AsNamedList<&[(K, V)]> {
728    type Error = std::convert::Infallible;
729
730    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
731        Ok(self.into_sexp())
732    }
733
734    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
735        self.try_into_sexp()
736    }
737
738    fn into_sexp(self) -> crate::SEXP {
739        // SAFETY: see the `Vec<(K, V)>` impl above — main thread, each value
740        // rooted before the next allocates. (#1030)
741        unsafe {
742            let scope = crate::gc_protect::ProtectScope::new();
743            let pairs: Vec<(&K, crate::SEXP)> = self
744                .0
745                .iter()
746                .map(|(k, v)| (k, scope.protect_raw(v.clone().into_sexp())))
747                .collect();
748            List::from_raw_pairs(pairs).into_sexp()
749        }
750    }
751}
752
753/// Wrap a tuple pair collection and convert it to a **named atomic R vector**
754/// (INTSXP, REALSXP, LGLSXP, RAWSXP, or STRSXP).
755///
756/// Preserves insertion order and allows duplicate names (sequence semantics).
757/// Values must be homogeneous and implement [`AtomicElement`].
758///
759/// # Supported input types
760///
761/// | Input | Bounds |
762/// |-------|--------|
763/// | `Vec<(K, V)>` | `K: AsRef<str>`, `V: AtomicElement` |
764/// | `[(K, V); N]` | `K: AsRef<str>`, `V: AtomicElement` |
765/// | `&[(K, V)]` | `K: AsRef<str>`, `V: Clone + AtomicElement` |
766///
767/// # Example
768///
769/// ```ignore
770/// #[miniextendr]
771/// fn make_scores() -> AsNamedVector<Vec<(&str, f64)>> {
772///     AsNamedVector(vec![("alice", 95.0), ("bob", 87.5)])
773/// }
774/// // In R: make_scores() returns c(alice = 95.0, bob = 87.5)
775/// ```
776#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for AsNamedVector<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "AsNamedVector",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for AsNamedVector<T> {
    #[inline]
    fn clone(&self) -> Self {
        AsNamedVector(::core::clone::Clone::clone(&self.0))
    }
}Clone)]
777pub struct AsNamedVector<T>(pub T);
778
779impl<T> From<T> for AsNamedVector<T> {
780    fn from(value: T) -> Self {
781        AsNamedVector(value)
782    }
783}
784
785impl<K: AsRef<str>, V: AtomicElement> IntoR for AsNamedVector<Vec<(K, V)>> {
786    type Error = std::convert::Infallible;
787
788    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
789        Ok(self.into_sexp())
790    }
791
792    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
793        self.try_into_sexp()
794    }
795
796    fn into_sexp(self) -> crate::SEXP {
797        named_vector_from_pairs(self.0)
798    }
799}
800
801impl<K: AsRef<str>, V: AtomicElement, const N: usize> IntoR for AsNamedVector<[(K, V); N]> {
802    type Error = std::convert::Infallible;
803
804    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
805        Ok(self.into_sexp())
806    }
807
808    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
809        self.try_into_sexp()
810    }
811
812    fn into_sexp(self) -> crate::SEXP {
813        named_vector_from_pairs(self.0)
814    }
815}
816
817impl<K: AsRef<str>, V: Clone + AtomicElement> IntoR for AsNamedVector<&[(K, V)]> {
818    type Error = std::convert::Infallible;
819
820    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
821        Ok(self.into_sexp())
822    }
823
824    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
825        self.try_into_sexp()
826    }
827
828    fn into_sexp(self) -> crate::SEXP {
829        let (keys, values): (Vec<&K>, Vec<V>) = self.0.iter().map(|(k, v)| (k, v.clone())).unzip();
830        let sexp = V::vec_to_sexp(values);
831        unsafe {
832            let _guard = crate::OwnedProtect::new(sexp);
833            crate::named_vector::set_names_on_sexp(sexp, &keys);
834        }
835        sexp
836    }
837}
838
839/// Shared helper: build a named atomic vector from an owning iterator of (key, value) pairs.
840fn named_vector_from_pairs<K, V>(pairs: impl IntoIterator<Item = (K, V)>) -> crate::SEXP
841where
842    K: AsRef<str>,
843    V: AtomicElement,
844{
845    let (keys, values): (Vec<K>, Vec<V>) = pairs.into_iter().unzip();
846    let sexp = V::vec_to_sexp(values);
847    unsafe {
848        let _guard = crate::OwnedProtect::new(sexp);
849        crate::named_vector::set_names_on_sexp(sexp, &keys);
850    }
851    sexp
852}
853// endregion
854
855// region: Extension traits for ergonomic wrapping
856//
857// These extension traits provide method-style wrapping that works even when
858// the destination type isn't constrained (i.e., `value.wrap_list()` instead
859// of `value.into()` which requires type inference).
860//
861// ```ignore
862// // These all work without type annotations:
863// let wrapped = my_struct.wrap_list();
864// let ptr = my_value.wrap_external_ptr();
865// let native = my_num.wrap_r_native();
866// ```
867
868/// Extension trait for wrapping values as [`AsList`].
869///
870/// This trait is automatically implemented for all types that implement [`IntoList`].
871///
872/// # Example
873///
874/// ```ignore
875/// use miniextendr_api::convert::AsListExt;
876///
877/// #[derive(IntoList)]
878/// struct Point { x: f64, y: f64 }
879///
880/// let point = Point { x: 1.0, y: 2.0 };
881/// let wrapped: AsList<Point> = point.wrap_list();
882/// ```
883pub trait AsListExt: IntoList + Sized {
884    /// Wrap `self` in [`AsList`] for R list conversion.
885    fn wrap_list(self) -> AsList<Self> {
886        AsList(self)
887    }
888}
889
890impl<T: IntoList> AsListExt for T {}
891
892/// Extension trait for wrapping values as [`AsExternalPtr`].
893///
894/// This trait is automatically implemented for all types that implement [`IntoExternalPtr`].
895///
896/// # Example
897///
898/// ```ignore
899/// use miniextendr_api::convert::AsExternalPtrExt;
900///
901/// #[derive(ExternalPtr)]
902/// struct Connection { handle: u64 }
903///
904/// let conn = Connection { handle: 42 };
905/// let wrapped: AsExternalPtr<Connection> = conn.wrap_external_ptr();
906/// ```
907pub trait AsExternalPtrExt: IntoExternalPtr + Sized {
908    /// Wrap `self` in [`AsExternalPtr`] for R external pointer conversion.
909    fn wrap_external_ptr(self) -> AsExternalPtr<Self> {
910        AsExternalPtr(self)
911    }
912}
913
914impl<T: IntoExternalPtr> AsExternalPtrExt for T {}
915
916/// Extension trait for wrapping values as [`AsRNative`].
917///
918/// This trait is automatically implemented for all types that implement [`RNativeType`].
919///
920/// # Example
921///
922/// ```ignore
923/// use miniextendr_api::convert::AsRNativeExt;
924///
925/// let x: f64 = 42.5;
926/// let wrapped: AsRNative<f64> = x.wrap_r_native();
927/// ```
928pub trait AsRNativeExt: RNativeType + Sized {
929    /// Wrap `self` in [`AsRNative`] for native R scalar conversion.
930    fn wrap_r_native(self) -> AsRNative<Self> {
931        AsRNative(self)
932    }
933}
934
935impl<T: RNativeType> AsRNativeExt for T {}
936
937/// Extension trait for wrapping values as [`AsDataFrame`].
938///
939/// Automatically implemented for all `T: IntoDataFrame` (typically `Vec<Row>` where `Row`
940/// derives `DataFrameRow`).
941pub trait AsDataFrameExt: crate::dataframe::IntoDataFrame + Sized {
942    /// Wrap `self` in [`AsDataFrame`] for R data.frame conversion.
943    fn wrap_data_frame(self) -> AsDataFrame<Self> {
944        AsDataFrame(self)
945    }
946}
947
948impl<T: crate::dataframe::IntoDataFrame> AsDataFrameExt for T {}
949
950/// Extension trait for wrapping values as [`AsVctrs`].
951///
952/// Automatically implemented for all `T: IntoVctrs` (typically a `#[derive(Vctrs)]` type).
953#[cfg(feature = "vctrs")]
954pub trait AsVctrsExt: crate::vctrs::IntoVctrs + Sized {
955    /// Wrap `self` in [`AsVctrs`] for R vctrs conversion.
956    fn wrap_vctrs(self) -> AsVctrs<Self> {
957        AsVctrs(self)
958    }
959}
960
961#[cfg(feature = "vctrs")]
962impl<T: crate::vctrs::IntoVctrs> AsVctrsExt for T {}
963
964/// Extension trait for wrapping tuple pair collections as [`AsNamedList`].
965///
966/// # Example
967///
968/// ```ignore
969/// let pairs = vec![("x".to_string(), 1i32), ("y".to_string(), 2i32)];
970/// let wrapped = pairs.wrap_named_list();
971/// ```
972pub trait AsNamedListExt: Sized {
973    /// Wrap `self` in [`AsNamedList`] for named R list conversion.
974    fn wrap_named_list(self) -> AsNamedList<Self> {
975        AsNamedList(self)
976    }
977}
978
979impl<K: AsRef<str>, V: IntoR> AsNamedListExt for Vec<(K, V)> {}
980impl<K: AsRef<str>, V: IntoR, const N: usize> AsNamedListExt for [(K, V); N] {}
981impl<K: AsRef<str>, V: Clone + IntoR> AsNamedListExt for &[(K, V)] {}
982
983/// Extension trait for wrapping tuple pair collections as [`AsNamedVector`].
984///
985/// # Example
986///
987/// ```ignore
988/// let pairs = vec![("alice".to_string(), 95.0f64), ("bob".to_string(), 87.5)];
989/// let wrapped = pairs.wrap_named_vector();
990/// ```
991pub trait AsNamedVectorExt: Sized {
992    /// Wrap `self` in [`AsNamedVector`] for named atomic R vector conversion.
993    fn wrap_named_vector(self) -> AsNamedVector<Self> {
994        AsNamedVector(self)
995    }
996}
997
998impl<K: AsRef<str>, V: AtomicElement> AsNamedVectorExt for Vec<(K, V)> {}
999impl<K: AsRef<str>, V: AtomicElement, const N: usize> AsNamedVectorExt for [(K, V); N] {}
1000impl<K: AsRef<str>, V: Clone + AtomicElement> AsNamedVectorExt for &[(K, V)] {}
1001// endregion
1002
1003// region: Display/FromStr trait adapters
1004
1005/// Wrap a `T: Display` and convert it to an R character scalar.
1006///
1007/// Any type implementing `std::fmt::Display` can be returned to R as a string
1008/// without implementing miniextendr traits.
1009///
1010/// # Example
1011///
1012/// ```ignore
1013/// use std::net::IpAddr;
1014///
1015/// #[miniextendr]
1016/// fn format_ip(ip: &str) -> AsDisplay<IpAddr> {
1017///     AsDisplay(ip.parse().unwrap())
1018/// }
1019/// // R gets: "192.168.1.1"
1020/// ```
1021#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for AsDisplay<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "AsDisplay",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for AsDisplay<T> {
    #[inline]
    fn clone(&self) -> Self {
        AsDisplay(::core::clone::Clone::clone(&self.0))
    }
}Clone, #[automatically_derived]
impl<T: ::core::marker::Copy> ::core::marker::Copy for AsDisplay<T> { }Copy)]
1022pub struct AsDisplay<T>(pub T);
1023
1024impl<T: std::fmt::Display> IntoR for AsDisplay<T> {
1025    type Error = std::convert::Infallible;
1026
1027    #[inline]
1028    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
1029        Ok(self.0.to_string().into_sexp())
1030    }
1031
1032    #[inline]
1033    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
1034        Ok(unsafe { self.0.to_string().into_sexp_unchecked() })
1035    }
1036}
1037
1038/// Wrap a `Vec<T: Display>` and convert it to an R character vector.
1039///
1040/// # Example
1041///
1042/// ```ignore
1043/// #[miniextendr]
1044/// fn format_errors(errors: Vec<std::io::Error>) -> AsDisplayVec<std::io::Error> {
1045///     AsDisplayVec(errors)
1046/// }
1047/// ```
1048#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for AsDisplayVec<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "AsDisplayVec",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for AsDisplayVec<T> {
    #[inline]
    fn clone(&self) -> Self {
        AsDisplayVec(::core::clone::Clone::clone(&self.0))
    }
}Clone)]
1049pub struct AsDisplayVec<T>(pub Vec<T>);
1050
1051impl<T: std::fmt::Display> IntoR for AsDisplayVec<T> {
1052    type Error = std::convert::Infallible;
1053
1054    #[inline]
1055    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
1056        let strings: Vec<String> = self.0.into_iter().map(|x| x.to_string()).collect();
1057        Ok(strings.into_sexp())
1058    }
1059
1060    #[inline]
1061    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
1062        let strings: Vec<String> = self.0.into_iter().map(|x| x.to_string()).collect();
1063        Ok(unsafe { strings.into_sexp_unchecked() })
1064    }
1065}
1066
1067/// Wrap a parsed `T: FromStr` from an R character scalar.
1068///
1069/// Pass an R character scalar and it will be parsed into `T` via `str::parse()`.
1070/// `NA_character_` is refused with [`SexpError::Na`](crate::from_r::SexpError::Na)
1071/// rather than parsed as `""`. A parse failure quotes the value:
1072/// `"not-an-ip": invalid IP address syntax`.
1073///
1074/// For numbers read like R's `as.numeric()` (from character, factor labels, or
1075/// numbers, with `NA` as `None`), use [`AsNumeric`] instead.
1076///
1077/// # Example
1078///
1079/// ```ignore
1080/// use std::net::IpAddr;
1081///
1082/// #[miniextendr]
1083/// fn check_ip(addr: AsFromStr<IpAddr>) -> bool {
1084///     addr.0.is_loopback()
1085/// }
1086/// // R: check_ip("127.0.0.1") → TRUE
1087/// ```
1088#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for AsFromStr<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "AsFromStr",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for AsFromStr<T> {
    #[inline]
    fn clone(&self) -> Self {
        AsFromStr(::core::clone::Clone::clone(&self.0))
    }
}Clone)]
1089pub struct AsFromStr<T>(pub T);
1090
1091impl<T: std::str::FromStr> crate::from_r::TryFromSexp for AsFromStr<T>
1092where
1093    T::Err: std::fmt::Display,
1094{
1095    type Error = crate::from_r::SexpError;
1096    const CHARACTER_ONLY: bool = true;
1097
1098    fn try_from_sexp(sexp: crate::SEXP) -> Result<Self, Self::Error> {
1099        let charsxp = crate::from_r::scalar_charsxp(sexp)?;
1100        if charsxp == crate::SEXP::na_string() {
1101            return Err(crate::from_r::SexpNaError {
1102                sexp_type: crate::SEXPTYPE::STRSXP,
1103            }
1104            .into());
1105        }
1106        // SAFETY: a non-NA CHARSXP from a live STRSXP argument.
1107        let s = unsafe { crate::from_r::charsxp_to_str(charsxp) };
1108        s.parse::<T>()
1109            .map(AsFromStr)
1110            .map_err(|e| crate::from_r::SexpError::InvalidValue(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0:?}: {1}", s, e))
    })format!("{s:?}: {e}")))
1111    }
1112}
1113
1114/// The value's `Display` text as a character scalar, which the marker parses
1115/// back. A trait's View passes an `AsFromStr` argument this way, so the value
1116/// crosses unchanged when `T::from_str(&t.to_string()) == t` (the std
1117/// numbers, `IpAddr`, `Uuid`, ...). This `IntoR` exists for the trait ABI; to
1118/// return text from a function, use [`AsDisplay`].
1119impl<T: std::fmt::Display> IntoR for AsFromStr<T> {
1120    type Error = <String as IntoR>::Error;
1121
1122    #[inline]
1123    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
1124        self.0.to_string().try_into_sexp()
1125    }
1126
1127    #[inline]
1128    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
1129        unsafe { self.0.to_string().try_into_sexp_unchecked() }
1130    }
1131
1132    #[inline]
1133    fn into_sexp(self) -> crate::SEXP {
1134        self.0.to_string().into_sexp()
1135    }
1136
1137    #[inline]
1138    unsafe fn into_sexp_unchecked(self) -> crate::SEXP {
1139        unsafe { self.0.to_string().into_sexp_unchecked() }
1140    }
1141}
1142
1143/// Wrap a `Vec<T: FromStr>` parsed from an R character vector.
1144///
1145/// Each element of the R character vector is parsed into `T`. Every failure is
1146/// collected into one error (the first 10 listed, then `"and N more"`), each
1147/// with its quoted value and 1-based position (`"n/a": invalid digit found in
1148/// string (element 2)`). An `NA_character_` element is refused
1149/// (`NA is not allowed (element 3)`), not parsed as `""`.
1150///
1151/// For numbers read like R's `as.numeric()`, with `NA` as `None`, use
1152/// [`AsNumericVec`] instead.
1153///
1154/// # Example
1155///
1156/// ```ignore
1157/// use std::net::IpAddr;
1158///
1159/// #[miniextendr]
1160/// fn parse_ips(addrs: AsFromStrVec<IpAddr>) -> Vec<bool> {
1161///     addrs.0.into_iter().map(|ip| ip.is_loopback()).collect()
1162/// }
1163/// // R: parse_ips(c("127.0.0.1", "8.8.8.8")) → c(TRUE, FALSE)
1164/// ```
1165#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for AsFromStrVec<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "AsFromStrVec",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for AsFromStrVec<T> {
    #[inline]
    fn clone(&self) -> Self {
        AsFromStrVec(::core::clone::Clone::clone(&self.0))
    }
}Clone)]
1166pub struct AsFromStrVec<T>(pub Vec<T>);
1167
1168impl<T: std::str::FromStr> crate::from_r::TryFromSexp for AsFromStrVec<T>
1169where
1170    T::Err: std::fmt::Display,
1171{
1172    type Error = crate::from_r::SexpError;
1173    const CHARACTER_ONLY: bool = true;
1174
1175    fn try_from_sexp(sexp: crate::SEXP) -> Result<Self, Self::Error> {
1176        let strings: Vec<Option<&str>> = crate::from_r::TryFromSexp::try_from_sexp(sexp)?;
1177        let mut result = Vec::with_capacity(strings.len());
1178        let mut errors = crate::from_r::BatchedErrors::default();
1179        for (i, s) in strings.into_iter().enumerate() {
1180            match s {
1181                None => errors.push(i, || "NA is not allowed".to_string()),
1182                Some(s) => match s.parse::<T>() {
1183                    Ok(v) => result.push(v),
1184                    Err(e) => errors.push(i, || ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0:?}: {1}", s, e))
    })format!("{s:?}: {e}")),
1185                },
1186            }
1187        }
1188        if errors.is_empty() {
1189            Ok(AsFromStrVec(result))
1190        } else {
1191            Err(errors.into_element_error())
1192        }
1193    }
1194}
1195
1196/// Each value's `Display` text as a character vector, which the marker parses
1197/// back element by element (see [`AsFromStr`]'s `IntoR`).
1198impl<T: std::fmt::Display> IntoR for AsFromStrVec<T> {
1199    type Error = <Vec<String> as IntoR>::Error;
1200
1201    #[inline]
1202    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
1203        display_strings(self.0).try_into_sexp()
1204    }
1205
1206    #[inline]
1207    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
1208        unsafe { display_strings(self.0).try_into_sexp_unchecked() }
1209    }
1210
1211    #[inline]
1212    fn into_sexp(self) -> crate::SEXP {
1213        display_strings(self.0).into_sexp()
1214    }
1215
1216    #[inline]
1217    unsafe fn into_sexp_unchecked(self) -> crate::SEXP {
1218        unsafe { display_strings(self.0).into_sexp_unchecked() }
1219    }
1220}
1221
1222fn display_strings<T: std::fmt::Display>(values: Vec<T>) -> Vec<String> {
1223    values.into_iter().map(|v| v.to_string()).collect()
1224}
1225// endregion
1226
1227// region: Collect — zero-allocation iterator-to-R-vector adapters
1228//
1229// Naming-convention exemption: the four `Collect*` types below are
1230// representation-forcing `IntoR` wrappers, like the `As*` family above, but they
1231// intentionally diverge from the `As*` prefix. `As*` wraps a finished value `T`
1232// and re-routes its conversion; `Collect*` wraps an *iterator*
1233// (`I: ExactSizeIterator`) and drains it straight into a freshly allocated R
1234// vector. The `As*`-of-`T` shape does not describe an iterator adapter, so
1235// `Collect` (the verb for the operation) is the accurate name. See the module-level
1236// docs, `analysis/conversion-control-surface-2026-06-07.md` (§3.4 / §4.5), and #871.
1237
1238/// Write an `ExactSizeIterator` of native R types directly into an R vector.
1239///
1240/// Skips the intermediate `Vec` allocation — the R vector is allocated once
1241/// and the iterator writes directly into it.
1242///
1243/// Requires `ExactSizeIterator` because R vectors must know their length
1244/// at allocation time.
1245///
1246/// # Naming
1247///
1248/// `Collect` is in the representation-forcing wrapper family but does not take the
1249/// `As*` prefix used by [`AsList`] / [`AsExternalPtr`] / [`AsRNative`]: those wrap a
1250/// finished value `T`, whereas `Collect` wraps an *iterator* and materializes it into
1251/// an R vector. The divergence is intentional — see the module docs and #871.
1252///
1253/// # Example
1254///
1255/// ```ignore
1256/// #[miniextendr]
1257/// fn sines(n: i32) -> Collect<impl ExactSizeIterator<Item = f64>> {
1258///     Collect((0..n).map(|i| (i as f64).sin()))
1259/// }
1260/// ```
1261pub struct Collect<I>(pub I);
1262
1263impl<I, T> IntoR for Collect<I>
1264where
1265    I: ExactSizeIterator<Item = T>,
1266    T: crate::RNativeType,
1267{
1268    type Error = std::convert::Infallible;
1269
1270    #[inline]
1271    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
1272        Ok(self.into_sexp())
1273    }
1274
1275    #[inline]
1276    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
1277        Ok(unsafe { self.into_sexp_unchecked() })
1278    }
1279
1280    #[inline]
1281    fn into_sexp(self) -> crate::SEXP {
1282        unsafe {
1283            let (sexp, dst) = crate::into_r::alloc_r_vector::<T>(self.0.len());
1284            for (slot, val) in dst.iter_mut().zip(self.0) {
1285                *slot = val;
1286            }
1287            sexp
1288        }
1289    }
1290
1291    #[inline]
1292    unsafe fn into_sexp_unchecked(self) -> crate::SEXP {
1293        unsafe {
1294            let (sexp, dst) = crate::into_r::alloc_r_vector_unchecked::<T>(self.0.len());
1295            for (slot, val) in dst.iter_mut().zip(self.0) {
1296                *slot = val;
1297            }
1298            sexp
1299        }
1300    }
1301}
1302
1303/// Write an `ExactSizeIterator` of `String` directly into an R character vector.
1304///
1305/// Strings require per-element CHARSXP allocation (no bulk `copy_from_slice`),
1306/// so this is a separate type from [`Collect`]. Like [`Collect`], it is an
1307/// iterator adapter and is exempt from the `As*` naming convention (see #871).
1308///
1309/// # Example
1310///
1311/// ```ignore
1312/// #[miniextendr]
1313/// fn upper(words: Vec<String>) -> CollectStrings<impl ExactSizeIterator<Item = String>> {
1314///     CollectStrings(words.into_iter().map(|w| w.to_uppercase()))
1315/// }
1316/// ```
1317pub struct CollectStrings<I>(pub I);
1318
1319impl<I> IntoR for CollectStrings<I>
1320where
1321    I: ExactSizeIterator<Item = String>,
1322{
1323    type Error = std::convert::Infallible;
1324
1325    #[inline]
1326    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
1327        // Collect String refs for str_iter_to_strsxp.
1328        let strings: Vec<String> = self.0.collect();
1329        Ok(crate::into_r::str_iter_to_strsxp(
1330            strings.iter().map(|s| s.as_str()),
1331        ))
1332    }
1333
1334    #[inline]
1335    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
1336        let strings: Vec<String> = self.0.collect();
1337        Ok(unsafe {
1338            crate::into_r::str_iter_to_strsxp_unchecked(strings.iter().map(|s| s.as_str()))
1339        })
1340    }
1341}
1342
1343/// Write an `ExactSizeIterator` of `Option<T>` directly into an R vector with NA support.
1344///
1345/// `None` values become `NA` in R. Works for `f64` and `i32`.
1346///
1347/// Like [`Collect`], this is an iterator adapter and is exempt from the `As*`
1348/// naming convention (see #871).
1349///
1350/// # Example
1351///
1352/// ```ignore
1353/// #[miniextendr]
1354/// fn with_gaps(n: i32) -> CollectNA<impl ExactSizeIterator<Item = Option<f64>>> {
1355///     CollectNA((0..n).map(|i| if i % 3 == 0 { None } else { Some(i as f64) }))
1356/// }
1357/// ```
1358pub struct CollectNA<I>(pub I);
1359
1360impl<I> IntoR for CollectNA<I>
1361where
1362    I: ExactSizeIterator<Item = Option<f64>>,
1363{
1364    type Error = std::convert::Infallible;
1365
1366    #[inline]
1367    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
1368        Ok(self.into_sexp())
1369    }
1370
1371    #[inline]
1372    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
1373        Ok(unsafe { self.into_sexp_unchecked() })
1374    }
1375
1376    #[inline]
1377    fn into_sexp(self) -> crate::SEXP {
1378        unsafe {
1379            let (sexp, dst) = crate::into_r::alloc_r_vector::<f64>(self.0.len());
1380            for (slot, val) in dst.iter_mut().zip(self.0) {
1381                *slot = val.unwrap_or(crate::altrep_traits::NA_REAL);
1382            }
1383            sexp
1384        }
1385    }
1386
1387    #[inline]
1388    unsafe fn into_sexp_unchecked(self) -> crate::SEXP {
1389        unsafe {
1390            let (sexp, dst) = crate::into_r::alloc_r_vector_unchecked::<f64>(self.0.len());
1391            for (slot, val) in dst.iter_mut().zip(self.0) {
1392                *slot = val.unwrap_or(crate::altrep_traits::NA_REAL);
1393            }
1394            sexp
1395        }
1396    }
1397}
1398
1399/// Write an `ExactSizeIterator` of `Option<i32>` directly into an R integer vector with NA.
1400///
1401/// Like [`Collect`], this is an iterator adapter and is exempt from the `As*`
1402/// naming convention (see #871).
1403pub struct CollectNAInt<I>(pub I);
1404
1405impl<I> IntoR for CollectNAInt<I>
1406where
1407    I: ExactSizeIterator<Item = Option<i32>>,
1408{
1409    type Error = std::convert::Infallible;
1410
1411    #[inline]
1412    fn try_into_sexp(self) -> Result<crate::SEXP, Self::Error> {
1413        Ok(self.into_sexp())
1414    }
1415
1416    #[inline]
1417    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::SEXP, Self::Error> {
1418        Ok(unsafe { self.into_sexp_unchecked() })
1419    }
1420
1421    #[inline]
1422    fn into_sexp(self) -> crate::SEXP {
1423        unsafe {
1424            let (sexp, dst) = crate::into_r::alloc_r_vector::<i32>(self.0.len());
1425            for (slot, val) in dst.iter_mut().zip(self.0) {
1426                *slot = val.unwrap_or(crate::altrep_traits::NA_INTEGER);
1427            }
1428            sexp
1429        }
1430    }
1431
1432    #[inline]
1433    unsafe fn into_sexp_unchecked(self) -> crate::SEXP {
1434        unsafe {
1435            let (sexp, dst) = crate::into_r::alloc_r_vector_unchecked::<i32>(self.0.len());
1436            for (slot, val) in dst.iter_mut().zip(self.0) {
1437                *slot = val.unwrap_or(crate::altrep_traits::NA_INTEGER);
1438            }
1439            sexp
1440        }
1441    }
1442}
1443// endregion
1444
1445#[cfg(test)]
1446mod tests {
1447    use super::*;
1448
1449    /// A trait's View converts each argument with `IntoR`, so every reading
1450    /// marker (and the `Option` of those that read `NULL`) is `IntoR`. The
1451    /// round trip through a real R is tested in `tests/as_numeric.rs` and
1452    /// `tests/as_character.rs` (`markers_round_trip`), and through a trait's
1453    /// View in rpkg (`marker_args_through_view`).
1454    #[test]
1455    fn reading_markers_are_into_r() {
1456        fn into_r<T: IntoR>() {}
1457        into_r::<AsNumeric>();
1458        into_r::<AsNumericVec>();
1459        into_r::<Option<AsNumeric>>();
1460        into_r::<Option<AsNumericVec>>();
1461        into_r::<AsCharacter>();
1462        into_r::<AsCharacterVec>();
1463        into_r::<Option<AsCharacter>>();
1464        into_r::<Option<AsCharacterVec>>();
1465        into_r::<AsFromStr<std::net::IpAddr>>();
1466        into_r::<AsFromStrVec<std::net::IpAddr>>();
1467    }
1468}