path helper fix

- remove directory segment and instead just have "file" for directories and
files and other kinds of files since we cant know just based on the path what it
is
- segmetns are stored as vecdeque in normalized path to allow for hopefully more
efficient poping in FIFO order (but definitely more ergonomic)
- test for trailing seperators, the last trailing seperator is ignored, multiple
trailing seperators are turned into noops because i parse paths left to
right (regex?) in the simplest way possible. in normalized paths noops are
completely ignored/removed
This commit is contained in:
Janis 2023-04-02 00:23:45 +02:00
parent 2af01747e7
commit 5a9918285f

View file

@ -1,4 +1,4 @@
use alloc::vec::Vec; use alloc::{collections::VecDeque, vec::Vec};
const SEPERATOR: u8 = b'/'; const SEPERATOR: u8 = b'/';
@ -31,15 +31,15 @@ pub trait Path: AsRef<[u8]> {
let iter = self.segment_iter(); let iter = self.segment_iter();
let mut segments = let mut segments =
Vec::with_capacity(self.as_ref().iter().filter(|&b| b == &SEPERATOR).count()); VecDeque::with_capacity(self.as_ref().iter().filter(|&b| b == &SEPERATOR).count());
for segment in iter { for segment in iter {
match segment { match segment {
Segment::NoOp | Segment::CurrentDir => {} Segment::NoOp | Segment::CurrentDir => {}
Segment::Root | Segment::Directory(_) | Segment::File(_) => segments.push(segment), Segment::Root | Segment::File(_) => segments.push_back(segment),
Segment::ParentDir => { Segment::ParentDir => {
if segments.last() != Some(&Segment::Root) { if segments.back() != Some(&Segment::Root) {
segments.pop(); segments.pop_back();
} }
} }
} }
@ -55,20 +55,26 @@ pub enum Segment<'a> {
NoOp, NoOp,
CurrentDir, CurrentDir,
ParentDir, ParentDir,
Directory(&'a [u8]),
File(&'a [u8]), File(&'a [u8]),
} }
impl<T> Path for T where T: AsRef<[u8]> {} impl<T> Path for T where T: AsRef<[u8]> {}
pub struct NormalizedPath<'a> { pub struct NormalizedPath<'a> {
segments: Vec<Segment<'a>>, segments: VecDeque<Segment<'a>>,
} }
impl<'a> NormalizedPath<'a> { impl<'a> NormalizedPath<'a> {
pub fn iter(&self) -> core::slice::Iter<Segment> { pub fn into_iter(self) -> alloc::collections::vec_deque::IntoIter<Segment<'a>> {
self.segments.into_iter()
}
pub fn iter(&self) -> alloc::collections::vec_deque::Iter<Segment> {
self.segments.iter() self.segments.iter()
} }
pub fn pop_segment(&mut self) -> Option<Segment<'a>> {
self.segments.pop_front()
}
} }
pub struct PathSegmentIter<'a> { pub struct PathSegmentIter<'a> {
@ -95,6 +101,9 @@ impl<'a> Iterator for PathSegmentIter<'a> {
if self.offset == 0 { if self.offset == 0 {
// handle root segment // handle root segment
Some(Segment::Root) Some(Segment::Root)
} else if bytes.is_empty() {
// trailign seperator
None
} else { } else {
// noop // noop
Some(Segment::NoOp) Some(Segment::NoOp)
@ -104,11 +113,7 @@ impl<'a> Iterator for PathSegmentIter<'a> {
} else if segment == &b".."[..] { } else if segment == &b".."[..] {
Some(Segment::ParentDir) Some(Segment::ParentDir)
} else { } else {
if next == bytes.len() {
Some(Segment::File(segment)) Some(Segment::File(segment))
} else {
Some(Segment::Directory(segment))
}
}; };
self.offset += bytes.len().min(next + 1); self.offset += bytes.len().min(next + 1);
@ -174,11 +179,11 @@ mod segment_iter_tests {
fn test_noop() { fn test_noop() {
let mut iter = b"this//a//path//some.file".segment_iter(); let mut iter = b"this//a//path//some.file".segment_iter();
assert_eq!(iter.next(), Some(Segment::Directory(&b"this"[..]))); assert_eq!(iter.next(), Some(Segment::File(&b"this"[..])));
assert_eq!(iter.next(), Some(Segment::NoOp)); assert_eq!(iter.next(), Some(Segment::NoOp));
assert_eq!(iter.next(), Some(Segment::Directory(&b"a"[..]))); assert_eq!(iter.next(), Some(Segment::File(&b"a"[..])));
assert_eq!(iter.next(), Some(Segment::NoOp)); assert_eq!(iter.next(), Some(Segment::NoOp));
assert_eq!(iter.next(), Some(Segment::Directory(&b"path"[..]))); assert_eq!(iter.next(), Some(Segment::File(&b"path"[..])));
assert_eq!(iter.next(), Some(Segment::NoOp)); assert_eq!(iter.next(), Some(Segment::NoOp));
assert_eq!(iter.next(), Some(Segment::File(&b"some.file"[..]))); assert_eq!(iter.next(), Some(Segment::File(&b"some.file"[..])));
assert_eq!(iter.next(), None); assert_eq!(iter.next(), None);
@ -203,17 +208,60 @@ mod segment_iter_tests {
} }
} }
#[test]
fn test_trailing_seperator() {
let mut iter = b"/this//".segment_iter();
assert_eq!(iter.next(), Some(Segment::Root));
assert_eq!(iter.next(), Some(Segment::File(&b"this"[..])));
assert_eq!(iter.next(), Some(Segment::NoOp));
assert_eq!(iter.next(), None);
let mut iter = b"this//".segment_iter();
assert_eq!(iter.next(), Some(Segment::File(&b"this"[..])));
assert_eq!(iter.next(), Some(Segment::NoOp));
assert_eq!(iter.next(), None);
let mut iter = b"/this////".segment_iter();
assert_eq!(iter.next(), Some(Segment::Root));
assert_eq!(iter.next(), Some(Segment::File(&b"this"[..])));
assert_eq!(iter.next(), Some(Segment::NoOp));
assert_eq!(iter.next(), Some(Segment::NoOp));
assert_eq!(iter.next(), Some(Segment::NoOp));
assert_eq!(iter.next(), None);
let mut iter = b"/this////".normalize().into_iter();
assert_eq!(iter.next(), Some(Segment::Root));
assert_eq!(iter.next(), Some(Segment::File(&b"this"[..])));
assert_eq!(iter.next(), None);
let mut iter = b"/this/".normalize().into_iter();
assert_eq!(iter.next(), Some(Segment::Root));
assert_eq!(iter.next(), Some(Segment::File(&b"this"[..])));
assert_eq!(iter.next(), None);
let mut iter = b"/this".normalize().into_iter();
assert_eq!(iter.next(), Some(Segment::Root));
assert_eq!(iter.next(), Some(Segment::File(&b"this"[..])));
assert_eq!(iter.next(), None);
}
#[test] #[test]
fn test_iter() { fn test_iter() {
let mut iter = b"/this/is/a//path/to/some.file".segment_iter(); let mut iter = b"/this/is/a//path/to/some.file".segment_iter();
assert_eq!(iter.next(), Some(Segment::Root)); assert_eq!(iter.next(), Some(Segment::Root));
assert_eq!(iter.next(), Some(Segment::Directory(&b"this"[..]))); assert_eq!(iter.next(), Some(Segment::File(&b"this"[..])));
assert_eq!(iter.next(), Some(Segment::Directory(&b"is"[..]))); assert_eq!(iter.next(), Some(Segment::File(&b"is"[..])));
assert_eq!(iter.next(), Some(Segment::Directory(&b"a"[..]))); assert_eq!(iter.next(), Some(Segment::File(&b"a"[..])));
assert_eq!(iter.next(), Some(Segment::NoOp)); assert_eq!(iter.next(), Some(Segment::NoOp));
assert_eq!(iter.next(), Some(Segment::Directory(&b"path"[..]))); assert_eq!(iter.next(), Some(Segment::File(&b"path"[..])));
assert_eq!(iter.next(), Some(Segment::Directory(&b"to"[..]))); assert_eq!(iter.next(), Some(Segment::File(&b"to"[..])));
assert_eq!(iter.next(), Some(Segment::File(&b"some.file"[..]))); assert_eq!(iter.next(), Some(Segment::File(&b"some.file"[..])));
assert_eq!(iter.next(), None); assert_eq!(iter.next(), None);
} }