#![warn(missing_docs)]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
use std::{
borrow::{Borrow, Cow},
cmp::Ordering,
convert::{Infallible, TryFrom, TryInto},
error,
ffi::{OsStr, OsString},
fmt,
fs::{self, Metadata},
hash::{Hash, Hasher},
io,
iter::FusedIterator,
ops::Deref,
path::*,
rc::Rc,
str::FromStr,
sync::Arc,
};
#[cfg(feature = "proptest1")]
mod proptest_impls;
#[cfg(feature = "serde1")]
mod serde_impls;
#[cfg(test)]
mod tests;
#[derive(Clone, Default)]
#[repr(transparent)]
pub struct Utf8PathBuf(PathBuf);
impl Utf8PathBuf {
#[must_use]
pub fn new() -> Utf8PathBuf {
Utf8PathBuf(PathBuf::new())
}
pub fn from_path_buf(path: PathBuf) -> Result<Utf8PathBuf, PathBuf> {
match path.into_os_string().into_string() {
Ok(string) => Ok(Utf8PathBuf::from(string)),
Err(os_string) => Err(PathBuf::from(os_string)),
}
}
pub fn from_os_string(os_string: OsString) -> Result<Utf8PathBuf, OsString> {
match os_string.into_string() {
Ok(string) => Ok(Utf8PathBuf::from(string)),
Err(os_string) => Err(os_string),
}
}
#[must_use = "`self` will be dropped if the result is not used"]
pub fn into_std_path_buf(self) -> PathBuf {
self.into()
}
#[allow(clippy::incompatible_msrv)]
#[must_use]
pub fn with_capacity(capacity: usize) -> Utf8PathBuf {
Utf8PathBuf(PathBuf::with_capacity(capacity))
}
#[must_use]
pub fn as_path(&self) -> &Utf8Path {
unsafe { Utf8Path::assume_utf8(&self.0) }
}
#[cfg(os_string_pathbuf_leak)]
#[allow(clippy::incompatible_msrv)]
#[inline]
pub fn leak<'a>(self) -> &'a mut Utf8Path {
unsafe { Utf8Path::assume_utf8_mut(self.0.leak()) }
}
pub fn push(&mut self, path: impl AsRef<Utf8Path>) {
self.0.push(&path.as_ref().0)
}
pub fn pop(&mut self) -> bool {
self.0.pop()
}
pub fn set_file_name(&mut self, file_name: impl AsRef<str>) {
self.0.set_file_name(file_name.as_ref())
}
pub fn set_extension(&mut self, extension: impl AsRef<str>) -> bool {
self.0.set_extension(extension.as_ref())
}
#[must_use = "`self` will be dropped if the result is not used"]
pub fn into_string(self) -> String {
self.into_os_string().into_string().unwrap()
}
#[must_use = "`self` will be dropped if the result is not used"]
pub fn into_os_string(self) -> OsString {
self.0.into_os_string()
}
#[must_use = "`self` will be dropped if the result is not used"]
pub fn into_boxed_path(self) -> Box<Utf8Path> {
let ptr = Box::into_raw(self.0.into_boxed_path()) as *mut Utf8Path;
unsafe { Box::from_raw(ptr) }
}
#[allow(clippy::incompatible_msrv)]
#[must_use]
pub fn capacity(&self) -> usize {
self.0.capacity()
}
#[allow(clippy::incompatible_msrv)]
pub fn clear(&mut self) {
self.0.clear()
}
#[allow(clippy::incompatible_msrv)]
pub fn reserve(&mut self, additional: usize) {
self.0.reserve(additional)
}
#[cfg(try_reserve_2)]
#[allow(clippy::incompatible_msrv)]
#[inline]
pub fn try_reserve(
&mut self,
additional: usize,
) -> Result<(), std::collections::TryReserveError> {
self.0.try_reserve(additional)
}
#[allow(clippy::incompatible_msrv)]
pub fn reserve_exact(&mut self, additional: usize) {
self.0.reserve_exact(additional)
}
#[cfg(try_reserve_2)]
#[allow(clippy::incompatible_msrv)]
#[inline]
pub fn try_reserve_exact(
&mut self,
additional: usize,
) -> Result<(), std::collections::TryReserveError> {
self.0.try_reserve_exact(additional)
}
#[allow(clippy::incompatible_msrv)]
pub fn shrink_to_fit(&mut self) {
self.0.shrink_to_fit()
}
#[allow(clippy::incompatible_msrv)]
#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.0.shrink_to(min_capacity)
}
}
impl Deref for Utf8PathBuf {
type Target = Utf8Path;
fn deref(&self) -> &Utf8Path {
self.as_path()
}
}
#[cfg(path_buf_deref_mut)]
#[allow(clippy::incompatible_msrv)]
impl std::ops::DerefMut for Utf8PathBuf {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { Utf8Path::assume_utf8_mut(&mut self.0) }
}
}
impl fmt::Debug for Utf8PathBuf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl fmt::Display for Utf8PathBuf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
impl<P: AsRef<Utf8Path>> Extend<P> for Utf8PathBuf {
fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
for path in iter {
self.push(path);
}
}
}
#[repr(transparent)]
pub struct Utf8Path(Path);
impl Utf8Path {
pub fn new(s: &(impl AsRef<str> + ?Sized)) -> &Utf8Path {
let path = Path::new(s.as_ref());
unsafe { Utf8Path::assume_utf8(path) }
}
pub fn from_path(path: &Path) -> Option<&Utf8Path> {
path.as_os_str().to_str().map(Utf8Path::new)
}
pub fn from_os_str(path: &OsStr) -> Option<&Utf8Path> {
path.to_str().map(Utf8Path::new)
}
#[inline]
pub fn as_std_path(&self) -> &Path {
self.as_ref()
}
#[inline]
#[must_use]
pub fn as_str(&self) -> &str {
unsafe { str_assume_utf8(self.as_os_str()) }
}
#[inline]
#[must_use]
pub fn as_os_str(&self) -> &OsStr {
self.0.as_os_str()
}
#[inline]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub fn to_path_buf(&self) -> Utf8PathBuf {
Utf8PathBuf(self.0.to_path_buf())
}
#[inline]
#[must_use]
pub fn is_absolute(&self) -> bool {
self.0.is_absolute()
}
#[inline]
#[must_use]
pub fn is_relative(&self) -> bool {
self.0.is_relative()
}
#[inline]
#[must_use]
pub fn has_root(&self) -> bool {
self.0.has_root()
}
#[inline]
#[must_use]
pub fn parent(&self) -> Option<&Utf8Path> {
self.0.parent().map(|path| {
unsafe { Utf8Path::assume_utf8(path) }
})
}
#[inline]
pub fn ancestors(&self) -> Utf8Ancestors<'_> {
Utf8Ancestors(self.0.ancestors())
}
#[inline]
#[must_use]
pub fn file_name(&self) -> Option<&str> {
self.0.file_name().map(|s| {
unsafe { str_assume_utf8(s) }
})
}
#[inline]
pub fn strip_prefix(&self, base: impl AsRef<Path>) -> Result<&Utf8Path, StripPrefixError> {
self.0.strip_prefix(base).map(|path| {
unsafe { Utf8Path::assume_utf8(path) }
})
}
#[inline]
#[must_use]
pub fn starts_with(&self, base: impl AsRef<Path>) -> bool {
self.0.starts_with(base)
}
#[inline]
#[must_use]
pub fn ends_with(&self, base: impl AsRef<Path>) -> bool {
self.0.ends_with(base)
}
#[inline]
#[must_use]
pub fn file_stem(&self) -> Option<&str> {
self.0.file_stem().map(|s| {
unsafe { str_assume_utf8(s) }
})
}
#[inline]
#[must_use]
pub fn extension(&self) -> Option<&str> {
self.0.extension().map(|s| {
unsafe { str_assume_utf8(s) }
})
}
#[inline]
#[must_use]
pub fn join(&self, path: impl AsRef<Utf8Path>) -> Utf8PathBuf {
Utf8PathBuf(self.0.join(&path.as_ref().0))
}
#[inline]
#[must_use]
pub fn join_os(&self, path: impl AsRef<Path>) -> PathBuf {
self.0.join(path)
}
#[inline]
#[must_use]
pub fn with_file_name(&self, file_name: impl AsRef<str>) -> Utf8PathBuf {
Utf8PathBuf(self.0.with_file_name(file_name.as_ref()))
}
#[inline]
pub fn with_extension(&self, extension: impl AsRef<str>) -> Utf8PathBuf {
Utf8PathBuf(self.0.with_extension(extension.as_ref()))
}
#[inline]
pub fn components(&self) -> Utf8Components<'_> {
Utf8Components(self.0.components())
}
#[inline]
pub fn iter(&self) -> Iter<'_> {
Iter {
inner: self.components(),
}
}
#[inline]
pub fn metadata(&self) -> io::Result<fs::Metadata> {
self.0.metadata()
}
#[inline]
pub fn symlink_metadata(&self) -> io::Result<fs::Metadata> {
self.0.symlink_metadata()
}
#[inline]
pub fn canonicalize(&self) -> io::Result<PathBuf> {
self.0.canonicalize()
}
pub fn canonicalize_utf8(&self) -> io::Result<Utf8PathBuf> {
self.canonicalize()
.and_then(|path| path.try_into().map_err(FromPathBufError::into_io_error))
}
#[inline]
pub fn read_link(&self) -> io::Result<PathBuf> {
self.0.read_link()
}
pub fn read_link_utf8(&self) -> io::Result<Utf8PathBuf> {
self.read_link()
.and_then(|path| path.try_into().map_err(FromPathBufError::into_io_error))
}
#[inline]
pub fn read_dir(&self) -> io::Result<fs::ReadDir> {
self.0.read_dir()
}
#[inline]
pub fn read_dir_utf8(&self) -> io::Result<ReadDirUtf8> {
self.0.read_dir().map(|inner| ReadDirUtf8 { inner })
}
#[must_use]
#[inline]
pub fn exists(&self) -> bool {
self.0.exists()
}
#[inline]
pub fn try_exists(&self) -> io::Result<bool> {
match fs::metadata(self) {
Ok(_) => Ok(true),
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false),
Err(error) => Err(error),
}
}
#[must_use]
#[inline]
pub fn is_file(&self) -> bool {
self.0.is_file()
}
#[must_use]
#[inline]
pub fn is_dir(&self) -> bool {
self.0.is_dir()
}
#[cfg_attr(unix, doc = "```no_run")]
#[cfg_attr(not(unix), doc = "```ignore")]
#[must_use]
pub fn is_symlink(&self) -> bool {
self.symlink_metadata()
.map(|m| m.file_type().is_symlink())
.unwrap_or(false)
}
#[must_use = "`self` will be dropped if the result is not used"]
#[inline]
pub fn into_path_buf(self: Box<Utf8Path>) -> Utf8PathBuf {
let ptr = Box::into_raw(self) as *mut Path;
let boxed_path = unsafe { Box::from_raw(ptr) };
Utf8PathBuf(boxed_path.into_path_buf())
}
#[inline]
unsafe fn assume_utf8(path: &Path) -> &Utf8Path {
&*(path as *const Path as *const Utf8Path)
}
#[cfg(path_buf_deref_mut)]
#[inline]
unsafe fn assume_utf8_mut(path: &mut Path) -> &mut Utf8Path {
&mut *(path as *mut Path as *mut Utf8Path)
}
}
impl Clone for Box<Utf8Path> {
fn clone(&self) -> Self {
let boxed: Box<Path> = self.0.into();
let ptr = Box::into_raw(boxed) as *mut Utf8Path;
unsafe { Box::from_raw(ptr) }
}
}
impl fmt::Display for Utf8Path {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
impl fmt::Debug for Utf8Path {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self.as_str(), f)
}
}
#[derive(Copy, Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[repr(transparent)]
pub struct Utf8Ancestors<'a>(Ancestors<'a>);
impl fmt::Debug for Utf8Ancestors<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl<'a> Iterator for Utf8Ancestors<'a> {
type Item = &'a Utf8Path;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|path| {
unsafe { Utf8Path::assume_utf8(path) }
})
}
}
impl FusedIterator for Utf8Ancestors<'_> {}
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Utf8Components<'a>(Components<'a>);
impl<'a> Utf8Components<'a> {
#[must_use]
#[inline]
pub fn as_path(&self) -> &'a Utf8Path {
unsafe { Utf8Path::assume_utf8(self.0.as_path()) }
}
}
impl<'a> Iterator for Utf8Components<'a> {
type Item = Utf8Component<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|component| {
unsafe { Utf8Component::new(component) }
})
}
}
impl FusedIterator for Utf8Components<'_> {}
impl DoubleEndedIterator for Utf8Components<'_> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().map(|component| {
unsafe { Utf8Component::new(component) }
})
}
}
impl fmt::Debug for Utf8Components<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl AsRef<Utf8Path> for Utf8Components<'_> {
#[inline]
fn as_ref(&self) -> &Utf8Path {
self.as_path()
}
}
impl AsRef<Path> for Utf8Components<'_> {
#[inline]
fn as_ref(&self) -> &Path {
self.as_path().as_ref()
}
}
impl AsRef<str> for Utf8Components<'_> {
#[inline]
fn as_ref(&self) -> &str {
self.as_path().as_ref()
}
}
impl AsRef<OsStr> for Utf8Components<'_> {
#[inline]
fn as_ref(&self) -> &OsStr {
self.as_path().as_os_str()
}
}
#[derive(Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Iter<'a> {
inner: Utf8Components<'a>,
}
impl fmt::Debug for Iter<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct DebugHelper<'a>(&'a Utf8Path);
impl fmt::Debug for DebugHelper<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.0.iter()).finish()
}
}
f.debug_tuple("Iter")
.field(&DebugHelper(self.as_path()))
.finish()
}
}
impl<'a> Iter<'a> {
#[must_use]
#[inline]
pub fn as_path(&self) -> &'a Utf8Path {
self.inner.as_path()
}
}
impl AsRef<Utf8Path> for Iter<'_> {
#[inline]
fn as_ref(&self) -> &Utf8Path {
self.as_path()
}
}
impl AsRef<Path> for Iter<'_> {
#[inline]
fn as_ref(&self) -> &Path {
self.as_path().as_ref()
}
}
impl AsRef<str> for Iter<'_> {
#[inline]
fn as_ref(&self) -> &str {
self.as_path().as_ref()
}
}
impl AsRef<OsStr> for Iter<'_> {
#[inline]
fn as_ref(&self) -> &OsStr {
self.as_path().as_os_str()
}
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a str;
#[inline]
fn next(&mut self) -> Option<&'a str> {
self.inner.next().map(|component| component.as_str())
}
}
impl<'a> DoubleEndedIterator for Iter<'a> {
#[inline]
fn next_back(&mut self) -> Option<&'a str> {
self.inner.next_back().map(|component| component.as_str())
}
}
impl FusedIterator for Iter<'_> {}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum Utf8Component<'a> {
Prefix(Utf8PrefixComponent<'a>),
RootDir,
CurDir,
ParentDir,
Normal(&'a str),
}
impl<'a> Utf8Component<'a> {
unsafe fn new(component: Component<'a>) -> Utf8Component<'a> {
match component {
Component::Prefix(prefix) => Utf8Component::Prefix(Utf8PrefixComponent(prefix)),
Component::RootDir => Utf8Component::RootDir,
Component::CurDir => Utf8Component::CurDir,
Component::ParentDir => Utf8Component::ParentDir,
Component::Normal(s) => Utf8Component::Normal(str_assume_utf8(s)),
}
}
#[must_use]
#[inline]
pub fn as_str(&self) -> &'a str {
unsafe { str_assume_utf8(self.as_os_str()) }
}
#[must_use]
pub fn as_os_str(&self) -> &'a OsStr {
match *self {
Utf8Component::Prefix(prefix) => prefix.as_os_str(),
Utf8Component::RootDir => Component::RootDir.as_os_str(),
Utf8Component::CurDir => Component::CurDir.as_os_str(),
Utf8Component::ParentDir => Component::ParentDir.as_os_str(),
Utf8Component::Normal(s) => OsStr::new(s),
}
}
}
impl fmt::Debug for Utf8Component<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self.as_os_str(), f)
}
}
impl fmt::Display for Utf8Component<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
impl AsRef<Utf8Path> for Utf8Component<'_> {
#[inline]
fn as_ref(&self) -> &Utf8Path {
self.as_str().as_ref()
}
}
impl AsRef<Path> for Utf8Component<'_> {
#[inline]
fn as_ref(&self) -> &Path {
self.as_os_str().as_ref()
}
}
impl AsRef<str> for Utf8Component<'_> {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<OsStr> for Utf8Component<'_> {
#[inline]
fn as_ref(&self) -> &OsStr {
self.as_os_str()
}
}
#[derive(Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub enum Utf8Prefix<'a> {
Verbatim(&'a str),
VerbatimUNC(&'a str, &'a str),
VerbatimDisk(u8),
DeviceNS(&'a str),
UNC(&'a str, &'a str),
Disk(u8),
}
impl Utf8Prefix<'_> {
#[must_use]
pub fn is_verbatim(&self) -> bool {
use Utf8Prefix::*;
matches!(self, Verbatim(_) | VerbatimDisk(_) | VerbatimUNC(..))
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Utf8PrefixComponent<'a>(PrefixComponent<'a>);
impl<'a> Utf8PrefixComponent<'a> {
#[must_use]
pub fn kind(&self) -> Utf8Prefix<'a> {
match self.0.kind() {
Prefix::Verbatim(prefix) => Utf8Prefix::Verbatim(unsafe { str_assume_utf8(prefix) }),
Prefix::VerbatimUNC(server, share) => {
let server = unsafe { str_assume_utf8(server) };
let share = unsafe { str_assume_utf8(share) };
Utf8Prefix::VerbatimUNC(server, share)
}
Prefix::VerbatimDisk(drive) => Utf8Prefix::VerbatimDisk(drive),
Prefix::DeviceNS(prefix) => Utf8Prefix::DeviceNS(unsafe { str_assume_utf8(prefix) }),
Prefix::UNC(server, share) => {
let server = unsafe { str_assume_utf8(server) };
let share = unsafe { str_assume_utf8(share) };
Utf8Prefix::UNC(server, share)
}
Prefix::Disk(drive) => Utf8Prefix::Disk(drive),
}
}
#[must_use]
#[inline]
pub fn as_str(&self) -> &'a str {
unsafe { str_assume_utf8(self.as_os_str()) }
}
#[must_use]
#[inline]
pub fn as_os_str(&self) -> &'a OsStr {
self.0.as_os_str()
}
}
impl fmt::Debug for Utf8PrefixComponent<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl fmt::Display for Utf8PrefixComponent<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
#[derive(Debug)]
pub struct ReadDirUtf8 {
inner: fs::ReadDir,
}
impl Iterator for ReadDirUtf8 {
type Item = io::Result<Utf8DirEntry>;
fn next(&mut self) -> Option<io::Result<Utf8DirEntry>> {
self.inner
.next()
.map(|entry| entry.and_then(Utf8DirEntry::new))
}
}
#[derive(Debug)]
pub struct Utf8DirEntry {
inner: fs::DirEntry,
path: Utf8PathBuf,
}
impl Utf8DirEntry {
fn new(inner: fs::DirEntry) -> io::Result<Self> {
let path = inner
.path()
.try_into()
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
Ok(Self { inner, path })
}
#[inline]
pub fn path(&self) -> &Utf8Path {
&self.path
}
#[inline]
pub fn metadata(&self) -> io::Result<Metadata> {
self.inner.metadata()
}
#[inline]
pub fn file_type(&self) -> io::Result<fs::FileType> {
self.inner.file_type()
}
pub fn file_name(&self) -> &str {
self.path
.file_name()
.expect("path created through DirEntry must have a filename")
}
#[inline]
pub fn into_inner(self) -> fs::DirEntry {
self.inner
}
#[inline]
#[must_use = "`self` will be dropped if the result is not used"]
pub fn into_path(self) -> Utf8PathBuf {
self.path
}
}
impl From<String> for Utf8PathBuf {
fn from(string: String) -> Utf8PathBuf {
Utf8PathBuf(string.into())
}
}
impl FromStr for Utf8PathBuf {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Utf8PathBuf(s.into()))
}
}
impl<'a> From<&'a str> for &'a Utf8Path {
fn from(s: &'a str) -> &'a Utf8Path {
Utf8Path::new(s)
}
}
impl<T: ?Sized + AsRef<str>> From<&T> for Utf8PathBuf {
fn from(s: &T) -> Utf8PathBuf {
Utf8PathBuf::from(s.as_ref().to_owned())
}
}
impl<T: ?Sized + AsRef<str>> From<&T> for Box<Utf8Path> {
fn from(s: &T) -> Box<Utf8Path> {
Utf8PathBuf::from(s).into_boxed_path()
}
}
impl From<&'_ Utf8Path> for Arc<Utf8Path> {
fn from(path: &Utf8Path) -> Arc<Utf8Path> {
let arc: Arc<Path> = Arc::from(AsRef::<Path>::as_ref(path));
let ptr = Arc::into_raw(arc) as *const Utf8Path;
unsafe { Arc::from_raw(ptr) }
}
}
impl From<&'_ Utf8Path> for Rc<Utf8Path> {
fn from(path: &Utf8Path) -> Rc<Utf8Path> {
let rc: Rc<Path> = Rc::from(AsRef::<Path>::as_ref(path));
let ptr = Rc::into_raw(rc) as *const Utf8Path;
unsafe { Rc::from_raw(ptr) }
}
}
impl<'a> From<&'a Utf8Path> for Cow<'a, Utf8Path> {
fn from(path: &'a Utf8Path) -> Cow<'a, Utf8Path> {
Cow::Borrowed(path)
}
}
impl From<&'_ Utf8Path> for Box<Path> {
fn from(path: &Utf8Path) -> Box<Path> {
AsRef::<Path>::as_ref(path).into()
}
}
impl From<&'_ Utf8Path> for Arc<Path> {
fn from(path: &Utf8Path) -> Arc<Path> {
AsRef::<Path>::as_ref(path).into()
}
}
impl From<&'_ Utf8Path> for Rc<Path> {
fn from(path: &Utf8Path) -> Rc<Path> {
AsRef::<Path>::as_ref(path).into()
}
}
impl<'a> From<&'a Utf8Path> for Cow<'a, Path> {
fn from(path: &'a Utf8Path) -> Cow<'a, Path> {
Cow::Borrowed(path.as_ref())
}
}
impl From<Box<Utf8Path>> for Utf8PathBuf {
fn from(path: Box<Utf8Path>) -> Utf8PathBuf {
path.into_path_buf()
}
}
impl From<Utf8PathBuf> for Box<Utf8Path> {
fn from(path: Utf8PathBuf) -> Box<Utf8Path> {
path.into_boxed_path()
}
}
impl<'a> From<Cow<'a, Utf8Path>> for Utf8PathBuf {
fn from(path: Cow<'a, Utf8Path>) -> Utf8PathBuf {
path.into_owned()
}
}
impl From<Utf8PathBuf> for String {
fn from(path: Utf8PathBuf) -> String {
path.into_string()
}
}
impl From<Utf8PathBuf> for OsString {
fn from(path: Utf8PathBuf) -> OsString {
path.into_os_string()
}
}
impl<'a> From<Utf8PathBuf> for Cow<'a, Utf8Path> {
fn from(path: Utf8PathBuf) -> Cow<'a, Utf8Path> {
Cow::Owned(path)
}
}
impl From<Utf8PathBuf> for Arc<Utf8Path> {
fn from(path: Utf8PathBuf) -> Arc<Utf8Path> {
let arc: Arc<Path> = Arc::from(path.0);
let ptr = Arc::into_raw(arc) as *const Utf8Path;
unsafe { Arc::from_raw(ptr) }
}
}
impl From<Utf8PathBuf> for Rc<Utf8Path> {
fn from(path: Utf8PathBuf) -> Rc<Utf8Path> {
let rc: Rc<Path> = Rc::from(path.0);
let ptr = Rc::into_raw(rc) as *const Utf8Path;
unsafe { Rc::from_raw(ptr) }
}
}
impl From<Utf8PathBuf> for PathBuf {
fn from(path: Utf8PathBuf) -> PathBuf {
path.0
}
}
impl From<Utf8PathBuf> for Box<Path> {
fn from(path: Utf8PathBuf) -> Box<Path> {
PathBuf::from(path).into_boxed_path()
}
}
impl From<Utf8PathBuf> for Arc<Path> {
fn from(path: Utf8PathBuf) -> Arc<Path> {
PathBuf::from(path).into()
}
}
impl From<Utf8PathBuf> for Rc<Path> {
fn from(path: Utf8PathBuf) -> Rc<Path> {
PathBuf::from(path).into()
}
}
impl<'a> From<Utf8PathBuf> for Cow<'a, Path> {
fn from(path: Utf8PathBuf) -> Cow<'a, Path> {
PathBuf::from(path).into()
}
}
impl TryFrom<PathBuf> for Utf8PathBuf {
type Error = FromPathBufError;
fn try_from(path: PathBuf) -> Result<Utf8PathBuf, Self::Error> {
Utf8PathBuf::from_path_buf(path).map_err(|path| FromPathBufError {
path,
error: FromPathError(()),
})
}
}
impl TryFrom<OsString> for Utf8PathBuf {
type Error = FromOsStringError;
fn try_from(os_string: OsString) -> Result<Utf8PathBuf, Self::Error> {
Utf8PathBuf::from_os_string(os_string).map_err(|os_string| FromOsStringError {
os_string,
error: FromOsStrError(()),
})
}
}
impl<'a> TryFrom<&'a Path> for &'a Utf8Path {
type Error = FromPathError;
fn try_from(path: &'a Path) -> Result<&'a Utf8Path, Self::Error> {
Utf8Path::from_path(path).ok_or(FromPathError(()))
}
}
impl<'a> TryFrom<&'a OsStr> for &'a Utf8Path {
type Error = FromOsStrError;
fn try_from(os_str: &'a OsStr) -> Result<&'a Utf8Path, Self::Error> {
Utf8Path::from_os_str(os_str).ok_or(FromOsStrError(()))
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FromPathBufError {
path: PathBuf,
error: FromPathError,
}
impl FromPathBufError {
#[inline]
pub fn as_path(&self) -> &Path {
&self.path
}
#[inline]
pub fn into_path_buf(self) -> PathBuf {
self.path
}
#[inline]
pub fn from_path_error(&self) -> FromPathError {
self.error
}
pub fn into_io_error(self) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, self)
}
}
impl fmt::Display for FromPathBufError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "PathBuf contains invalid UTF-8: {}", self.path.display())
}
}
impl error::Error for FromPathBufError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(&self.error)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct FromPathError(());
impl FromPathError {
pub fn into_io_error(self) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, self)
}
}
impl fmt::Display for FromPathError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Path contains invalid UTF-8")
}
}
impl error::Error for FromPathError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FromOsStringError {
os_string: OsString,
error: FromOsStrError,
}
impl FromOsStringError {
#[inline]
pub fn as_os_str(&self) -> &OsStr {
&self.os_string
}
#[inline]
pub fn into_os_string(self) -> OsString {
self.os_string
}
#[inline]
pub fn from_os_str_error(&self) -> FromOsStrError {
self.error
}
pub fn into_io_error(self) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, self)
}
}
impl fmt::Display for FromOsStringError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"OsString contains invalid UTF-8: {}",
PathBuf::from(&self.os_string).display() )
}
}
impl error::Error for FromOsStringError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(&self.error)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct FromOsStrError(());
impl FromOsStrError {
pub fn into_io_error(self) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, self)
}
}
impl fmt::Display for FromOsStrError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "OsStr contains invalid UTF-8")
}
}
impl error::Error for FromOsStrError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
impl AsRef<Utf8Path> for Utf8Path {
#[inline]
fn as_ref(&self) -> &Utf8Path {
self
}
}
impl AsRef<Utf8Path> for Utf8PathBuf {
#[inline]
fn as_ref(&self) -> &Utf8Path {
self.as_path()
}
}
impl AsRef<Utf8Path> for str {
#[inline]
fn as_ref(&self) -> &Utf8Path {
Utf8Path::new(self)
}
}
impl AsRef<Utf8Path> for String {
#[inline]
fn as_ref(&self) -> &Utf8Path {
Utf8Path::new(self)
}
}
impl AsRef<Path> for Utf8Path {
#[inline]
fn as_ref(&self) -> &Path {
&self.0
}
}
impl AsRef<Path> for Utf8PathBuf {
#[inline]
fn as_ref(&self) -> &Path {
&self.0
}
}
impl AsRef<str> for Utf8Path {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<str> for Utf8PathBuf {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<OsStr> for Utf8Path {
#[inline]
fn as_ref(&self) -> &OsStr {
self.as_os_str()
}
}
impl AsRef<OsStr> for Utf8PathBuf {
#[inline]
fn as_ref(&self) -> &OsStr {
self.as_os_str()
}
}
impl Borrow<Utf8Path> for Utf8PathBuf {
#[inline]
fn borrow(&self) -> &Utf8Path {
self.as_path()
}
}
impl ToOwned for Utf8Path {
type Owned = Utf8PathBuf;
#[inline]
fn to_owned(&self) -> Utf8PathBuf {
self.to_path_buf()
}
}
impl<P: AsRef<Utf8Path>> std::iter::FromIterator<P> for Utf8PathBuf {
fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Utf8PathBuf {
let mut buf = Utf8PathBuf::new();
buf.extend(iter);
buf
}
}
impl PartialEq for Utf8PathBuf {
#[inline]
fn eq(&self, other: &Utf8PathBuf) -> bool {
self.components() == other.components()
}
}
impl Eq for Utf8PathBuf {}
impl Hash for Utf8PathBuf {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_path().hash(state)
}
}
impl PartialOrd for Utf8PathBuf {
#[inline]
fn partial_cmp(&self, other: &Utf8PathBuf) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Utf8PathBuf {
fn cmp(&self, other: &Utf8PathBuf) -> Ordering {
self.components().cmp(other.components())
}
}
impl PartialEq for Utf8Path {
#[inline]
fn eq(&self, other: &Utf8Path) -> bool {
self.components().eq(other.components())
}
}
impl Eq for Utf8Path {}
impl Hash for Utf8Path {
fn hash<H: Hasher>(&self, state: &mut H) {
for component in self.components() {
component.hash(state)
}
}
}
impl PartialOrd for Utf8Path {
#[inline]
fn partial_cmp(&self, other: &Utf8Path) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Utf8Path {
fn cmp(&self, other: &Utf8Path) -> Ordering {
self.components().cmp(other.components())
}
}
impl<'a> IntoIterator for &'a Utf8PathBuf {
type Item = &'a str;
type IntoIter = Iter<'a>;
#[inline]
fn into_iter(self) -> Iter<'a> {
self.iter()
}
}
impl<'a> IntoIterator for &'a Utf8Path {
type Item = &'a str;
type IntoIter = Iter<'a>;
#[inline]
fn into_iter(self) -> Iter<'a> {
self.iter()
}
}
macro_rules! impl_cmp {
($lhs:ty, $rhs: ty) => {
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialEq<$rhs> for $lhs {
#[inline]
fn eq(&self, other: &$rhs) -> bool {
<Utf8Path as PartialEq>::eq(self, other)
}
}
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialEq<$lhs> for $rhs {
#[inline]
fn eq(&self, other: &$lhs) -> bool {
<Utf8Path as PartialEq>::eq(self, other)
}
}
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialOrd<$rhs> for $lhs {
#[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
<Utf8Path as PartialOrd>::partial_cmp(self, other)
}
}
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialOrd<$lhs> for $rhs {
#[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
<Utf8Path as PartialOrd>::partial_cmp(self, other)
}
}
};
}
impl_cmp!(Utf8PathBuf, Utf8Path);
impl_cmp!(Utf8PathBuf, &'a Utf8Path);
impl_cmp!(Cow<'a, Utf8Path>, Utf8Path);
impl_cmp!(Cow<'a, Utf8Path>, &'b Utf8Path);
impl_cmp!(Cow<'a, Utf8Path>, Utf8PathBuf);
macro_rules! impl_cmp_std_path {
($lhs:ty, $rhs: ty) => {
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialEq<$rhs> for $lhs {
#[inline]
fn eq(&self, other: &$rhs) -> bool {
<Path as PartialEq>::eq(self.as_ref(), other)
}
}
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialEq<$lhs> for $rhs {
#[inline]
fn eq(&self, other: &$lhs) -> bool {
<Path as PartialEq>::eq(self, other.as_ref())
}
}
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialOrd<$rhs> for $lhs {
#[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<std::cmp::Ordering> {
<Path as PartialOrd>::partial_cmp(self.as_ref(), other)
}
}
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialOrd<$lhs> for $rhs {
#[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<std::cmp::Ordering> {
<Path as PartialOrd>::partial_cmp(self, other.as_ref())
}
}
};
}
impl_cmp_std_path!(Utf8PathBuf, Path);
impl_cmp_std_path!(Utf8PathBuf, &'a Path);
impl_cmp_std_path!(Utf8PathBuf, Cow<'a, Path>);
impl_cmp_std_path!(Utf8PathBuf, PathBuf);
impl_cmp_std_path!(Utf8Path, Path);
impl_cmp_std_path!(Utf8Path, &'a Path);
impl_cmp_std_path!(Utf8Path, Cow<'a, Path>);
impl_cmp_std_path!(Utf8Path, PathBuf);
impl_cmp_std_path!(&'a Utf8Path, Path);
impl_cmp_std_path!(&'a Utf8Path, Cow<'b, Path>);
impl_cmp_std_path!(&'a Utf8Path, PathBuf);
macro_rules! impl_cmp_str {
($lhs:ty, $rhs: ty) => {
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialEq<$rhs> for $lhs {
#[inline]
fn eq(&self, other: &$rhs) -> bool {
<Utf8Path as PartialEq>::eq(self, Utf8Path::new(other))
}
}
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialEq<$lhs> for $rhs {
#[inline]
fn eq(&self, other: &$lhs) -> bool {
<Utf8Path as PartialEq>::eq(Utf8Path::new(self), other)
}
}
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialOrd<$rhs> for $lhs {
#[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<std::cmp::Ordering> {
<Utf8Path as PartialOrd>::partial_cmp(self, Utf8Path::new(other))
}
}
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialOrd<$lhs> for $rhs {
#[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<std::cmp::Ordering> {
<Utf8Path as PartialOrd>::partial_cmp(Utf8Path::new(self), other)
}
}
};
}
impl_cmp_str!(Utf8PathBuf, str);
impl_cmp_str!(Utf8PathBuf, &'a str);
impl_cmp_str!(Utf8PathBuf, Cow<'a, str>);
impl_cmp_str!(Utf8PathBuf, String);
impl_cmp_str!(Utf8Path, str);
impl_cmp_str!(Utf8Path, &'a str);
impl_cmp_str!(Utf8Path, Cow<'a, str>);
impl_cmp_str!(Utf8Path, String);
impl_cmp_str!(&'a Utf8Path, str);
impl_cmp_str!(&'a Utf8Path, Cow<'b, str>);
impl_cmp_str!(&'a Utf8Path, String);
macro_rules! impl_cmp_os_str {
($lhs:ty, $rhs: ty) => {
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialEq<$rhs> for $lhs {
#[inline]
fn eq(&self, other: &$rhs) -> bool {
<Path as PartialEq>::eq(self.as_ref(), other.as_ref())
}
}
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialEq<$lhs> for $rhs {
#[inline]
fn eq(&self, other: &$lhs) -> bool {
<Path as PartialEq>::eq(self.as_ref(), other.as_ref())
}
}
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialOrd<$rhs> for $lhs {
#[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<std::cmp::Ordering> {
<Path as PartialOrd>::partial_cmp(self.as_ref(), other.as_ref())
}
}
#[allow(clippy::extra_unused_lifetimes)]
impl<'a, 'b> PartialOrd<$lhs> for $rhs {
#[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<std::cmp::Ordering> {
<Path as PartialOrd>::partial_cmp(self.as_ref(), other.as_ref())
}
}
};
}
impl_cmp_os_str!(Utf8PathBuf, OsStr);
impl_cmp_os_str!(Utf8PathBuf, &'a OsStr);
impl_cmp_os_str!(Utf8PathBuf, Cow<'a, OsStr>);
impl_cmp_os_str!(Utf8PathBuf, OsString);
impl_cmp_os_str!(Utf8Path, OsStr);
impl_cmp_os_str!(Utf8Path, &'a OsStr);
impl_cmp_os_str!(Utf8Path, Cow<'a, OsStr>);
impl_cmp_os_str!(Utf8Path, OsString);
impl_cmp_os_str!(&'a Utf8Path, OsStr);
impl_cmp_os_str!(&'a Utf8Path, Cow<'b, OsStr>);
impl_cmp_os_str!(&'a Utf8Path, OsString);
#[cfg(absolute_path)]
pub fn absolute_utf8<P: AsRef<Path>>(path: P) -> io::Result<Utf8PathBuf> {
let path = path.as_ref();
#[allow(clippy::incompatible_msrv)]
Utf8PathBuf::try_from(std::path::absolute(path)?).map_err(|error| error.into_io_error())
}
#[inline]
unsafe fn str_assume_utf8(string: &OsStr) -> &str {
#[cfg(os_str_bytes)]
{
unsafe {
std::str::from_utf8_unchecked(
#[allow(clippy::incompatible_msrv)]
string.as_encoded_bytes(),
)
}
}
#[cfg(not(os_str_bytes))]
{
match string.to_str() {
Some(val) => val,
None => std::hint::unreachable_unchecked(),
}
}
}