use std::any::{Any, TypeId};
use std::hash::Hasher;
use std::sync::Arc;
use whale::ShardedMap;
use crate::asset::{AssetKey, AssetLocator, DurabilityLevel};
use crate::key::{AssetCacheKey, FullCacheKey};
use crate::query::Query;
use crate::runtime::DbDispatch;
#[derive(Clone)]
pub enum CachedValue<T> {
Ok(T),
UserError(Arc<anyhow::Error>),
}
#[derive(Clone)]
pub enum CachedEntry {
Ok(Arc<dyn Any + Send + Sync>),
UserError(Arc<anyhow::Error>),
AssetReady(Arc<dyn Any + Send + Sync>),
AssetError(Arc<anyhow::Error>),
}
impl CachedEntry {
pub fn to_cached_value<T: Send + Sync + 'static>(&self) -> Option<CachedValue<Arc<T>>> {
match self {
CachedEntry::Ok(arc) => arc.clone().downcast::<T>().ok().map(CachedValue::Ok),
CachedEntry::UserError(e) => Some(CachedValue::UserError(e.clone())),
CachedEntry::AssetReady(_) | CachedEntry::AssetError(_) => None,
}
}
}
pub(crate) enum ErasedLocateResult {
Ready {
value: Arc<dyn Any + Send + Sync>,
durability: DurabilityLevel,
},
Pending,
}
pub(crate) struct ErasedLocator<T: crate::Tracer> {
inner: Box<dyn Any + Send + Sync>,
locate_with_locator_ctx_fn: DynAssetLocatorWithLocatorContext<T>,
}
type DynAssetLocatorWithLocatorContext<T> =
fn(
&dyn Any,
&crate::runtime::LocatorContext<'_, T>,
&dyn Any,
) -> Option<Result<ErasedLocateResult, crate::QueryError>>;
impl<T: crate::Tracer> ErasedLocator<T> {
pub fn new<K: AssetKey, L: AssetLocator<K>>(locator: L) -> Self {
Self {
inner: Box::new(locator),
locate_with_locator_ctx_fn: erased_locate_with_locator_ctx::<K, L, T>,
}
}
pub fn locate_with_locator_ctx(
&self,
locator_ctx: &crate::runtime::LocatorContext<'_, T>,
key: &dyn Any,
) -> Option<Result<ErasedLocateResult, crate::QueryError>> {
(self.locate_with_locator_ctx_fn)(&*self.inner, locator_ctx, key)
}
}
fn erased_locate_with_locator_ctx<K: AssetKey, L: AssetLocator<K>, T: crate::Tracer>(
locator: &dyn Any,
locator_ctx: &crate::runtime::LocatorContext<'_, T>,
key: &dyn Any,
) -> Option<Result<ErasedLocateResult, crate::QueryError>> {
let locator = locator.downcast_ref::<L>()?;
let key = key.downcast_ref::<K>()?;
let db = DbDispatch::LocatorContext(locator_ctx);
Some(locator.locate(&db, key).map(|result| match result {
crate::asset::LocateResult::Ready { value, durability } => ErasedLocateResult::Ready {
value: Arc::new(value) as Arc<dyn Any + Send + Sync>,
durability,
},
crate::asset::LocateResult::Pending => ErasedLocateResult::Pending,
}))
}
pub(crate) struct LocatorStorage<T: crate::Tracer> {
locators: ShardedMap<TypeId, Arc<ErasedLocator<T>>>,
}
impl<T: crate::Tracer> Default for LocatorStorage<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: crate::Tracer> LocatorStorage<T> {
pub fn new() -> Self {
Self {
locators: ShardedMap::new(),
}
}
pub fn insert<K: AssetKey, L: AssetLocator<K>>(&self, locator: L) {
self.locators.insert(
TypeId::of::<K>(),
Arc::new(ErasedLocator::new::<K, L>(locator)),
);
}
pub fn locate_with_locator_ctx(
&self,
key_type: TypeId,
locator_ctx: &crate::runtime::LocatorContext<'_, T>,
key: &dyn Any,
) -> Option<Result<ErasedLocateResult, crate::QueryError>> {
self.locators
.get(&key_type)
.and_then(|locator| locator.locate_with_locator_ctx(locator_ctx, key))
}
}
pub(crate) struct PendingStorage {
pending: ShardedMap<AssetCacheKey, Arc<dyn Any + Send + Sync>>,
}
impl Default for PendingStorage {
fn default() -> Self {
Self::new()
}
}
impl PendingStorage {
pub fn new() -> Self {
Self {
pending: ShardedMap::new(),
}
}
pub fn insert<K: AssetKey>(&self, asset_key: AssetCacheKey, key: K) {
self.pending
.insert(asset_key, Arc::new(key) as Arc<dyn Any + Send + Sync>);
}
pub fn remove(&self, key: &AssetCacheKey) -> bool {
self.pending.remove(key).is_some()
}
pub fn is_empty(&self) -> bool {
self.pending.is_empty()
}
pub fn get_of_type<K: AssetKey>(&self) -> Vec<K> {
let key_type = TypeId::of::<K>();
let mut keys = Vec::new();
self.pending.for_each(|k, v| {
if k.asset_key_type() == key_type {
if let Some(key) = v.downcast_ref::<K>() {
keys.push(key.clone());
}
}
});
keys
}
pub fn get_all(&self) -> Vec<crate::asset::PendingAsset> {
let mut assets = Vec::new();
self.pending.for_each(|k, v| {
assets.push(crate::asset::PendingAsset::new_from_parts(
k.asset_key_type(),
&k.debug_repr(),
v.clone(),
));
});
assets
}
}
pub(crate) struct QueryRegistry {
entries: ShardedMap<TypeId, Arc<QueryTypeRegistry>>,
}
#[derive(Default)]
struct QueryTypeRegistry {
queries: ShardedMap<u64, Arc<dyn Any + Send + Sync>>,
}
impl Default for QueryRegistry {
fn default() -> Self {
Self::new()
}
}
impl QueryRegistry {
pub fn new() -> Self {
Self {
entries: ShardedMap::new(),
}
}
pub fn register<Q: Query>(&self, query: &Q) -> bool {
let type_id = TypeId::of::<Q>();
let mut hasher = ahash::AHasher::default();
query.dyn_hash(&mut hasher);
let key_hash = hasher.finish();
let (type_registry, _) = self.entries.get_or_insert_with(type_id, Default::default);
type_registry.queries.compute(key_hash, |slot| {
if slot.is_some() {
return false; }
*slot = Some(Arc::new(query.clone()) as Arc<dyn Any + Send + Sync>);
true
})
}
pub fn get_all<Q: Query>(&self) -> Vec<Q> {
let type_id = TypeId::of::<Q>();
let Some(type_registry) = self.entries.get(&type_id) else {
return Vec::new();
};
let mut queries = Vec::new();
type_registry.queries.for_each(|_, arc| {
if let Some(query) = arc.downcast_ref::<Q>() {
queries.push(query.clone());
}
});
queries
}
pub fn remove<Q: Query>(&self, query: &Q) -> bool {
let type_id = TypeId::of::<Q>();
let mut hasher = ahash::AHasher::default();
query.dyn_hash(&mut hasher);
let key_hash = hasher.finish();
self.entries
.get(&type_id)
.is_some_and(|type_registry| type_registry.queries.remove(&key_hash).is_some())
}
}
pub(crate) struct AssetKeyRegistry {
entries: ShardedMap<TypeId, Arc<AssetKeyTypeRegistry>>,
}
#[derive(Default)]
struct AssetKeyTypeRegistry {
keys: ShardedMap<u64, Arc<dyn Any + Send + Sync>>,
}
impl Default for AssetKeyRegistry {
fn default() -> Self {
Self::new()
}
}
impl AssetKeyRegistry {
pub fn new() -> Self {
Self {
entries: ShardedMap::new(),
}
}
pub fn register<K: AssetKey>(&self, key: &K) -> bool {
let type_id = TypeId::of::<K>();
let mut hasher = ahash::AHasher::default();
key.dyn_hash(&mut hasher);
let key_hash = hasher.finish();
let (type_registry, _) = self.entries.get_or_insert_with(type_id, Default::default);
type_registry.keys.compute(key_hash, |slot| {
if slot.is_some() {
return false; }
*slot = Some(Arc::new(key.clone()) as Arc<dyn Any + Send + Sync>);
true
})
}
pub fn get_all<K: AssetKey>(&self) -> Vec<K> {
let type_id = TypeId::of::<K>();
let Some(type_registry) = self.entries.get(&type_id) else {
return Vec::new();
};
let mut keys = Vec::new();
type_registry.keys.for_each(|_, arc| {
if let Some(key) = arc.downcast_ref::<K>() {
keys.push(key.clone());
}
});
keys
}
pub fn remove<K: AssetKey>(&self, key: &K) -> bool {
let type_id = TypeId::of::<K>();
let mut hasher = ahash::AHasher::default();
key.dyn_hash(&mut hasher);
let key_hash = hasher.finish();
self.entries
.get(&type_id)
.is_some_and(|type_registry| type_registry.keys.remove(&key_hash).is_some())
}
}
pub(crate) trait AnyVerifier: Send + Sync + 'static {
fn verify(&self, runtime: &dyn std::any::Any) -> Result<(), crate::QueryError>;
}
pub(crate) struct QueryVerifier<Q: Query, T: crate::Tracer> {
query: Q,
_marker: std::marker::PhantomData<T>,
}
impl<Q: Query, T: crate::Tracer> QueryVerifier<Q, T> {
pub fn new(query: Q) -> Self {
Self {
query,
_marker: std::marker::PhantomData,
}
}
}
impl<Q: Query, T: crate::Tracer + 'static> AnyVerifier for QueryVerifier<Q, T> {
fn verify(&self, runtime: &dyn std::any::Any) -> Result<(), crate::QueryError> {
if let Some(runtime) = runtime.downcast_ref::<crate::QueryRuntime<T>>() {
match runtime.query(self.query.clone()) {
Ok(_) => Ok(()),
Err(crate::QueryError::UserError(_)) => Ok(()),
Err(e) => Err(e),
}
} else {
Ok(())
}
}
}
pub(crate) struct AssetVerifier<K: AssetKey, T: crate::Tracer> {
key: K,
_marker: std::marker::PhantomData<T>,
}
impl<K: AssetKey, T: crate::Tracer> AssetVerifier<K, T> {
pub fn new(key: K) -> Self {
Self {
key,
_marker: std::marker::PhantomData,
}
}
}
impl<K: AssetKey, T: crate::Tracer + 'static> AnyVerifier for AssetVerifier<K, T> {
fn verify(&self, runtime: &dyn std::any::Any) -> Result<(), crate::QueryError> {
if let Some(runtime) = runtime.downcast_ref::<crate::QueryRuntime<T>>() {
match runtime.get_asset(self.key.clone()) {
Ok(_) => Ok(()),
Err(crate::QueryError::UserError(_)) => Ok(()),
Err(e) => Err(e),
}
} else {
Ok(())
}
}
}
pub(crate) struct VerifierStorage {
verifiers: ShardedMap<FullCacheKey, Arc<dyn AnyVerifier>>,
}
impl Default for VerifierStorage {
fn default() -> Self {
Self::new()
}
}
impl VerifierStorage {
pub fn new() -> Self {
Self {
verifiers: ShardedMap::new(),
}
}
pub fn insert<Q: Query, T: crate::Tracer + 'static>(&self, key: FullCacheKey, query: Q) {
self.verifiers
.insert(key, Arc::new(QueryVerifier::<Q, T>::new(query)));
}
pub fn insert_asset<K: AssetKey, T: crate::Tracer + 'static>(
&self,
key: FullCacheKey,
asset_key: K,
) {
self.verifiers
.insert(key, Arc::new(AssetVerifier::<K, T>::new(asset_key)));
}
pub fn get(&self, key: &FullCacheKey) -> Option<Arc<dyn AnyVerifier>> {
self.verifiers.get(key)
}
pub fn remove(&self, key: &FullCacheKey) -> bool {
self.verifiers.remove(key).is_some()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cached_entry_to_cached_value() {
let entry = CachedEntry::Ok(Arc::new("hello".to_string()) as Arc<dyn Any + Send + Sync>);
let result: Option<CachedValue<Arc<String>>> = entry.to_cached_value();
assert!(result.is_some());
match result.unwrap() {
CachedValue::Ok(v) => assert_eq!(*v, "hello"),
CachedValue::UserError(_) => panic!("expected Ok"),
}
let err = Arc::new(anyhow::anyhow!("test error"));
let entry = CachedEntry::UserError(err.clone());
let result: Option<CachedValue<Arc<String>>> = entry.to_cached_value();
assert!(result.is_some());
match result.unwrap() {
CachedValue::Ok(_) => panic!("expected UserError"),
CachedValue::UserError(e) => assert_eq!(e.to_string(), "test error"),
}
let entry = CachedEntry::Ok(Arc::new(42i32) as Arc<dyn Any + Send + Sync>);
let result: Option<CachedValue<Arc<String>>> = entry.to_cached_value();
assert!(result.is_none()); }
}