use std::any::{Any, TypeId};
use std::hash::Hasher;
use std::sync::Arc;
use papaya::HashMap;
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: HashMap<TypeId, ErasedLocator<T>, ahash::RandomState>,
}
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: HashMap::with_hasher(ahash::RandomState::new()),
}
}
pub fn insert<K: AssetKey, L: AssetLocator<K>>(&self, locator: L) {
let pinned = self.locators.pin();
pinned.insert(TypeId::of::<K>(), 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>> {
let pinned = self.locators.pin();
pinned
.get(&key_type)
.and_then(|locator| locator.locate_with_locator_ctx(locator_ctx, key))
}
}
pub(crate) struct PendingStorage {
pending: HashMap<AssetCacheKey, Arc<dyn Any + Send + Sync>, ahash::RandomState>,
}
impl Default for PendingStorage {
fn default() -> Self {
Self::new()
}
}
impl PendingStorage {
pub fn new() -> Self {
Self {
pending: HashMap::with_hasher(ahash::RandomState::new()),
}
}
pub fn insert<K: AssetKey>(&self, asset_key: AssetCacheKey, key: K) {
let pinned = self.pending.pin();
pinned.insert(asset_key, Arc::new(key) as Arc<dyn Any + Send + Sync>);
}
pub fn remove(&self, key: &AssetCacheKey) -> bool {
let pinned = self.pending.pin();
pinned.remove(key).is_some()
}
pub fn is_empty(&self) -> bool {
let pinned = self.pending.pin();
pinned.is_empty()
}
pub fn get_of_type<K: AssetKey>(&self) -> Vec<K> {
let pinned = self.pending.pin();
let key_type = TypeId::of::<K>();
pinned
.iter()
.filter(|(k, _)| k.asset_key_type() == key_type)
.filter_map(|(_, v)| v.downcast_ref::<K>().cloned())
.collect()
}
pub fn get_all(&self) -> Vec<crate::asset::PendingAsset> {
let pinned = self.pending.pin();
pinned
.iter()
.map(|(k, v)| {
crate::asset::PendingAsset::new_from_parts(
k.asset_key_type(),
&k.debug_repr(),
v.clone(),
)
})
.collect()
}
}
pub(crate) struct QueryRegistry {
entries: HashMap<TypeId, QueryTypeRegistry, ahash::RandomState>,
}
struct QueryTypeRegistry {
queries: HashMap<u64, Arc<dyn Any + Send + Sync>, ahash::RandomState>,
}
impl Default for QueryRegistry {
fn default() -> Self {
Self::new()
}
}
impl QueryRegistry {
pub fn new() -> Self {
Self {
entries: HashMap::with_hasher(ahash::RandomState::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 entries_pinned = self.entries.pin();
if let Some(type_registry) = entries_pinned.get(&type_id) {
let queries_pinned = type_registry.queries.pin();
if queries_pinned.contains_key(&key_hash) {
return false; }
queries_pinned.insert(
key_hash,
Arc::new(query.clone()) as Arc<dyn Any + Send + Sync>,
);
true
} else {
let type_registry = QueryTypeRegistry {
queries: HashMap::with_hasher(ahash::RandomState::new()),
};
type_registry.queries.pin().insert(
key_hash,
Arc::new(query.clone()) as Arc<dyn Any + Send + Sync>,
);
entries_pinned.insert(type_id, type_registry);
true
}
}
pub fn get_all<Q: Query>(&self) -> Vec<Q> {
let type_id = TypeId::of::<Q>();
let entries_pinned = self.entries.pin();
if let Some(type_registry) = entries_pinned.get(&type_id) {
let queries_pinned = type_registry.queries.pin();
queries_pinned
.iter()
.filter_map(|(_, arc)| arc.downcast_ref::<Q>().cloned())
.collect()
} else {
Vec::new()
}
}
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();
let entries_pinned = self.entries.pin();
if let Some(type_registry) = entries_pinned.get(&type_id) {
let queries_pinned = type_registry.queries.pin();
queries_pinned.remove(&key_hash).is_some()
} else {
false
}
}
}
pub(crate) struct AssetKeyRegistry {
entries: HashMap<TypeId, AssetKeyTypeRegistry, ahash::RandomState>,
}
struct AssetKeyTypeRegistry {
keys: HashMap<u64, Arc<dyn Any + Send + Sync>, ahash::RandomState>,
}
impl Default for AssetKeyRegistry {
fn default() -> Self {
Self::new()
}
}
impl AssetKeyRegistry {
pub fn new() -> Self {
Self {
entries: HashMap::with_hasher(ahash::RandomState::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 entries_pinned = self.entries.pin();
if let Some(type_registry) = entries_pinned.get(&type_id) {
let keys_pinned = type_registry.keys.pin();
if keys_pinned.contains_key(&key_hash) {
return false; }
keys_pinned.insert(
key_hash,
Arc::new(key.clone()) as Arc<dyn Any + Send + Sync>,
);
true
} else {
let type_registry = AssetKeyTypeRegistry {
keys: HashMap::with_hasher(ahash::RandomState::new()),
};
type_registry.keys.pin().insert(
key_hash,
Arc::new(key.clone()) as Arc<dyn Any + Send + Sync>,
);
entries_pinned.insert(type_id, type_registry);
true
}
}
pub fn get_all<K: AssetKey>(&self) -> Vec<K> {
let type_id = TypeId::of::<K>();
let entries_pinned = self.entries.pin();
if let Some(type_registry) = entries_pinned.get(&type_id) {
let keys_pinned = type_registry.keys.pin();
keys_pinned
.iter()
.filter_map(|(_, arc)| arc.downcast_ref::<K>().cloned())
.collect()
} else {
Vec::new()
}
}
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();
let entries_pinned = self.entries.pin();
if let Some(type_registry) = entries_pinned.get(&type_id) {
let keys_pinned = type_registry.keys.pin();
keys_pinned.remove(&key_hash).is_some()
} else {
false
}
}
}
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: HashMap<FullCacheKey, Arc<dyn AnyVerifier>, ahash::RandomState>,
}
impl Default for VerifierStorage {
fn default() -> Self {
Self::new()
}
}
impl VerifierStorage {
pub fn new() -> Self {
Self {
verifiers: HashMap::with_hasher(ahash::RandomState::new()),
}
}
pub fn insert<Q: Query, T: crate::Tracer + 'static>(&self, key: FullCacheKey, query: Q) {
let pinned = self.verifiers.pin();
pinned.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,
) {
let pinned = self.verifiers.pin();
pinned.insert(key, Arc::new(AssetVerifier::<K, T>::new(asset_key)));
}
pub fn get(&self, key: &FullCacheKey) -> Option<Arc<dyn AnyVerifier>> {
let pinned = self.verifiers.pin();
pinned.get(key).cloned()
}
pub fn remove(&self, key: &FullCacheKey) -> bool {
let pinned = self.verifiers.pin();
pinned.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()); }
}