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(
414 &args,
415 &format!("{{ const f = window['{id}']; return f ? f({args}) : undefined; }}"),
416 )
417 } else {
418 Function::new_with_args(
419 &self.args.join(", "),
420 &format!("{{ {}\nreturn {} }}", self.body, self.return_value),
421 )
422 }
423 }
424}
425
426impl FnWithArgs<1> {
427 pub fn run_rust_fn<A, B, FN: Fn(A) -> B>(mut self, _func: FN) -> Self {
428 let fn_name = std::any::type_name::<FN>()
429 .split("::")
430 .collect::<Vec<_>>()
431 .into_iter()
432 .next_back()
433 .unwrap();
434
435 self.body = format!(
436 "{}\nconst _out_ = window.callbacks.{}({});",
437 self.body,
438 fn_name,
439 self.args.join(", ")
440 );
441 self.js_return_value("_out_")
442 }
443
444 #[track_caller]
445 pub fn rust_closure<F: Fn(JsValue) -> JsValue + 'static>(mut self, closure: F) -> Self {
446 let js_closure = wasm_bindgen::closure::Closure::wrap(
447 Box::new(closure) as Box<dyn Fn(JsValue) -> JsValue>
448 );
449 let js_sys_fn: &js_sys::Function = js_closure.as_ref().unchecked_ref();
450
451 let js_window = gloo_utils::window();
452 let id = uuid::Uuid::new_v4().to_string();
453 Reflect::set(&js_window, &JsValue::from_str(&id), js_sys_fn).unwrap();
454 js_closure.forget();
455
456 gloo_console::debug!(format!(
457 "Closure at {}:{}:{} set at window.['{id}'].",
458 file!(),
459 line!(),
460 column!()
461 ));
462 self.closure_id = Some(id);
463 self
464 }
465}
466
467impl FnWithArgs<2> {
468 pub fn run_rust_fn<A, B, C, FN: Fn(A, B) -> C>(mut self, _func: FN) -> Self {
469 let fn_name = std::any::type_name::<FN>()
470 .split("::")
471 .collect::<Vec<_>>()
472 .into_iter()
473 .next_back()
474 .unwrap();
475
476 self.body = format!(
477 "{}\nconst _out_ = window.callbacks.{}({});",
478 self.body,
479 fn_name,
480 self.args.join(", ")
481 );
482 self.js_return_value("_out_")
483 }
484
485 #[track_caller]
486 pub fn rust_closure<F: Fn(JsValue, JsValue) -> JsValue + 'static>(
487 mut self,
488 closure: F,
489 ) -> Self {
490 let js_closure = wasm_bindgen::closure::Closure::wrap(
491 Box::new(closure) as Box<dyn Fn(JsValue, JsValue) -> JsValue>
492 );
493 let js_sys_fn: &js_sys::Function = js_closure.as_ref().unchecked_ref();
494
495 let js_window = gloo_utils::window();
496 let id = uuid::Uuid::new_v4().to_string();
497 Reflect::set(&js_window, &JsValue::from_str(&id), js_sys_fn).unwrap();
498 js_closure.forget();
499
500 gloo_console::debug!(format!(
501 "Closure at {}:{}:{} set at window.['{id}'].",
502 file!(),
503 line!(),
504 column!()
505 ));
506 self.closure_id = Some(id);
507 self
508 }
509}
510
511impl FnWithArgs<3> {
512 pub fn run_rust_fn<A, B, C, D, FN: Fn(A, B, C) -> D>(mut self, _func: FN) -> Self {
513 let fn_name = std::any::type_name::<FN>()
514 .split("::")
515 .collect::<Vec<_>>()
516 .into_iter()
517 .next_back()
518 .unwrap();
519
520 self.body = format!(
521 "{}\nconst _out_ = window.callbacks.{}({});",
522 self.body,
523 fn_name,
524 self.args.join(", ")
525 );
526 self.js_return_value("_out_")
527 }
528
529 #[track_caller]
530 pub fn rust_closure<F: Fn(JsValue, JsValue, JsValue) -> JsValue + 'static>(
531 mut self,
532 closure: F,
533 ) -> Self {
534 let js_closure = wasm_bindgen::closure::Closure::wrap(
535 Box::new(closure) as Box<dyn Fn(JsValue, JsValue, JsValue) -> JsValue>
536 );
537 let js_sys_fn: &js_sys::Function = js_closure.as_ref().unchecked_ref();
538
539 let js_window = gloo_utils::window();
540 let id = uuid::Uuid::new_v4().to_string();
541 Reflect::set(&js_window, &JsValue::from_str(&id), js_sys_fn).unwrap();
542 js_closure.forget();
543
544 gloo_console::debug!(format!(
545 "Closure at {}:{}:{} set at window.['{id}'].",
546 file!(),
547 line!(),
548 column!()
549 ));
550 self.closure_id = Some(id);
551 self
552 }
553}
554
555impl FnWithArgs<4> {
556 pub fn run_rust_fn<A, B, C, D, E, FN: Fn(A, B, C, D) -> E>(mut self, _func: FN) -> Self {
557 let fn_name = std::any::type_name::<FN>()
558 .split("::")
559 .collect::<Vec<_>>()
560 .into_iter()
561 .next_back()
562 .unwrap();
563
564 self.body = format!(
565 "{}\nconst _out_ = window.callbacks.{}({});",
566 self.body,
567 fn_name,
568 self.args.join(", ")
569 );
570 self.js_return_value("_out_")
571 }
572
573 #[track_caller]
574 pub fn rust_closure<F: Fn(JsValue, JsValue, JsValue, JsValue) -> JsValue + 'static>(
575 mut self,
576 closure: F,
577 ) -> Self {
578 let js_closure = wasm_bindgen::closure::Closure::wrap(
579 Box::new(closure) as Box<dyn Fn(JsValue, JsValue, JsValue, JsValue) -> JsValue>
580 );
581 let js_sys_fn: &js_sys::Function = js_closure.as_ref().unchecked_ref();
582
583 let js_window = gloo_utils::window();
584 let id = uuid::Uuid::new_v4().to_string();
585 Reflect::set(&js_window, &JsValue::from_str(&id), js_sys_fn).unwrap();
586 js_closure.forget();
587
588 gloo_console::debug!(format!(
589 "Closure at {}:{}:{} set at window.['{id}'].",
590 file!(),
591 line!(),
592 column!()
593 ));
594 self.closure_id = Some(id);
595 self
596 }
597}
598
599impl FnWithArgs<5> {
600 pub fn run_rust_fn<A, B, C, D, E, F, FN: Fn(A, B, C, D, E) -> F>(mut self, _func: FN) -> Self {
601 let fn_name = std::any::type_name::<FN>()
602 .split("::")
603 .collect::<Vec<_>>()
604 .into_iter()
605 .next_back()
606 .unwrap();
607
608 self.body = format!(
609 "{}\nconst _out_ = window.callbacks.{}({});",
610 self.body,
611 fn_name,
612 self.args.join(", ")
613 );
614 self.js_return_value("_out_")
615 }
616
617 #[track_caller]
618 pub fn rust_closure<F: Fn(JsValue, JsValue, JsValue, JsValue, JsValue) -> JsValue + 'static>(
619 mut self,
620 closure: F,
621 ) -> Self {
622 let js_closure = wasm_bindgen::closure::Closure::wrap(Box::new(closure)
623 as Box<dyn Fn(JsValue, JsValue, JsValue, JsValue, JsValue) -> JsValue>);
624 let js_sys_fn: &js_sys::Function = js_closure.as_ref().unchecked_ref();
625
626 let js_window = gloo_utils::window();
627 let id = uuid::Uuid::new_v4().to_string();
628 Reflect::set(&js_window, &JsValue::from_str(&id), js_sys_fn).unwrap();
629 js_closure.forget();
630
631 gloo_console::debug!(format!(
632 "Closure at {}:{}:{} set at window.['{id}'].",
633 file!(),
634 line!(),
635 column!()
636 ));
637 self.closure_id = Some(id);
638 self
639 }
640}
641
642impl FnWithArgs<6> {
643 pub fn run_rust_fn<A, B, C, D, E, F, G, FN: Fn(A, B, C, D, E, F) -> G>(
644 mut self,
645 _func: FN,
646 ) -> Self {
647 let fn_name = std::any::type_name::<FN>()
648 .split("::")
649 .collect::<Vec<_>>()
650 .into_iter()
651 .next_back()
652 .unwrap();
653
654 self.body = format!(
655 "{}\nconst _out_ = window.callbacks.{}({});",
656 self.body,
657 fn_name,
658 self.args.join(", ")
659 );
660 self.js_return_value("_out_")
661 }
662
663 #[track_caller]
664 pub fn rust_closure<
665 F: Fn(JsValue, JsValue, JsValue, JsValue, JsValue, JsValue) -> JsValue + 'static,
666 >(
667 mut self,
668 closure: F,
669 ) -> Self {
670 let js_closure = wasm_bindgen::closure::Closure::wrap(Box::new(closure)
671 as Box<dyn Fn(JsValue, JsValue, JsValue, JsValue, JsValue, JsValue) -> JsValue>);
672 let js_sys_fn: &js_sys::Function = js_closure.as_ref().unchecked_ref();
673
674 let js_window = gloo_utils::window();
675 let id = uuid::Uuid::new_v4().to_string();
676 Reflect::set(&js_window, &JsValue::from_str(&id), js_sys_fn).unwrap();
677 js_closure.forget();
678
679 gloo_console::debug!(format!(
680 "Closure at {}:{}:{} set at window.['{id}'].",
681 file!(),
682 line!(),
683 column!()
684 ));
685 self.closure_id = Some(id);
686 self
687 }
688}
689
690impl FnWithArgs<7> {
692 pub fn run_rust_fn<A, B, C, D, E, F, G, H, FN: Fn(A, B, C, D, E, F, G) -> H>(
693 mut self,
694 _func: FN,
695 ) -> Self {
696 let fn_name = std::any::type_name::<FN>()
697 .split("::")
698 .collect::<Vec<_>>()
699 .into_iter()
700 .next_back()
701 .unwrap();
702
703 self.body = format!(
704 "{}\nconst _out_ = window.callbacks.{}({});",
705 self.body,
706 fn_name,
707 self.args.join(", ")
708 );
709 self.js_return_value("_out_")
710 }
711
712 #[track_caller]
713 pub fn rust_closure<
714 F: Fn(JsValue, JsValue, JsValue, JsValue, JsValue, JsValue, JsValue) -> JsValue + 'static,
715 >(
716 mut self,
717 closure: F,
718 ) -> Self {
719 let js_closure = wasm_bindgen::closure::Closure::wrap(Box::new(closure)
720 as Box<
721 dyn Fn(JsValue, JsValue, JsValue, JsValue, JsValue, JsValue, JsValue) -> JsValue,
722 >);
723 let js_sys_fn: &js_sys::Function = js_closure.as_ref().unchecked_ref();
724
725 let js_window = gloo_utils::window();
726 let id = uuid::Uuid::new_v4().to_string();
727 Reflect::set(&js_window, &JsValue::from_str(&id), js_sys_fn).unwrap();
728 js_closure.forget();
729
730 gloo_console::debug!(format!(
731 "Closure at {}:{}:{} set at window.['{id}'].",
732 file!(),
733 line!(),
734 column!()
735 ));
736 self.closure_id = Some(id);
737 self
738 }
739}
740
741#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
742#[serde(untagged)]
743pub enum FnWithArgsOrT<const N: usize, T> {
744 T(T),
745 FnWithArgs(FnWithArgs<N>),
746}
747
748impl<const N: usize, T: for<'a> Deserialize<'a>> FnWithArgsOrT<N, T> {
749 pub fn rationalise_1_level(obj: &JsValue, name: &'static str) {
750 super::rationalise_1_level::<N, Self>(obj, name, |o| match o {
751 FnWithArgsOrT::T(_) => (),
752 FnWithArgsOrT::FnWithArgs(fnwa) => {
753 let _ = Reflect::set(obj, &name.into(), &fnwa.build());
754 }
755 })
756 }
757 pub fn rationalise_2_levels(obj: &JsValue, name: (&'static str, &'static str)) {
758 super::rationalise_2_levels::<N, Self>(obj, name, |a, o| match o {
759 FnWithArgsOrT::T(_) => (),
760 FnWithArgsOrT::FnWithArgs(fnwa) => {
761 let _ = Reflect::set(&a, &name.1.into(), &fnwa.build());
762 }
763 })
764 }
765}
766#[allow(private_bounds)]
767impl<const N: usize, T: ChartJsRsObject> FnWithArgsOrT<N, T> {
768 pub fn is_empty(&self) -> bool {
769 match self {
770 FnWithArgsOrT::T(a) => a.is_empty(),
771 FnWithArgsOrT::FnWithArgs(fnwa) => fnwa.is_empty(),
772 }
773 }
774}
775impl<const N: usize, T: Default> Default for FnWithArgsOrT<N, T> {
776 fn default() -> Self {
777 FnWithArgsOrT::T(T::default())
778 }
779}
780impl<const N: usize, T: Into<String>> From<T> for FnWithArgsOrT<N, String> {
781 fn from(s: T) -> Self {
782 Self::T(s.into())
783 }
784}
785impl<const N: usize, T: Into<NumberString>> From<T> for FnWithArgsOrT<N, NumberString> {
786 fn from(ns: T) -> Self {
787 Self::T(ns.into())
788 }
789}
790impl<const N: usize, T: Into<BoolString>> From<T> for FnWithArgsOrT<N, BoolString> {
791 fn from(bs: T) -> Self {
792 Self::T(bs.into())
793 }
794}
795impl<const N: usize, T> From<FnWithArgs<N>> for FnWithArgsOrT<N, T> {
796 fn from(value: FnWithArgs<N>) -> Self {
797 Self::FnWithArgs(value)
798 }
799}
800
801#[derive(Debug, Clone, Default, PartialEq, Eq)]
802pub struct NumberString(String);
803impl From<NumberOrDateString> for NumberString {
804 fn from(value: NumberOrDateString) -> Self {
805 value.0.into()
806 }
807}
808impl NumberString {
809 pub fn is_empty(&self) -> bool {
810 self.0.is_empty()
811 }
812}
813impl ChartJsRsObject for NumberString {
814 fn is_empty(&self) -> bool {
815 self.is_empty()
816 }
817}
818impl PartialOrd for NumberString {
819 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
820 Some(self.cmp(other))
821 }
822}
823impl Ord for NumberString {
824 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
825 if let Some((s, o)) = self
826 .0
827 .parse::<rust_decimal::Decimal>()
828 .ok()
829 .zip(other.0.parse::<rust_decimal::Decimal>().ok())
830 {
831 s.cmp(&o)
832 } else {
833 self.0.cmp(&other.0)
834 }
835 }
836}
837impl<T: Display> From<T> for NumberString {
838 fn from(s: T) -> Self {
839 Self(s.to_string())
840 }
841}
842#[allow(clippy::to_string_trait_impl)]
843impl ToString for NumberString {
844 fn to_string(&self) -> String {
845 self.0.to_string()
846 }
847}
848impl Serialize for NumberString {
849 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
850 where
851 S: serde::Serializer,
852 {
853 let fnum: Result<f64, _> = self.0.parse();
854 let inum: Result<i64, _> = self.0.parse();
855 if self.0.eq_ignore_ascii_case("null") {
856 return serializer.serialize_none();
857 }
858 match (fnum, inum) {
859 (Ok(_), Ok(inum)) => serializer.serialize_i64(inum),
860 (Ok(fnum), _) => serializer.serialize_f64(fnum),
861 _ => serializer.serialize_str(&self.0),
862 }
863 }
864}
865impl<'de> Deserialize<'de> for NumberString {
866 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
867 where
868 D: serde::Deserializer<'de>,
869 {
870 Any::deserialize(deserializer).map(|soi| Self(soi.to_string()))
871 }
872}
873
874#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
875#[serde(untagged)]
876pub enum NumberStringOrT<T: Serialize + DeserializeOwned> {
877 T(T),
878 NumberString(NumberString),
879}
880impl<'de, T: Serialize + DeserializeOwned> Deserialize<'de> for NumberStringOrT<T> {
881 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
882 where
883 D: de::Deserializer<'de>,
884 {
885 let value = serde_json::Value::deserialize(deserializer)?;
886
887 match serde_json::from_value::<NumberString>(value.clone()) {
888 Ok(ns) => Ok(Self::NumberString(ns)),
889 Err(_) => serde_json::from_value::<T>(value)
890 .map(Self::T)
891 .map_err(de::Error::custom),
892 }
893 }
894}
895impl<T: Serialize + DeserializeOwned> NumberStringOrT<T> {
896 pub fn is_empty(&self) -> bool {
897 match self {
898 NumberStringOrT::T(_t) => false,
899 NumberStringOrT::NumberString(ns) => ns.is_empty(),
900 }
901 }
902}
903
904impl<T: Serialize + ChartJsRsObject, U: Serialize + DeserializeOwned> From<T>
905 for NumberStringOrT<U>
906{
907 fn from(value: T) -> Self {
908 serde_json::from_value(serde_json::to_value(value).unwrap()).unwrap()
909 }
910}