Skip to main content

miniextendr_api/altrep_data/iter/
coerce.rs

1//! Iterator-backed ALTREP data adaptors with coercion support.
2//!
3//! Provides `IterIntCoerceData`, `IterRealCoerceData`, and `IterIntFromBoolData`
4//! for iterators that produce values coercible to the target R type, plus the
5//! string/complex adaptors (`IterStringData`, `IterComplexData`).
6//!
7//! See the iterator-adaptor section in the [`altrep_data`](crate::altrep_data)
8//! module docs for how to expose
9//! these adaptors to R (wrap in a `#[derive(Altrep*)]` + `#[altrep(manual)]`
10//! struct).
11
12use super::IterState;
13use super::macros::define_iter_data;
14use crate::altrep_data::{AltComplexData, AltIntegerData, AltRealData, AltStringData, AltrepLen};
15
16/// Iterator-backed integer vector data adaptor with coercion from any integer-like type.
17///
18/// Wraps an iterator producing values that coerce to `i32` (e.g., `u16`, `i8`, etc.)
19/// and implements the data-level traits ([`AltrepLen`] + [`AltIntegerData`]).
20/// To expose it to R, wrap it in a `#[derive(AltrepInteger)]` +
21/// `#[altrep(manual)]` struct (see the iterator module documentation).
22///
23/// # Example
24///
25/// ```ignore
26/// use miniextendr_api::altrep_data::IterIntCoerceData;
27///
28/// // Create from an iterator of u16 values
29/// let iter = (0..10u16).map(|x| x * 100);
30/// let data = IterIntCoerceData::from_iter(iter, 10);
31/// // Values are coerced from u16 to i32 when accessed
32/// ```
33pub struct IterIntCoerceData<I, T>
34where
35    I: Iterator<Item = T>,
36    T: crate::coerce::Coerce<i32> + Copy,
37{
38    state: IterState<I, T>,
39}
40
41impl<I, T> IterIntCoerceData<I, T>
42where
43    I: Iterator<Item = T>,
44    T: crate::coerce::Coerce<i32> + Copy,
45{
46    /// Create from an iterator with explicit length.
47    pub fn from_iter(iter: I, len: usize) -> Self {
48        Self {
49            state: IterState::new(iter, len),
50        }
51    }
52}
53
54impl<I, T> IterIntCoerceData<I, T>
55where
56    I: ExactSizeIterator<Item = T>,
57    T: crate::coerce::Coerce<i32> + Copy,
58{
59    /// Create from an ExactSizeIterator (length auto-detected).
60    pub fn from_exact_iter(iter: I) -> Self {
61        Self {
62            state: IterState::from_exact_size(iter),
63        }
64    }
65}
66
67impl<I, T> AltrepLen for IterIntCoerceData<I, T>
68where
69    I: Iterator<Item = T>,
70    T: crate::coerce::Coerce<i32> + Copy,
71{
72    fn altrep_len(&self) -> usize {
73        self.state.len()
74    }
75}
76
77impl<I, T> AltIntegerData for IterIntCoerceData<I, T>
78where
79    I: Iterator<Item = T>,
80    T: crate::coerce::Coerce<i32> + Copy,
81{
82    fn elt(&self, i: usize) -> i32 {
83        self.state
84            .get_element(i)
85            .map(|val| val.coerce())
86            .unwrap_or(crate::altrep_traits::NA_INTEGER)
87    }
88}
89
90/// Iterator-backed real vector data adaptor with coercion from any float-like type.
91///
92/// Wraps an iterator producing values that coerce to `f64` (e.g., `f32`, integer types).
93///
94/// # Example
95///
96/// ```ignore
97/// use miniextendr_api::altrep_data::IterRealCoerceData;
98///
99/// // Create from an iterator of f32 values
100/// let iter = (0..5).map(|x| x as f32 * 1.5);
101/// let data = IterRealCoerceData::from_iter(iter, 5);
102/// // Values are coerced from f32 to f64 when accessed
103/// ```
104pub struct IterRealCoerceData<I, T>
105where
106    I: Iterator<Item = T>,
107    T: crate::coerce::Coerce<f64> + Copy,
108{
109    state: IterState<I, T>,
110}
111
112impl<I, T> IterRealCoerceData<I, T>
113where
114    I: Iterator<Item = T>,
115    T: crate::coerce::Coerce<f64> + Copy,
116{
117    /// Create from an iterator with explicit length.
118    pub fn from_iter(iter: I, len: usize) -> Self {
119        Self {
120            state: IterState::new(iter, len),
121        }
122    }
123}
124
125impl<I, T> IterRealCoerceData<I, T>
126where
127    I: ExactSizeIterator<Item = T>,
128    T: crate::coerce::Coerce<f64> + Copy,
129{
130    /// Create from an ExactSizeIterator (length auto-detected).
131    pub fn from_exact_iter(iter: I) -> Self {
132        Self {
133            state: IterState::from_exact_size(iter),
134        }
135    }
136}
137
138impl<I, T> AltrepLen for IterRealCoerceData<I, T>
139where
140    I: Iterator<Item = T>,
141    T: crate::coerce::Coerce<f64> + Copy,
142{
143    fn altrep_len(&self) -> usize {
144        self.state.len()
145    }
146}
147
148impl<I, T> AltRealData for IterRealCoerceData<I, T>
149where
150    I: Iterator<Item = T>,
151    T: crate::coerce::Coerce<f64> + Copy,
152{
153    fn elt(&self, i: usize) -> f64 {
154        self.state
155            .get_element(i)
156            .map(|val| val.coerce())
157            .unwrap_or(crate::altrep_traits::NA_REAL)
158    }
159}
160
161#[doc =
r" Iterator-backed integer vector data adaptor with coercion from bool."]
#[doc = r""]
#[doc = r" Wraps an iterator producing `bool` values that coerce to `i32`."]
#[doc = r" Useful for converting boolean iterators to integer vectors."]
pub struct IterIntFromBoolData<I: Iterator<Item = bool>> {
    state: super::IterState<I, bool>,
}
#[automatically_derived]
impl<I: ::core::clone::Clone + Iterator<Item = bool>> ::core::clone::Clone for
    IterIntFromBoolData<I> {
    #[inline]
    fn clone(&self) -> Self {
        IterIntFromBoolData {
            state: ::core::clone::Clone::clone(&self.state),
        }
    }
}
impl<I: Iterator<Item = bool>> IterIntFromBoolData<I> {
    /// Create a lazy adapter for exactly the declared prefix.
    ///
    /// The iterator is not advanced during construction.
    pub fn from_iter(iter: I, len: usize) -> Self {
        Self { state: super::IterState::new(iter, len) }
    }
}
impl<I: Iterator<Item = bool>> crate::altrep_data::AltrepSerialize for
    IterIntFromBoolData<I> {
    fn serialized_state(&self) -> crate::SEXP {
        match self.state.try_materialize_all() {
            Ok(values) => crate::IntoR::into_sexp(values.to_vec()),
            Err(_) => crate::SEXP::null(),
        }
    }
    fn unserialize(state: crate::SEXP) -> Option<Self> {
        let values =
            <Vec<bool> as crate::TryFromSexp>::try_from_sexp(state).ok()?;
        Some(Self { state: super::IterState::from_values(values) })
    }
}
impl<I: ExactSizeIterator<Item = bool>> IterIntFromBoolData<I> {
    /// Create a lazy adapter using the iterator's remaining length.
    pub fn from_exact_iter(iter: I) -> Self {
        let len = iter.len();
        Self::from_iter(iter, len)
    }
}
impl<I: Iterator<Item = bool>> crate::altrep_data::AltrepLen for
    IterIntFromBoolData<I> {
    fn altrep_len(&self) -> usize { self.state.len() }
}define_iter_data! {
162/// Iterator-backed integer vector data adaptor with coercion from bool.
163///
164/// Wraps an iterator producing `bool` values that coerce to `i32`.
165/// Useful for converting boolean iterators to integer vectors.
166IterIntFromBoolData, bool
167}
168
169impl<I> AltIntegerData for IterIntFromBoolData<I>
170where
171    I: Iterator<Item = bool>,
172{
173    fn elt(&self, i: usize) -> i32 {
174        use crate::coerce::Coerce;
175        self.state
176            .get_element(i)
177            .map(|val| val.coerce())
178            .unwrap_or(crate::altrep_traits::NA_INTEGER)
179    }
180}
181
182#[doc = r" Iterator-backed string vector data adaptor."]
#[doc = r""]
#[doc =
r" Wraps an iterator producing `String` values and implements the data-level"]
#[doc =
r" traits ([`AltrepLen`] + [`AltStringData`]) for backing an ALTREP character"]
#[doc =
r" vector. To expose it to R, wrap it in a `#[derive(AltrepString)]` +"]
#[doc =
r" `#[altrep(manual)]` struct (see the iterator module documentation)."]
#[doc = r""]
#[doc = r" # Performance Warning"]
#[doc = r""]
#[doc =
r" Unlike other `Iter*Data` types, **accessing ANY element forces full materialization"]
#[doc =
r" of the entire iterator**. This is because R's `AltStringData::elt()` returns a borrowed"]
#[doc =
r" `&str`, which requires stable storage. The internal `RefCell` cannot provide the required"]
#[doc = r" lifetime, so all strings must be materialized upfront."]
#[doc = r""]
#[doc = r" This means:"]
#[doc =
r" - `elt(0)` on a million-element iterator will generate ALL million strings"]
#[doc = r" - There is no lazy evaluation benefit for string iterators"]
#[doc =
r" - Memory usage equals the full vector regardless of access patterns"]
#[doc = r""]
#[doc =
r" For truly lazy string ALTREP, consider implementing a custom type that stores"]
#[doc =
r" strings in a way that allows borrowing without full materialization (e.g., arena"]
#[doc = r" allocation or caching generated strings incrementally)."]
#[doc = r""]
#[doc = r" # Example"]
#[doc = r""]
#[doc = r" ```ignore"]
#[doc = r" use miniextendr_api::altrep_data::IterStringData;"]
#[doc = r""]
#[doc = r#" let iter = (0..5).map(|x| format!("item_{}", x));"#]
#[doc = r" let data = IterStringData::from_iter(iter, 5);"]
#[doc = r" // First access to ANY element will materialize all 5 strings"]
#[doc = r" ```"]
pub struct IterStringData<I: Iterator<Item = String>> {
    state: super::IterState<I, String>,
}
#[automatically_derived]
impl<I: ::core::clone::Clone + Iterator<Item = String>> ::core::clone::Clone
    for IterStringData<I> {
    #[inline]
    fn clone(&self) -> Self {
        IterStringData { state: ::core::clone::Clone::clone(&self.state) }
    }
}
impl<I: Iterator<Item = String>> IterStringData<I> {
    /// Create a lazy adapter for exactly the declared prefix.
    ///
    /// The iterator is not advanced during construction.
    pub fn from_iter(iter: I, len: usize) -> Self {
        Self { state: super::IterState::new(iter, len) }
    }
}
impl<I: Iterator<Item = String>> crate::altrep_data::AltrepSerialize for
    IterStringData<I> {
    fn serialized_state(&self) -> crate::SEXP {
        match self.state.try_materialize_all() {
            Ok(values) => crate::IntoR::into_sexp(values.to_vec()),
            Err(_) => crate::SEXP::null(),
        }
    }
    fn unserialize(state: crate::SEXP) -> Option<Self> {
        let values =
            <Vec<String> as crate::TryFromSexp>::try_from_sexp(state).ok()?;
        Some(Self { state: super::IterState::from_values(values) })
    }
}
impl<I: ExactSizeIterator<Item = String>> IterStringData<I> {
    /// Create a lazy adapter using the iterator's remaining length.
    pub fn from_exact_iter(iter: I) -> Self {
        let len = iter.len();
        Self::from_iter(iter, len)
    }
}
impl<I: Iterator<Item = String>> crate::altrep_data::AltrepLen for
    IterStringData<I> {
    fn altrep_len(&self) -> usize { self.state.len() }
}define_iter_data! {
183/// Iterator-backed string vector data adaptor.
184///
185/// Wraps an iterator producing `String` values and implements the data-level
186/// traits ([`AltrepLen`] + [`AltStringData`]) for backing an ALTREP character
187/// vector. To expose it to R, wrap it in a `#[derive(AltrepString)]` +
188/// `#[altrep(manual)]` struct (see the iterator module documentation).
189///
190/// # Performance Warning
191///
192/// Unlike other `Iter*Data` types, **accessing ANY element forces full materialization
193/// of the entire iterator**. This is because R's `AltStringData::elt()` returns a borrowed
194/// `&str`, which requires stable storage. The internal `RefCell` cannot provide the required
195/// lifetime, so all strings must be materialized upfront.
196///
197/// This means:
198/// - `elt(0)` on a million-element iterator will generate ALL million strings
199/// - There is no lazy evaluation benefit for string iterators
200/// - Memory usage equals the full vector regardless of access patterns
201///
202/// For truly lazy string ALTREP, consider implementing a custom type that stores
203/// strings in a way that allows borrowing without full materialization (e.g., arena
204/// allocation or caching generated strings incrementally).
205///
206/// # Example
207///
208/// ```ignore
209/// use miniextendr_api::altrep_data::IterStringData;
210///
211/// let iter = (0..5).map(|x| format!("item_{}", x));
212/// let data = IterStringData::from_iter(iter, 5);
213/// // First access to ANY element will materialize all 5 strings
214/// ```
215IterStringData, String
216}
217
218impl<I> AltStringData for IterStringData<I>
219where
220    I: Iterator<Item = String>,
221{
222    fn elt(&self, i: usize) -> Option<&str> {
223        // Materialize to get stable storage for &str references
224        // This is necessary because we can't return &str from RefCell borrows
225        let materialized = self.state.materialize_all();
226        materialized.get(i).map(|s| s.as_str())
227    }
228}
229
230#[doc = r" Iterator-backed complex number vector data adaptor."]
#[doc = r""]
#[doc =
r" Wraps an iterator producing `Rcomplex` values and implements the data-level"]
#[doc =
r" traits ([`AltrepLen`] + [`AltComplexData`]) for backing an ALTREP complex"]
#[doc =
r" vector. To expose it to R, wrap it in a `#[derive(AltrepComplex)]` +"]
#[doc =
r" `#[altrep(manual)]` struct (see the iterator module documentation)."]
#[doc = r""]
#[doc = r" # Example"]
#[doc = r""]
#[doc = r" ```ignore"]
#[doc = r" use miniextendr_api::altrep_data::IterComplexData;"]
#[doc = r" use miniextendr_api::Rcomplex;"]
#[doc = r""]
#[doc =
r" let iter = (0..5).map(|x| Rcomplex { r: x as f64, i: (x * 2) as f64 });"]
#[doc = r" let data = IterComplexData::from_iter(iter, 5);"]
#[doc = r" ```"]
pub struct IterComplexData<I: Iterator<Item = crate::Rcomplex>> {
    state: super::IterState<I, crate::Rcomplex>,
}
#[automatically_derived]
impl<I: ::core::clone::Clone + Iterator<Item = crate::Rcomplex>>
    ::core::clone::Clone for IterComplexData<I> {
    #[inline]
    fn clone(&self) -> Self {
        IterComplexData { state: ::core::clone::Clone::clone(&self.state) }
    }
}
impl<I: Iterator<Item = crate::Rcomplex>> IterComplexData<I> {
    /// Create a lazy adapter for exactly the declared prefix.
    ///
    /// The iterator is not advanced during construction.
    pub fn from_iter(iter: I, len: usize) -> Self {
        Self { state: super::IterState::new(iter, len) }
    }
}
impl<I: Iterator<Item = crate::Rcomplex>> crate::altrep_data::AltrepSerialize
    for IterComplexData<I> {
    fn serialized_state(&self) -> crate::SEXP {
        match self.state.try_materialize_all() {
            Ok(values) => crate::IntoR::into_sexp(values.to_vec()),
            Err(_) => crate::SEXP::null(),
        }
    }
    fn unserialize(state: crate::SEXP) -> Option<Self> {
        let values =
            <Vec<crate::Rcomplex> as
                            crate::TryFromSexp>::try_from_sexp(state).ok()?;
        Some(Self { state: super::IterState::from_values(values) })
    }
}
impl<I: ExactSizeIterator<Item = crate::Rcomplex>> IterComplexData<I> {
    /// Create a lazy adapter using the iterator's remaining length.
    pub fn from_exact_iter(iter: I) -> Self {
        let len = iter.len();
        Self::from_iter(iter, len)
    }
}
impl<I: Iterator<Item = crate::Rcomplex>> crate::altrep_data::AltrepLen for
    IterComplexData<I> {
    fn altrep_len(&self) -> usize { self.state.len() }
}define_iter_data! {
231/// Iterator-backed complex number vector data adaptor.
232///
233/// Wraps an iterator producing `Rcomplex` values and implements the data-level
234/// traits ([`AltrepLen`] + [`AltComplexData`]) for backing an ALTREP complex
235/// vector. To expose it to R, wrap it in a `#[derive(AltrepComplex)]` +
236/// `#[altrep(manual)]` struct (see the iterator module documentation).
237///
238/// # Example
239///
240/// ```ignore
241/// use miniextendr_api::altrep_data::IterComplexData;
242/// use miniextendr_api::Rcomplex;
243///
244/// let iter = (0..5).map(|x| Rcomplex { r: x as f64, i: (x * 2) as f64 });
245/// let data = IterComplexData::from_iter(iter, 5);
246/// ```
247IterComplexData, crate::Rcomplex
248}
249
250impl<I> AltComplexData for IterComplexData<I>
251where
252    I: Iterator<Item = crate::Rcomplex>,
253{
254    fn elt(&self, i: usize) -> crate::Rcomplex {
255        self.state
256            .get_element(i)
257            .unwrap_or(<crate::Rcomplex as crate::RNativeType>::R_NA)
258    }
259
260    fn as_slice(&self) -> Option<&[crate::Rcomplex]> {
261        self.state.as_materialized()
262    }
263}