use os_str_bytes::{OsStrBytes, OsStringBytes};
use std::ffi::{OsStr, OsString};
use std::path::{Component, Path, PathBuf};
#[derive(Debug, Clone, PartialEq)]
pub struct PathRegex {
start: Option<PathStart>,
dirs: Vec<DirKind>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum PathStart {
Root,
Pwd,
Parent,
None,
}
#[derive(Debug, Clone, PartialEq)]
enum DirKind {
Normal(OsString),
Regex(Vec<Part>),
Parent,
AnyDirs,
}
#[derive(Debug, Clone, PartialEq)]
enum Part {
Literal(OsString),
ZeroOrMore,
AnyCharcater,
CharRange(Vec<u8>), }
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathParseError {
NothingGiven,
}
impl PathStart {
fn parse(component: Component<'_>) -> Option<Self> {
match component {
Component::Prefix(_) => todo!("handle prefixes on windows"),
Component::RootDir => Some(PathStart::Root),
Component::CurDir => Some(PathStart::Pwd),
Component::ParentDir => Some(PathStart::Parent),
Component::Normal(_) => None,
}
}
}
impl DirKind {
fn parse(component: Component<'_>) -> Result<Self, PathParseError> {
let source = match component {
Component::Normal(x) if x == "**" => return Ok(Self::AnyDirs),
Component::Normal(osstr) => osstr,
Component::ParentDir => return Ok(Self::Parent),
_ => unreachable!("others are only yielded at the start components"),
};
let mut parts = Vec::new();
let mut literal = Vec::new();
let raw = source.to_raw_bytes();
let mut iter = raw.iter();
for &byte in iter {
if !b"?*[".contains(&byte) {
literal.push(byte);
continue;
}
if !literal.is_empty() {
parts.push(Part::Literal(OsString::assert_from_raw_vec(std::mem::take(&mut literal))));
}
match byte {
b'?' => parts.push(Part::AnyCharcater),
b'*' => parts.push(Part::ZeroOrMore),
b'[' => todo!("start a char match"),
_ => unreachable!("already handled by the if statement above"),
}
}
if parts.is_empty() {
return Ok(Self::Normal(OsString::assert_from_raw_vec(literal)));
}
if !literal.is_empty() {
parts.push(Part::Literal(OsString::assert_from_raw_vec(literal)))
}
Ok(Self::Regex(parts))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PathMatch {
Yes,
PartialDirMatch,
No,
}
impl PathRegex {
pub fn new<T: AsRef<Path> + ?Sized>(source: &T) -> Result<Self, PathParseError> {
let path = source.as_ref();
let start = PathStart::parse(path.components().next().ok_or(PathParseError::NothingGiven)?);
let mut components = path.components();
if start.is_some() {
let _ = components.next();
}
let dirs = components.map(DirKind::parse).collect::<Result<_, _>>()?;
Ok(Self { start, dirs })
}
pub fn parse(source: &OsStr) -> Result<Self, PathParseError> {
todo!()
}
pub fn matches<T: AsRef<Path> + ?Sized>(&self, path: &T) -> PathMatch {
todo!();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn foo() {
let path = Path::new("./foo/bar/baz");
}
}