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