pub struct StrSplit<'a> {
remainder: Option<&'a str>,
delimiter: &'a str,
}
impl<'a> StrSplit<'a> {
pub fn new(haystack: &'a str, delimiter: &'a str) -> Self {
Self {
remainder: Some(haystack), delimiter, }
}
}
impl<'a> Iterator for StrSplit<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
let remainder = self.remainder.as_mut()?;
if let Some(pos) = remainder.find(self.delimiter) {
let until_delimiter = &remainder[..pos];
let next_index = pos + self.delimiter.len();
*remainder = &remainder[next_index..];
Some(until_delimiter)
} else {
self.remainder.take()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn split_basic() {
let text = "a,b,c";
let mut splitter = StrSplit::new(text, ",");
assert_eq!(splitter.next(), Some("a"));
assert_eq!(splitter.next(), Some("b"));
assert_eq!(splitter.next(), Some("c"));
assert_eq!(splitter.next(), None);
}
#[test]
fn delimiter_not_found() {
let text = "hello";
let mut splitter = StrSplit::new(text, ",");
assert_eq!(splitter.next(), Some("hello"));
assert_eq!(splitter.next(), None);
}
#[test]
fn delimiter_at_start() {
let text = ",a,b";
let mut splitter = StrSplit::new(text, ",");
assert_eq!(splitter.next(), Some(""));
assert_eq!(splitter.next(), Some("a"));
assert_eq!(splitter.next(), Some("b"));
assert_eq!(splitter.next(), None);
}
#[test]
fn delimiter_at_end() {
let text = "a,b,";
let mut splitter = StrSplit::new(text, ",");
assert_eq!(splitter.next(), Some("a"));
assert_eq!(splitter.next(), Some("b"));
assert_eq!(splitter.next(), Some(""));
assert_eq!(splitter.next(), None);
}
#[test]
fn multi_char_delimiter() {
let text = "a--b--c";
let mut splitter = StrSplit::new(text, "--");
assert_eq!(splitter.next(), Some("a"));
assert_eq!(splitter.next(), Some("b"));
assert_eq!(splitter.next(), Some("c"));
assert_eq!(splitter.next(), None);
}
#[test]
fn collect_iterator() {
let text = "x:y:z";
let parts: Vec<&str> = StrSplit::new(text, ":").collect();
assert_eq!(parts, vec!["x", "y", "z"]);
}
}