web_component/resource/
mod.rs1use 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}