#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Path<'a> {
bytes: &'a [u8],
}
impl<'a> Path<'a> {
pub fn new(bytes: &'a [u8]) -> Option<Self> {
if bytes.first() != Some(&b'/') {
return None;
}
Some(Self { bytes })
}
pub(crate) fn components(&self) -> Components<'a> {
Components { bytes: self.bytes }
}
pub fn as_bytes(&self) -> &'a [u8] {
self.bytes
}
}
impl<'a> From<&'a str> for Path<'a> {
fn from(s: &'a str) -> Self {
Self {
bytes: s.as_bytes(),
}
}
}
pub(crate) struct Components<'a> {
bytes: &'a [u8],
}
impl<'a> Iterator for Components<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<&'a [u8]> {
while let Some(b'/') = self.bytes.first() {
self.bytes = &self.bytes[1..];
}
if self.bytes.is_empty() {
return None;
}
let end = self
.bytes
.iter()
.position(|b| *b == b'/')
.unwrap_or(self.bytes.len());
let component = &self.bytes[..end];
self.bytes = &self.bytes[end..];
if component == b"." {
self.next()
} else {
Some(component)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rejects_non_absolute_path() {
assert!(Path::new(b"relative").is_none());
assert!(Path::new(b"").is_none());
}
#[test]
fn iterates_components() {
let p = Path::new(b"/a/b/c").unwrap();
let parts: alloc::vec::Vec<&[u8]> = p.components().collect();
assert_eq!(parts, &[&b"a"[..], &b"b"[..], &b"c"[..]]);
}
#[test]
fn collapses_double_slashes() {
let p = Path::new(b"//a///b//c/").unwrap();
let parts: alloc::vec::Vec<&[u8]> = p.components().collect();
assert_eq!(parts, &[&b"a"[..], &b"b"[..], &b"c"[..]]);
}
#[test]
fn skips_dot_components() {
let p = Path::new(b"/./a/./b").unwrap();
let parts: alloc::vec::Vec<&[u8]> = p.components().collect();
assert_eq!(parts, &[&b"a"[..], &b"b"[..]]);
}
#[test]
fn root_path_yields_no_components() {
let p = Path::new(b"/").unwrap();
let parts: alloc::vec::Vec<&[u8]> = p.components().collect();
assert!(parts.is_empty());
}
}