use std::{
error::Error,
path::PathBuf,
sync::{
atomic::{AtomicU64, Ordering},
Arc,
},
};
use crossbeam_channel::Sender;
use dashmap::DashMap;
use distill_core::{AssetMetadata, AssetRef, AssetTypeId, AssetUuid};
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct LoadHandle(pub u64);
impl LoadHandle {
pub fn is_indirect(&self) -> bool {
(self.0 & (1 << 63)) == 1 << 63
}
pub(crate) fn set_indirect(self) -> LoadHandle {
LoadHandle(self.0 | (1 << 63))
}
}
pub(crate) enum HandleOp {
Error(LoadHandle, u32, Box<dyn Error + Send>),
Complete(LoadHandle, u32),
Drop(LoadHandle, u32),
}
pub struct AssetLoadOp {
sender: Option<Sender<HandleOp>>,
handle: LoadHandle,
version: u32,
}
impl AssetLoadOp {
pub(crate) fn new(sender: Sender<HandleOp>, handle: LoadHandle, version: u32) -> Self {
Self {
sender: Some(sender),
handle,
version,
}
}
pub fn load_handle(&self) -> LoadHandle {
self.handle
}
pub fn complete(mut self) {
let _ = self
.sender
.as_ref()
.unwrap()
.send(HandleOp::Complete(self.handle, self.version));
self.sender = None;
}
pub fn error<E: Error + 'static + Send>(mut self, error: E) {
let _ = self.sender.as_ref().unwrap().send(HandleOp::Error(
self.handle,
self.version,
Box::new(error),
));
self.sender = None;
}
}
impl Drop for AssetLoadOp {
fn drop(&mut self) {
if let Some(ref sender) = self.sender {
let _ = sender.send(HandleOp::Drop(self.handle, self.version));
}
}
}
pub trait AssetStorage {
fn update_asset(
&self,
loader_info: &dyn LoaderInfoProvider,
asset_type_id: &AssetTypeId,
data: Vec<u8>,
load_handle: LoadHandle,
load_op: AssetLoadOp,
version: u32,
) -> Result<(), Box<dyn Error + Send + 'static>>;
fn commit_asset_version(&self, asset_type: &AssetTypeId, load_handle: LoadHandle, version: u32);
fn free(&self, asset_type_id: &AssetTypeId, load_handle: LoadHandle, version: u32);
}
#[derive(Debug)]
pub enum LoadStatus {
NotRequested,
Unresolved,
Loading,
Loaded,
Unloading,
DoesNotExist,
Error(Box<dyn Error>),
}
#[derive(Debug)]
pub struct LoadInfo {
pub asset_id: AssetUuid,
pub refs: u32,
pub path: Option<String>,
pub file_name: Option<String>,
pub asset_name: Option<String>,
}
pub trait LoaderInfoProvider: Send + Sync {
fn get_load_handle(&self, asset_ref: &AssetRef) -> Option<LoadHandle>;
fn get_asset_id(&self, load: LoadHandle) -> Option<AssetUuid>;
}
pub trait HandleAllocator: Send + Sync + 'static {
fn alloc(&self) -> LoadHandle;
fn free(&self, handle: LoadHandle);
}
pub struct AtomicHandleAllocator(AtomicU64);
impl AtomicHandleAllocator {
pub const fn new(starting_value: u64) -> Self {
Self(AtomicU64::new(starting_value))
}
}
impl Default for AtomicHandleAllocator {
fn default() -> Self {
Self(AtomicU64::new(1))
}
}
impl HandleAllocator for AtomicHandleAllocator {
fn alloc(&self) -> LoadHandle {
LoadHandle(self.0.fetch_add(1, Ordering::Relaxed))
}
fn free(&self, _handle: LoadHandle) {}
}
impl HandleAllocator for &'static AtomicHandleAllocator {
fn alloc(&self) -> LoadHandle {
LoadHandle(self.0.fetch_add(1, Ordering::Relaxed))
}
fn free(&self, _handle: LoadHandle) {}
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub enum IndirectIdentifier {
PathWithTagAndType(String, String, AssetTypeId),
PathWithType(String, AssetTypeId),
Path(String),
}
impl IndirectIdentifier {
pub fn path(&self) -> &str {
match self {
IndirectIdentifier::PathWithTagAndType(path, _, _) => path.as_str(),
IndirectIdentifier::PathWithType(path, _) => path.as_str(),
IndirectIdentifier::Path(path) => path.as_str(),
}
}
pub fn type_id(&self) -> Option<&AssetTypeId> {
match self {
IndirectIdentifier::PathWithTagAndType(_, _, ty) => Some(ty),
IndirectIdentifier::PathWithType(_, ty) => Some(ty),
IndirectIdentifier::Path(_) => None,
}
}
}
pub trait IndirectionResolver {
fn resolve(
&self,
id: &IndirectIdentifier,
candidates: Vec<(PathBuf, Vec<AssetMetadata>)>,
) -> Option<AssetUuid>;
}
pub struct DefaultIndirectionResolver;
impl IndirectionResolver for DefaultIndirectionResolver {
fn resolve(
&self,
id: &IndirectIdentifier,
candidates: Vec<(PathBuf, Vec<AssetMetadata>)>,
) -> Option<AssetUuid> {
let id_type = id.type_id();
for candidate in candidates {
for asset in candidate.1 {
if let Some(artifact) = asset.artifact {
if id_type.is_none() || *id_type.unwrap() == artifact.type_id {
return Some(asset.id);
}
}
}
}
None
}
}
#[derive(Clone)]
pub struct IndirectionTable(pub(crate) Arc<DashMap<LoadHandle, LoadHandle>>);
impl IndirectionTable {
pub fn resolve(&self, indirect_handle: LoadHandle) -> Option<LoadHandle> {
self.0.get(&indirect_handle).map(|l| *l)
}
}