use std::{
convert::Infallible,
ffi::{OsStr, OsString},
path::{Component, Path, PathBuf},
str::FromStr,
};
use crate::{PathPrefix, PathType, PavedPath, UNIX_PATH_SEPARATOR, WINDOWS_PATH_SEPARATOR};
impl AsRef<PathBuf> for PavedPath {
fn as_ref(&self) -> &PathBuf {
&self.inner
}
}
impl AsRef<Path> for PavedPath {
fn as_ref(&self) -> &Path {
self.inner.as_path()
}
}
impl From<PavedPath> for PathBuf {
fn from(value: PavedPath) -> Self {
value.inner
}
}
impl From<PavedPath> for OsString {
fn from(value: PavedPath) -> Self {
value.inner.into_os_string()
}
}
impl From<PavedPath> for String {
fn from(value: PavedPath) -> Self {
value.inner.to_string_lossy().to_string()
}
}
impl IntoIterator for PavedPath {
type Item = OsString;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
[self.directories, self.file.as_slice().to_vec()]
.concat()
.into_iter()
}
}
impl From<&Path> for PavedPath {
fn from(path: &Path) -> Self {
let path_type = if path.is_absolute() || path.starts_with(UNIX_PATH_SEPARATOR.to_string()) {
PathType::Absolute
} else {
PathType::Relative
};
let mut new_paved_path = PavedPath::new(path_type);
let drive;
{
use std::path::Prefix::*;
if let Some(Component::Prefix(prefix)) = path.components().next() {
drive = match prefix.kind() {
Disk(d) | VerbatimDisk(d) => {
Some(PathPrefix::Drive((d as char).to_ascii_uppercase()))
}
UNC(s1, s2) | VerbatimUNC(s1, s2) => {
Some(PathPrefix::UNC(s1.to_os_string(), s2.to_os_string()))
}
_ => None,
}
} else {
drive = None;
}
}
if let Some(drive) = drive {
new_paved_path.set_prefix(drive);
}
for c in path.components() {
match c {
Component::ParentDir => new_paved_path.push_dir_raw(OsString::from("..")),
Component::Normal(os_str) => new_paved_path.push_dir_raw(os_str.to_os_string()),
_ => {}
}
}
if new_paved_path.last().is_some() {
let raw = path.as_os_str().to_string_lossy();
if !raw.ends_with(UNIX_PATH_SEPARATOR) && !raw.ends_with(WINDOWS_PATH_SEPARATOR) {
let new_file = new_paved_path.pop_dir().unwrap(); new_paved_path.set_file(new_file);
}
}
new_paved_path.rebuild_inner();
new_paved_path
}
}
impl From<PathBuf> for PavedPath {
fn from(value: PathBuf) -> Self {
value.as_path().into()
}
}
impl From<String> for PavedPath {
fn from(value: String) -> Self {
Path::new(&value).into()
}
}
impl From<&str> for PavedPath {
fn from(value: &str) -> Self {
Path::new(value).into()
}
}
impl FromStr for PavedPath {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Path::new(s).into())
}
}
impl From<&OsStr> for PavedPath {
fn from(value: &OsStr) -> Self {
Path::new(value).into()
}
}
impl From<OsString> for PavedPath {
fn from(value: OsString) -> Self {
value.as_os_str().into()
}
}
impl<P: Into<OsString>> Extend<P> for PavedPath {
fn extend<T: IntoIterator<Item = P>>(&mut self, iter: T) {
for v in iter {
self.push_dir_raw(v.into());
}
self.rebuild_inner();
}
}
impl std::ops::Add for PavedPath {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
let mut paved = self;
paved += rhs;
paved
}
}
impl std::ops::AddAssign for PavedPath {
fn add_assign(&mut self, rhs: Self) {
self.extend(rhs.get_dirs());
if !self.has_file()
&& let Some(file) = rhs.get_file()
{
self.file = Some(file.to_os_string());
self.rebuild_inner();
}
}
}