use super::error::ScalarItemError;
use super::options::{
CollectionConversionOptions,
EmptyItemPolicy,
};
use super::scalar_item::ScalarItem;
#[must_use]
#[derive(Debug, Clone)]
pub struct ScalarItems<'a> {
value: &'a str,
delimiters: &'a [char],
ascii_delimiters: Option<[bool; 128]>,
non_ascii_delimiters: Option<Vec<char>>,
split_scalar_strings: bool,
trim_items: bool,
empty_item_policy: EmptyItemPolicy,
max_items: usize,
retained_items: usize,
next_start: Option<usize>,
next_source_index: usize,
}
impl<'a> ScalarItems<'a> {
pub(super) fn new(
options: &'a CollectionConversionOptions,
value: &'a str,
) -> Self {
let delimiters = options.delimiters();
let (ascii_delimiters, non_ascii_delimiters) = if delimiters.len() > 8 {
let mut ascii = [false; 128];
let mut non_ascii = Vec::new();
for &delimiter in delimiters {
if delimiter.is_ascii() {
ascii[delimiter as usize] = true;
} else {
non_ascii.push(delimiter);
}
}
non_ascii.sort_unstable();
non_ascii.dedup();
(Some(ascii), Some(non_ascii))
} else {
(None, None)
};
Self {
value,
delimiters,
ascii_delimiters,
non_ascii_delimiters,
split_scalar_strings: options.split_scalar_strings(),
trim_items: options.trim_items(),
empty_item_policy: options.empty_item_policy(),
max_items: options.max_items(),
retained_items: 0,
next_start: Some(0),
next_source_index: 0,
}
}
fn next_raw(&mut self) -> Option<ScalarItem<'a>> {
let start = self.next_start?;
let source_index = self.next_source_index;
self.next_source_index += 1;
if !self.split_scalar_strings {
self.next_start = None;
return Some(ScalarItem {
source_index,
value: self.value,
});
}
let remaining = &self.value[start..];
let ascii_delimiters = self.ascii_delimiters.as_ref();
let non_ascii_delimiters = self.non_ascii_delimiters.as_deref();
let delimiters = self.delimiters;
match remaining.char_indices().find(|(_, character)| {
let Some(ascii) = ascii_delimiters else {
return delimiters.contains(character);
};
if character.is_ascii() {
ascii[*character as usize]
} else {
non_ascii_delimiters.is_some_and(|sorted| {
sorted.binary_search(character).is_ok()
})
}
}) {
Some((relative_end, delimiter)) => {
let end = start + relative_end;
self.next_start = Some(end + delimiter.len_utf8());
Some(ScalarItem {
source_index,
value: &self.value[start..end],
})
}
None => {
self.next_start = None;
Some(ScalarItem {
source_index,
value: remaining,
})
}
}
}
fn retain_item(
&mut self,
item: ScalarItem<'a>,
) -> Result<ScalarItem<'a>, ScalarItemError> {
if self.retained_items >= self.max_items {
self.next_start = None;
return Err(ScalarItemError::item_limit_exceeded(
item.source_index,
self.max_items,
));
}
self.retained_items += 1;
Ok(item)
}
}
impl<'a> Iterator for ScalarItems<'a> {
type Item = Result<ScalarItem<'a>, ScalarItemError>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let mut item = self.next_raw()?;
if self.trim_items {
item.value = item.value.trim();
}
if !item.value.is_empty() {
return Some(self.retain_item(item));
}
match self.empty_item_policy {
EmptyItemPolicy::Keep => return Some(self.retain_item(item)),
EmptyItemPolicy::Skip => {}
EmptyItemPolicy::Reject => {
return Some(Err(ScalarItemError::new(item.source_index)));
}
}
}
}
}