#![warn(missing_docs)]
use crate::core::visitor::{Visit, VisitResult, Visitor};
use std::borrow::Cow;
use std::ops::{Deref, DerefMut};
use std::{
fmt::Debug,
future::Future,
path::{Path, PathBuf},
pin::Pin,
sync::{Arc, Mutex, MutexGuard},
task::{Context, Poll, Waker},
};
pub mod fbx;
pub mod model;
pub mod texture;
pub trait ResourceData: 'static + Default + Debug + Visit + Send {
fn path(&self) -> Cow<Path>;
}
pub trait ResourceLoadError: 'static + Debug + Send + Sync {}
impl<T> ResourceLoadError for T where T: 'static + Debug + Send + Sync {}
#[derive(Debug)]
pub enum ResourceState<T: ResourceData, E: ResourceLoadError> {
Pending {
path: PathBuf,
wakers: Vec<Waker>,
},
LoadError {
path: PathBuf,
error: Option<Arc<E>>,
},
Ok(T),
}
impl<T: ResourceData, E: ResourceLoadError> Visit for ResourceState<T, E> {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
let mut id = self.id();
if id.visit("Id", visitor).is_ok() {
if visitor.is_reading() {
*self = Self::from_id(id)?;
}
match self {
Self::Pending { .. } => unreachable!(),
Self::LoadError { path, .. } => path.visit("Path", visitor)?,
Self::Ok(details) => details.visit("Details", visitor)?,
}
visitor.leave_region()
} else {
visitor.leave_region()?;
let mut details = T::default();
details.visit(name, visitor)?;
*self = Self::Ok(details);
Ok(())
}
}
}
#[derive(Debug)]
pub struct Resource<T: ResourceData, E: ResourceLoadError> {
state: Option<Arc<Mutex<ResourceState<T, E>>>>,
}
#[doc(hidden)]
pub struct ResourceDataRef<'a, T: ResourceData, E: ResourceLoadError> {
guard: MutexGuard<'a, ResourceState<T, E>>,
}
impl<'a, T: ResourceData, E: ResourceLoadError> Deref for ResourceDataRef<'a, T, E> {
type Target = T;
fn deref(&self) -> &Self::Target {
match *self.guard {
ResourceState::Pending { .. } => {
panic!("attempt to get reference to resource data while it is not loaded!")
}
ResourceState::LoadError { .. } => {
panic!("attempt to get reference to resource data which failed to load!")
}
ResourceState::Ok(ref data) => data,
}
}
}
impl<'a, T: ResourceData, E: ResourceLoadError> DerefMut for ResourceDataRef<'a, T, E> {
fn deref_mut(&mut self) -> &mut Self::Target {
match *self.guard {
ResourceState::Pending { .. } => {
panic!("attempt to get reference to resource data while it is not loaded!")
}
ResourceState::LoadError { .. } => {
panic!("attempt to get reference to resource data which failed to load!")
}
ResourceState::Ok(ref mut data) => data,
}
}
}
impl<T: ResourceData, E: ResourceLoadError> Resource<T, E> {
pub fn new(state: ResourceState<T, E>) -> Self {
Self {
state: Some(Arc::new(Mutex::new(state))),
}
}
pub fn into_inner(self) -> Arc<Mutex<ResourceState<T, E>>> {
self.state.unwrap()
}
pub fn state(&self) -> MutexGuard<'_, ResourceState<T, E>> {
self.state.as_ref().unwrap().lock().unwrap()
}
pub fn try_acquire_state(&self) -> Option<MutexGuard<'_, ResourceState<T, E>>> {
self.state.as_ref().unwrap().try_lock().ok()
}
pub fn use_count(&self) -> usize {
Arc::strong_count(&self.state.as_ref().unwrap())
}
pub fn key(&self) -> usize {
(&**self.state.as_ref().unwrap() as *const _) as usize
}
pub fn data_ref(&self) -> ResourceDataRef<'_, T, E> {
ResourceDataRef {
guard: self.state(),
}
}
}
impl<T: ResourceData, E: ResourceLoadError> Default for Resource<T, E> {
fn default() -> Self {
Self { state: None }
}
}
impl<T: ResourceData, E: ResourceLoadError> Clone for Resource<T, E> {
fn clone(&self) -> Self {
Self {
state: self.state.clone(),
}
}
}
impl<T: ResourceData, E: ResourceLoadError> From<Arc<Mutex<ResourceState<T, E>>>>
for Resource<T, E>
{
fn from(state: Arc<Mutex<ResourceState<T, E>>>) -> Self {
Self { state: Some(state) }
}
}
impl<T: ResourceData, E: ResourceLoadError> Into<Arc<Mutex<ResourceState<T, E>>>>
for Resource<T, E>
{
fn into(self) -> Arc<Mutex<ResourceState<T, E>>> {
self.state.unwrap()
}
}
impl<T: ResourceData, E: ResourceLoadError> Future for Resource<T, E> {
type Output = Result<Self, Option<Arc<E>>>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let state = self.as_ref().state.clone();
match *state.unwrap().lock().unwrap() {
ResourceState::Pending { ref mut wakers, .. } => {
let cx_waker = cx.waker();
if let Some(pos) = wakers.iter().position(|waker| waker.will_wake(cx_waker)) {
wakers[pos] = cx_waker.clone();
} else {
wakers.push(cx_waker.clone())
}
Poll::Pending
}
ResourceState::LoadError { ref error, .. } => Poll::Ready(Err(error.clone())),
ResourceState::Ok(_) => Poll::Ready(Ok(self.clone())),
}
}
}
impl<T, E> Visit for Resource<T, E>
where
T: ResourceData,
E: ResourceLoadError,
{
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
if self.state.visit("State", visitor).is_err() {
visitor.leave_region()?;
let mut state = Arc::new(Mutex::new(ResourceState::Ok(Default::default())));
state.visit(name, visitor)?;
self.state = Some(state);
Ok(())
} else {
visitor.leave_region()
}
}
}
impl<T: ResourceData, E: ResourceLoadError> ResourceState<T, E> {
pub(in crate) fn new_pending(path: PathBuf) -> Self {
Self::Pending {
path,
wakers: Default::default(),
}
}
fn id(&self) -> u32 {
match self {
Self::Pending { .. } => 0,
Self::LoadError { .. } => 1,
Self::Ok(_) => 2,
}
}
fn from_id(id: u32) -> Result<Self, String> {
match id {
0 => Ok(Self::Pending {
path: Default::default(),
wakers: Default::default(),
}),
1 => Ok(Self::LoadError {
path: Default::default(),
error: None,
}),
2 => Ok(Self::Ok(Default::default())),
_ => Err(format!("Invalid resource id {}", id)),
}
}
pub fn path(&self) -> Cow<Path> {
match self {
Self::Pending { path, .. } => Cow::Borrowed(path.as_path()),
Self::LoadError { path, .. } => Cow::Borrowed(path.as_path()),
Self::Ok(details) => details.path(),
}
}
pub fn commit(&mut self, state: ResourceState<T, E>) {
let wakers = if let ResourceState::Pending { ref mut wakers, .. } = self {
std::mem::take(wakers)
} else {
unreachable!()
};
*self = state;
for waker in wakers {
waker.wake();
}
}
}
impl<T: ResourceData, E: ResourceLoadError> Default for ResourceState<T, E> {
fn default() -> Self {
Self::Ok(Default::default())
}
}