path helpers
This commit is contained in:
parent
fa66bf415e
commit
8ce787a837
|
@ -23,6 +23,7 @@ extern crate alloc;
|
|||
|
||||
pub mod crc32c;
|
||||
pub mod files;
|
||||
pub mod path;
|
||||
pub mod structs;
|
||||
pub mod tree;
|
||||
pub mod v2;
|
||||
|
|
220
btrfs/src/path.rs
Normal file
220
btrfs/src/path.rs
Normal file
|
@ -0,0 +1,220 @@
|
|||
use alloc::vec::Vec;
|
||||
|
||||
const SEPERATOR: u8 = b'/';
|
||||
|
||||
pub trait Path: AsRef<[u8]> {
|
||||
fn is_absolute(&self) -> bool {
|
||||
self.as_ref().get(0) == Some(&SEPERATOR)
|
||||
}
|
||||
|
||||
fn is_relative(&self) -> bool {
|
||||
!self.is_absolute()
|
||||
}
|
||||
|
||||
fn path_segment_iter<'a>(&'a self) -> PathIterator<'a> {
|
||||
let bytes = if self.is_absolute() {
|
||||
&self.as_ref()[1..]
|
||||
} else {
|
||||
self.as_ref()
|
||||
};
|
||||
PathIterator { bytes }
|
||||
}
|
||||
|
||||
fn segment_iter<'a>(&'a self) -> PathSegmentIter<'a> {
|
||||
PathSegmentIter {
|
||||
bytes: self.as_ref(),
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize(&self) -> NormalizedPath<'_> {
|
||||
let iter = self.segment_iter();
|
||||
|
||||
let mut segments =
|
||||
Vec::with_capacity(self.as_ref().iter().filter(|&b| b == &SEPERATOR).count());
|
||||
|
||||
for segment in iter {
|
||||
match segment {
|
||||
Segment::NoOp | Segment::CurrentDir => {}
|
||||
Segment::Root | Segment::Directory(_) | Segment::File(_) => segments.push(segment),
|
||||
Segment::ParentDir => {
|
||||
if segments.last() != Some(&Segment::Root) {
|
||||
segments.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NormalizedPath { segments }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Segment<'a> {
|
||||
Root,
|
||||
NoOp,
|
||||
CurrentDir,
|
||||
ParentDir,
|
||||
Directory(&'a [u8]),
|
||||
File(&'a [u8]),
|
||||
}
|
||||
|
||||
impl<T> Path for T where T: AsRef<[u8]> {}
|
||||
|
||||
pub struct NormalizedPath<'a> {
|
||||
segments: Vec<Segment<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> NormalizedPath<'a> {
|
||||
pub fn iter(&self) -> core::slice::Iter<Segment> {
|
||||
self.segments.iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PathSegmentIter<'a> {
|
||||
bytes: &'a [u8],
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for PathSegmentIter<'a> {
|
||||
type Item = Segment<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let bytes = &self.bytes[self.offset..];
|
||||
if !bytes.is_empty() {
|
||||
let next = bytes
|
||||
.iter()
|
||||
.position(|c| c == &SEPERATOR)
|
||||
.unwrap_or(bytes.len());
|
||||
|
||||
// bytes of segment
|
||||
let segment = &bytes[..next];
|
||||
log::info!("{}", alloc::string::String::from_utf8_lossy(segment));
|
||||
|
||||
let segment = if segment.is_empty() {
|
||||
if self.offset == 0 {
|
||||
// handle root segment
|
||||
Some(Segment::Root)
|
||||
} else {
|
||||
// noop
|
||||
Some(Segment::NoOp)
|
||||
}
|
||||
} else if segment == &b"."[..] {
|
||||
Some(Segment::CurrentDir)
|
||||
} else if segment == &b".."[..] {
|
||||
Some(Segment::ParentDir)
|
||||
} else {
|
||||
if next == bytes.len() {
|
||||
Some(Segment::File(segment))
|
||||
} else {
|
||||
Some(Segment::Directory(segment))
|
||||
}
|
||||
};
|
||||
self.offset += bytes.len().min(next + 1);
|
||||
|
||||
segment
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// iterator that returns path segments
|
||||
pub struct PathIterator<'a> {
|
||||
bytes: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> PathIterator<'a> {}
|
||||
|
||||
impl<'a> Iterator for PathIterator<'a> {
|
||||
type Item = &'a [u8];
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if !self.bytes.is_empty() {
|
||||
let next = self
|
||||
.bytes
|
||||
.iter()
|
||||
.position(|c| c == &SEPERATOR)
|
||||
.unwrap_or(self.bytes.len());
|
||||
let segment = &self.bytes[..next];
|
||||
self.bytes = &self.bytes[self.bytes.len().min(next + 1)..];
|
||||
Some(segment)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use test_log::test;
|
||||
|
||||
#[test]
|
||||
fn test_iter() {
|
||||
let mut iter = b"/this/is/a//path/to/some.file".path_segment_iter();
|
||||
|
||||
assert_eq!(iter.next(), Some(&b"this"[..]));
|
||||
assert_eq!(iter.next(), Some(&b"is"[..]));
|
||||
assert_eq!(iter.next(), Some(&b"a"[..]));
|
||||
assert_eq!(iter.next(), Some(&b""[..]));
|
||||
assert_eq!(iter.next(), Some(&b"path"[..]));
|
||||
assert_eq!(iter.next(), Some(&b"to"[..]));
|
||||
assert_eq!(iter.next(), Some(&b"some.file"[..]));
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod segment_iter_tests {
|
||||
use super::*;
|
||||
use test_log::test;
|
||||
|
||||
#[test]
|
||||
fn test_noop() {
|
||||
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::NoOp));
|
||||
assert_eq!(iter.next(), Some(Segment::Directory(&b"a"[..])));
|
||||
assert_eq!(iter.next(), Some(Segment::NoOp));
|
||||
assert_eq!(iter.next(), Some(Segment::Directory(&b"path"[..])));
|
||||
assert_eq!(iter.next(), Some(Segment::NoOp));
|
||||
assert_eq!(iter.next(), Some(Segment::File(&b"some.file"[..])));
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root() {
|
||||
let path = b"/absolute/path";
|
||||
let path2 = b"relative/path";
|
||||
let path3 = b"../relative/path";
|
||||
let path4 = b"./relative/path";
|
||||
let path5 = b".//relative/path";
|
||||
let relative_paths = [&path2[..], &path3[..], &path4[..], &path5[..]];
|
||||
assert!(path.is_absolute());
|
||||
let mut iter = path.segment_iter();
|
||||
assert_eq!(iter.next(), Some(Segment::Root));
|
||||
|
||||
for path in relative_paths {
|
||||
assert!(path.is_relative());
|
||||
let mut iter = path.segment_iter();
|
||||
assert_ne!(iter.next(), Some(Segment::Root));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_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::Directory(&b"this"[..])));
|
||||
assert_eq!(iter.next(), Some(Segment::Directory(&b"is"[..])));
|
||||
assert_eq!(iter.next(), Some(Segment::Directory(&b"a"[..])));
|
||||
assert_eq!(iter.next(), Some(Segment::NoOp));
|
||||
assert_eq!(iter.next(), Some(Segment::Directory(&b"path"[..])));
|
||||
assert_eq!(iter.next(), Some(Segment::Directory(&b"to"[..])));
|
||||
assert_eq!(iter.next(), Some(Segment::File(&b"some.file"[..])));
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue