use crate::{
core::{reflect::prelude::*, uuid::Uuid, visitor::prelude::*},
manager::ResourceManager,
ResourceData, ResourceLoadError,
};
use std::{
ops::{Deref, DerefMut},
sync::Arc,
task::Waker,
};
#[doc(hidden)]
#[derive(Reflect, Debug, Default)]
#[reflect(hide_all)]
pub struct WakersList(Vec<Waker>);
impl Deref for WakersList {
type Target = Vec<Waker>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for WakersList {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Reflect, Debug, Clone, Default)]
#[reflect(hide_all)]
pub struct LoadError(pub Option<Arc<dyn ResourceLoadError>>);
impl LoadError {
pub fn new<T: ResourceLoadError>(value: T) -> Self {
Self(Some(Arc::new(value)))
}
}
#[derive(Debug, Reflect)]
pub enum ResourceState {
Pending {
wakers: WakersList,
},
LoadError {
error: LoadError,
},
Ok(Box<dyn ResourceData>),
}
impl Default for ResourceState {
fn default() -> Self {
Self::LoadError {
error: Default::default(),
}
}
}
impl Drop for ResourceState {
fn drop(&mut self) {
if let ResourceState::Pending { wakers, .. } = self {
assert_eq!(wakers.len(), 0);
}
}
}
impl Visit for ResourceState {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
if visitor.is_reading() {
let mut type_uuid = Uuid::default();
type_uuid.visit("TypeUuid", visitor)?;
let resource_manager = visitor.blackboard.get::<ResourceManager>().expect(
"Resource data constructor container must be \
provided when serializing resources!",
);
let resource_manager_state = resource_manager.state();
if let Some(mut instance) = resource_manager_state
.constructors_container
.try_create(&type_uuid)
{
drop(resource_manager_state);
instance.visit(name, visitor)?;
*self = Self::Ok(instance);
Ok(())
} else {
Err(VisitError::User(format!(
"There's no constructor registered for type {type_uuid}!"
)))
}
} else if let Self::Ok(instance) = self {
instance.visit(name, visitor)?;
Ok(())
} else {
Ok(())
}
}
}
impl ResourceState {
#[inline]
pub fn new_pending() -> Self {
Self::Pending {
wakers: Default::default(),
}
}
#[inline]
pub fn new_load_error(error: LoadError) -> Self {
Self::LoadError { error }
}
#[inline]
pub fn new_ok<T: ResourceData>(data: T) -> Self {
Self::Ok(Box::new(data))
}
pub fn is_loading(&self) -> bool {
matches!(self, ResourceState::Pending { .. })
}
pub fn switch_to_pending_state(&mut self) {
*self = ResourceState::Pending {
wakers: Default::default(),
};
}
#[inline]
pub fn commit(&mut self, state: ResourceState) {
assert!(!matches!(state, ResourceState::Pending { .. }));
let wakers = if let ResourceState::Pending { ref mut wakers } = self {
std::mem::take(wakers)
} else {
unreachable!()
};
*self = state;
for waker in wakers.0 {
waker.wake();
}
}
pub fn commit_ok<T: ResourceData>(&mut self, data: T) {
self.commit(ResourceState::Ok(Box::new(data)))
}
pub fn commit_error<E: ResourceLoadError>(&mut self, error: E) {
self.commit(ResourceState::LoadError {
error: LoadError::new(error),
})
}
}
#[cfg(test)]
mod test {
use fyrox_core::{
reflect::{FieldInfo, Reflect},
TypeUuidProvider,
};
use std::error::Error;
use std::path::Path;
use super::*;
#[derive(Debug, Default, Reflect, Visit)]
struct Stub {}
impl ResourceData for Stub {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn type_uuid(&self) -> Uuid {
Uuid::default()
}
fn save(&mut self, _path: &Path) -> Result<(), Box<dyn Error>> {
Err("Saving is not supported!".to_string().into())
}
fn can_be_saved(&self) -> bool {
false
}
}
impl TypeUuidProvider for Stub {
fn type_uuid() -> Uuid {
Uuid::default()
}
}
#[test]
fn resource_state_new_pending() {
let state = ResourceState::new_pending();
assert!(matches!(state, ResourceState::Pending { wakers: _ }));
assert!(state.is_loading());
}
#[test]
fn resource_state_new_load_error() {
let state = ResourceState::new_load_error(Default::default());
assert!(matches!(state, ResourceState::LoadError { error: _ }));
assert!(!state.is_loading());
}
#[test]
fn resource_state_new_ok() {
let state = ResourceState::new_ok(Stub {});
assert!(matches!(state, ResourceState::Ok(_)));
assert!(!state.is_loading());
}
#[test]
fn resource_state_switch_to_pending_state() {
let mut state = ResourceState::new_ok(Stub {});
state.switch_to_pending_state();
assert!(matches!(state, ResourceState::Pending { wakers: _ }));
let mut state = ResourceState::new_load_error(Default::default());
state.switch_to_pending_state();
assert!(matches!(state, ResourceState::Pending { wakers: _ }));
let mut state = ResourceState::new_pending();
state.switch_to_pending_state();
assert!(matches!(state, ResourceState::Pending { wakers: _ }));
}
#[test]
fn visit_for_resource_state() {
let mut state = ResourceState::new_pending();
let mut visitor = Visitor::default();
assert!(state.visit("name", &mut visitor).is_ok());
let mut state = ResourceState::new_load_error(Default::default());
let mut visitor = Visitor::default();
assert!(state.visit("name", &mut visitor).is_ok());
let mut state = ResourceState::new_ok(Stub {});
let mut visitor = Visitor::default();
assert!(state.visit("name", &mut visitor).is_ok());
}
}