path helpers

This commit is contained in:
Janis 2023-04-01 16:35:16 +02:00
parent fa66bf415e
commit 8ce787a837
2 changed files with 221 additions and 0 deletions

View file

@ -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
View 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);
}
}