use std::{
ffi::OsStr,
fmt::{Debug, Display},
hash::Hash,
ops::Deref,
path::{Path, PathBuf},
};
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
enum PathKind {
Path,
Str,
}
union InnerMaybePath<'a> {
path: &'a Path,
str: &'a str,
}
impl Copy for InnerMaybePath<'_> {}
impl Clone for InnerMaybePath<'_> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
#[derive(Copy, Clone)]
pub struct MaybePath<'a> {
kind: PathKind,
inner: InnerMaybePath<'a>,
}
impl<'a> MaybePath<'a> {
pub const fn new_str(str: &'a str) -> Self {
Self {
kind: PathKind::Str,
inner: InnerMaybePath { str },
}
}
pub fn new_path<P: AsRef<OsStr> + ?Sized>(path: &'a P) -> Self {
let path = Path::new(path);
Self {
kind: PathKind::Path,
inner: InnerMaybePath { path },
}
}
#[inline]
pub fn is_path(&self) -> bool {
self.kind == PathKind::Path
}
#[inline]
pub fn as_path(&self) -> &'a Path {
if self.kind == PathKind::Path {
unsafe { self.inner.path }
} else {
Path::new(unsafe { self.inner.str })
}
}
#[inline]
pub fn as_str(&self) -> Option<&'a str> {
if self.kind == PathKind::Path {
unsafe { self.inner.path.to_str() }
} else {
unsafe { Some(self.inner.str) }
}
}
#[inline]
pub unsafe fn as_path_unchecked(&self) -> &Path {
self.inner.path
}
#[inline]
pub unsafe fn as_str_unchecked(&self) -> &str {
self.inner.str
}
pub fn to_owned(&self) -> PathBuf {
self.as_path().to_path_buf()
}
}
impl Default for MaybePath<'_> {
fn default() -> Self {
Self::new_str("")
}
}
impl Debug for MaybePath<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dbg = f.debug_struct("MaybePath");
if self.is_path() {
dbg.field("path", &self.as_path());
} else {
dbg.field("str", &self.as_str());
}
dbg.finish()
}
}
impl Ord for MaybePath<'_> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_path().cmp(other.as_path())
}
}
impl PartialOrd for MaybePath<'_> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for MaybePath<'_> {
fn eq(&self, other: &Self) -> bool {
self.as_path() == other.as_path()
}
}
impl Eq for MaybePath<'_> {}
impl Hash for MaybePath<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
if self.is_path() {
self.as_path().hash(state);
} else {
self.as_str().hash(state);
}
}
}
impl Display for MaybePath<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.as_path().display(), f)
}
}
impl Deref for MaybePath<'_> {
type Target = Path;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_path()
}
}
impl AsRef<Path> for MaybePath<'_> {
#[inline]
fn as_ref(&self) -> &Path {
self
}
}
impl serde::Serialize for MaybePath<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.as_path().serialize(serializer)
}
}