1#![allow(unreachable_patterns)]
2
3use {
4 crate::traits::*,
5 js_sys::{Function, Reflect},
6 serde::{
7 de::{self, DeserializeOwned},
8 Deserialize, Serialize,
9 },
10 std::fmt::{Debug, Display},
11 wasm_bindgen::{JsCast, JsValue},
12};
13
14#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq)]
15#[serde(transparent)]
16pub struct DatasetData(pub serde_json::Value);
17impl DatasetData {
18 pub fn is_empty(&self) -> bool {
19 serde_json::to_value(self)
20 .unwrap()
21 .as_array()
22 .unwrap()
23 .is_empty()
24 }
25
26 pub fn from_single_point_array(iter: impl Iterator<Item = [NumberOrDateString; 1]>) -> Self {
27 DatasetData(serde_json::to_value(iter.collect::<Vec<_>>()).unwrap())
28 }
29
30 pub fn from_minmax_array(iter: impl Iterator<Item = [NumberOrDateString; 2]>) -> Self {
31 DatasetData(serde_json::to_value(iter.collect::<Vec<_>>()).unwrap())
32 }
33}
34impl PartialOrd for DatasetData {
35 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
36 Some(self.cmp(other))
37 }
38}
39impl Ord for DatasetData {
40 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
41 self.0.to_string().cmp(&other.0.to_string())
42 }
43}
44
45#[derive(Debug, Clone, Deserialize, Serialize, Default)]
46pub struct NoDatasets {}
47impl DatasetTrait for NoDatasets {
48 fn labels(self) -> Vec<NumberOrDateString> {
49 Vec::new()
50 }
51}
52
53#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
54#[serde(bound = "D: DatasetTrait")]
55#[allow(unreachable_patterns)]
56pub struct Dataset<D: DatasetTrait> {
57 datasets: D,
58 #[serde(skip_serializing_if = "Option::is_none", rename(serialize = "labels"))]
59 forced_labels: Option<Vec<NumberOrDateString>>,
60 #[serde(skip_serializing_if = "option_vec_is_none")]
61 labels: Option<Vec<NumberOrDateString>>,
62}
63impl<D: DatasetTrait> Dataset<D> {
64 pub fn new() -> Self {
65 Self {
66 datasets: D::default(),
67 labels: None,
68 forced_labels: None,
69 }
70 }
71
72 pub fn get_datasets(&mut self) -> &mut D {
73 &mut self.datasets
74 }
75
76 pub fn datasets(mut self, datasets: impl Into<D>) -> Self {
77 self.datasets = datasets.into();
78
79 if self.forced_labels.is_none() {
80 let labels = self.datasets.clone();
81 self._labels(labels.labels())
82 } else {
83 self
84 }
85 }
86
87 pub fn get_labels(&mut self) -> &mut Option<Vec<NumberOrDateString>> {
88 match (&self.labels, &self.forced_labels) {
89 (Some(_), None) => &mut self.labels,
90 _ => &mut self.forced_labels,
91 }
92 }
93
94 fn _labels<T: Into<NumberOrDateString>>(mut self, labels: impl IntoIterator<Item = T>) -> Self {
95 self.labels = Some(labels.into_iter().map(Into::into).collect());
96
97 self
98 }
99
100 pub fn labels<T: Into<NumberOrDateString>>(
101 mut self,
102 labels: impl IntoIterator<Item = T>,
103 ) -> Self {
104 self.forced_labels = Some(labels.into_iter().map(Into::into).collect());
105 self.labels = None;
106
107 self
108 }
109}
110fn option_vec_is_none<T: Default + PartialEq + Clone>(opt: &Option<Vec<T>>) -> bool {
111 match opt {
112 Some(vec) => vec.is_empty() || vec.clone().try_into() == Ok([T::default()]),
113 None => true,
114 }
115}
116
117#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
118#[serde(untagged)]
119pub enum Any {
120 Null(Option<()>),
121 String(String),
122 Int(isize),
123 Bool(bool),
124 Vec(Vec<()>),
125}
126impl From<bool> for Any {
127 fn from(value: bool) -> Self {
128 Self::Bool(value)
129 }
130}
131impl From<String> for Any {
132 fn from(value: String) -> Self {
133 Self::String(value)
134 }
135}
136impl Any {
137 pub fn is_empty(&self) -> bool {
138 match self {
139 Any::String(s) => s.is_empty(),
140 Any::Int(_i) => false,
141 Any::Bool(_b) => false,
142 Any::Vec(v) => v.is_empty(),
143 Any::Null(_) => true,
144 }
145 }
146}
147impl Display for Any {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 match self {
150 Any::String(s) => write!(f, "{s}"),
151 Any::Bool(b) => write!(f, "{b}"),
152 Any::Int(i) => write!(f, "{i}"),
153 Any::Vec(_) => write!(f, ""),
154 Any::Null(_) => write!(f, "null"),
155 }
156 }
157}
158#[derive(Debug, Clone, Default, PartialEq, Eq)]
159pub struct NumberOrDateString(String);
160impl From<NumberString> for NumberOrDateString {
161 fn from(value: NumberString) -> Self {
162 value.0.into()
163 }
164}
165impl NumberOrDateString {
166 pub fn is_empty(&self) -> bool {
167 self.0.is_empty()
168 }
169}
170impl PartialOrd for NumberOrDateString {
171 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
172 Some(self.cmp(other))
173 }
174}
175impl Ord for NumberOrDateString {
176 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
177 if let Some((s, o)) = self
178 .0
179 .parse::<rust_decimal::Decimal>()
180 .ok()
181 .zip(other.0.parse::<rust_decimal::Decimal>().ok())
182 {
183 s.cmp(&o)
184 } else {
185 self.0.cmp(&other.0)
186 }
187 }
188}
189impl<T: Display> From<T> for NumberOrDateString {
190 fn from(s: T) -> Self {
191 Self(s.to_string())
192 }
193}
194#[allow(unknown_lints, clippy::to_string_trait_impl)]
195impl ToString for NumberOrDateString {
196 fn to_string(&self) -> String {
197 self.0.to_string()
198 }
199}
200impl Serialize for NumberOrDateString {
201 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
202 where
203 S: serde::Serializer,
204 {
205 let fnum: Result<f64, _> = self.0.parse();
206 let inum: Result<i64, _> = self.0.parse();
207 if self.0.eq_ignore_ascii_case("null") {
208 return serializer.serialize_none();
209 }
210 match (fnum, inum) {
211 (Ok(_), Ok(inum)) => serializer.serialize_i64(inum),
212 (Ok(fnum), _) => serializer.serialize_f64(fnum),
213 _ => serializer.serialize_str(&self.0),
214 }
215 }
216}
217impl<'de> Deserialize<'de> for NumberOrDateString {
218 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
219 where
220 D: serde::Deserializer<'de>,
221 {
222 Any::deserialize(deserializer).map(|soi| Self(soi.to_string()))
223 }
224}
225
226#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
227pub struct BoolString(String);
228impl BoolString {
229 pub fn opt_true() -> Option<BoolString> {
230 BoolString("true".into()).into()
231 }
232 pub fn opt_false() -> Option<BoolString> {
233 BoolString("false".into()).into()
234 }
235 pub fn _true() -> BoolString {
236 BoolString("true".into())
237 }
238 pub fn _false() -> BoolString {
239 BoolString("false".into())
240 }
241 pub fn is_empty(&self) -> bool {
242 self.0.is_empty()
243 }
244}
245impl Default for BoolString {
246 fn default() -> Self {
247 Self::_false()
248 }
249}
250impl ChartJsRsObject for BoolString {
251 fn is_empty(&self) -> bool {
252 self.is_empty()
253 }
254}
255impl<T: Display> From<T> for BoolString {
256 fn from(s: T) -> Self {
257 Self(s.to_string())
258 }
259}
260impl Serialize for BoolString {
261 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
262 where
263 S: serde::Serializer,
264 {
265 let bool_: Result<bool, _> = self.0.parse();
266 let any: Result<String, _> = self.0.parse();
267 match (bool_, any) {
268 (Ok(bool_), _) => serializer.serialize_bool(bool_),
269 (_, Ok(any)) => serializer.serialize_str(&any),
270 _ => unreachable!(),
271 }
272 }
273}
274impl<'de> Deserialize<'de> for BoolString {
275 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
276 where
277 D: serde::Deserializer<'de>,
278 {
279 Any::deserialize(deserializer).map(|soi| Self(soi.to_string()))
280 }
281}
282
283#[derive(Debug, Deserialize, Serialize)]
284struct JavascriptFunction {
285 args: Vec<String>,
286 body: String,
287 return_value: String,
288 closure_id: Option<String>,
289}
290
291const ALPHABET: [&str; 32] = [
292 "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
293 "t", "u", "v", "w", "x", "y", "z", "aa", "bb", "cc", "dd", "ee", "ff",
294];
295
296#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
297pub struct FnWithArgs<const N: usize> {
298 pub(crate) args: [String; N],
299 pub(crate) body: String,
300 pub(crate) return_value: String,
301 pub(crate) closure_id: Option<String>,
302}
303impl<const N: usize> FnWithArgs<N> {
304 pub fn rationalise_1_level(obj: &JsValue, name: &'static str) {
305 super::rationalise_1_level::<N, Self>(obj, name, |o| {
306 let _ = Reflect::set(obj, &name.into(), &o.build());
307 })
308 }
309 pub fn rationalise_2_levels(obj: &JsValue, name: (&'static str, &'static str)) {
310 super::rationalise_2_levels::<N, Self>(obj, name, |a, o| {
311 let _ = Reflect::set(&a, &name.1.into(), &o.build());
312 })
313 }
314}
315
316impl<const N: usize> Default for FnWithArgs<N> {
317 fn default() -> Self {
318 Self {
319 args: (0..N)
320 .map(|idx| ALPHABET[idx].to_string())
321 .collect::<Vec<_>>()
322 .try_into()
323 .unwrap(),
324 body: Default::default(),
325 return_value: Default::default(),
326 closure_id: None,
327 }
328 }
329}
330impl<'de, const N: usize> Deserialize<'de> for FnWithArgs<N> {
331 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
332 where
333 D: serde::Deserializer<'de>,
334 {
335 let js = JavascriptFunction::deserialize(deserializer)?;
336 Ok(FnWithArgs::<N> {
337 args: js.args.clone().try_into().map_err(|_| {
338 de::Error::custom(format!("Array had length {}, needed {}.", js.args.len(), N))
339 })?,
340 body: js.body,
341 return_value: js.return_value,
342 closure_id: js.closure_id,
343 })
344 }
345}
346impl<const N: usize> Serialize for FnWithArgs<N> {
347 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
348 where
349 S: serde::Serializer,
350 {
351 JavascriptFunction::serialize(
352 &JavascriptFunction {
353 args: self.args.to_vec(),
354 body: self.body.clone(),
355 return_value: self.return_value.clone(),
356 closure_id: self.closure_id.clone(),
357 },
358 serializer,
359 )
360 }
361}
362
363impl<const N: usize> FnWithArgs<N> {
364 pub fn is_empty(&self) -> bool {
365 match self.closure_id {
366 Some(_) => false,
367 None => self.body.is_empty(),
368 }
369 }
370
371 pub fn new() -> Self {
372 Self::default()
373 }
374
375 pub fn args<S: AsRef<str>>(mut self, args: [S; N]) -> Self {
376 self.args = args
377 .into_iter()
378 .enumerate()
379 .map(|(idx, s)| {
380 let arg = s.as_ref();
381 if arg.is_empty() { ALPHABET[idx] } else { arg }.to_string()
382 })
383 .collect::<Vec<_>>()
384 .try_into()
385 .unwrap();
386 self
387 }
388
389 pub fn js_body(mut self, body: &str) -> Self {
390 self.body = format!("{}\n{body}", self.body);
391 self.to_owned()
392 }
393
394 pub fn js_return_value(self, return_value: &str) -> Self {
395 let mut s = if self.body.is_empty() {
396 self.js_body("")
397 } else {
398 self
399 };
400 s.return_value = return_value.to_string();
401 s.to_owned()
402 }
403
404 pub fn build(self) -> Function {
405 if let Some(id) = self.closure_id {
406 let args = self.args.join(", ");
407 Function::new_with_args(&args, &format!("{{ return window['{id}']({args}) }}"))
408 } else {
409 Function::new_with_args(
410 &self.args.join(", "),
411 &format!("{{ {}\nreturn {} }}", self.body, self.return_value),
412 )
413 }
414 }
415}
416
417impl FnWithArgs<1> {
418 pub fn run_rust_fn<A, B, FN: Fn(A) -> B>(mut self, _func: FN) -> Self {
419 let fn_name = std::any::type_name::<FN>()
420 .split("::")
421 .collect::<Vec<_>>()
422 .into_iter()
423 .next_back()
424 .unwrap();
425
426 self.body = format!(
427 "{}\nconst _out_ = window.callbacks.{}({});",
428 self.body,
429 fn_name,
430 self.args.join(", ")
431 );
432 self.js_return_value("_out_")
433 }
434
435 #[track_caller]
436 pub fn rust_closure<F: Fn(JsValue) -> JsValue + 'static>(mut self, closure: F) -> Self {
437 let js_closure = wasm_bindgen::closure::Closure::wrap(
438 Box::new(closure) as Box<dyn Fn(JsValue) -> JsValue>
439 );
440 let js_sys_fn: &js_sys::Function = js_closure.as_ref().unchecked_ref();
441
442 let js_window = gloo_utils::window();
443 let id = uuid::Uuid::new_v4().to_string();
444 Reflect::set(&js_window, &JsValue::from_str(&id), js_sys_fn).unwrap();
445 js_closure.forget();
446
447 gloo_console::debug!(format!(
448 "Closure at {}:{}:{} set at window.['{id}'].",
449 file!(),
450 line!(),
451 column!()
452 ));
453 self.closure_id = Some(id);
454 self
455 }
456}
457
458impl FnWithArgs<2> {
459 pub fn run_rust_fn<A, B, C, FN: Fn(A, B) -> C>(mut self, _func: FN) -> Self {
460 let fn_name = std::any::type_name::<FN>()
461 .split("::")
462 .collect::<Vec<_>>()
463 .into_iter()
464 .next_back()
465 .unwrap();
466
467 self.body = format!(
468 "{}\nconst _out_ = window.callbacks.{}({});",
469 self.body,
470 fn_name,
471 self.args.join(", ")
472 );
473 self.js_return_value("_out_")
474 }
475
476 #[track_caller]
477 pub fn rust_closure<F: Fn(JsValue, JsValue) -> JsValue + 'static>(
478 mut self,
479 closure: F,
480 ) -> Self {
481 let js_closure = wasm_bindgen::closure::Closure::wrap(
482 Box::new(closure) as Box<dyn Fn(JsValue, JsValue) -> JsValue>
483 );
484 let js_sys_fn: &js_sys::Function = js_closure.as_ref().unchecked_ref();
485
486 let js_window = gloo_utils::window();
487 let id = uuid::Uuid::new_v4().to_string();
488 Reflect::set(&js_window, &JsValue::from_str(&id), js_sys_fn).unwrap();
489 js_closure.forget();
490
491 gloo_console::debug!(format!(
492 "Closure at {}:{}:{} set at window.['{id}'].",
493 file!(),
494 line!(),
495 column!()
496 ));
497 self.closure_id = Some(id);
498 self
499 }
500}
501
502impl FnWithArgs<3> {
503 pub fn run_rust_fn<A, B, C, D, FN: Fn(A, B, C) -> D>(mut self, _func: FN) -> Self {
504 let fn_name = std::any::type_name::<FN>()
505 .split("::")
506 .collect::<Vec<_>>()
507 .into_iter()
508 .next_back()
509 .unwrap();
510
511 self.body = format!(
512 "{}\nconst _out_ = window.callbacks.{}({});",
513 self.body,
514 fn_name,
515 self.args.join(", ")
516 );
517 self.js_return_value("_out_")
518 }
519
520 #[track_caller]
521 pub fn rust_closure<F: Fn(JsValue, JsValue, JsValue) -> JsValue + 'static>(
522 mut self,
523 closure: F,
524 ) -> Self {
525 let js_closure = wasm_bindgen::closure::Closure::wrap(
526 Box::new(closure) as Box<dyn Fn(JsValue, JsValue, JsValue) -> JsValue>
527 );
528 let js_sys_fn: &js_sys::Function = js_closure.as_ref().unchecked_ref();
529
530 let js_window = gloo_utils::window();
531 let id = uuid::Uuid::new_v4().to_string();
532 Reflect::set(&js_window, &JsValue::from_str(&id), js_sys_fn).unwrap();
533 js_closure.forget();
534
535 gloo_console::debug!(format!(
536 "Closure at {}:{}:{} set at window.['{id}'].",
537 file!(),
538 line!(),
539 column!()
540 ));
541 self.closure_id = Some(id);
542 self
543 }
544}
545
546impl FnWithArgs<4> {
547 pub fn run_rust_fn<A, B, C, D, E, FN: Fn(A, B, C, D) -> E>(mut self, _func: FN) -> Self {
548 let fn_name = std::any::type_name::<FN>()
549 .split("::")
550 .collect::<Vec<_>>()
551 .into_iter()
552 .next_back()
553 .unwrap();
554
555 self.body = format!(
556 "{}\nconst _out_ = window.callbacks.{}({});",
557 self.body,
558 fn_name,
559 self.args.join(", ")
560 );
561 self.js_return_value("_out_")
562 }
563
564 #[track_caller]
565 pub fn rust_closure<F: Fn(JsValue, JsValue, JsValue, JsValue) -> JsValue + 'static>(
566 mut self,
567 closure: F,
568 ) -> Self {
569 let js_closure = wasm_bindgen::closure::Closure::wrap(
570 Box::new(closure) as Box<dyn Fn(JsValue, JsValue, JsValue, JsValue) -> JsValue>
571 );
572 let js_sys_fn: &js_sys::Function = js_closure.as_ref().unchecked_ref();
573
574 let js_window = gloo_utils::window();
575 let id = uuid::Uuid::new_v4().to_string();
576 Reflect::set(&js_window, &JsValue::from_str(&id), js_sys_fn).unwrap();
577 js_closure.forget();
578
579 gloo_console::debug!(format!(
580 "Closure at {}:{}:{} set at window.['{id}'].",
581 file!(),
582 line!(),
583 column!()
584 ));
585 self.closure_id = Some(id);
586 self
587 }
588}
589
590impl FnWithArgs<5> {
591 pub fn run_rust_fn<A, B, C, D, E, F, FN: Fn(A, B, C, D, E) -> F>(mut self, _func: FN) -> Self {
592 let fn_name = std::any::type_name::<FN>()
593 .split("::")
594 .collect::<Vec<_>>()
595 .into_iter()
596 .next_back()
597 .unwrap();
598
599 self.body = format!(
600 "{}\nconst _out_ = window.callbacks.{}({});",
601 self.body,
602 fn_name,
603 self.args.join(", ")
604 );
605 self.js_return_value("_out_")
606 }
607
608 #[track_caller]
609 pub fn rust_closure<F: Fn(JsValue, JsValue, JsValue, JsValue, JsValue) -> JsValue + 'static>(
610 mut self,
611 closure: F,
612 ) -> Self {
613 let js_closure = wasm_bindgen::closure::Closure::wrap(Box::new(closure)
614 as Box<dyn Fn(JsValue, JsValue, JsValue, JsValue, JsValue) -> JsValue>);
615 let js_sys_fn: &js_sys::Function = js_closure.as_ref().unchecked_ref();
616
617 let js_window = gloo_utils::window();
618 let id = uuid::Uuid::new_v4().to_string();
619 Reflect::set(&js_window, &JsValue::from_str(&id), js_sys_fn).unwrap();
620 js_closure.forget();
621
622 gloo_console::debug!(format!(
623 "Closure at {}:{}:{} set at window.['{id}'].",
624 file!(),
625 line!(),
626 column!()
627 ));
628 self.closure_id = Some(id);
629 self
630 }
631}
632
633impl FnWithArgs<6> {
634 pub fn run_rust_fn<A, B, C, D, E, F, G, FN: Fn(A, B, C, D, E, F) -> G>(
635 mut self,
636 _func: FN,
637 ) -> Self {
638 let fn_name = std::any::type_name::<FN>()
639 .split("::")
640 .collect::<Vec<_>>()
641 .into_iter()
642 .next_back()
643 .unwrap();
644
645 self.body = format!(
646 "{}\nconst _out_ = window.callbacks.{}({});",
647 self.body,
648 fn_name,
649 self.args.join(", ")
650 );
651 self.js_return_value("_out_")
652 }
653
654 #[track_caller]
655 pub fn rust_closure<
656 F: Fn(JsValue, JsValue, JsValue, JsValue, JsValue, JsValue) -> JsValue + 'static,
657 >(
658 mut self,
659 closure: F,
660 ) -> Self {
661 let js_closure = wasm_bindgen::closure::Closure::wrap(Box::new(closure)
662 as Box<dyn Fn(JsValue, JsValue, JsValue, JsValue, JsValue, JsValue) -> JsValue>);
663 let js_sys_fn: &js_sys::Function = js_closure.as_ref().unchecked_ref();
664
665 let js_window = gloo_utils::window();
666 let id = uuid::Uuid::new_v4().to_string();
667 Reflect::set(&js_window, &JsValue::from_str(&id), js_sys_fn).unwrap();
668 js_closure.forget();
669
670 gloo_console::debug!(format!(
671 "Closure at {}:{}:{} set at window.['{id}'].",
672 file!(),
673 line!(),
674 column!()
675 ));
676 self.closure_id = Some(id);
677 self
678 }
679}
680
681impl FnWithArgs<7> {
683 pub fn run_rust_fn<A, B, C, D, E, F, G, H, FN: Fn(A, B, C, D, E, F, G) -> H>(
684 mut self,
685 _func: FN,
686 ) -> Self {
687 let fn_name = std::any::type_name::<FN>()
688 .split("::")
689 .collect::<Vec<_>>()
690 .into_iter()
691 .next_back()
692 .unwrap();
693
694 self.body = format!(
695 "{}\nconst _out_ = window.callbacks.{}({});",
696 self.body,
697 fn_name,
698 self.args.join(", ")
699 );
700 self.js_return_value("_out_")
701 }
702
703 #[track_caller]
704 pub fn rust_closure<
705 F: Fn(JsValue, JsValue, JsValue, JsValue, JsValue, JsValue, JsValue) -> JsValue + 'static,
706 >(
707 mut self,
708 closure: F,
709 ) -> Self {
710 let js_closure = wasm_bindgen::closure::Closure::wrap(Box::new(closure)
711 as Box<
712 dyn Fn(JsValue, JsValue, JsValue, JsValue, JsValue, JsValue, JsValue) -> JsValue,
713 >);
714 let js_sys_fn: &js_sys::Function = js_closure.as_ref().unchecked_ref();
715
716 let js_window = gloo_utils::window();
717 let id = uuid::Uuid::new_v4().to_string();
718 Reflect::set(&js_window, &JsValue::from_str(&id), js_sys_fn).unwrap();
719 js_closure.forget();
720
721 gloo_console::debug!(format!(
722 "Closure at {}:{}:{} set at window.['{id}'].",
723 file!(),
724 line!(),
725 column!()
726 ));
727 self.closure_id = Some(id);
728 self
729 }
730}
731
732#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
733#[serde(untagged)]
734pub enum FnWithArgsOrT<const N: usize, T> {
735 T(T),
736 FnWithArgs(FnWithArgs<N>),
737}
738
739impl<const N: usize, T: for<'a> Deserialize<'a>> FnWithArgsOrT<N, T> {
740 pub fn rationalise_1_level(obj: &JsValue, name: &'static str) {
741 super::rationalise_1_level::<N, Self>(obj, name, |o| match o {
742 FnWithArgsOrT::T(_) => (),
743 FnWithArgsOrT::FnWithArgs(fnwa) => {
744 let _ = Reflect::set(obj, &name.into(), &fnwa.build());
745 }
746 })
747 }
748 pub fn rationalise_2_levels(obj: &JsValue, name: (&'static str, &'static str)) {
749 super::rationalise_2_levels::<N, Self>(obj, name, |a, o| match o {
750 FnWithArgsOrT::T(_) => (),
751 FnWithArgsOrT::FnWithArgs(fnwa) => {
752 let _ = Reflect::set(&a, &name.1.into(), &fnwa.build());
753 }
754 })
755 }
756}
757#[allow(private_bounds)]
758impl<const N: usize, T: ChartJsRsObject> FnWithArgsOrT<N, T> {
759 pub fn is_empty(&self) -> bool {
760 match self {
761 FnWithArgsOrT::T(a) => a.is_empty(),
762 FnWithArgsOrT::FnWithArgs(fnwa) => fnwa.is_empty(),
763 }
764 }
765}
766impl<const N: usize, T: Default> Default for FnWithArgsOrT<N, T> {
767 fn default() -> Self {
768 FnWithArgsOrT::T(T::default())
769 }
770}
771impl<const N: usize, T: Into<String>> From<T> for FnWithArgsOrT<N, String> {
772 fn from(s: T) -> Self {
773 Self::T(s.into())
774 }
775}
776impl<const N: usize, T: Into<NumberString>> From<T> for FnWithArgsOrT<N, NumberString> {
777 fn from(ns: T) -> Self {
778 Self::T(ns.into())
779 }
780}
781impl<const N: usize, T: Into<BoolString>> From<T> for FnWithArgsOrT<N, BoolString> {
782 fn from(bs: T) -> Self {
783 Self::T(bs.into())
784 }
785}
786impl<const N: usize, T> From<FnWithArgs<N>> for FnWithArgsOrT<N, T> {
787 fn from(value: FnWithArgs<N>) -> Self {
788 Self::FnWithArgs(value)
789 }
790}
791
792#[derive(Debug, Clone, Default, PartialEq, Eq)]
793pub struct NumberString(String);
794impl From<NumberOrDateString> for NumberString {
795 fn from(value: NumberOrDateString) -> Self {
796 value.0.into()
797 }
798}
799impl NumberString {
800 pub fn is_empty(&self) -> bool {
801 self.0.is_empty()
802 }
803}
804impl ChartJsRsObject for NumberString {
805 fn is_empty(&self) -> bool {
806 self.is_empty()
807 }
808}
809impl PartialOrd for NumberString {
810 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
811 Some(self.cmp(other))
812 }
813}
814impl Ord for NumberString {
815 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
816 if let Some((s, o)) = self
817 .0
818 .parse::<rust_decimal::Decimal>()
819 .ok()
820 .zip(other.0.parse::<rust_decimal::Decimal>().ok())
821 {
822 s.cmp(&o)
823 } else {
824 self.0.cmp(&other.0)
825 }
826 }
827}
828impl<T: Display> From<T> for NumberString {
829 fn from(s: T) -> Self {
830 Self(s.to_string())
831 }
832}
833#[allow(clippy::to_string_trait_impl)]
834impl ToString for NumberString {
835 fn to_string(&self) -> String {
836 self.0.to_string()
837 }
838}
839impl Serialize for NumberString {
840 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
841 where
842 S: serde::Serializer,
843 {
844 let fnum: Result<f64, _> = self.0.parse();
845 let inum: Result<i64, _> = self.0.parse();
846 if self.0.eq_ignore_ascii_case("null") {
847 return serializer.serialize_none();
848 }
849 match (fnum, inum) {
850 (Ok(_), Ok(inum)) => serializer.serialize_i64(inum),
851 (Ok(fnum), _) => serializer.serialize_f64(fnum),
852 _ => serializer.serialize_str(&self.0),
853 }
854 }
855}
856impl<'de> Deserialize<'de> for NumberString {
857 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
858 where
859 D: serde::Deserializer<'de>,
860 {
861 Any::deserialize(deserializer).map(|soi| Self(soi.to_string()))
862 }
863}
864
865#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
866#[serde(untagged)]
867pub enum NumberStringOrT<T: Serialize + DeserializeOwned> {
868 T(T),
869 NumberString(NumberString),
870}
871impl<'de, T: Serialize + DeserializeOwned> Deserialize<'de> for NumberStringOrT<T> {
872 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
873 where
874 D: de::Deserializer<'de>,
875 {
876 let value = serde_json::Value::deserialize(deserializer)?;
877
878 match serde_json::from_value::<NumberString>(value.clone()) {
879 Ok(ns) => Ok(Self::NumberString(ns)),
880 Err(_) => serde_json::from_value::<T>(value)
881 .map(Self::T)
882 .map_err(de::Error::custom),
883 }
884 }
885}
886impl<T: Serialize + DeserializeOwned> NumberStringOrT<T> {
887 pub fn is_empty(&self) -> bool {
888 match self {
889 NumberStringOrT::T(_t) => false,
890 NumberStringOrT::NumberString(ns) => ns.is_empty(),
891 }
892 }
893}
894
895impl<T: Serialize + ChartJsRsObject, U: Serialize + DeserializeOwned> From<T>
896 for NumberStringOrT<U>
897{
898 fn from(value: T) -> Self {
899 serde_json::from_value(serde_json::to_value(value).unwrap()).unwrap()
900 }
901}