apache_echarts_wrapper/
options.rs

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/// Root object for ECharts configuration
8#[derive(Serialize, Debug, Clone)]
9#[serde(rename_all = "camelCase")]
10pub struct EChartOptions<X:AxisKindMarker,Y:AxisKindMarker> {
11    /// Chart title options
12    #[serde(skip_serializing_if = "Option::is_none")]
13    pub(crate) title: Option<Title>,
14
15    /// Grid positioning and style options
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub grid: Option<Grid>,
18
19    /// Tooltip options
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub tooltip: Option<Tooltip>,
22
23    /// Legend options
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub legend: Option<Legend>,
26
27    /// Dataset component for providing data
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub(crate) dataset: Option<Vec<DatasetComponent<X,Y>>>,
30
31    /// X-axis options (Cartesian charts)
32    pub x_axis: Axis<X>,
33
34    /// Y-axis options (Cartesian charts)
35    pub y_axis: Axis<Y>,
36
37    /// Series data
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub(crate) series: Option<Vec<Series<X,Y>>>,
40
41    /// Additional raw options not covered by this binding
42    #[serde(flatten)]
43    pub extra: Option<Value>,
44}
45
46/// Keyword positions supported by ECharts
47#[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/// Position enum supporting keyword, numeric px, percent, or other strings
61#[derive(Serialize, Deserialize, Debug, Clone)]
62#[serde(untagged)]
63pub enum Position {
64    /// Predefined keyword position
65    Keyword(PositionKeyword),
66    /// Numeric pixel value
67    Number(f64),
68    /// Percentage value (e.g., 50 => "50%")
69    Percent(Percent)
70}
71
72/// Title component
73#[derive(Serialize, Deserialize, Debug, Clone,Default)]
74#[serde(rename_all = "camelCase")]
75pub struct Title {
76    /// Main title text
77    pub text: Option<String>,
78
79    /// Subtitle text
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub sub_text: Option<String>,
82
83    /// Link for title
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub link: Option<String>,
86
87    /// Left position (Keyword, numeric px, percent, or other)
88    #[serde(skip_serializing_if = "Option::is_none")]
89    pub left: Option<Position>,
90
91    /// Top position (Keyword, numeric px, percent, or other)
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub top: Option<Position>,
94
95    /// Right position (Keyword, numeric px, percent, or other)
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub right: Option<Position>,
98
99    /// Bottom position (Keyword, numeric px, percent, or other)
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub bottom: Option<Position>,
102
103    /// Additional raw title options
104    #[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/// Grid component
124#[derive(Serialize, Deserialize, Debug, Clone)]
125#[serde(rename_all = "camelCase")]
126pub struct Grid {
127    /// Distance between grid and left side (Keyword, numeric px, percent, or other)
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub left: Option<Position>,
130
131    /// Distance between grid and right side (Keyword, numeric px, percent, or other)
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub right: Option<Position>,
134
135    /// Distance between grid and top side (Keyword, numeric px, percent, or other)
136    #[serde(skip_serializing_if = "Option::is_none")]
137    pub top: Option<Position>,
138
139    /// Distance between grid and bottom side (Keyword, numeric px, percent, or other)
140    #[serde(skip_serializing_if = "Option::is_none")]
141    pub bottom: Option<Position>,
142
143    /// Whether the grid area contain the axis labels
144    #[serde(skip_serializing_if = "Option::is_none")]
145    pub contain_label: Option<bool>,
146
147    /// Background color of the grid
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub background_color: Option<Value>,
150
151    /// Border color of the grid
152    #[serde(skip_serializing_if = "Option::is_none")]
153    pub border_color: Option<Value>,
154
155    /// Border width of the grid
156    #[serde(skip_serializing_if = "Option::is_none")]
157    pub border_width: Option<f64>,
158
159    /// Show the border of the grid
160    #[serde(skip_serializing_if = "Option::is_none")]
161    pub show: Option<bool>,
162
163    /// z-index of the grid component
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub z: Option<i32>,
166
167    /// z-level of the grid component
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub zlevel: Option<i32>,
170
171    /// Additional raw grid options
172    #[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/// Tooltip component
223#[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    /// Trigger mode: item, axis
235    #[serde(skip_serializing_if = "Option::is_none")]
236    pub trigger: Option<TooltipTrigger>,
237
238    /// Tooltip formatter template or callback
239    #[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/// Legend component
255#[derive(Serialize, Deserialize, Debug, Clone)]
256#[serde(rename_all = "camelCase")]
257pub struct Legend {
258    /// Data items in the legend
259    #[serde(skip_serializing_if = "Option::is_none")]
260    pub data: Option<Vec<String>>,
261
262    /// Legend orientation: vertical or horizontal
263    #[serde(skip_serializing_if = "Option::is_none")]
264    pub orient: Option<LegendOrient>,
265
266    /// Left position (Keyword, numeric px, percent, or other)
267    #[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/// Axis types supported by ECharts
295#[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/// Axis (cartesian)
330#[derive(Serialize, Debug, Clone)]
331#[serde(rename_all = "camelCase")]
332pub struct Axis<T:AxisKindMarker> {
333    /// Axis type: value, category, time, log
334    pub(crate) r#type: AxisType,
335
336    #[serde(skip_serializing_if = "Option::is_none")]
337    pub(crate) inverse: Option<bool>,
338
339    /// Axis name
340    #[serde(skip_serializing_if = "Option::is_none")]
341    pub(crate) name: Option<String>,
342
343    /// Data for category axis
344    #[serde(skip_serializing_if = "Option::is_none")]
345    pub data: Option<Vec<ValueSerializeWrapper<T>>>,
346
347    /// Additional raw axis options
348    #[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/// Available series types in ECharts
400#[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    /// Single dimension data (string/int/float)
432    Data(Vec<ValueSerializeWrapper<X>>),
433
434    /// Two-dimensional data: [x, y]
435    Pair(Vec<(ValueSerializeWrapper<X>,ValueSerializeWrapper<Y>)>),
436
437    /// Single dimension data as an object with a name field
438    Named(Vec<NamedValue<X>>),
439
440    /// Two-dimensional data with the name: { value: [x, y], name: "str" }
441    NamedPair(Vec<NamedValuePair<X,Y>>),
442}
443
444/// Internal enum to represent the data source for a series
445#[derive(Serialize, Debug, Clone)]
446#[serde(rename_all = "camelCase")]
447pub enum SeriesDataSource<X:AxisKindMarker,Y:AxisKindMarker>{
448    /// Direct data
449    Data(DataVariant<X,Y>),
450    /// Reference to a dataset by index
451    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    /// Creates a new SeriesDataSource that references a dataset by index
484    pub fn from_dataset_index(index: usize) -> Self {
485        Self::DatasetIndex(index)
486    }
487
488    /// Creates a SeriesDataSource with a simple array of values
489    pub fn from_values(values: Vec<X>) -> Self {
490        Self::Data(DataVariant::Data(values.into_iter().map(ValueSerializeWrapper::from).collect()))
491    }
492
493    /// Creates a SeriesDataSource with an array of [x,y] pairs
494    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    /// Creates a SeriesDataSource with named values
504    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    /// Creates a SeriesDataSource with named pairs
518    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/// Series definition
543#[derive(Serialize, Debug, Clone)]
544#[serde(rename_all = "camelCase")]
545pub struct Series<X:AxisKindMarker,Y:AxisKindMarker> {
546    /// Chart type
547    #[serde(skip_serializing_if = "Option::is_none")]
548    pub r#type: Option<SeriesType>,
549
550    /// Series name
551    #[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    /// Data array
561    #[serde(flatten)]
562    pub data: SeriesDataSource<X,Y>,
563
564    /// if showSymbol is false symbol will still show on hover
565    /// if the tooltip trigger is an axis
566    #[serde(skip_serializing_if = "Option::is_none")]
567    pub show_symbol: Option<bool>,
568
569    ///if the show symbol is none, line won't react on hover anymore
570    #[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    /// Additional raw series options
577    #[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/// Dataset component for providing and transforming data
660#[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    /// Constructor for the labeled source. Always put the label last
726    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/// Transform applied to a dataset
748#[derive(Serialize, Deserialize, Debug, Clone)]
749#[serde(rename_all = "camelCase")]
750pub struct DatasetTransform {
751    /// Transform type
752    r#type: DatasetTransformType,
753
754    /// Transform configuration
755    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/// regression methods supported by ecStat
796#[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/// Configuration for regression transforms
821#[derive(Serialize, Deserialize, Debug, Clone)]
822#[serde(rename_all = "camelCase")]
823pub struct RegressionConfig {
824    /// Regression method
825    pub method: RegressionMethod,
826
827    /// Polynomial order (only used when the method is Polynomial)
828    #[serde(skip_serializing_if = "Option::is_none")]
829    pub order: Option<u8>,
830
831    /// Additional raw regression config options
832    #[serde(flatten)]
833    pub extra: Option<Value>,
834}
835
836
837
838/// Configuration for clustering transforms
839#[derive(Serialize, Deserialize, Debug, Clone)]
840#[serde(rename_all = "camelCase")]
841pub struct ClusteringConfig {
842
843    /// The number of clusters to generate (must be > 1).
844    pub cluster_count: u8,
845
846    ///dimension to which the cluster index will be written
847    pub output_cluster_index_dimension: u8,
848
849    /// dimensions of source data that will be used in calculation of a cluster
850    pub dimensions : Option<Vec<usize>>,
851
852}