#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
use std::{
collections::{HashMap, HashSet},
ffi::OsStr,
fmt::Debug,
hash::{BuildHasherDefault, Hash, Hasher},
ops::Deref,
path::{Path, PathBuf},
};
pub use camino::{Utf8Component, Utf8Components, Utf8Path, Utf8PathBuf, Utf8Prefix};
use dashmap::{DashMap, DashSet};
use indexmap::IndexSet;
#[cfg(feature = "cacheable")]
use rspack_cacheable::{
ContextGuard, Error as CacheableError, cacheable,
utils::PortablePath,
with::{Custom, CustomConverter},
};
use rspack_intern::{InternSliceStorage, InternedSlice, SliceInternable};
use rustc_hash::FxHasher;
pub use ustr::IdentityHasher;
pub trait AssertUtf8 {
type Output;
fn assert_utf8(self) -> Self::Output;
}
impl AssertUtf8 for PathBuf {
type Output = Utf8PathBuf;
fn assert_utf8(self) -> Self::Output {
Utf8PathBuf::from_path_buf(self).unwrap_or_else(|p| {
panic!("expected UTF-8 path, got: {}", p.display());
})
}
}
impl<'a> AssertUtf8 for &'a Path {
type Output = &'a Utf8Path;
fn assert_utf8(self) -> Self::Output {
Utf8Path::from_path(self).unwrap_or_else(|| {
panic!("expected UTF-8 path, got: {}", self.display());
})
}
}
pub struct PreHashedPath;
impl SliceInternable for PreHashedPath {
type Header = u64;
type Item = u8;
#[inline]
fn hash(header: &u64, _bytes: &[u8]) -> u64 {
*header
}
fn eq(a: &[u8], b: &[u8]) -> bool {
#[cfg(unix)]
{
a == b
}
#[cfg(not(unix))]
{
a == b || path_from_bytes(a) == path_from_bytes(b)
}
}
fn storage() -> &'static InternSliceStorage<Self> {
static STORAGE: InternSliceStorage<PreHashedPath> = InternSliceStorage::new();
&STORAGE
}
}
#[inline]
fn path_from_bytes(bytes: &[u8]) -> &Path {
Path::new(unsafe { OsStr::from_encoded_bytes_unchecked(bytes) })
}
#[cfg_attr(feature = "cacheable", cacheable(with=Custom))]
#[derive(Clone, PartialEq, Eq)]
pub struct InternedPath(InternedSlice<PreHashedPath>);
impl Debug for InternedPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_path().fmt(f)
}
}
impl InternedPath {
pub fn new(path: &Path) -> Self {
Self::from_parts(hash_path(path), path)
}
#[inline]
pub fn from_parts(hash: u64, path: &Path) -> Self {
Self(InternedSlice::new(
hash,
path.as_os_str().as_encoded_bytes(),
))
}
#[inline]
pub fn as_path(&self) -> &Path {
path_from_bytes(self.0.items())
}
#[inline]
pub fn precomputed_hash(&self) -> u64 {
*self.0.header()
}
}
#[inline]
pub fn hash_path(path: &Path) -> u64 {
let mut hasher = FxHasher::default();
#[cfg(unix)]
hasher.write(path.as_os_str().as_bytes());
#[cfg(not(unix))]
path.hash(&mut hasher);
hasher.finish()
}
impl Deref for InternedPath {
type Target = Path;
fn deref(&self) -> &Self::Target {
self.as_path()
}
}
impl AsRef<Path> for InternedPath {
fn as_ref(&self) -> &Path {
self.as_path()
}
}
impl From<PathBuf> for InternedPath {
fn from(value: PathBuf) -> Self {
InternedPath::new(&value)
}
}
impl From<&PathBuf> for InternedPath {
fn from(value: &PathBuf) -> Self {
InternedPath::new(value)
}
}
impl From<&Path> for InternedPath {
fn from(value: &Path) -> Self {
InternedPath::new(value)
}
}
impl From<Utf8PathBuf> for InternedPath {
fn from(value: Utf8PathBuf) -> Self {
InternedPath::new(value.as_std_path())
}
}
impl From<&Utf8Path> for InternedPath {
fn from(value: &Utf8Path) -> Self {
InternedPath::new(value.as_std_path())
}
}
impl From<&InternedPath> for InternedPath {
fn from(value: &InternedPath) -> Self {
value.clone()
}
}
impl From<&str> for InternedPath {
fn from(value: &str) -> Self {
InternedPath::new(<str as std::convert::AsRef<Path>>::as_ref(value))
}
}
#[cfg(feature = "cacheable")]
impl CustomConverter for InternedPath {
type Target = PortablePath;
fn serialize(&self, guard: &ContextGuard) -> Result<Self::Target, CacheableError> {
Ok(PortablePath::new(self.as_path(), guard.project_root()))
}
fn deserialize(data: Self::Target, guard: &ContextGuard) -> Result<Self, CacheableError> {
Ok(Self::from(PathBuf::from(
data.into_path_string(guard.project_root()),
)))
}
}
impl Hash for InternedPath {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.precomputed_hash());
}
}
pub type InternedPathMap<V> = HashMap<InternedPath, V, BuildHasherDefault<IdentityHasher>>;
pub type InternedPathSet = HashSet<InternedPath, BuildHasherDefault<IdentityHasher>>;
pub type InternedPathDashMap<V> = DashMap<InternedPath, V, BuildHasherDefault<IdentityHasher>>;
pub type InternedPathDashSet = DashSet<InternedPath, BuildHasherDefault<IdentityHasher>>;
pub type InternedPathIndexSet = IndexSet<InternedPath, BuildHasherDefault<IdentityHasher>>;