use crate::fs::path::Path;
use crate::fs::SEPARATOR;
use crate::{CStr16, CString16, Char16};
use core::fmt::{Display, Formatter};
#[derive(Clone, Debug, Default, Eq, PartialOrd, Ord)]
pub struct PathBuf(CString16);
impl PathBuf {
#[must_use]
pub fn new() -> Self {
Self::default()
}
fn new_from_cstring16(mut string: CString16) -> Self {
const SEARCH: Char16 = unsafe { Char16::from_u16_unchecked('/' as u16) };
string.replace_char(SEARCH, SEPARATOR);
Self(string)
}
pub fn push<P: AsRef<Path>>(&mut self, path: P) {
const SEARCH: Char16 = unsafe { Char16::from_u16_unchecked('/' as u16) };
if path.as_ref().is_empty() {
return;
}
let empty = self.0.is_empty();
let needs_sep = *self
.0
.as_slice_with_nul()
.last()
.expect("Should have at least null character")
!= SEPARATOR;
if !empty && needs_sep {
self.0.push(SEPARATOR)
}
self.0.push_str(path.as_ref().to_cstr16());
self.0.replace_char(SEARCH, SEPARATOR);
}
}
impl PartialEq for PathBuf {
fn eq(&self, other: &Self) -> bool {
let path1: &Path = self.as_ref();
let path2: &Path = other.as_ref();
path1 == path2
}
}
impl Display for PathBuf {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
Display::fmt(self.to_cstr16(), f)
}
}
mod convenience_impls {
use super::*;
use core::borrow::Borrow;
use core::ops::Deref;
impl From<CString16> for PathBuf {
fn from(value: CString16) -> Self {
Self::new_from_cstring16(value)
}
}
impl From<&CStr16> for PathBuf {
fn from(value: &CStr16) -> Self {
Self::new_from_cstring16(CString16::from(value))
}
}
impl Deref for PathBuf {
type Target = Path;
fn deref(&self) -> &Self::Target {
Path::new(&self.0)
}
}
impl AsRef<Path> for PathBuf {
fn as_ref(&self) -> &Path {
self
}
}
impl Borrow<Path> for PathBuf {
fn borrow(&self) -> &Path {
self
}
}
impl AsRef<CStr16> for PathBuf {
fn as_ref(&self) -> &CStr16 {
&self.0
}
}
impl Borrow<CStr16> for PathBuf {
fn borrow(&self) -> &CStr16 {
&self.0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cstr16;
use alloc::string::ToString;
#[test]
fn from_cstr16() {
let source: &CStr16 = cstr16!("\\hello\\foo\\bar");
let _path: PathBuf = source.into();
}
#[test]
fn from_cstring16() {
let source = CString16::try_from("\\hello\\foo\\bar").unwrap();
let _path: PathBuf = source.as_ref().into();
let _path: PathBuf = source.clone().into();
let _path: PathBuf = PathBuf::new_from_cstring16(source);
}
#[test]
fn from_std_string() {
let std_string = "\\hello\\foo\\bar".to_string();
let _path = PathBuf::new_from_cstring16(CString16::try_from(std_string.as_str()).unwrap());
}
#[test]
fn push() {
let mut pathbuf = PathBuf::new();
pathbuf.push(cstr16!("first"));
pathbuf.push(cstr16!("second"));
pathbuf.push(cstr16!("third"));
assert_eq!(pathbuf.to_cstr16(), cstr16!("first\\second\\third"));
let mut pathbuf = PathBuf::new();
pathbuf.push(cstr16!("\\first"));
pathbuf.push(cstr16!("second"));
assert_eq!(pathbuf.to_cstr16(), cstr16!("\\first\\second"));
let empty_cstring16 = CString16::try_from("").unwrap();
let mut pathbuf = PathBuf::new();
pathbuf.push(cstr16!("first"));
pathbuf.push(empty_cstring16.as_ref());
pathbuf.push(empty_cstring16.as_ref());
pathbuf.push(empty_cstring16.as_ref());
pathbuf.push(cstr16!("second"));
assert_eq!(pathbuf.to_cstr16(), cstr16!("first\\second"));
}
#[test]
fn partial_eq() {
let mut pathbuf1 = PathBuf::new();
pathbuf1.push(cstr16!("first"));
pathbuf1.push(cstr16!("second"));
pathbuf1.push(cstr16!("third"));
assert_eq!(pathbuf1, pathbuf1);
let mut pathbuf2 = PathBuf::new();
pathbuf2.push(cstr16!("\\first"));
pathbuf2.push(cstr16!("second"));
assert_eq!(pathbuf2, pathbuf2);
assert_ne!(pathbuf1, pathbuf2);
}
}