use std::{collections::HashMap, hash::BuildHasher, sync::Arc};
use camino::Utf8Path;
use ltk_hash::WadHash;
pub trait PathResolver {
fn resolve(&self, path_hash: WadHash) -> Option<String>;
fn is_known(&self, path_hash: WadHash) -> bool {
self.resolve(path_hash).is_some()
}
fn resolve_all(&self, path_hashes: &[WadHash]) -> Vec<Option<String>> {
path_hashes.iter().map(|&hash| self.resolve(hash)).collect()
}
}
impl<R: PathResolver + ?Sized> PathResolver for &R {
fn resolve(&self, path_hash: WadHash) -> Option<String> {
(**self).resolve(path_hash)
}
fn is_known(&self, path_hash: WadHash) -> bool {
(**self).is_known(path_hash)
}
fn resolve_all(&self, path_hashes: &[WadHash]) -> Vec<Option<String>> {
(**self).resolve_all(path_hashes)
}
}
impl<R: PathResolver + ?Sized> PathResolver for Box<R> {
fn resolve(&self, path_hash: WadHash) -> Option<String> {
(**self).resolve(path_hash)
}
fn is_known(&self, path_hash: WadHash) -> bool {
(**self).is_known(path_hash)
}
fn resolve_all(&self, path_hashes: &[WadHash]) -> Vec<Option<String>> {
(**self).resolve_all(path_hashes)
}
}
impl<R: PathResolver + ?Sized> PathResolver for Arc<R> {
fn resolve(&self, path_hash: WadHash) -> Option<String> {
(**self).resolve(path_hash)
}
fn is_known(&self, path_hash: WadHash) -> bool {
(**self).is_known(path_hash)
}
fn resolve_all(&self, path_hashes: &[WadHash]) -> Vec<Option<String>> {
(**self).resolve_all(path_hashes)
}
}
impl<S: BuildHasher> PathResolver for HashMap<WadHash, String, S> {
fn resolve(&self, path_hash: WadHash) -> Option<String> {
self.get(&path_hash).cloned()
}
fn is_known(&self, path_hash: WadHash) -> bool {
self.contains_key(&path_hash)
}
}
pub(crate) fn resolve_all_checked<R: PathResolver + ?Sized>(
resolver: &R,
path_hashes: &[WadHash],
) -> Vec<Option<String>> {
let resolved = resolver.resolve_all(path_hashes);
assert_eq!(
resolved.len(),
path_hashes.len(),
"PathResolver::resolve_all answered {} of {} hashes",
resolved.len(),
path_hashes.len()
);
resolved
}
#[derive(Debug, Clone, Copy, Default)]
pub struct NoResolver;
impl PathResolver for NoResolver {
fn resolve(&self, _path_hash: WadHash) -> Option<String> {
None
}
fn is_known(&self, _path_hash: WadHash) -> bool {
false
}
fn resolve_all(&self, path_hashes: &[WadHash]) -> Vec<Option<String>> {
vec![None; path_hashes.len()]
}
}
pub fn hex_name(path_hash: WadHash) -> String {
format!("{path_hash:016x}")
}
pub fn hex_chunk_hash(path: &Utf8Path) -> Option<WadHash> {
let file_stem = path.file_stem().unwrap_or("");
if file_stem.len() != 16 || !file_stem.chars().all(|c| c.is_ascii_hexdigit()) {
return None;
}
WadHash::from_str_radix(file_stem, 16).ok()
}
pub fn is_hex_chunk_path(path: &Utf8Path) -> bool {
hex_chunk_hash(path).is_some()
}