path helpers
This commit is contained in:
parent
fa66bf415e
commit
8ce787a837
|
@ -23,6 +23,7 @@ extern crate alloc;
|
||||||
|
|
||||||
pub mod crc32c;
|
pub mod crc32c;
|
||||||
pub mod files;
|
pub mod files;
|
||||||
|
pub mod path;
|
||||||
pub mod structs;
|
pub mod structs;
|
||||||
pub mod tree;
|
pub mod tree;
|
||||||
pub mod v2;
|
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