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)]
248#[serde(rename_all = "lowercase")]
249pub enum LegendOrient {
250 Horizontal,
251 Vertical
252}
253
254#[derive(Serialize, Deserialize, Debug, Clone)]
256#[serde(rename_all = "camelCase")]
257pub struct Legend {
258 #[serde(skip_serializing_if = "Option::is_none")]
260 pub data: Option<Vec<String>>,
261
262 #[serde(skip_serializing_if = "Option::is_none")]
264 pub orient: Option<LegendOrient>,
265
266 #[serde(skip_serializing_if = "Option::is_none")]
268 pub left: Option<Position>,
269
270 #[serde(skip_serializing_if = "Option::is_none")]
271 pub right: Option<Position>,
272
273 #[serde(skip_serializing_if = "Option::is_none")]
274 pub top: Option<Position>,
275
276 #[serde(skip_serializing_if = "Option::is_none")]
277 pub bottom: Option<Position>
278}
279
280impl Default for Legend {
281 fn default() -> Self {
282 Self{
283 data: None,
284 orient: None,
285 left: None,
286 right: None,
287 top: None,
288 bottom: None,
289 }
290 }
291}
292
293
294#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
296#[serde(rename_all = "lowercase")]
297pub enum AxisType {
298 Value,
299 Category,
300 Time,
301 Log
302}
303
304#[derive(Serialize, Debug, Clone)]
305#[serde(rename_all = "camelCase")]
306pub struct NamedValuePair<X:AxisKindMarker,Y:AxisKindMarker>{
307 value: (ValueSerializeWrapper<X>,ValueSerializeWrapper<Y>),
308 name: String,
309}
310
311impl<X:AxisKindMarker,Y:AxisKindMarker> NamedValuePair<X,Y> {
312 pub fn new(x: X, y: Y, name: String) -> Self {
313 Self{
314 value: (x.into(), y.into()),
315 name,
316 }
317 }
318
319}
320
321#[derive(Serialize, Debug, Clone)]
322#[serde(rename_all = "camelCase")]
323pub struct NamedValue<X:AxisKindMarker>{
324 value: ValueSerializeWrapper<X>,
325 name: String,
326}
327
328
329#[derive(Serialize, Debug, Clone)]
331#[serde(rename_all = "camelCase")]
332pub struct Axis<T:AxisKindMarker> {
333 pub(crate) r#type: AxisType,
335
336 #[serde(skip_serializing_if = "Option::is_none")]
337 pub(crate) inverse: Option<bool>,
338
339 #[serde(skip_serializing_if = "Option::is_none")]
341 pub(crate) name: Option<String>,
342
343 #[serde(skip_serializing_if = "Option::is_none")]
345 pub data: Option<Vec<ValueSerializeWrapper<T>>>,
346
347 #[serde(flatten)]
349 pub extra: Option<Value>,
350}
351
352impl<T:AxisKindMarker> Default for Axis<T> {
353 fn default() -> Self {
354 Self{
355 r#type: T::AxisType::AXIS_TYPE,
356 inverse: None,
357 name: None,
358 data: None,
359 extra: None,
360 }
361 }
362}
363
364
365impl<T:AxisKindMarker> Axis<T>{
366
367 pub fn new_named(name: String)-> Self{
368 Self{
369 r#type: T::AxisType::AXIS_TYPE,
370 name: Some(name),
371 inverse: None,
372 data: None,
373 extra: None
374 }
375 }
376
377 pub fn new(name: String, is_log:bool, inverse: bool)-> Self{
378 if T::AxisType::AXIS_TYPE == AxisType::Value && is_log {
379 Self{
380 r#type: AxisType::Log,
381 name: Some(name),
382 inverse: Some(inverse),
383 data: None,
384 extra: None,
385 }
386 }else {
387 Self{
388 r#type: T::AxisType::AXIS_TYPE,
389 name: Some(name.to_string()),
390 inverse: Some(inverse),
391 data: None,
392 extra: None,
393 }
394 }
395 }
396}
397
398
399#[derive(Serialize, Deserialize, Debug, Clone)]
401#[serde(rename_all = "camelCase")]
402pub enum SeriesType {
403 Line,
404 Bar,
405 Pie,
406 Scatter,
407 EffectScatter,
408 Radar,
409 Tree,
410 Treemap,
411 Sunburst,
412 Boxplot,
413 Candlestick,
414 Heatmap,
415 Map,
416 Parallel,
417 Lines,
418 Graph,
419 Sankey,
420 Funnel,
421 Gauge,
422 PictorialBar,
423 ThemeRiver,
424 Custom
425}
426
427
428#[derive(Serialize, Debug, Clone)]
429#[serde(untagged)]
430pub enum DataVariant<X:AxisKindMarker,Y:AxisKindMarker>{
431 Data(Vec<ValueSerializeWrapper<X>>),
433
434 Pair(Vec<(ValueSerializeWrapper<X>,ValueSerializeWrapper<Y>)>),
436
437 Named(Vec<NamedValue<X>>),
439
440 NamedPair(Vec<NamedValuePair<X,Y>>),
442}
443
444#[derive(Serialize, Debug, Clone)]
446#[serde(rename_all = "camelCase")]
447pub enum SeriesDataSource<X:AxisKindMarker,Y:AxisKindMarker>{
448 Data(DataVariant<X,Y>),
450 DatasetIndex(usize)
452}
453
454impl<X:AxisKindMarker,Y:AxisKindMarker> From<Vec<(X, Y)>> for SeriesDataSource<X,Y>
455{
456 fn from(value: Vec<(X, Y)>) -> Self {
457 SeriesDataSource::from_pairs(value)
458 }
459}
460
461impl<const N: usize,X:AxisKindMarker,Y:AxisKindMarker> From<[(X, Y); N]> for SeriesDataSource<X,Y>
462{
463 fn from(value: [(X, Y); N]) -> Self {
464 Self::Data(DataVariant::Pair(value.into_iter().map(|(x, y)| (x.into(), y.into())).collect()))
465 }
466}
467
468impl<X:AxisKindMarker,Y:AxisKindMarker> From<Vec<(X, Y, String)>> for SeriesDataSource<X,Y>
469{
470 fn from(value: Vec<(X, Y, String)>) -> Self {
471 SeriesDataSource::from_tuples_with_label(value)
472 }
473}
474
475impl<X:AxisKindMarker,Y:AxisKindMarker> From<Vec<X>> for SeriesDataSource<X,Y>
476{
477 fn from(value: Vec<X>) -> Self {
478 SeriesDataSource::from_values(value)
479 }
480}
481
482impl<X:AxisKindMarker,Y:AxisKindMarker> SeriesDataSource<X,Y> {
483 pub fn from_dataset_index(index: usize) -> Self {
485 Self::DatasetIndex(index)
486 }
487
488 pub fn from_values(values: Vec<X>) -> Self {
490 Self::Data(DataVariant::Data(values.into_iter().map(ValueSerializeWrapper::from).collect()))
491 }
492
493 pub fn from_pairs(pairs: Vec<(X,Y)>) -> Self {
495 Self::Data(DataVariant::Pair(pairs.into_iter().map(|(x,y)| (x.into(),y.into())).collect()))
496 }
497
498 pub fn from_named_value_pairs(named_pairs: Vec<NamedValuePair<X,Y>>) -> Self{
499 Self::Data(DataVariant::NamedPair(named_pairs))
500 }
501
502
503 pub fn with_named_values(named_values: Vec<NamedValue<X>>) -> Self {
505 Self::Data(DataVariant::Named(named_values))
506 }
507
508 pub fn from_labeled_values(values: Vec<(X, String)>) -> Self
509 {
510 let data_named_values = values.into_iter().map(|(x,label)| NamedValue{
511 value: x.into(),
512 name: label
513 }).collect();
514 Self::with_named_values(data_named_values)
515 }
516
517 pub fn from_tuples_with_label(values: Vec<(X, Y, String)>) -> Self
519 {
520 let data_named_pairs = values.into_iter()
521 .map(|(x, y,label) | NamedValuePair::new(x.into(), y.into(),label))
522 .collect();
523 Self::from_named_value_pairs(data_named_pairs)
524 }
525
526}
527
528
529#[derive(Serialize, Deserialize, Debug, Clone)]
530#[serde(rename_all = "camelCase")]
531pub enum DataPointSymbol {
532 Circle,
533 Rect,
534 RoundRect,
535 Triangle,
536 Diamond,
537 Pin,
538 Arrow,
539 None
540}
541
542#[derive(Serialize, Debug, Clone)]
544#[serde(rename_all = "camelCase")]
545pub struct Series<X:AxisKindMarker,Y:AxisKindMarker> {
546 #[serde(skip_serializing_if = "Option::is_none")]
548 pub r#type: Option<SeriesType>,
549
550 #[serde(skip_serializing_if = "Option::is_none")]
552 pub name: Option<String>,
553
554 #[serde(skip_serializing_if = "Option::is_none")]
555 pub smooth: Option<bool>,
556
557 #[serde(skip_serializing_if = "Option::is_none")]
558 pub area_style: Option<AreaStyle>,
559
560 #[serde(flatten)]
562 pub data: SeriesDataSource<X,Y>,
563
564 #[serde(skip_serializing_if = "Option::is_none")]
567 pub show_symbol: Option<bool>,
568
569 #[serde(skip_serializing_if = "Option::is_none")]
571 pub symbol: Option<DataPointSymbol>,
572
573 #[serde(skip_serializing_if = "Option::is_none")]
574 pub symbol_size: Option<usize>,
575
576 #[serde(flatten)]
578 pub extra: Option<Value>,
579}
580
581impl<X:AxisKindMarker,Y:AxisKindMarker> Series <X,Y>{
582 pub fn new(name:String, r#type: SeriesType, data: SeriesDataSource<X,Y>) -> Series<X,Y> {
583 Self{
584 r#type: Some(r#type),
585 name: Some(name),
586 smooth: None,
587 area_style: None,
588 data,
589 show_symbol: None,
590 symbol: None,
591 symbol_size: None,
592 extra: None,
593 }
594 }
595}
596
597
598#[derive(Serialize, Deserialize, Debug, Clone)]
599#[serde(rename_all = "camelCase")]
600pub enum AxisFillOrigin{
601 Auto,
602 Start,
603 End,
604 Number
605}
606
607#[derive(Serialize, Deserialize, Debug, Clone)]
608#[serde(rename_all = "camelCase")]
609pub struct AreaStyle{
610 #[serde(skip_serializing_if = "Option::is_none")]
611 color: Option<Value>,
612 #[serde(skip_serializing_if = "Option::is_none")]
613 origin: Option<AxisFillOrigin>
614}
615
616#[derive(Serialize, Deserialize, Debug, Clone)]
617#[serde(rename_all = "camelCase")]
618pub struct Transform{
619 pub transform: Vec<DatasetTransform>,
620
621 #[serde(skip_serializing_if = "Option::is_none")]
622 pub from_dataset_index: Option<usize>
623}
624
625#[derive(Serialize, Debug, Clone)]
626#[serde(rename_all = "camelCase")]
627pub struct Source<X:AxisKindMarker,Y:AxisKindMarker>{
628 pub source: Vec<(ValueSerializeWrapper<X>,ValueSerializeWrapper<Y>)>,
629}
630
631impl<X:AxisKindMarker,Y:AxisKindMarker> From<Vec<(X,Y)>> for Source<X,Y>{
632 fn from(value: Vec<(X,Y)>) -> Self {
633 Self{
634 source: value.into_iter().map(|(x,y)| (x.into(),y.into())).collect()
635 }
636 }
637}
638
639impl<const N:usize,X:AxisKindMarker,Y:AxisKindMarker> From<[(X,Y);N]> for Source<X,Y>
640{
641 fn from(value: [(X,Y);N]) -> Self {
642 Self{
643 source: value.into_iter().map(|(x,y)| (x.into(),y.into())).collect()
644 }
645 }
646}
647
648
649#[derive(Serialize, Debug, Clone)]
650#[serde(rename_all = "camelCase")]
651pub struct LabelledSource<X:AxisKindMarker,Y:AxisKindMarker>{
652 pub source: Vec<(ValueSerializeWrapper<X>,ValueSerializeWrapper<Y>,String)>,
653}
654
655
656
657
658
659#[derive(Serialize, Debug, Clone)]
661#[serde(rename_all = "camelCase")]
662#[serde(untagged)]
663pub enum DatasetComponent<X:AxisKindMarker,Y:AxisKindMarker> {
664 Source(Source<X,Y>),
665 LabelledSource(LabelledSource<X,Y>),
666 Transform(Transform)
667}
668
669
670impl<const N:usize,X:AxisKindMarker,Y:AxisKindMarker> From<[(X,Y);N]> for DatasetComponent<X,Y>
671{
672 fn from(value: [(X,Y);N]) -> Self {
673 DatasetComponent::Source(value.into())
674 }
675}
676
677impl<X:AxisKindMarker,Y:AxisKindMarker> From<Vec<(X,Y)>> for DatasetComponent<X,Y>
678{
679 fn from(value: Vec<(X, Y)>) -> Self {
680 DatasetComponent::src(value)
681 }
682}
683
684
685impl<const N:usize,X:AxisKindMarker,Y:AxisKindMarker> From<[(X,Y,String);N]> for DatasetComponent<X,Y>
686{
687 fn from(value: [(X,Y,String);N] ) -> Self {
688 Self::LabelledSource(
689 LabelledSource{
690 source: value.into_iter().map(|(x,y,label)| (x.into(),y.into(),label)).collect()
691 }
692 )
693 }
694}
695
696impl<X:AxisKindMarker,Y:AxisKindMarker> From<Vec<(X,Y,String)>> for DatasetComponent<X,Y>
697{
698 fn from(value: Vec<(X, Y, String)>) -> Self {
699 DatasetComponent::labelled_source(value)
700 }
701}
702
703
704impl<X:AxisKindMarker,Y:AxisKindMarker> DatasetComponent<X,Y> {
705 pub fn tr(transform: DatasetTransform, index: usize) -> Self{
706 Self::Transform(Transform {
707 transform: vec![transform],
708 from_dataset_index: Some(index),
709 })
710 }
711
712 pub fn trs(transform: Vec<DatasetTransform>, index: usize) -> Self{
713 Self::Transform(
714 Transform {
715 transform,
716 from_dataset_index: Some(index)
717 }
718 )
719 }
720
721 pub fn src(source: Vec<(X,Y)>) -> Self {
722 Self::Source(source.into())
723 }
724
725 pub fn labelled_source(source: Vec<(X,Y,String)>) -> Self {
727 Self::LabelledSource(
728 LabelledSource{
729 source: source.into_iter().map(|(x,y,label)| (x.into(),y.into(),label)).collect()
730 }
731 )
732 }
733}
734
735#[derive(Serialize, Deserialize, Debug, Clone)]
736#[serde(rename_all = "camelCase")]
737pub enum DatasetTransformType {
738 Filter,
739 Sort,
740 Boxplot,
741 #[serde(rename = "ecStat:regression")]
742 Regression,
743 #[serde(rename = "ecStat:clustering")]
744 Clustering
745}
746
747#[derive(Serialize, Deserialize, Debug, Clone)]
749#[serde(rename_all = "camelCase")]
750pub struct DatasetTransform {
751 r#type: DatasetTransformType,
753
754 config: DatasetTransformConfig,
756
757}
758
759
760impl DatasetTransform {
761
762 pub fn regression(config: RegressionConfig) -> Self {
763 Self{
764 r#type: DatasetTransformType::Regression,
765 config: DatasetTransformConfig::Regression(config)
766 }
767 }
768
769 pub fn clustering(clustering_config: ClusteringConfig)->Self{
770 Self{
771 r#type: DatasetTransformType::Clustering,
772 config: DatasetTransformConfig::Clustering(clustering_config)
773 }
774 }
775
776 pub fn sort(sort_config: SortConfig) -> Self {
777 Self{
778 r#type: DatasetTransformType::Sort,
779 config: DatasetTransformConfig::Sort(sort_config)
780 }
781 }
782
783}
784
785
786#[derive(Serialize, Deserialize, Debug, Clone)]
787#[serde(untagged)]
788enum DatasetTransformConfig {
789 Regression(RegressionConfig),
790 Clustering(ClusteringConfig),
791 Sort(SortConfig)
792}
793
794
795#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
797#[serde(rename_all = "lowercase")]
798pub enum RegressionMethod {
799 Linear,
800 Exponential,
801 Logarithmic,
802 Polynomial,
803}
804
805#[derive(Serialize, Deserialize, Debug, Clone)]
806#[serde(rename_all = "camelCase")]
807pub struct SortConfig {
808 dimension: u8,
809 order : Order
810}
811
812#[derive(Serialize, Deserialize, Debug, Clone)]
813#[serde(rename_all = "camelCase")]
814pub enum Order {
815 Asc,
816 Desc
817}
818
819
820#[derive(Serialize, Deserialize, Debug, Clone)]
822#[serde(rename_all = "camelCase")]
823pub struct RegressionConfig {
824 pub method: RegressionMethod,
826
827 #[serde(skip_serializing_if = "Option::is_none")]
829 pub order: Option<u8>,
830
831 #[serde(flatten)]
833 pub extra: Option<Value>,
834}
835
836
837
838#[derive(Serialize, Deserialize, Debug, Clone)]
840#[serde(rename_all = "camelCase")]
841pub struct ClusteringConfig {
842
843 pub cluster_count: u8,
845
846 pub output_cluster_index_dimension: u8,
848
849 pub dimensions : Option<Vec<usize>>,
851
852}