web_component/resource/
mod.rsuse std::{future::Future, sync::Arc};
use tokio::sync::{Mutex, MutexGuard};
use dioxus::{prelude::spawn, signals::{Readable, Signal, Writable}};
use enum_as_inner::EnumAsInner;
#[derive(Clone)]
pub struct Resource<T> {
state: Arc<Mutex<ResourceState<T>>>
}
impl<T> Default for Resource<T> {
fn default() -> Self {
let state = Default::default();
Self { state }
}
}
#[derive(Clone, EnumAsInner)]
pub enum ResourceState<T> {
Unloaded,
Loading,
Unavailable,
Loaded(T)
}
impl<T> Default for ResourceState<T> {
fn default() -> Self {
Self::Unloaded
}
}
impl<T> Resource<T> {
pub fn new(future: impl Future<Output = Option<T>> + 'static) -> Self
where T: 'static
{
let state = Default::default();
let mut resource = Self { state };
resource.load(future);
resource
}
pub fn get(&self) -> Option<T>
where T: Clone
{
match self.state.try_lock().unwrap().clone() {
ResourceState::Loaded(value) => Some(value),
_ => None
}
}
pub fn clear(&mut self) {
*self.state.blocking_lock() = ResourceState::Unloaded;
}
pub fn is_unloaded(&self) -> bool {
self.state.blocking_lock().is_unloaded()
}
pub fn get_state(&self) -> MutexGuard<ResourceState<T>> {
self.state.blocking_lock()
}
pub fn load(&mut self, future: impl Future<Output = Option<T>> + 'static)
where T: 'static
{
let state = self.state.clone();
*state.blocking_lock() = ResourceState::Loading;
spawn(async move {
*state.lock().await = future
.await
.map(ResourceState::Loaded)
.unwrap_or(ResourceState::Unavailable);
});
}
pub fn load_and_notify<C>(&mut self, mut component: Signal<C>, future: impl Future<Output = Option<T>> + 'static)
where T: 'static
{
let state = self.state.clone();
*state.blocking_lock() = ResourceState::Loading;
spawn(async move {
*state.lock().await = future
.await
.map(ResourceState::Loaded)
.unwrap_or(ResourceState::Unavailable);
component.write();
});
}
pub fn as_loaded(&self) -> Option<T>
where T: Clone
{
self.state.blocking_lock().as_loaded().cloned()
}
}
pub trait ResourceTrait<Component> {
fn update_resource<Type>(&self, f: impl FnOnce(&Component) -> &Resource<Type>, future: impl Future<Output = Option<Type>> + 'static)
where Type: Clone + 'static;
fn acquire_resource<Type>(&self, f: impl FnMut(&Component) -> &Resource<Type>, future: impl Future<Output = Option<Type>> + 'static) -> ResourceState<Type>
where Type: Clone + 'static;
fn web_get<Type>(&self, f: impl FnMut(&Component) -> &Resource<Type>, url: String) -> ResourceState<Type>
where Type: Clone + 'static + serde::de::DeserializeOwned;
fn web_post<Type>(&self, f: impl FnMut(&Component) -> &Resource<Type>, url: String, body: impl serde::Serialize + 'static) -> ResourceState<Type>
where Type: Clone + 'static + serde::de::DeserializeOwned;
}
impl<Component> ResourceTrait<Component> for Signal<Component> {
fn update_resource<Type>(&self, f: impl FnOnce(&Component) -> &Resource<Type>, future: impl Future<Output = Option<Type>> + 'static)
where Type: Clone + 'static
{
let mut resource = f(&*self.read()).clone();
resource.load_and_notify(self.clone(), future);
}
fn acquire_resource<Type>(&self, mut f: impl FnMut(&Component) -> &Resource<Type>, future: impl Future<Output = Option<Type>> + 'static) -> ResourceState<Type>
where Type: Clone + 'static
{
let resource = f(&*self.read()).clone();
if resource.is_unloaded() {
self.update_resource(f, future);
}
let x = resource.get_state().clone();
x
}
fn web_get<Type>(&self, mut f: impl FnMut(&Component) -> &Resource<Type>, url: String) -> ResourceState<Type>
where Type: Clone + 'static + serde::de::DeserializeOwned
{
let resource = f(&*self.read()).clone();
if resource.is_unloaded() {
self.update_resource(f, async move {
reqwest::get(url)
.await
.ok()?
.json::<Type>()
.await
.ok()
});
}
let x = resource.get_state().clone();
x
}
fn web_post<Type>(&self, mut f: impl FnMut(&Component) -> &Resource<Type>, url: String, body: impl serde::Serialize + 'static) -> ResourceState<Type>
where Type: Clone + 'static + serde::de::DeserializeOwned
{
let resource = f(&*self.read()).clone();
if resource.is_unloaded() {
self.update_resource(f, async move {
reqwest::Client::new()
.post(url)
.json(&body)
.send()
.await
.ok()?
.json::<Type>()
.await
.ok()
});
}
let x = resource.get_state().clone();
x
}
}