use crate::{
Asset, AssetCache, BoxedError, Error, FileAsset, Handle, SharedString, Storable,
source::{DirEntry, Source},
};
use std::{fmt, io, marker::PhantomData};
pub trait DirLoadable: Storable {
fn select_ids(cache: &AssetCache, id: &SharedString) -> io::Result<Vec<SharedString>>;
#[inline]
fn sub_directories(
cache: &AssetCache,
id: &SharedString,
mut f: impl FnMut(&str),
) -> io::Result<()> {
cache.source().read_dir(id, &mut |entry| {
if let DirEntry::Directory(id) = entry {
f(id);
}
})
}
}
impl<T> DirLoadable for T
where
T: FileAsset,
{
#[inline]
fn select_ids(cache: &AssetCache, id: &SharedString) -> io::Result<Vec<SharedString>> {
fn inner(
cache: &AssetCache,
id: &str,
extensions: &[&str],
) -> io::Result<Vec<SharedString>> {
let mut ids = Vec::new();
cache.source().read_dir(id, &mut |entry| {
if let DirEntry::File(id, ext) = entry
&& extensions.contains(&ext)
{
ids.push(id.into());
}
})?;
Ok(ids)
}
inner(cache, id, T::EXTENSIONS)
}
}
impl<T> DirLoadable for std::sync::Arc<T>
where
T: DirLoadable,
{
#[inline]
fn select_ids(cache: &AssetCache, id: &SharedString) -> io::Result<Vec<SharedString>> {
T::select_ids(cache, id)
}
#[inline]
fn sub_directories(
cache: &AssetCache,
id: &SharedString,
f: impl FnMut(&str),
) -> io::Result<()> {
T::sub_directories(cache, id, f)
}
}
pub struct RawDirectory<T> {
ids: Vec<SharedString>,
_marker: PhantomData<T>,
}
impl<T> Asset for RawDirectory<T>
where
T: DirLoadable,
{
fn load(cache: &AssetCache, id: &SharedString) -> Result<Self, BoxedError> {
let mut ids = T::select_ids(cache, id)?;
ids.sort_unstable();
ids.dedup();
Ok(RawDirectory {
ids,
_marker: PhantomData,
})
}
const HOT_RELOADED: bool = true;
}
impl<T> RawDirectory<T> {
pub fn ids(&self) -> impl ExactSizeIterator<Item = &SharedString> {
self.ids.iter()
}
}
impl<T> RawDirectory<T>
where
T: Storable,
{
#[inline]
pub fn iter_cached<'h, 'a: 'h>(
&'h self,
cache: &'a AssetCache,
) -> impl Iterator<Item = &'a Handle<T>> + 'h {
self.ids().filter_map(move |id| cache.get(id))
}
}
impl<T> RawDirectory<T>
where
T: Asset,
{
#[inline]
pub fn iter<'h, 'a: 'h>(
&'h self,
cache: &'a AssetCache,
) -> impl ExactSizeIterator<Item = Result<&'a Handle<T>, Error>> + 'h {
self.ids().map(move |id| cache.load(id))
}
}
impl<T> fmt::Debug for RawDirectory<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawDirectory")
.field("ids", &self.ids)
.finish()
}
}
pub struct RawRecursiveDirectory<T> {
ids: Vec<SharedString>,
_marker: PhantomData<T>,
}
impl<T> Asset for RawRecursiveDirectory<T>
where
T: DirLoadable,
{
fn load(cache: &AssetCache, id: &SharedString) -> Result<Self, BoxedError> {
let this = cache.load::<RawDirectory<T>>(id)?;
let mut ids = this.read().ids.clone();
T::sub_directories(cache, id, |id| {
if let Ok(child) = cache.load::<RawRecursiveDirectory<T>>(id) {
ids.extend_from_slice(&child.read().ids);
}
})?;
Ok(RawRecursiveDirectory {
ids,
_marker: PhantomData,
})
}
const HOT_RELOADED: bool = true;
}
impl<T> RawRecursiveDirectory<T> {
pub fn ids(&self) -> impl ExactSizeIterator<Item = &SharedString> {
self.ids.iter()
}
}
impl<T> RawRecursiveDirectory<T>
where
T: Storable,
{
#[inline]
pub fn iter_cached<'h, 'a: 'h>(
&'h self,
cache: &'a AssetCache,
) -> impl Iterator<Item = &'a Handle<T>> + 'h {
self.ids().filter_map(move |id| cache.get(id))
}
}
impl<T> RawRecursiveDirectory<T>
where
T: Asset,
{
#[inline]
pub fn iter<'h, 'a: 'h>(
&'h self,
cache: &'a AssetCache,
) -> impl ExactSizeIterator<Item = Result<&'a Handle<T>, Error>> + 'h {
self.ids().map(move |id| cache.load(id))
}
}
impl<T> fmt::Debug for RawRecursiveDirectory<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawRecursiveDirectory")
.field("ids", &self.ids)
.finish()
}
}
pub struct Directory<T> {
ids: Vec<SharedString>,
_marker: PhantomData<T>,
}
impl<T> Asset for Directory<T>
where
T: DirLoadable + Asset,
{
fn load(cache: &AssetCache, id: &SharedString) -> Result<Self, BoxedError> {
let raw = cache.load::<RawDirectory<T>>(id)?;
let ids = &raw.read().ids;
cache.no_record(|| {
for id in ids {
let _ = cache.load::<T>(id);
}
});
Ok(Directory {
ids: ids.clone(),
_marker: PhantomData,
})
}
const HOT_RELOADED: bool = true;
}
impl<T> Directory<T> {
pub fn ids(&self) -> impl ExactSizeIterator<Item = &SharedString> {
self.ids.iter()
}
}
impl<T> Directory<T>
where
T: Storable,
{
#[inline]
pub fn iter_cached<'h, 'a: 'h>(
&'h self,
cache: &'a AssetCache,
) -> impl Iterator<Item = &'a Handle<T>> + 'h {
self.ids().filter_map(move |id| cache.get(id))
}
}
impl<T> Directory<T>
where
T: Asset,
{
#[inline]
pub fn iter<'h, 'a: 'h>(
&'h self,
cache: &'a AssetCache,
) -> impl ExactSizeIterator<Item = Result<&'a Handle<T>, Error>> + 'h {
self.ids().map(move |id| cache.load(id))
}
}
impl<T> fmt::Debug for Directory<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Directory").field("ids", &self.ids).finish()
}
}
pub struct RecursiveDirectory<T> {
ids: Vec<SharedString>,
_marker: PhantomData<T>,
}
impl<T> Asset for RecursiveDirectory<T>
where
T: DirLoadable + Asset,
{
fn load(cache: &AssetCache, id: &SharedString) -> Result<Self, BoxedError> {
let raw = cache.load::<RawRecursiveDirectory<T>>(id)?;
let ids = &raw.read().ids;
cache.no_record(|| {
for id in ids {
let _ = cache.load::<T>(id);
}
});
Ok(RecursiveDirectory {
ids: ids.clone(),
_marker: PhantomData,
})
}
const HOT_RELOADED: bool = true;
}
impl<T> RecursiveDirectory<T> {
pub fn ids(&self) -> impl ExactSizeIterator<Item = &SharedString> {
self.ids.iter()
}
}
impl<T> RecursiveDirectory<T>
where
T: Storable,
{
#[inline]
pub fn iter_cached<'h, 'a: 'h>(
&'h self,
cache: &'a AssetCache,
) -> impl Iterator<Item = &'a Handle<T>> + 'h {
self.ids().filter_map(move |id| cache.get(id))
}
}
impl<T> RecursiveDirectory<T>
where
T: Asset,
{
#[inline]
pub fn iter<'h, 'a: 'h>(
&'h self,
cache: &'a AssetCache,
) -> impl ExactSizeIterator<Item = Result<&'a Handle<T>, Error>> + 'h {
self.ids().map(move |id| cache.load(id))
}
}
impl<T> fmt::Debug for RecursiveDirectory<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RecursiveDirectory")
.field("ids", &self.ids)
.finish()
}
}