1use crate::{
2 css::css_manager::CssManager,
3 fetch::request_builder::{RequestBody, RequestBuilder},
4 Context, Css, Dependencies, DropResource, FutureBox, Instant, JsJson, WebsocketMessage,
5};
6use std::cell::RefCell;
7use std::{future::Future, pin::Pin, rc::Rc};
8
9use crate::driver_module::dom::DriverDom;
10use crate::{driver_module::api::ApiImport, driver_module::utils::futures_spawn::spawn_local};
11
12use super::api::DomAccess;
13
14#[derive(Debug, Clone, Copy)]
15pub enum FetchMethod {
16 GET,
17 HEAD,
18 POST,
19 PUT,
20 DELETE,
21 CONNECT,
22 OPTIONS,
23 TRACE,
24 PATCH,
25}
26
27impl FetchMethod {
28 pub fn to_str(&self) -> String {
29 match self {
30 Self::GET => "GET",
31 Self::HEAD => "HEAD",
32 Self::POST => "POST",
33 Self::PUT => "PUT",
34 Self::DELETE => "DELETE",
35 Self::CONNECT => "CONNECT",
36 Self::OPTIONS => "OPTIONS",
37 Self::TRACE => "TRACE",
38 Self::PATCH => "PATCH",
39 }
40 .into()
41 }
42}
43
44type Executable = dyn Fn(Pin<Box<dyn Future<Output = ()> + 'static>>);
45type PlainHandler = dyn Fn(&str) -> Option<String>;
46
47pub struct DriverInner {
48 pub(crate) api: ApiImport,
49 pub(crate) dependencies: &'static Dependencies,
50 pub(crate) css_manager: CssManager,
51 pub(crate) dom: &'static DriverDom,
52 spawn_executor: Rc<Executable>,
53 _subscribe: DropResource,
54 _plains_handler: RefCell<Option<Rc<PlainHandler>>>,
55}
56
57impl DriverInner {
58 pub fn new() -> &'static Self {
59 let dependencies: &'static Dependencies = Box::leak(Box::default());
60
61 let api = ApiImport::default();
62
63 let spawn_executor = {
64 let api = api.clone();
65
66 Rc::new(move |fut: Pin<Box<dyn Future<Output = ()> + 'static>>| {
67 spawn_local(api.clone(), fut);
68 })
69 };
70
71 let dom = DriverDom::new(&api);
72 let css_manager = {
73 let driver_dom = dom;
74 CssManager::new(move |selector: &str, value: &str| {
75 driver_dom.insert_css(selector, value);
76 })
77 };
78
79 let subscribe = dependencies.hooks.on_after_transaction(move || {
80 dom.flush_dom_changes();
81 });
82
83 Box::leak(Box::new(DriverInner {
84 api,
85 dependencies,
86 css_manager,
87 dom,
88 spawn_executor,
89 _subscribe: subscribe,
90 _plains_handler: RefCell::new(None),
91 }))
92 }
93}
94
95pub type FetchResult = Result<(u32, RequestBody), String>;
101
102#[derive(Clone, Copy)]
104pub struct Driver {
105 pub(crate) inner: &'static DriverInner,
106}
107
108impl Default for Driver {
109 fn default() -> Self {
110 let driver = DriverInner::new();
111
112 Driver { inner: driver }
113 }
114}
115
116impl Driver {
117 pub fn cookie_get(&self, cname: &str) -> String {
119 self.inner.api.cookie_get(cname)
120 }
121
122 pub fn cookie_get_json(&self, cname: &str) -> JsJson {
124 self.inner.api.cookie_get_json(cname)
125 }
126
127 pub fn cookie_set(&self, cname: &str, cvalue: &str, expires_in: u64) {
129 self.inner.api.cookie_set(cname, cvalue, expires_in)
130 }
131
132 pub fn cookie_set_json(&self, cname: &str, cvalue: JsJson, expires_in: u64) {
134 self.inner.api.cookie_set_json(cname, cvalue, expires_in)
135 }
136
137 pub fn history_back(&self) {
139 self.inner.api.history_back();
140 }
141
142 pub fn history_replace(&self, new_url: &str) {
144 self.inner.api.replace_history_location(new_url)
145 }
146
147 #[must_use]
149 pub fn set_interval(&self, time: u32, func: impl Fn() + 'static) -> DropResource {
150 self.inner.api.interval_set(time, func)
151 }
152
153 pub fn now(&self) -> Instant {
155 Instant::now(self.inner.api.clone())
156 }
157
158 pub fn utc_now(&self) -> i64 {
160 self.inner.api.utc_now()
161 }
162
163 pub fn timezone_offset(&self) -> i32 {
167 self.inner.api.timezone_offset()
168 }
169
170 #[must_use]
172 pub fn request_get(&self, url: impl Into<String>) -> RequestBuilder {
173 RequestBuilder::get(url)
174 }
175
176 #[must_use]
178 pub fn request_post(&self, url: impl Into<String>) -> RequestBuilder {
179 RequestBuilder::post(url)
180 }
181
182 #[must_use]
183 pub fn sleep(&self, time: u32) -> FutureBox<()> {
184 let (sender, future) = FutureBox::new();
185 self.inner.api.set_timeout_and_detach(time, move || {
186 sender.publish(());
187 });
188
189 future
190 }
191
192 pub fn get_random(&self, min: u32, max: u32) -> u32 {
193 self.inner.api.get_random(min, max)
194 }
195
196 pub fn get_random_from<K: Clone>(&self, list: &[K]) -> Option<K> {
197 let len = list.len();
198
199 if len < 1 {
200 return None;
201 }
202
203 let max_index = len - 1;
204
205 let index = self.get_random(0, max_index as u32);
206 Some(list[index as usize].clone())
207 }
208
209 #[must_use]
211 pub fn websocket<F: Fn(WebsocketMessage) + 'static>(
212 &self,
213 host: impl Into<String>,
214 callback: F,
215 ) -> DropResource {
216 self.inner.api.websocket(host, callback)
217 }
218
219 pub fn spawn(&self, future: impl Future<Output = ()> + 'static) {
221 let future = Box::pin(future);
222 let spawn_executor = self.inner.spawn_executor.clone();
223 spawn_executor(future);
224 }
225
226 pub fn transaction<R, F: FnOnce(&Context) -> R>(&self, func: F) -> R {
229 self.inner.dependencies.transaction(func)
230 }
231
232 pub fn dom_access(&self) -> DomAccess {
233 self.inner.api.dom_access()
234 }
235
236 pub fn on_after_transaction(&self, callback: impl Fn() + 'static) -> DropResource {
238 self.inner.dependencies.hooks.on_after_transaction(callback)
239 }
240
241 pub fn is_browser(&self) -> bool {
253 self.inner.api.is_browser()
254 }
255
256 pub fn is_server(&self) -> bool {
257 !self.is_browser()
258 }
259
260 pub fn env(&self, name: impl Into<String>) -> Option<String> {
261 let name = name.into();
262 self.inner.api.get_env(name)
263 }
264
265 pub fn plains(&mut self, callback: impl Fn(&str) -> Option<String> + 'static) {
281 let mut mut_plains = self.inner._plains_handler.borrow_mut();
282 *mut_plains = Some(Rc::new(callback));
283 }
284
285 pub fn try_get_plain(&self) {
286 if self.is_server() {
287 let url = self.inner.api.get_history_location();
288 match self.inner._plains_handler.try_borrow() {
289 Ok(callback_ref) => {
290 if let Some(callback) = callback_ref.as_deref() {
291 if let Some(body) = callback(&url) {
292 self.inner.api.plain_response(body)
293 }
294 }
295 }
296 Err(err) => log::error!("Error invoking plains: {err}"),
297 }
298 } else {
299 log::info!("Browser mode, not invoking try_get_plain");
300 }
301 }
302
303 pub fn set_status(&self, status: u16) {
311 if self.is_server() {
312 self.inner.api.set_status(status);
313 }
314 }
315
316 pub fn class_name_for(&mut self, css: &Css) -> String {
320 self.inner.css_manager.get_class_name(css)
321 }
322}