use core::fmt;
use core::iter::FusedIterator;
#[derive(Clone)]
pub struct Path<'a> {
full: &'a str,
idx: usize,
}
impl<'a> Path<'a> {
pub fn new(full: &'a str) -> Self {
Self { full, idx: 0 }
}
pub fn full(&self) -> &'a str {
self.full
}
}
impl fmt::Debug for Path<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"{}\"", self.full)
}
}
impl<'a> From<&'a str> for Path<'a> {
fn from(path: &'a str) -> Self {
Self::new(path)
}
}
impl<'a> Iterator for Path<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
if self.idx >= self.full.len() {
return None;
}
match self.full[self.idx..].find('.') {
Some(next_sep_idx) => {
let x = &self.full[self.idx..][..next_sep_idx];
self.idx = self.idx.strict_add(next_sep_idx).strict_add('.'.len_utf8());
Some(x)
}
None => {
let x = &self.full[self.idx..];
self.idx = self.full.len();
Some(x)
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.full.len().strict_sub(self.idx)))
}
}
impl ExactSizeIterator for Path<'_> {
fn len(&self) -> usize {
if self.idx >= self.full.len() {
return 0;
}
self.full[self.idx..]
.chars()
.filter(|c| *c == '.')
.count()
.strict_add(1)
}
}
impl Path<'_> {
pub fn is_empty(&self) -> bool {
self.idx >= self.full.len()
}
}
impl FusedIterator for Path<'_> {}
#[cfg(test)]
mod tests {
use core::iter::zip;
use super::*;
fn x(x: &str, expected: &[&str]) {
let path = Path::new(x);
assert_eq!(path.len(), expected.len());
for (a, b) in zip(path, expected) {
assert_eq!(a, *b);
}
}
#[test]
fn test_simple() {
x("foo.bar", &["foo", "bar"]);
x("foo.bar.foobar.barfoo", &["foo", "bar", "foobar", "barfoo"]);
x("foo", &["foo"]);
}
#[test]
fn test_empty() {
x("", &[]);
x(".", &["", ""]);
x("..", &["", "", ""]);
x("...", &["", "", "", ""]);
}
#[test]
fn test_end_in_sep() {
x("foo.", &["foo", ""]);
x("foo.bar.", &["foo", "bar", ""]);
}
#[test]
fn test_start_with_sep() {
x(".foo", &["", "foo"]);
x(".foo.bar", &["", "foo", "bar"]);
}
#[test]
fn test_double_sep() {
x("foo..bar.foobar", &["foo", "", "bar", "foobar"]);
x("barfoo.foo..bar", &["barfoo", "foo", "", "bar"]);
}
#[test]
fn test_triple_sep() {
x("foo...bar.foobar", &["foo", "", "", "bar", "foobar"]);
x("barfoo.foo...bar", &["barfoo", "foo", "", "", "bar"]);
}
}