use crate::*;
use wrapper::symbol::levels_symbol;
pub type NamedListIter = std::iter::Zip<StrIter, ListIter>;
#[derive(Clone)]
pub struct StrIter {
vector: Robj,
i: usize,
len: usize,
levels: SEXP,
}
impl Default for StrIter {
fn default() -> Self {
StrIter::new(0)
}
}
impl StrIter {
pub fn new(len: usize) -> Self {
unsafe {
Self {
vector: ().into(),
i: 0,
len,
levels: R_NilValue,
}
}
}
pub fn na_iter(len: usize) -> StrIter {
Self {
len,
..Default::default()
}
}
}
fn str_from_strsxp<'a>(sexp: SEXP, index: isize) -> &'a str {
unsafe {
if index < 0 || index >= Rf_xlength(sexp) {
<&str>::na()
} else {
let charsxp = STRING_ELT(sexp, index);
if charsxp == R_NaString {
<&str>::na()
} else if TYPEOF(charsxp) == CHARSXP as i32 {
let ptr = R_CHAR(charsxp) as *const u8;
let slice = std::slice::from_raw_parts(ptr, Rf_xlength(charsxp) as usize);
std::str::from_utf8_unchecked(slice)
} else {
<&str>::na()
}
}
}
}
impl Iterator for StrIter {
type Item = &'static str;
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let i = self.i;
self.i += 1;
let vector = self.vector.get();
if i >= self.len {
None
} else if TYPEOF(vector) as u32 == STRSXP {
Some(str_from_strsxp(vector, i as isize))
} else if TYPEOF(vector) as u32 == INTSXP && TYPEOF(self.levels) as u32 == STRSXP {
let j = *(INTEGER(vector).add(i));
Some(str_from_strsxp(self.levels, j as isize - 1))
} else if TYPEOF(vector) as u32 == NILSXP {
Some(<&str>::na())
} else {
None
}
}
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.i += n;
self.next()
}
}
impl ExactSizeIterator for StrIter {
fn len(&self) -> usize {
self.len - self.i
}
}
macro_rules! impl_iter_debug {
($name: ty) => {
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[")?;
let mut comma = "";
for s in self.clone() {
write!(f, "{}{:?}", comma, s)?;
comma = ", ";
}
write!(f, "]")
}
}
};
}
impl_iter_debug!(ListIter);
impl_iter_debug!(PairlistIter);
impl_iter_debug!(StrIter);
impl_iter_debug!(EnvIter);
pub trait AsStrIter: GetSexp + Types + Length + Attributes + Rinternals {
fn as_str_iter(&self) -> Option<StrIter> {
let i = 0;
let len = self.len();
match self.sexptype() {
STRSXP => unsafe {
Some(StrIter {
vector: self.as_robj().clone(),
i,
len,
levels: R_NilValue,
})
},
INTSXP => unsafe {
if let Some(levels) = self.get_attrib(levels_symbol()) {
if self.is_factor() && levels.sexptype() == STRSXP {
Some(StrIter {
vector: self.as_robj().clone(),
i,
len,
levels: levels.get(),
})
} else {
None
}
} else {
None
}
},
_ => None,
}
}
}
impl AsStrIter for Robj {}