1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![recursion_limit = "256"]
3
4pub mod components;
5pub mod mathml;
6
7use std::borrow::Cow;
8
9use flams_utils::{hashstr, CSS};
10
11#[cfg(feature = "ssr")]
12#[derive(Default, Clone)]
13pub struct CssIds(
14 flams_utils::triomphe::Arc<
15 flams_utils::parking_lot::Mutex<flams_utils::vecmap::VecSet<Cow<'static, str>>>,
16 >,
17);
18
19#[cfg(feature = "ssr")]
20pub async fn blocking_server_fn<T: Send + 'static>(
22 f: impl FnOnce() -> Result<T, String> + Send + 'static,
23) -> Result<T, leptos::prelude::ServerFnError<String>> {
24 tokio::task::spawn_blocking(f)
25 .await
26 .map_err(|e| e.to_string())?
27 .map_err(Into::into)
28}
29
30pub fn do_css(css: CSS) {
31 match css {
32 CSS::Inline(s) => {
33 let id = hashstr("id_", &s);
34 #[cfg(not(target_family = "wasm"))]
35 let s = String::from(s);
36 do_inject_css(id.into(), s.into());
37 }
38 CSS::Class { name, css } => {
39 #[cfg(not(target_family = "wasm"))]
40 let name = String::from(name);
41 #[cfg(not(target_family = "wasm"))]
42 let css = String::from(css);
43 do_inject_css(name.into(), css.into());
44 }
45 CSS::Link(s) => {
46 let id = hashstr("id_", &s);
47 #[cfg(feature = "ssr")]
48 {
49 use leptos::prelude::expect_context;
50 use leptos_meta::Stylesheet;
51 let ids = expect_context::<CssIds>();
52 let mut ids = ids.0.lock();
53 if !ids.0.contains(&std::borrow::Cow::Borrowed(&id)) {
54 ids.insert(id.clone().into());
55 let _ = leptos::view! {
56 <Stylesheet id=id href=s.to_string()/>
57 };
58 }
59 drop(ids);
60 }
61 #[cfg(all(any(feature = "hydrate", feature = "csr"), not(feature = "ssr")))]
62 {
63 use leptos::prelude::document;
64 let Some(head) = document().head() else {
65 leptos::logging::log!("ERROR: head does not exist");
66 return;
67 };
68 match head.query_selector(&format!("link#{id}")) {
69 Ok(Some(_)) => return,
70 Err(e) => {
71 leptos::logging::log!("ERROR: query link element error: {e:?}");
72 return;
73 }
74 Ok(None) => (),
75 };
76 let Ok(style) = document().create_element("link") else {
77 leptos::logging::log!("ERROR: error creating style element");
78 return;
79 };
80 _ = style.set_attribute("id", &id);
81 _ = style.set_attribute("rel", "stylesheet");
82 _ = style.set_attribute("href", &s);
83 _ = head.prepend_with_node_1(&style);
84 }
85 }
86 }
87}
88
89#[inline]
90pub fn inject_css(id: &'static str, content: &'static str) {
91 do_inject_css(Cow::Borrowed(id), Cow::Borrowed(content));
92}
93
94#[macro_export]
95macro_rules! console_log {
96 () => {};
97 ($arg:expr) => {
98 ::web_sys::console::log_1(&::web_sys::js_sys::JsValue::from($l))
99 };
100 ($arg1:expr,$arg2:expr) => {
101 ::web_sys::console::log_2(
102 &::web_sys::js_sys::JsValue::from($l),
103 &::web_sys::js_sys::JsValue::from($l),
104 )
105 };
106}
107
108#[allow(clippy::missing_const_for_fn)]
109#[allow(clippy::needless_pass_by_value)]
110fn do_inject_css(id: Cow<'static, str>, content: Cow<'static, str>) {
111 #[cfg(feature = "ssr")]
112 {
113 use leptos_meta::Style;
114
115 use leptos::prelude::expect_context;
116 let ids = expect_context::<CssIds>();
117 let mut ids = ids.0.lock();
118 if !ids.0.contains(&id) {
119 ids.insert(id.clone());
120 let _ = leptos::view! {
121 <Style id=id>
122 {content}
123 </Style>
124 };
125 }
126 drop(ids);
127 }
128 #[cfg(not(feature = "ssr"))]
129 {
130 use leptos::prelude::document;
131 let Some(head) = document().head() else {
132 leptos::logging::log!("ERROR: head does not exist");
133 return;
134 };
135 let Ok(style) = head.query_selector(&format!("style#{id}")) else {
136 leptos::logging::log!("ERROR: query style element error");
137 return;
138 };
139 if style.is_some() {
140 return;
141 }
142
143 let Ok(style) = document().create_element("style") else {
144 leptos::logging::log!("ERROR: error creating style element");
145 return;
146 };
147 _ = style.set_attribute("id", &id);
148 style.set_text_content(Some(&content));
149 _ = head.prepend_with_node_1(&style);
150 }
151}
152
153pub fn try_catch<R>(run: impl FnOnce() -> R) -> Result<R, leptos::wasm_bindgen::JsError> {
155 std::panic::catch_unwind(std::panic::AssertUnwindSafe(run)).map_err(|e| {
156 if let Some(s) = e.downcast_ref::<&str>() {
157 return leptos::wasm_bindgen::JsError::new(*s);
158 }
159 if let Some(s) = e.downcast_ref::<String>() {
160 return leptos::wasm_bindgen::JsError::new(s);
161 }
162 leptos::wasm_bindgen::JsError::new("Box<dyn Error>")
163 })
164}
165
166#[cfg(feature = "ssr")]
167pub use http;
168#[cfg(feature = "ssr")]
169pub use leptos_axum;
170
171#[cfg(feature = "ssr")]
172#[macro_export]
173macro_rules! not_found{
174 (! $($e:tt)*) => { {
175 let response = expect_context::<$crate::leptos_axum::ResponseOptions>();
176 response.set_status($crate::http::StatusCode::NOT_FOUND);
177 format!($($e)*).into()
178 }};
179 ($($e:tt)*) => { return Err(not_found!(! $($e)*))};
180}