mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-05 00:39:31 +00:00
Components now iterates over Component.
This commit is contained in:
parent
1fa7dffddf
commit
3828aeec9b
@ -1,23 +1,35 @@
|
||||
//! Entry finding
|
||||
|
||||
// Imports
|
||||
use crate::{DirEntry, DirEntryKind, DirEntryPtr, DirPtr, Path, PathBuf};
|
||||
use crate::{path::Component, DirEntry, DirEntryKind, DirEntryPtr, DirPtr, Path, PathBuf};
|
||||
use std::io;
|
||||
|
||||
/// Finds an entry given it's path
|
||||
pub fn find_entry<R: io::Seek + io::Read>(
|
||||
reader: &mut R, path: &Path,
|
||||
) -> Result<(DirEntryPtr, DirEntry), FindEntryError> {
|
||||
// Current directory pointer
|
||||
let mut cur_dir_ptr = DirPtr::root();
|
||||
|
||||
// Current entry
|
||||
let mut cur_entry = None;
|
||||
|
||||
let mut components = path.components();
|
||||
while let Some(entry_name) = components.next() {
|
||||
while let Some(component) = components.next() {
|
||||
// Get the next entry's name
|
||||
let entry_name = match component {
|
||||
// Note: We start at the root, so `Root` doesn't do anything to us
|
||||
Component::Root | Component::CurDir => continue,
|
||||
Component::ParentDir => return Err(FindEntryError::ParentDir),
|
||||
Component::Normal(entry) => entry,
|
||||
};
|
||||
|
||||
// Find the entry
|
||||
let (entry_ptr, entry) = cur_dir_ptr
|
||||
.find_entry(reader, entry_name.as_ascii())
|
||||
.find_entry(reader, entry_name)
|
||||
.map_err(FindEntryError::FindEntry)?;
|
||||
|
||||
// If there's no component after this, return the current entry
|
||||
// If there's no component after this, break
|
||||
if components.clone().next().is_none() {
|
||||
return Ok((entry_ptr, entry));
|
||||
}
|
||||
@ -32,12 +44,15 @@ pub fn find_entry<R: io::Seek + io::Read>(
|
||||
},
|
||||
|
||||
// If we got a directory, continue
|
||||
DirEntryKind::Dir { ptr } => cur_dir_ptr = ptr,
|
||||
DirEntryKind::Dir { ptr } => {
|
||||
cur_entry = Some((entry_ptr, entry));
|
||||
cur_dir_ptr = ptr;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// If we get here, the path was empty
|
||||
Err(FindEntryError::EmptyPath)
|
||||
// If we got here, try to return our current entry
|
||||
cur_entry.ok_or(FindEntryError::EmptyPath)
|
||||
}
|
||||
|
||||
/// Error type for [`find_entry`]
|
||||
@ -47,6 +62,10 @@ pub enum FindEntryError {
|
||||
#[error("Unable to find entry")]
|
||||
FindEntry(#[source] crate::ptr::FindEntryError),
|
||||
|
||||
/// Cannot go back to parent directory
|
||||
#[error("Cannot go back to parent directory")]
|
||||
ParentDir,
|
||||
|
||||
/// Expected directory
|
||||
#[error("Expected directory at {path}")]
|
||||
ExpectedDir {
|
||||
|
||||
@ -2,10 +2,14 @@
|
||||
//!
|
||||
//! See the [`Path`] type for more details.
|
||||
|
||||
// Modules
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
// Imports
|
||||
use ascii::{AsciiChar, AsciiStr, AsciiString};
|
||||
use ref_cast::RefCast;
|
||||
use std::{fmt, ops};
|
||||
use std::{fmt, iter::FusedIterator, ops};
|
||||
|
||||
/// A path
|
||||
///
|
||||
@ -74,34 +78,10 @@ impl Path {
|
||||
|
||||
/// Returns an iterator over all components of this path
|
||||
#[must_use]
|
||||
pub fn components(&self) -> Components {
|
||||
pub const fn components(&self) -> Components {
|
||||
Components::new(self)
|
||||
}
|
||||
|
||||
/// Splits this path at it's first component
|
||||
#[must_use]
|
||||
pub fn split_first(&self) -> Option<(&AsciiStr, &Self)> {
|
||||
let mut components = self.components();
|
||||
let first = components.next()?;
|
||||
Some((first.as_ascii(), components.path))
|
||||
}
|
||||
|
||||
/// Splits this path at it's last component
|
||||
#[must_use]
|
||||
pub fn split_last(&self) -> Option<(&Self, &AsciiStr)> {
|
||||
// Get the last component
|
||||
let (idx, last) = self.components().enumerate().last()?;
|
||||
|
||||
// If it was the start component, return
|
||||
if idx == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Else separate them
|
||||
let start = &self.0[..(self.len() - last.len())];
|
||||
Some((Self::new(start), last.as_ascii()))
|
||||
}
|
||||
|
||||
/// Converts this path into a [`PathBuf`]
|
||||
#[must_use]
|
||||
pub fn to_path_buf(&self) -> PathBuf {
|
||||
@ -181,11 +161,9 @@ pub struct Components<'a> {
|
||||
|
||||
impl<'a> Components<'a> {
|
||||
/// Creates new components
|
||||
pub(self) fn new(path: &'a Path) -> Self {
|
||||
pub(self) const fn new(path: &'a Path) -> Self {
|
||||
// Trim all trailing `\`
|
||||
Self {
|
||||
path: path.trim_trailing(),
|
||||
}
|
||||
Self { path }
|
||||
}
|
||||
|
||||
/// Returns the remaining path
|
||||
@ -195,21 +173,55 @@ impl<'a> Components<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FusedIterator for Components<'a> {}
|
||||
|
||||
impl<'a> Iterator for Components<'a> {
|
||||
type Item = &'a Path;
|
||||
type Item = Component<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// Trim all leading `\`
|
||||
self.path = self.path.trim_leading();
|
||||
// Read until the next `\\` or eof
|
||||
let (cmpt, rest) = match self.path.0.chars().position(|ch| ch == AsciiChar::BackSlash) {
|
||||
// If we found it first, emit a root component
|
||||
Some(0) => (Component::Root, self.path),
|
||||
|
||||
// Then split at the first `\` we find
|
||||
let (path, rest) = match self.path.0.chars().position(|ch| ch == AsciiChar::BackSlash) {
|
||||
Some(idx) => (Path::new(&self.path.0[..idx]), Path::new(&self.path.0[idx..])),
|
||||
None if !self.path.is_empty() => (self.path, Path::empty()),
|
||||
// Else it's a normal component
|
||||
// Note: We handle `.` and `..` below
|
||||
Some(idx) => (Component::Normal(self.path[..idx].as_ascii()), &self.path[idx..]),
|
||||
|
||||
// If we didn't find `\\`, but we're not empty, return the remaining path
|
||||
None if !self.path.is_empty() => (Component::Normal(self.path.as_ascii()), Path::empty()),
|
||||
|
||||
// Else we're done
|
||||
None => return None,
|
||||
};
|
||||
|
||||
// Trim all remaining leading `\\`s
|
||||
let rest = rest.trim_leading();
|
||||
|
||||
// If the component is a normal `.` or `..`, change it
|
||||
let cmpt = match cmpt {
|
||||
Component::Normal(path) if path == "." => Component::CurDir,
|
||||
Component::Normal(path) if path == ".." => Component::ParentDir,
|
||||
_ => cmpt,
|
||||
};
|
||||
|
||||
self.path = rest;
|
||||
Some(path)
|
||||
Some(cmpt)
|
||||
}
|
||||
}
|
||||
|
||||
/// Component
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
pub enum Component<'a> {
|
||||
/// Root, `\\` at the start of the path
|
||||
Root,
|
||||
|
||||
/// Cur dir, `.`
|
||||
CurDir,
|
||||
|
||||
/// Parent dir, `..`
|
||||
ParentDir,
|
||||
|
||||
/// Normal
|
||||
Normal(&'a AsciiStr),
|
||||
}
|
||||
|
||||
62
dcb-drv/src/path/test.rs
Normal file
62
dcb-drv/src/path/test.rs
Normal file
@ -0,0 +1,62 @@
|
||||
//! Tests
|
||||
|
||||
// Imports
|
||||
use super::*;
|
||||
#[allow(clippy::enum_glob_use)] // It's a limited scope
|
||||
use Component::*;
|
||||
|
||||
/// Creates an ascii string from `path`
|
||||
fn ascii(path: &str) -> &AsciiStr {
|
||||
AsciiStr::from_ascii(path).expect("Unable to create path")
|
||||
}
|
||||
|
||||
/// Asserts components of `path` are `cmpts`
|
||||
fn assert_components_eq(path: &Path, cmpts: &[Component]) {
|
||||
assert_eq!(path.components().collect::<Vec<_>>(), cmpts);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple() {
|
||||
self::assert_components_eq(Path::new(ascii("A\\B\\C")), &[
|
||||
Normal(ascii("A")),
|
||||
Normal(ascii("B")),
|
||||
Normal(ascii("C")),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root() {
|
||||
self::assert_components_eq(Path::new(ascii("\\A")), &[Root, Normal(ascii("A"))]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cur() {
|
||||
self::assert_components_eq(Path::new(ascii(".\\A\\.")), &[CurDir, Normal(ascii("A")), CurDir]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parent() {
|
||||
self::assert_components_eq(Path::new(ascii("..\\A\\..")), &[
|
||||
ParentDir,
|
||||
Normal(ascii("A")),
|
||||
ParentDir,
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading() {
|
||||
self::assert_components_eq(Path::new(ascii("\\\\\\\\A")), &[Root, Normal(ascii("A"))]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing() {
|
||||
self::assert_components_eq(Path::new(ascii("A\\\\\\\\")), &[Normal(ascii("A"))]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extra_separators() {
|
||||
self::assert_components_eq(Path::new(ascii("A\\\\\\\\B")), &[
|
||||
Normal(ascii("A")),
|
||||
Normal(ascii("B")),
|
||||
]);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user