#![allow(clippy::module_inception)]
use crate::fs::path::{PathBuf, SEPARATOR};
use crate::{CStr16, CString16};
use core::fmt::{Display, Formatter};
#[derive(Debug, Eq, PartialOrd, Ord)]
pub struct Path(CStr16);
impl Path {
#[must_use]
pub fn new<S: AsRef<CStr16> + ?Sized>(s: &S) -> &Self {
unsafe { &*(s.as_ref() as *const CStr16 as *const Self) }
}
#[must_use]
pub fn to_cstr16(&self) -> &CStr16 {
&self.0
}
#[must_use]
pub fn to_path_buf(&self) -> PathBuf {
let cstring = CString16::from(&self.0);
PathBuf::from(cstring)
}
#[must_use]
pub fn components(&self) -> Components {
Components {
path: self.as_ref(),
i: 0,
}
}
#[must_use]
pub fn parent(&self) -> Option<PathBuf> {
let components_count = self.components().count();
if components_count == 0 {
return None;
}
let sep_count = self
.0
.as_slice()
.iter()
.filter(|char| **char == SEPARATOR)
.count();
if sep_count == 0 {
return None;
}
let path =
self.components()
.take(components_count - 1)
.fold(CString16::new(), |mut acc, next| {
if !acc.is_empty() && *acc.as_slice().last().unwrap() != SEPARATOR {
acc.push(SEPARATOR);
}
acc.push_str(next.as_ref());
acc
});
let path = PathBuf::from(path);
Some(path)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.to_cstr16().is_empty()
}
}
impl Display for Path {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
Display::fmt(self.to_cstr16(), f)
}
}
impl PartialEq for Path {
fn eq(&self, other: &Self) -> bool {
self.components().count() == other.components().count()
&& !self
.components()
.zip(other.components())
.any(|(c1, c2)| c1 != c2)
}
}
#[derive(Debug)]
pub struct Components<'a> {
path: &'a CStr16,
i: usize,
}
impl<'a> Iterator for Components<'a> {
type Item = CString16;
fn next(&mut self) -> Option<Self::Item> {
if self.path.is_empty() {
return None;
}
if self.path.num_chars() == 1 && self.path.as_slice()[0] == SEPARATOR {
return None;
}
if self.i == 0 && *self.path.as_slice().first().unwrap() == SEPARATOR {
self.i = 1;
}
let len = self
.path
.iter()
.skip(self.i)
.take_while(|c| **c != SEPARATOR)
.count();
let progress = self.i + len;
if progress > self.path.num_chars() {
None
} else {
let part = &self.path.as_slice()[self.i..self.i + len];
let mut string = CString16::new();
part.iter().for_each(|c| string.push(*c));
self.i = progress + 1;
Some(string)
}
}
}
mod convenience_impls {
use super::*;
use core::borrow::Borrow;
impl AsRef<Path> for &Path {
fn as_ref(&self) -> &Path {
self
}
}
impl<'a> From<&'a CStr16> for &'a Path {
fn from(value: &'a CStr16) -> Self {
Path::new(value)
}
}
impl AsRef<CStr16> for Path {
fn as_ref(&self) -> &CStr16 {
&self.0
}
}
impl Borrow<CStr16> for Path {
fn borrow(&self) -> &CStr16 {
&self.0
}
}
impl AsRef<Path> for CStr16 {
fn as_ref(&self) -> &Path {
Path::new(self)
}
}
impl Borrow<Path> for CStr16 {
fn borrow(&self) -> &Path {
Path::new(self)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec::Vec;
use uefi_macros::cstr16;
#[test]
fn from_cstr16() {
let source: &CStr16 = cstr16!("\\hello\\foo\\bar");
let _path: &Path = source.into();
let _path: &Path = Path::new(source);
}
#[test]
fn from_cstring16() {
let source = CString16::try_from("\\hello\\foo\\bar").unwrap();
let _path: &Path = source.as_ref().into();
let _path: &Path = Path::new(source.as_ref());
}
#[test]
fn components_iter() {
let path = Path::new(cstr16!("foo\\bar\\hello"));
let components = path.components().collect::<Vec<_>>();
let components: Vec<&CStr16> = components.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
let expected: &[&CStr16] = &[cstr16!("foo"), cstr16!("bar"), cstr16!("hello")];
assert_eq!(components.as_slice(), expected);
let path = Path::new(cstr16!("\\foo\\bar\\hello"));
let components = path.components().collect::<Vec<_>>();
let components: Vec<&CStr16> = components.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
let expected: &[&CStr16] = &[cstr16!("foo"), cstr16!("bar"), cstr16!("hello")];
assert_eq!(components.as_slice(), expected);
let empty_cstring16 = CString16::try_from("").unwrap();
let path = Path::new(empty_cstring16.as_ref());
let components = path.components().collect::<Vec<_>>();
let expected: &[CString16] = &[];
assert_eq!(components.as_slice(), expected);
let _path = Path::new(cstr16!());
let path = Path::new(cstr16!(""));
let components = path.components().collect::<Vec<_>>();
let components: Vec<&CStr16> = components.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
let expected: &[&CStr16] = &[];
assert_eq!(components.as_slice(), expected);
let path = Path::new(cstr16!("\\"));
let components = path.components().collect::<Vec<_>>();
let components: Vec<&CStr16> = components.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
let expected: &[&CStr16] = &[];
assert_eq!(components.as_slice(), expected);
}
#[test]
fn test_parent() {
assert_eq!(None, Path::new(cstr16!("")).parent());
assert_eq!(None, Path::new(cstr16!("\\")).parent());
assert_eq!(
Path::new(cstr16!("a\\b")).parent(),
Some(PathBuf::from(cstr16!("a"))),
);
assert_eq!(
Path::new(cstr16!("\\a\\b")).parent(),
Some(PathBuf::from(cstr16!("a"))),
);
assert_eq!(
Path::new(cstr16!("a\\b\\c\\d")).parent(),
Some(PathBuf::from(cstr16!("a\\b\\c"))),
);
assert_eq!(Path::new(cstr16!("abc")).parent(), None,);
}
#[test]
fn partial_eq() {
let path1 = Path::new(cstr16!(r"a\b"));
let path2 = Path::new(cstr16!(r"\a\b"));
let path3 = Path::new(cstr16!(r"a\b\c"));
assert_eq!(path1, path1);
assert_eq!(path2, path2);
assert_eq!(path3, path3);
assert_eq!(path1, path2);
assert_eq!(path2, path1);
assert_ne!(path1, path3);
assert_ne!(path3, path1);
}
}