#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
use std::{
fmt,
hash::{Hash, Hasher},
ops::Deref,
path::{Path, PathBuf},
sync::Arc,
};
use rustc_hash::FxHasher;
#[derive(Clone)]
pub struct ResolverPath {
hash: u64,
path: Arc<Path>,
}
impl ResolverPath {
pub fn new(path: Arc<Path>) -> Self {
let hash = hash_path(&path);
Self { hash, path }
}
#[inline]
pub(crate) fn from_parts(hash: u64, path: Arc<Path>) -> Self {
Self { hash, path }
}
#[inline]
pub fn as_path(&self) -> &Path {
&self.path
}
#[inline]
pub fn as_arc(&self) -> &Arc<Path> {
&self.path
}
#[inline]
pub fn into_arc(self) -> Arc<Path> {
self.path
}
#[inline]
pub fn precomputed_hash(&self) -> u64 {
self.hash
}
}
#[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 Hash for ResolverPath {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.hash);
}
}
impl PartialEq for ResolverPath {
fn eq(&self, other: &Self) -> bool {
#[cfg(unix)]
{
self.path.as_os_str() == other.path.as_os_str()
}
#[cfg(not(unix))]
{
self.path == other.path
}
}
}
impl Eq for ResolverPath {}
impl Deref for ResolverPath {
type Target = Path;
fn deref(&self) -> &Self::Target {
&self.path
}
}
impl AsRef<Path> for ResolverPath {
fn as_ref(&self) -> &Path {
&self.path
}
}
impl From<PathBuf> for ResolverPath {
fn from(path: PathBuf) -> Self {
Self::new(Arc::from(path))
}
}
impl From<&Path> for ResolverPath {
fn from(path: &Path) -> Self {
Self::new(Arc::from(path))
}
}
impl From<&PathBuf> for ResolverPath {
fn from(path: &PathBuf) -> Self {
Self::new(Arc::from(path.as_path()))
}
}
impl From<Arc<Path>> for ResolverPath {
fn from(path: Arc<Path>) -> Self {
Self::new(path)
}
}
impl fmt::Debug for ResolverPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.path.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hash_is_path_byte_hash() {
let p: &Path = Path::new("/a/b/c.js");
let rp = ResolverPath::from(p);
assert_eq!(rp.precomputed_hash(), hash_path(p));
}
#[test]
fn equal_paths_have_equal_hashes() {
let a = ResolverPath::from(PathBuf::from("/x/y"));
let b = ResolverPath::from(Path::new("/x/y"));
assert_eq!(a, b);
assert_eq!(a.precomputed_hash(), b.precomputed_hash());
}
#[test]
fn writes_u64_into_hasher() {
use std::{collections::HashSet, hash::BuildHasherDefault};
#[derive(Default)]
struct IdHasher(u64);
impl Hasher for IdHasher {
fn write(&mut self, _: &[u8]) {
unreachable!()
}
fn write_u64(&mut self, n: u64) {
self.0 = n;
}
fn finish(&self) -> u64 {
self.0
}
}
let mut set: HashSet<ResolverPath, BuildHasherDefault<IdHasher>> = HashSet::default();
set.insert(ResolverPath::from(Path::new("/a/b")));
assert!(set.contains(&ResolverPath::from(PathBuf::from("/a/b"))));
}
}