web_component/resource/
mod.rs

1use std::{future::Future, sync::Arc};
2use tokio::sync::{Mutex, MutexGuard};
3
4use dioxus::{prelude::spawn, signals::{Readable, Signal, Writable}};
5use enum_as_inner::EnumAsInner;
6
7#[derive(Clone)]
8pub struct Resource<T> {
9    state: Arc<Mutex<ResourceState<T>>>
10}
11
12impl<T> Default for Resource<T> {
13    fn default() -> Self {
14        let state = Default::default();
15        Self { state }
16    }
17}
18
19#[derive(Clone, EnumAsInner)]
20pub enum ResourceState<T> {
21    Unloaded,
22    Loading,
23    Unavailable,
24    Loaded(T)
25}
26
27impl<T> Default for ResourceState<T> {
28    fn default() -> Self {
29        Self::Unloaded
30    }
31}
32
33impl<T> Resource<T> {
34    pub fn new(future: impl Future<Output = Option<T>> + 'static) -> Self
35    where T: 'static
36    {
37        let state = Default::default();
38        let mut resource = Self { state };
39        resource.load(future);
40        resource
41    }
42
43    pub fn get(&self) -> Option<T>
44    where T: Clone
45    {
46        match self.state.try_lock().unwrap().clone() {
47            ResourceState::Loaded(value) => Some(value),
48            _ => None
49        }
50    }
51
52    pub fn clear(&mut self) {
53        *self.state.blocking_lock() = ResourceState::Unloaded;
54    }
55
56    pub fn is_unloaded(&self) -> bool {
57        self.state.blocking_lock().is_unloaded()
58    }
59
60    pub fn get_state(&self) -> MutexGuard<ResourceState<T>> {
61        self.state.blocking_lock()
62    }
63
64    pub fn load(&mut self, future: impl Future<Output = Option<T>> + 'static)
65    where T: 'static
66    {
67        let state = self.state.clone();
68        *state.blocking_lock() = ResourceState::Loading;
69        spawn(async move {
70            *state.lock().await = future
71                .await
72                .map(ResourceState::Loaded)
73                .unwrap_or(ResourceState::Unavailable);
74        });
75    }
76
77    pub fn load_and_notify<C>(&mut self, mut component: Signal<C>, future: impl Future<Output = Option<T>> + 'static)
78    where T: 'static
79    {
80        let state = self.state.clone();
81        *state.blocking_lock() = ResourceState::Loading;
82        spawn(async move {
83            *state.lock().await = future
84                .await
85                .map(ResourceState::Loaded)
86                .unwrap_or(ResourceState::Unavailable);
87            component.write();
88        });
89    }
90
91    pub fn as_loaded(&self) -> Option<T>
92    where T: Clone
93    {
94        self.state.blocking_lock().as_loaded().cloned()
95    }
96}
97
98pub trait ResourceTrait<Component> {
99    fn update_resource<Type>(&self, f: impl FnOnce(&Component) -> &Resource<Type>, future: impl Future<Output = Option<Type>> + 'static)
100    where Type: Clone + 'static;
101
102    fn acquire_resource<Type>(&self, f: impl FnMut(&Component) -> &Resource<Type>, future: impl Future<Output = Option<Type>> + 'static) -> ResourceState<Type>
103    where Type: Clone + 'static;
104
105    fn web_get<Type>(&self, f: impl FnMut(&Component) -> &Resource<Type>, url: String) -> ResourceState<Type>
106    where Type: Clone + 'static + serde::de::DeserializeOwned;
107
108    fn web_post<Type>(&self, f: impl FnMut(&Component) -> &Resource<Type>, url: String, body: impl serde::Serialize + 'static) -> ResourceState<Type>
109    where Type: Clone + 'static + serde::de::DeserializeOwned;
110}
111
112impl<Component> ResourceTrait<Component> for Signal<Component> {
113    fn update_resource<Type>(&self, f: impl FnOnce(&Component) -> &Resource<Type>, future: impl Future<Output = Option<Type>> + 'static)
114    where Type: Clone + 'static
115    {
116        let mut resource = f(&*self.read()).clone();
117        resource.load_and_notify(self.clone(), future);
118    }
119
120    fn acquire_resource<Type>(&self, mut f: impl FnMut(&Component) -> &Resource<Type>, future: impl Future<Output = Option<Type>> + 'static) -> ResourceState<Type>
121        where Type: Clone + 'static
122    {
123        let resource = f(&*self.read()).clone();
124        if resource.is_unloaded() {
125            self.update_resource(f, future);
126        }
127        let x = resource.get_state().clone();
128        x
129    }
130
131    fn web_get<Type>(&self, mut f: impl FnMut(&Component) -> &Resource<Type>, url: String) -> ResourceState<Type>
132    where Type: Clone + 'static + serde::de::DeserializeOwned
133    {
134        let resource = f(&*self.read()).clone();
135        if resource.is_unloaded() {
136            self.update_resource(f, async move {
137                reqwest::get(url)
138                    .await
139                    .ok()?
140                    .json::<Type>()
141                    .await
142                    .ok()
143            });
144        }
145        let x = resource.get_state().clone();
146        x
147        }
148
149    fn web_post<Type>(&self, mut f: impl FnMut(&Component) -> &Resource<Type>, url: String, body: impl serde::Serialize + 'static) -> ResourceState<Type>
150    where Type: Clone + 'static + serde::de::DeserializeOwned
151    {
152        let resource = f(&*self.read()).clone();
153        if resource.is_unloaded() {
154            self.update_resource(f, async move {
155                reqwest::Client::new()
156                    .post(url)
157                    .json(&body)
158                    .send()
159                    .await
160                    .ok()?
161                    .json::<Type>()
162                    .await
163                    .ok()
164            });
165        }
166        let x = resource.get_state().clone();
167        x
168    }
169}