1use std::fmt::Debug;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use crate::axis_typing::{AxisInfo, AxisKindMarker, ValueSerializeWrapper};
5use crate::common::Percent;
6
7#[derive(Serialize, Debug, Clone)]
9#[serde(rename_all = "camelCase")]
10pub struct EChartOptions<X:AxisKindMarker,Y:AxisKindMarker> {
11 #[serde(skip_serializing_if = "Option::is_none")]
13 pub(crate) title: Option<Title>,
14
15 #[serde(skip_serializing_if = "Option::is_none")]
17 pub grid: Option<Grid>,
18
19 #[serde(skip_serializing_if = "Option::is_none")]
21 pub tooltip: Option<Tooltip>,
22
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub legend: Option<Legend>,
26
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub(crate) dataset: Option<Vec<DatasetComponent<X,Y>>>,
30
31 pub x_axis: Axis<X>,
33
34 pub y_axis: Axis<Y>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub(crate) series: Option<Vec<Series<X,Y>>>,
40
41 #[serde(flatten)]
43 pub extra: Option<Value>,
44}
45
46#[derive(Serialize, Deserialize, Debug, Clone)]
48#[serde(rename_all = "camelCase")]
49pub enum PositionKeyword {
50 Left,
51 Right,
52 Top,
53 Bottom,
54 Middle,
55 Center,
56 Auto
57}
58
59
60#[derive(Serialize, Deserialize, Debug, Clone)]
62#[serde(untagged)]
63pub enum Position {
64 Keyword(PositionKeyword),
66 Number(f64),
68 Percent(Percent)
70}
71
72#[derive(Serialize, Deserialize, Debug, Clone,Default)]
74#[serde(rename_all = "camelCase")]
75pub struct Title {
76 pub text: Option<String>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub sub_text: Option<String>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub link: Option<String>,
86
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub left: Option<Position>,
90
91 #[serde(skip_serializing_if = "Option::is_none")]
93 pub top: Option<Position>,
94
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub right: Option<Position>,
98
99 #[serde(skip_serializing_if = "Option::is_none")]
101 pub bottom: Option<Position>,
102
103 #[serde(flatten)]
105 pub extra: Option<Value>,
106}
107
108impl Title {
109 pub fn new(text: &str) -> Title {
110 Self{
111 text: Some(text.to_string()),
112 sub_text: None,
113 link: None,
114 left: None,
115 top: None,
116 right: None,
117 bottom: None,
118 extra: None,
119 }
120 }
121}
122
123#[derive(Serialize, Deserialize, Debug, Clone)]
125#[serde(rename_all = "camelCase")]
126pub struct Grid {
127 #[serde(skip_serializing_if = "Option::is_none")]
129 pub left: Option<Position>,
130
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub right: Option<Position>,
134
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub top: Option<Position>,
138
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub bottom: Option<Position>,
142
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub contain_label: Option<bool>,
146
147 #[serde(skip_serializing_if = "Option::is_none")]
149 pub background_color: Option<Value>,
150
151 #[serde(skip_serializing_if = "Option::is_none")]
153 pub border_color: Option<Value>,
154
155 #[serde(skip_serializing_if = "Option::is_none")]
157 pub border_width: Option<f64>,
158
159 #[serde(skip_serializing_if = "Option::is_none")]
161 pub show: Option<bool>,
162
163 #[serde(skip_serializing_if = "Option::is_none")]
165 pub z: Option<i32>,
166
167 #[serde(skip_serializing_if = "Option::is_none")]
169 pub zlevel: Option<i32>,
170
171 #[serde(flatten)]
173 pub extra: Option<Value>,
174}
175
176#[derive(Serialize, Deserialize, Debug, Clone)]
177#[serde(rename_all = "camelCase")]
178pub enum TooltipTrigger {
179 Item,
180 Axis,
181 None
182}
183
184#[derive(Serialize, Deserialize, Debug, Clone)]
185#[serde(rename_all = "camelCase")]
186pub enum AxisPointerType {
187 Cross,
188 Line,
189 Shadow,
190 None
191}
192
193#[derive(Serialize, Deserialize, Debug, Clone)]
194#[serde(rename_all = "camelCase")]
195pub enum AxisPointerTargetAxis{
196 Auto,
197 X,
198 Y,
199 Radius,
200 Angle
201}
202
203
204#[derive(Serialize, Deserialize, Debug, Clone)]
205#[serde(rename_all = "camelCase")]
206pub struct AxisPointer{
207 #[serde(skip_serializing_if = "Option::is_none")]
208 pub r#type: Option<AxisPointerType>,
209
210 #[serde(skip_serializing_if = "Option::is_none")]
211 pub snap : Option<bool>,
212
213 #[serde(skip_serializing_if = "Option::is_none")]
214 pub animation : Option<bool>,
215
216 #[serde(skip_serializing_if = "Option::is_none")]
217 pub axis: Option<AxisPointerTargetAxis>
218
219
220}
221
222#[derive(Serialize, Deserialize, Debug, Clone)]
224#[serde(rename_all = "camelCase")]
225pub struct Tooltip {
226 pub show: bool,
227
228 #[serde(skip_serializing_if = "Option::is_none")]
229 pub show_delay: Option<i32>,
230
231 #[serde(skip_serializing_if = "Option::is_none")]
232 pub hide_delay: Option<i32>,
233
234 #[serde(skip_serializing_if = "Option::is_none")]
236 pub trigger: Option<TooltipTrigger>,
237
238 #[serde(skip_serializing_if = "Option::is_none")]
240 pub formatter: Option<Value>,
241
242 #[serde(skip_serializing_if = "Option::is_none")]
243 pub axis_pointer: Option<AxisPointer>,
244
245}
246
247#[derive(Serialize, Deserialize, Debug, Clone)]
249#[serde(rename_all = "camelCase")]
250pub struct Legend {
251 #[serde(skip_serializing_if = "Option::is_none")]
253 pub data: Option<Vec<String>>,
254
255 #[serde(skip_serializing_if = "Option::is_none")]
257 pub orient: Option<String>,
258
259 #[serde(skip_serializing_if = "Option::is_none")]
261 pub left: Option<Position>,
262
263 #[serde(skip_serializing_if = "Option::is_none")]
264 pub right: Option<Position>,
265
266 #[serde(skip_serializing_if = "Option::is_none")]
267 pub top: Option<Position>,
268
269 #[serde(skip_serializing_if = "Option::is_none")]
270 pub bottom: Option<Position>
271}
272
273impl Default for Legend {
274 fn default() -> Self {
275 Self{
276 data: None,
277 orient: None,
278 left: None,
279 right: None,
280 top: None,
281 bottom: None,
282 }
283 }
284}
285
286
287#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
289#[serde(rename_all = "lowercase")]
290pub enum AxisType {
291 Value,
292 Category,
293 Time,
294 Log
295}
296
297#[derive(Serialize, Debug, Clone)]
298#[serde(rename_all = "camelCase")]
299pub struct NamedValuePair<X:AxisKindMarker,Y:AxisKindMarker>{
300 value: (ValueSerializeWrapper<X>,ValueSerializeWrapper<Y>),
301 name: String,
302}
303
304impl<X:AxisKindMarker,Y:AxisKindMarker> NamedValuePair<X,Y> {
305 pub fn new(x: X, y: Y, name: String) -> Self {
306 Self{
307 value: (x.into(), y.into()),
308 name,
309 }
310 }
311
312}
313
314#[derive(Serialize, Debug, Clone)]
315#[serde(rename_all = "camelCase")]
316pub struct NamedValue<X:AxisKindMarker>{
317 value: ValueSerializeWrapper<X>,
318 name: String,
319}
320
321
322#[derive(Serialize, Debug, Clone)]
324#[serde(rename_all = "camelCase")]
325pub struct Axis<T:AxisKindMarker> {
326 pub(crate) r#type: AxisType,
328
329 #[serde(skip_serializing_if = "Option::is_none")]
330 pub(crate) inverse: Option<bool>,
331
332 #[serde(skip_serializing_if = "Option::is_none")]
334 pub(crate) name: Option<String>,
335
336 #[serde(skip_serializing_if = "Option::is_none")]
338 pub data: Option<Vec<ValueSerializeWrapper<T>>>,
339
340 #[serde(flatten)]
342 pub extra: Option<Value>,
343}
344
345impl<T:AxisKindMarker> Default for Axis<T> {
346 fn default() -> Self {
347 Self{
348 r#type: T::AxisType::AXIS_TYPE,
349 inverse: None,
350 name: None,
351 data: None,
352 extra: None,
353 }
354 }
355}
356
357
358impl<T:AxisKindMarker> Axis<T>{
359
360 pub fn new_named(name: String)-> Self{
361 Self{
362 r#type: T::AxisType::AXIS_TYPE,
363 name: Some(name),
364 inverse: None,
365 data: None,
366 extra: None
367 }
368 }
369
370 pub fn new(name: String, is_log:bool, inverse: bool)-> Self{
371 if T::AxisType::AXIS_TYPE == AxisType::Value && is_log {
372 Self{
373 r#type: AxisType::Log,
374 name: Some(name),
375 inverse: Some(inverse),
376 data: None,
377 extra: None,
378 }
379 }else {
380 Self{
381 r#type: T::AxisType::AXIS_TYPE,
382 name: Some(name.to_string()),
383 inverse: Some(inverse),
384 data: None,
385 extra: None,
386 }
387 }
388 }
389}
390
391
392#[derive(Serialize, Deserialize, Debug, Clone)]
394#[serde(rename_all = "camelCase")]
395pub enum SeriesType {
396 Line,
397 Bar,
398 Pie,
399 Scatter,
400 EffectScatter,
401 Radar,
402 Tree,
403 Treemap,
404 Sunburst,
405 Boxplot,
406 Candlestick,
407 Heatmap,
408 Map,
409 Parallel,
410 Lines,
411 Graph,
412 Sankey,
413 Funnel,
414 Gauge,
415 PictorialBar,
416 ThemeRiver,
417 Custom
418}
419
420
421#[derive(Serialize, Debug, Clone)]
422#[serde(untagged)]
423pub enum DataVariant<X:AxisKindMarker,Y:AxisKindMarker>{
424 Data(Vec<ValueSerializeWrapper<X>>),
426
427 Pair(Vec<(ValueSerializeWrapper<X>,ValueSerializeWrapper<Y>)>),
429
430 Named(Vec<NamedValue<X>>),
432
433 NamedPair(Vec<NamedValuePair<X,Y>>),
435}
436
437#[derive(Serialize, Debug, Clone)]
439#[serde(rename_all = "camelCase")]
440pub enum SeriesDataSource<X:AxisKindMarker,Y:AxisKindMarker>{
441 Data(DataVariant<X,Y>),
443 DatasetIndex(usize)
445}
446
447impl<X:AxisKindMarker,Y:AxisKindMarker> From<Vec<(X, Y)>> for SeriesDataSource<X,Y>
448{
449 fn from(value: Vec<(X, Y)>) -> Self {
450 SeriesDataSource::from_pairs(value)
451 }
452}
453
454impl<const N: usize,X:AxisKindMarker,Y:AxisKindMarker> From<[(X, Y); N]> for SeriesDataSource<X,Y>
455{
456 fn from(value: [(X, Y); N]) -> Self {
457 Self::Data(DataVariant::Pair(value.into_iter().map(|(x, y)| (x.into(), y.into())).collect()))
458 }
459}
460
461impl<X:AxisKindMarker,Y:AxisKindMarker> From<Vec<(X, Y, String)>> for SeriesDataSource<X,Y>
462{
463 fn from(value: Vec<(X, Y, String)>) -> Self {
464 SeriesDataSource::from_tuples_with_label(value)
465 }
466}
467
468impl<X:AxisKindMarker,Y:AxisKindMarker> From<Vec<X>> for SeriesDataSource<X,Y>
469{
470 fn from(value: Vec<X>) -> Self {
471 SeriesDataSource::from_values(value)
472 }
473}
474
475impl<X:AxisKindMarker,Y:AxisKindMarker> SeriesDataSource<X,Y> {
476 pub fn from_dataset_index(index: usize) -> Self {
478 Self::DatasetIndex(index)
479 }
480
481 pub fn from_values(values: Vec<X>) -> Self {
483 Self::Data(DataVariant::Data(values.into_iter().map(ValueSerializeWrapper::from).collect()))
484 }
485
486 pub fn from_pairs(pairs: Vec<(X,Y)>) -> Self {
488 Self::Data(DataVariant::Pair(pairs.into_iter().map(|(x,y)| (x.into(),y.into())).collect()))
489 }
490
491 pub fn from_named_value_pairs(named_pairs: Vec<NamedValuePair<X,Y>>) -> Self{
492 Self::Data(DataVariant::NamedPair(named_pairs))
493 }
494
495
496 pub fn with_named_values(named_values: Vec<NamedValue<X>>) -> Self {
498 Self::Data(DataVariant::Named(named_values))
499 }
500
501 pub fn from_labeled_values(values: Vec<(X, String)>) -> Self
502 {
503 let data_named_values = values.into_iter().map(|(x,label)| NamedValue{
504 value: x.into(),
505 name: label
506 }).collect();
507 Self::with_named_values(data_named_values)
508 }
509
510 pub fn from_tuples_with_label(values: Vec<(X, Y, String)>) -> Self
512 {
513 let data_named_pairs = values.into_iter()
514 .map(|(x, y,label) | NamedValuePair::new(x.into(), y.into(),label))
515 .collect();
516 Self::from_named_value_pairs(data_named_pairs)
517 }
518
519}
520
521
522#[derive(Serialize, Deserialize, Debug, Clone)]
523#[serde(rename_all = "camelCase")]
524pub enum DataPointSymbol {
525 Circle,
526 Rect,
527 RoundRect,
528 Triangle,
529 Diamond,
530 Pin,
531 Arrow,
532 None
533}
534
535#[derive(Serialize, Debug, Clone)]
537#[serde(rename_all = "camelCase")]
538pub struct Series<X:AxisKindMarker,Y:AxisKindMarker> {
539 #[serde(skip_serializing_if = "Option::is_none")]
541 pub r#type: Option<SeriesType>,
542
543 #[serde(skip_serializing_if = "Option::is_none")]
545 pub name: Option<String>,
546
547 #[serde(skip_serializing_if = "Option::is_none")]
548 pub smooth: Option<bool>,
549
550 #[serde(skip_serializing_if = "Option::is_none")]
551 pub area_style: Option<AreaStyle>,
552
553 #[serde(flatten)]
555 pub data: SeriesDataSource<X,Y>,
556
557 #[serde(skip_serializing_if = "Option::is_none")]
558 pub symbol: Option<DataPointSymbol>,
559
560 #[serde(skip_serializing_if = "Option::is_none")]
561 pub symbol_size: Option<usize>,
562
563 #[serde(flatten)]
565 pub extra: Option<Value>,
566}
567
568impl<X:AxisKindMarker,Y:AxisKindMarker> Series <X,Y>{
569 pub fn new(name:String, r#type: SeriesType, data: SeriesDataSource<X,Y>) -> Series<X,Y> {
570 Self{
571 r#type: Some(r#type),
572 name: Some(name),
573 smooth: None,
574 area_style: None,
575 data,
576 symbol: None,
577 symbol_size: None,
578 extra: None,
579 }
580 }
581}
582
583
584#[derive(Serialize, Deserialize, Debug, Clone)]
585#[serde(rename_all = "camelCase")]
586pub enum AxisFillOrigin{
587 Auto,
588 Start,
589 End,
590 Number
591}
592
593#[derive(Serialize, Deserialize, Debug, Clone)]
594#[serde(rename_all = "camelCase")]
595pub struct AreaStyle{
596 #[serde(skip_serializing_if = "Option::is_none")]
597 color: Option<Value>,
598 #[serde(skip_serializing_if = "Option::is_none")]
599 origin: Option<AxisFillOrigin>
600}
601
602#[derive(Serialize, Deserialize, Debug, Clone)]
603#[serde(rename_all = "camelCase")]
604pub struct Transform{
605 pub transform: Vec<DatasetTransform>,
606
607 #[serde(skip_serializing_if = "Option::is_none")]
608 pub from_dataset_index: Option<usize>
609}
610
611#[derive(Serialize, Debug, Clone)]
612#[serde(rename_all = "camelCase")]
613pub struct Source<X:AxisKindMarker,Y:AxisKindMarker>{
614 pub source: Vec<(ValueSerializeWrapper<X>,ValueSerializeWrapper<Y>)>,
615}
616
617impl<X:AxisKindMarker,Y:AxisKindMarker> From<Vec<(X,Y)>> for Source<X,Y>{
618 fn from(value: Vec<(X,Y)>) -> Self {
619 Self{
620 source: value.into_iter().map(|(x,y)| (x.into(),y.into())).collect()
621 }
622 }
623}
624
625impl<const N:usize,X:AxisKindMarker,Y:AxisKindMarker> From<[(X,Y);N]> for Source<X,Y>
626{
627 fn from(value: [(X,Y);N]) -> Self {
628 Self{
629 source: value.into_iter().map(|(x,y)| (x.into(),y.into())).collect()
630 }
631 }
632}
633
634
635#[derive(Serialize, Debug, Clone)]
636#[serde(rename_all = "camelCase")]
637pub struct LabelledSource<X:AxisKindMarker,Y:AxisKindMarker>{
638 pub source: Vec<(ValueSerializeWrapper<X>,ValueSerializeWrapper<Y>,String)>,
639}
640
641
642
643
644
645#[derive(Serialize, Debug, Clone)]
647#[serde(rename_all = "camelCase")]
648#[serde(untagged)]
649pub enum DatasetComponent<X:AxisKindMarker,Y:AxisKindMarker> {
650 Source(Source<X,Y>),
651 LabelledSource(LabelledSource<X,Y>),
652 Transform(Transform)
653}
654
655
656impl<const N:usize,X:AxisKindMarker,Y:AxisKindMarker> From<[(X,Y);N]> for DatasetComponent<X,Y>
657{
658 fn from(value: [(X,Y);N]) -> Self {
659 DatasetComponent::Source(value.into())
660 }
661}
662
663impl<X:AxisKindMarker,Y:AxisKindMarker> From<Vec<(X,Y)>> for DatasetComponent<X,Y>
664{
665 fn from(value: Vec<(X, Y)>) -> Self {
666 DatasetComponent::src(value)
667 }
668}
669
670
671impl<const N:usize,X:AxisKindMarker,Y:AxisKindMarker> From<[(X,Y,String);N]> for DatasetComponent<X,Y>
672{
673 fn from(value: [(X,Y,String);N] ) -> Self {
674 Self::LabelledSource(
675 LabelledSource{
676 source: value.into_iter().map(|(x,y,label)| (x.into(),y.into(),label)).collect()
677 }
678 )
679 }
680}
681
682impl<X:AxisKindMarker,Y:AxisKindMarker> From<Vec<(X,Y,String)>> for DatasetComponent<X,Y>
683{
684 fn from(value: Vec<(X, Y, String)>) -> Self {
685 DatasetComponent::labelled_source(value)
686 }
687}
688
689
690impl<X:AxisKindMarker,Y:AxisKindMarker> DatasetComponent<X,Y> {
691 pub fn tr(transform: DatasetTransform, index: usize) -> Self{
692 Self::Transform(Transform {
693 transform: vec![transform],
694 from_dataset_index: Some(index),
695 })
696 }
697
698 pub fn trs(transform: Vec<DatasetTransform>, index: usize) -> Self{
699 Self::Transform(
700 Transform {
701 transform,
702 from_dataset_index: Some(index)
703 }
704 )
705 }
706
707 pub fn src(source: Vec<(X,Y)>) -> Self {
708 Self::Source(source.into())
709 }
710
711 pub fn labelled_source(source: Vec<(X,Y,String)>) -> Self {
713 Self::LabelledSource(
714 LabelledSource{
715 source: source.into_iter().map(|(x,y,label)| (x.into(),y.into(),label)).collect()
716 }
717 )
718 }
719}
720
721#[derive(Serialize, Deserialize, Debug, Clone)]
722#[serde(rename_all = "camelCase")]
723pub enum DatasetTransformType {
724 Filter,
725 Sort,
726 Boxplot,
727 #[serde(rename = "ecStat:regression")]
728 Regression,
729 #[serde(rename = "ecStat:clustering")]
730 Clustering
731}
732
733#[derive(Serialize, Deserialize, Debug, Clone)]
735#[serde(rename_all = "camelCase")]
736pub struct DatasetTransform {
737 r#type: DatasetTransformType,
739
740 config: DatasetTransformConfig,
742
743}
744
745
746impl DatasetTransform {
747
748 pub fn regression(config: RegressionConfig) -> Self {
749 Self{
750 r#type: DatasetTransformType::Regression,
751 config: DatasetTransformConfig::Regression(config)
752 }
753 }
754
755 pub fn clustering(clustering_config: ClusteringConfig)->Self{
756 Self{
757 r#type: DatasetTransformType::Clustering,
758 config: DatasetTransformConfig::Clustering(clustering_config)
759 }
760 }
761
762 pub fn sort(sort_config: SortConfig) -> Self {
763 Self{
764 r#type: DatasetTransformType::Sort,
765 config: DatasetTransformConfig::Sort(sort_config)
766 }
767 }
768
769}
770
771
772#[derive(Serialize, Deserialize, Debug, Clone)]
773#[serde(untagged)]
774enum DatasetTransformConfig {
775 Regression(RegressionConfig),
776 Clustering(ClusteringConfig),
777 Sort(SortConfig)
778}
779
780
781#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
783#[serde(rename_all = "lowercase")]
784pub enum RegressionMethod {
785 Linear,
786 Exponential,
787 Logarithmic,
788 Polynomial,
789}
790
791#[derive(Serialize, Deserialize, Debug, Clone)]
792#[serde(rename_all = "camelCase")]
793pub struct SortConfig {
794 dimension: u8,
795 order : Order
796}
797
798#[derive(Serialize, Deserialize, Debug, Clone)]
799#[serde(rename_all = "camelCase")]
800pub enum Order {
801 Asc,
802 Desc
803}
804
805
806#[derive(Serialize, Deserialize, Debug, Clone)]
808#[serde(rename_all = "camelCase")]
809pub struct RegressionConfig {
810 pub method: RegressionMethod,
812
813 #[serde(skip_serializing_if = "Option::is_none")]
815 pub order: Option<u8>,
816
817 #[serde(flatten)]
819 pub extra: Option<Value>,
820}
821
822
823
824#[derive(Serialize, Deserialize, Debug, Clone)]
826#[serde(rename_all = "camelCase")]
827pub struct ClusteringConfig {
828
829 pub cluster_count: u8,
831
832 pub output_cluster_index_dimension: u8,
834
835 pub dimensions : Option<Vec<usize>>,
837
838}