chart_js_rs/objects/
chart_objects.rs

1use {
2    crate::{objects::helper_objects::*, traits::*, utils::FnWithArgs},
3    serde::{de, Deserialize, Serialize},
4    serde_json::Value,
5    std::{collections::HashMap, fmt::Debug},
6};
7
8#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
9pub struct SinglePointDataset {
10    #[serde(skip_serializing_if = "Vec::is_empty", default)]
11    pub(crate) backgroundColor: Vec<String>,
12    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
13    pub(crate) barPercentage: NumberString,
14    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
15    pub(crate) barThickness: NumberString,
16    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
17    pub(crate) base: NumberString,
18    #[serde(skip_serializing_if = "String::is_empty", default)]
19    pub(crate) borderColor: String,
20    #[serde(skip_serializing_if = "String::is_empty", default)]
21    pub(crate) borderJoinStyle: String,
22    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
23    pub(crate) borderRadius: NumberString,
24    #[serde(skip_serializing_if = "String::is_empty", default)]
25    pub(crate) borderSkipped: String,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub(crate) borderWidth: Option<NumberStringOrT<Border>>,
28    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
29    pub(crate) categoryPercentage: NumberString,
30    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
31    pub(crate) clip: NumberString,
32    #[serde(skip_serializing_if = "Vec::is_empty", default)]
33    pub(crate) data: Vec<NumberString>,
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub(crate) datalabels: Option<DataLabels>,
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub(crate) grouped: Option<bool>,
38    #[serde(skip_serializing_if = "String::is_empty", default)]
39    pub(crate) hoverBackgroundColor: String,
40    #[serde(skip_serializing_if = "String::is_empty", default)]
41    pub(crate) hoverBorderColor: String,
42    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
43    pub(crate) hoverBorderRadius: NumberString,
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub(crate) hoverBorderWidth: Option<NumberStringOrT<Border>>,
46    #[serde(skip_serializing_if = "String::is_empty", default)]
47    pub(crate) indexAxis: String,
48    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
49    pub(crate) inflateAmount: NumberString,
50    #[serde(skip_serializing_if = "String::is_empty", default)]
51    pub(crate) label: String,
52    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
53    pub(crate) maxBarThickness: NumberString,
54    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
55    pub(crate) minBarLength: NumberString,
56    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
57    pub(crate) order: NumberString,
58    #[serde(skip_serializing_if = "String::is_empty", default)]
59    pub(crate) pointBackgroundColor: String,
60    #[serde(skip_serializing_if = "String::is_empty", default)]
61    pub(crate) pointBorderColor: String,
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub(crate) pointBorderWidth: Option<NumberStringOrT<Border>>,
64    #[serde(skip_serializing_if = "String::is_empty", default)]
65    pub(crate) pointHoverBackgroundColor: String,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub(crate) pointHoverBorderWidth: Option<NumberStringOrT<Border>>,
68    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
69    pub(crate) pointHoverRadius: NumberOrDateString,
70    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
71    pub(crate) pointRadius: NumberString,
72    #[serde(skip_serializing_if = "String::is_empty", default)]
73    pub(crate) pointStyle: String,
74    #[serde(rename = "type")]
75    #[serde(skip_serializing_if = "String::is_empty", default)]
76    pub(crate) r#type: String,
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub(crate) skipNull: Option<bool>,
79    #[serde(skip_serializing_if = "String::is_empty", default)]
80    pub(crate) stack: String,
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub(crate) stepped: Option<bool>,
83    #[serde(skip_serializing_if = "String::is_empty", default)]
84    pub(crate) xAxisID: String,
85    #[serde(skip_serializing_if = "String::is_empty", default)]
86    pub(crate) yAxisID: String,
87}
88
89#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
90pub struct XYDataset {
91    #[serde(skip_serializing_if = "FnWithArgsOrAny::is_empty", default)]
92    pub(crate) backgroundColor: FnWithArgsOrAny<2>,
93    #[serde(
94        skip_serializing_if = "Vec::is_empty",
95        default,
96        rename(serialize = "backgroundColor")
97    )]
98    pub(crate) backgroundColorArray: Vec<String>,
99    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
100    pub(crate) barPercentage: NumberString,
101    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
102    pub(crate) barThickness: NumberString,
103    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
104    pub(crate) base: NumberString,
105    #[serde(skip_serializing_if = "String::is_empty", default)]
106    pub(crate) borderColor: String,
107    #[serde(skip_serializing_if = "Vec::is_empty", default)]
108    pub(crate) borderDash: Vec<NumberString>,
109    #[serde(skip_serializing_if = "String::is_empty", default)]
110    pub(crate) borderJoinStyle: String,
111    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
112    pub(crate) borderRadius: NumberString,
113    #[serde(skip_serializing_if = "String::is_empty", default)]
114    pub(crate) borderSkipped: String,
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub(crate) borderWidth: Option<NumberStringOrT<Border>>,
117    #[serde(skip_serializing_if = "String::is_empty", default)]
118    pub(crate) category_label: String,
119    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
120    pub(crate) categoryPercentage: NumberString,
121    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
122    pub(crate) clip: NumberString,
123    #[serde(skip_serializing_if = "DatasetData::is_empty", default)]
124    pub(crate) data: DatasetData,
125    /// Use Default::default() if this isn't required
126    pub(crate) datalabels: DataLabels,
127    #[serde(skip_serializing_if = "String::is_empty", default)]
128    pub(crate) description: String,
129    #[serde(skip_serializing_if = "String::is_empty", default)]
130    pub(crate) fill: String,
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub(crate) grouped: Option<bool>,
133    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
134    pub(crate) hitRadius: NumberString,
135    #[serde(skip_serializing_if = "String::is_empty", default)]
136    pub(crate) hoverBackgroundColor: String,
137    #[serde(skip_serializing_if = "String::is_empty", default)]
138    pub(crate) hoverBorderColor: String,
139    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
140    pub(crate) hoverBorderRadius: NumberString,
141    #[serde(skip_serializing_if = "Option::is_none")]
142    pub(crate) hoverBorderWidth: Option<NumberStringOrT<Border>>,
143    #[serde(skip_serializing_if = "String::is_empty", default)]
144    pub(crate) axis: String,
145    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
146    pub(crate) inflateAmount: NumberString,
147    #[serde(skip_serializing_if = "String::is_empty", default)]
148    pub(crate) label: String,
149    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
150    pub(crate) maxBarThickness: NumberString,
151    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
152    pub(crate) minBarLength: NumberString,
153    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
154    pub(crate) order: NumberString,
155    #[serde(skip_serializing_if = "String::is_empty", default)]
156    pub(crate) pointBackgroundColor: String,
157    #[serde(skip_serializing_if = "String::is_empty", default)]
158    pub(crate) pointBorderColor: String,
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub(crate) pointBorderWidth: Option<NumberStringOrT<Border>>,
161    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
162    pub(crate) pointHitRadius: NumberString,
163    #[serde(skip_serializing_if = "String::is_empty", default)]
164    pub(crate) pointHoverBackgroundColor: String,
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub(crate) pointHoverBorderWidth: Option<NumberStringOrT<Border>>,
167    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
168    pub(crate) pointHoverRadius: NumberOrDateString,
169    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
170    pub(crate) pointRadius: NumberString,
171    #[serde(skip_serializing_if = "String::is_empty", default)]
172    pub(crate) pointStyle: String,
173    #[serde(skip_serializing_if = "Option::is_none")]
174    pub(crate) segment: Option<Segment>,
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pub(crate) skipNull: Option<bool>,
177    #[serde(skip_serializing_if = "Option::is_none")]
178    pub(crate) spanGaps: Option<bool>,
179    #[serde(skip_serializing_if = "String::is_empty", default)]
180    pub(crate) stack: String,
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub(crate) stepped: Option<BoolString>,
183    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
184    pub(crate) tension: NumberString,
185    #[serde(rename = "type")]
186    #[serde(skip_serializing_if = "String::is_empty", default)]
187    pub(crate) r#type: String,
188    #[serde(skip_serializing_if = "String::is_empty", default)]
189    pub(crate) xAxisID: String,
190    #[serde(skip_serializing_if = "String::is_empty", default)]
191    pub(crate) yAxisID: String,
192    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
193    pub(crate) z: NumberString,
194}
195
196#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq)]
197pub struct XYPoint {
198    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
199    pub(crate) x: NumberOrDateString,
200
201    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
202    pub(crate) y: NumberString,
203
204    #[serde(skip_serializing_if = "serde_json::Value::is_null", default)]
205    pub(crate) description: serde_json::Value,
206}
207
208#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq)]
209pub struct ChartOptions<A: Annotation> {
210    #[serde(skip_serializing_if = "Option::is_none")]
211    pub(crate) animation: Option<Animation>,
212    #[serde(skip_serializing_if = "Option::is_none")]
213    pub(crate) elements: Option<ChartElements>,
214    #[serde(skip_serializing_if = "Option::is_none")]
215    pub(crate) interaction: Option<ChartInteraction>,
216    #[serde(skip_serializing_if = "String::is_empty", default)]
217    pub(crate) indexAxis: String,
218    #[serde(skip_serializing_if = "Option::is_none")]
219    pub(crate) legend: Option<ChartLegend>,
220    #[serde(skip_serializing_if = "Option::is_none")]
221    pub(crate) layout: Option<ChartLayout>,
222    #[serde(skip_serializing_if = "Option::is_none")]
223    pub(crate) maintainAspectRatio: Option<bool>,
224    #[serde(skip_serializing_if = "Option::is_none")]
225    pub(crate) plugins: Option<ChartPlugins<A>>,
226    #[serde(skip_serializing_if = "Option::is_none")]
227    pub(crate) responsive: Option<bool>,
228    #[serde(skip_serializing_if = "Option::is_none")]
229    pub(crate) scales: Option<HashMap<String, ChartScale>>,
230    #[serde(skip_serializing_if = "Option::is_none")]
231    pub(crate) spanGaps: Option<bool>,
232}
233
234#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
235pub struct Animation {
236    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
237    pub(crate) duration: NumberString,
238}
239
240#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq)]
241pub struct ChartPlugins<A: Annotation> {
242    #[serde(skip_serializing_if = "Option::is_none")]
243    pub(crate) annotation: Option<Annotations<A>>,
244    #[serde(skip_serializing_if = "Option::is_none")]
245    pub(crate) autocolors: Option<AutoColors>,
246    #[serde(skip_serializing_if = "Option::is_none")]
247    pub(crate) legend: Option<PluginLegend>,
248    #[serde(skip_serializing_if = "Option::is_none")]
249    pub(crate) title: Option<Title>,
250    #[serde(skip_serializing_if = "Option::is_none")]
251    pub(crate) tooltip: Option<TooltipPlugin>,
252}
253
254#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
255pub struct PluginLegend {
256    #[serde(skip_serializing_if = "Option::is_none")]
257    pub(crate) display: Option<bool>,
258    #[serde(skip_serializing_if = "Option::is_none")]
259    pub(crate) labels: Option<LegendLabel>,
260    #[serde(skip_serializing_if = "String::is_empty", default)]
261    pub(crate) position: String,
262    #[serde(skip_serializing_if = "Option::is_none")]
263    pub(crate) reverse: Option<bool>,
264}
265
266#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq)]
267pub struct Annotations<A: Annotation> {
268    #[serde(skip_serializing_if = "Option::is_none")]
269    pub(crate) annotations: Option<HashMap<String, A>>,
270}
271
272#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
273pub struct AutoColors {
274    #[serde(skip_serializing_if = "String::is_empty", default)]
275    pub(crate) mode: String,
276}
277
278#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
279pub struct TooltipPlugin {
280    #[serde(skip_serializing_if = "String::is_empty", default)]
281    pub(crate) backgroundColor: String,
282    #[serde(skip_serializing_if = "String::is_empty", default)]
283    pub(crate) bodyAlign: String,
284    #[serde(skip_serializing_if = "String::is_empty", default)]
285    pub(crate) bodyColor: String,
286    #[serde(skip_serializing_if = "Option::is_none")]
287    pub(crate) callbacks: Option<TooltipCallbacks>,
288    #[serde(skip_serializing_if = "FnWithArgs::is_empty", skip_deserializing)]
289    // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
290    pub(crate) filter: FnWithArgs<1>,
291    #[serde(skip_serializing_if = "Option::is_none")]
292    pub(crate) displayColors: Option<bool>,
293    #[serde(skip_serializing_if = "Option::is_none")]
294    pub(crate) enabled: Option<bool>,
295    #[serde(skip_serializing_if = "String::is_empty", default)]
296    pub(crate) titleAlign: String,
297    #[serde(skip_serializing_if = "String::is_empty", default)]
298    pub(crate) titleColor: String,
299    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
300    pub(crate) titleMarginBottom: NumberString,
301}
302
303#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
304pub struct ChartLayout {
305    #[serde(skip_serializing_if = "Option::is_none")]
306    pub(crate) padding: Option<Padding>,
307}
308
309#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
310pub struct TooltipCallbacks {
311    #[serde(skip_serializing_if = "FnWithArgs::is_empty", skip_deserializing)]
312    // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
313    pub(crate) label: FnWithArgs<1>,
314    #[serde(skip_serializing_if = "FnWithArgs::is_empty", skip_deserializing)]
315    // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
316    pub(crate) title: FnWithArgs<1>,
317}
318
319#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
320pub struct ChartScale {
321    #[serde(skip_serializing_if = "FnWithArgs::is_empty", skip_deserializing)]
322    // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
323    pub(crate) afterBuildTicks: FnWithArgs<1>,
324    #[serde(skip_serializing_if = "Option::is_none")]
325    pub(crate) alignToPixels: Option<bool>,
326    #[serde(skip_serializing_if = "String::is_empty", default)]
327    pub(crate) backgroundColor: String,
328    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
329    pub(crate) barPercentage: NumberString,
330    #[serde(skip_serializing_if = "FnWithArgs::is_empty", skip_deserializing)]
331    // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
332    pub(crate) beforeFit: FnWithArgs<1>,
333    #[serde(skip_serializing_if = "Option::is_none")]
334    pub(crate) beginAtZero: Option<bool>,
335    #[serde(skip_serializing_if = "Option::is_none")]
336    pub(crate) border: Option<ScaleBorder>,
337    #[serde(skip_serializing_if = "String::is_empty", default)]
338    pub(crate) bounds: String,
339    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
340    pub(crate) categoryPercentage: NumberString,
341    #[serde(skip_serializing_if = "Option::is_none")]
342    pub(crate) display: Option<bool>,
343    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
344    pub(crate) grace: NumberOrDateString,
345    #[serde(skip_serializing_if = "Option::is_none")]
346    pub(crate) grid: Option<Grid>,
347    #[serde(skip_serializing_if = "Option::is_none")]
348    pub(crate) grouped: Option<bool>,
349    #[serde(skip_serializing_if = "Option::is_none")]
350    pub(crate) labels: Option<Vec<NumberOrDateString>>,
351    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
352    pub(crate) max: NumberOrDateString,
353    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
354    pub(crate) min: NumberOrDateString,
355    #[serde(skip_serializing_if = "Option::is_none")]
356    pub(crate) offset: Option<bool>,
357    #[serde(skip_serializing_if = "String::is_empty", default)]
358    pub(crate) position: String,
359    #[serde(skip_serializing_if = "Option::is_none")]
360    pub(crate) reverse: Option<bool>,
361    #[serde(skip_serializing_if = "Option::is_none")]
362    pub(crate) stacked: Option<bool>,
363    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
364    pub(crate) suggestedMax: NumberOrDateString,
365    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
366    pub(crate) suggestedMin: NumberOrDateString,
367    #[serde(skip_serializing_if = "Option::is_none")]
368    pub(crate) ticks: Option<ScaleTicks>,
369    #[serde(skip_serializing_if = "Option::is_none")]
370    pub(crate) time: Option<ScaleTime>,
371    #[serde(skip_serializing_if = "Option::is_none")]
372    pub(crate) title: Option<Title>,
373    #[serde(skip_serializing_if = "String::is_empty", default)]
374    #[serde(rename = "type")]
375    pub(crate) r#type: String,
376    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
377    pub(crate) weight: NumberString,
378}
379
380#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
381pub struct ScaleBorder {
382    #[serde(skip_serializing_if = "String::is_empty", default)]
383    pub(crate) color: String,
384    #[serde(skip_serializing_if = "Vec::is_empty", default)]
385    pub(crate) dash: Vec<NumberString>,
386    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
387    pub(crate) dashOffset: NumberString,
388    #[serde(skip_serializing_if = "Option::is_none")]
389    pub(crate) display: Option<bool>,
390    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
391    pub(crate) width: NumberString,
392    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
393    pub(crate) z: NumberString,
394}
395
396#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
397pub struct Grid {
398    #[serde(skip_serializing_if = "String::is_empty", default)]
399    pub(crate) color: String,
400    #[serde(skip_serializing_if = "Option::is_none")]
401    pub(crate) display: Option<bool>,
402    #[serde(skip_serializing_if = "Option::is_none")]
403    pub(crate) drawOnChartArea: Option<bool>,
404    #[serde(skip_serializing_if = "String::is_empty", default, skip_deserializing)]
405    // the skip_deserializing needed because chartjs sets a default with a different type, FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
406    pub(crate) tickColor: String,
407    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
408    pub(crate) z: NumberString,
409}
410
411#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
412pub struct LineAnnotation {
413    #[serde(skip_serializing_if = "String::is_empty", default)]
414    pub(crate) borderColor: String,
415    #[serde(skip_serializing_if = "Vec::is_empty", default)]
416    pub(crate) borderDash: Vec<NumberString>,
417    #[serde(skip_serializing_if = "Option::is_none")]
418    pub(crate) borderWidth: Option<NumberStringOrT<Border>>,
419    #[serde(skip_serializing_if = "String::is_empty", default)]
420    pub(crate) drawTime: String,
421    #[serde(default, rename = "type")]
422    pub(crate) r#type: LineAnnotationType,
423    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
424    pub(crate) xMax: NumberOrDateString,
425    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
426    pub(crate) xMin: NumberOrDateString,
427    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
428    pub(crate) yMax: NumberOrDateString,
429    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
430    pub(crate) yMin: NumberOrDateString,
431    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
432    pub(crate) yScaleID: NumberString,
433}
434#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
435pub struct LineAnnotationType;
436
437#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
438pub struct BoxAnnotation {
439    #[serde(skip_serializing_if = "String::is_empty", default)]
440    pub(crate) backgroundColor: String,
441    #[serde(skip_serializing_if = "String::is_empty", default)]
442    pub(crate) borderColor: String,
443    #[serde(skip_serializing_if = "Vec::is_empty", default)]
444    pub(crate) borderDash: Vec<NumberString>,
445    #[serde(skip_serializing_if = "Option::is_none")]
446    pub(crate) borderWidth: Option<NumberStringOrT<Border>>,
447    #[serde(skip_serializing_if = "String::is_empty", default)]
448    pub(crate) drawTime: String,
449    #[serde(default, rename = "type")]
450    pub(crate) r#type: BoxAnnotationType,
451    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
452    pub(crate) xMax: NumberString,
453    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
454    pub(crate) xMin: NumberString,
455    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
456    pub(crate) yMax: NumberString,
457    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
458    pub(crate) yMin: NumberString,
459}
460#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
461pub struct BoxAnnotationType;
462
463#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
464pub struct ScaleTime {
465    #[serde(skip_serializing_if = "Option::is_none")]
466    pub(crate) displayFormats: Option<DisplayFormats>,
467    #[serde(skip_serializing_if = "String::is_empty", default)]
468    pub(crate) unit: String,
469}
470
471#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
472pub struct DisplayFormats {
473    #[serde(skip_serializing_if = "String::is_empty", default)]
474    pub(crate) day: String,
475    #[serde(skip_serializing_if = "String::is_empty", default)]
476    pub(crate) hour: String,
477    #[serde(skip_serializing_if = "String::is_empty", default)]
478    pub(crate) minute: String,
479    #[serde(skip_serializing_if = "String::is_empty", default)]
480    pub(crate) month: String,
481    #[serde(skip_serializing_if = "String::is_empty", default)]
482    pub(crate) quarter: String,
483    #[serde(skip_serializing_if = "String::is_empty", default)]
484    pub(crate) week: String,
485    #[serde(skip_serializing_if = "String::is_empty", default)]
486    pub(crate) year: String,
487}
488
489#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
490pub struct ScaleTicks {
491    #[serde(skip_serializing_if = "Option::is_none")]
492    pub(crate) autoSkip: Option<bool>,
493    #[serde(skip_serializing_if = "String::is_empty", default)]
494    pub(crate) align: String,
495    #[serde(
496        skip_serializing_if = "FnWithArgs::is_empty",
497        default,
498        skip_deserializing // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
499    )]
500    pub(crate) callback: FnWithArgs<3>,
501    #[serde(skip_serializing_if = "String::is_empty", default)]
502    pub(crate) color: String,
503    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
504    pub(crate) count: NumberString,
505    #[serde(skip_serializing_if = "Option::is_none")]
506    pub(crate) font: Option<Font>,
507    #[serde(skip_serializing_if = "Option::is_none")]
508    pub(crate) includeBounds: Option<bool>,
509    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
510    pub(crate) maxTicksLimit: NumberString,
511    #[serde(skip_serializing_if = "Option::is_none", skip_deserializing)]
512    // the skip_deserializing needed because chartjs sets a default with a different type, FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
513    pub(crate) padding: Option<Padding>,
514    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
515    pub(crate) precision: NumberString,
516    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
517    pub(crate) stepSize: NumberString,
518}
519
520#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
521pub struct Title {
522    #[serde(skip_serializing_if = "String::is_empty", default)]
523    pub(crate) color: String,
524    #[serde(skip_serializing_if = "Option::is_none")]
525    pub(crate) display: Option<bool>,
526    #[serde(skip_serializing_if = "Option::is_none")]
527    pub(crate) font: Option<Font>,
528    #[serde(skip_serializing_if = "String::is_empty", default)]
529    pub(crate) text: String,
530}
531
532#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
533pub struct ChartInteraction {
534    #[serde(skip_serializing_if = "String::is_empty", default)]
535    pub(crate) axis: String,
536    #[serde(skip_serializing_if = "Option::is_none")]
537    pub(crate) intersect: Option<bool>,
538    #[serde(skip_serializing_if = "String::is_empty", default)]
539    pub(crate) mode: String,
540}
541
542#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
543pub struct ChartLegend {
544    #[serde(skip_serializing_if = "Option::is_none")]
545    pub(crate) display: Option<bool>,
546    #[serde(skip_serializing_if = "Option::is_none")]
547    pub(crate) labels: Option<LegendLabel>,
548    #[serde(skip_serializing_if = "String::is_empty", default)]
549    pub(crate) position: String,
550}
551
552#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
553pub struct LegendLabel {
554    #[serde(skip_serializing_if = "Option::is_none")]
555    pub(crate) boxHeight: Option<u32>,
556    #[serde(skip_serializing_if = "Option::is_none")]
557    pub(crate) boxWidth: Option<u32>,
558    #[serde(skip_serializing_if = "String::is_empty", default)]
559    pub(crate) color: String,
560    #[serde(skip_serializing_if = "FnWithArgs::is_empty", skip_deserializing)]
561    // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
562    pub(crate) filter: FnWithArgs<2>,
563    #[serde(skip_serializing_if = "Option::is_none")]
564    pub(crate) font: Option<Font>,
565    #[serde(skip_serializing_if = "String::is_empty", default)]
566    pub(crate) pointStyle: String,
567    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
568    pub(crate) pointStyleWidth: NumberString,
569    #[serde(skip_serializing_if = "Option::is_none")]
570    pub(crate) useBorderRadius: Option<bool>,
571    #[serde(skip_serializing_if = "Option::is_none")]
572    pub(crate) usePointStyle: Option<bool>,
573}
574
575#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
576pub struct ChartElements {
577    #[serde(skip_serializing_if = "Option::is_none")]
578    pub(crate) bar: Option<BarElementConfiguration>,
579    #[serde(skip_serializing_if = "Option::is_none")]
580    pub(crate) line: Option<LineElementConfiguration>,
581    #[serde(skip_serializing_if = "Option::is_none")]
582    pub(crate) point: Option<PointElementConfiguration>,
583}
584
585#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
586pub struct BarElementConfiguration {
587    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
588    pub(crate) borderRadius: NumberString,
589    #[serde(skip_serializing_if = "Option::is_none")]
590    pub(crate) borderWidth: Option<NumberStringOrT<Border>>,
591    #[serde(skip_serializing_if = "Option::is_none")]
592    pub(crate) fill: Option<bool>,
593    #[serde(skip_serializing_if = "Option::is_none")]
594    pub(crate) hoverBorderWidth: Option<NumberStringOrT<Border>>,
595}
596
597#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
598pub struct LineElementConfiguration {
599    #[serde(skip_serializing_if = "Option::is_none")]
600    pub(crate) borderWidth: Option<NumberStringOrT<Border>>,
601    #[serde(skip_serializing_if = "String::is_empty", default)]
602    pub(crate) cubicInterpolationMode: String,
603    #[serde(skip_serializing_if = "Option::is_none")]
604    pub(crate) fill: Option<bool>,
605}
606
607#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
608pub struct PointElementConfiguration {
609    #[serde(skip_serializing_if = "Option::is_none")]
610    pub(crate) borderWidth: Option<NumberStringOrT<Border>>,
611    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
612    pub(crate) hitRadius: NumberString,
613    #[serde(skip_serializing_if = "Option::is_none")]
614    pub(crate) hoverBorderWidth: Option<NumberStringOrT<Border>>,
615    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
616    pub(crate) hoverRadius: NumberString,
617    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
618    pub(crate) radius: NumberString,
619}
620
621#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
622pub struct DataLabels {
623    #[serde(skip_serializing_if = "FnWithArgsOrAny::is_empty", default)]
624    pub(crate) align: FnWithArgsOrAny<1>,
625    #[serde(skip_serializing_if = "FnWithArgsOrAny::is_empty", default)]
626    pub(crate) anchor: FnWithArgsOrAny<1>,
627    #[serde(skip_serializing_if = "FnWithArgsOrAny::is_empty", default)]
628    pub(crate) backgroundColor: FnWithArgsOrAny<1>,
629    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
630    pub(crate) borderRadius: NumberString,
631    #[serde(skip_serializing_if = "Option::is_none")]
632    pub(crate) clamp: Option<bool>,
633    #[serde(skip_serializing_if = "Option::is_none")]
634    pub(crate) clip: Option<bool>,
635    #[serde(skip_serializing_if = "String::is_empty", default)]
636    pub(crate) color: String,
637    #[serde(default = "BoolString::_false")]
638    pub(crate) display: BoolString,
639    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
640    pub(crate) drawTime: NumberString,
641    #[serde(skip_serializing_if = "Option::is_none")]
642    pub(crate) font: Option<Font>,
643    #[serde(skip_serializing_if = "FnWithArgs::is_empty", skip_deserializing)]
644    // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
645    pub(crate) formatter: FnWithArgs<2>,
646    #[serde(skip_serializing_if = "FnWithArgsOrAny::is_empty", default)]
647    // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
648    pub(crate) offset: FnWithArgsOrAny<1>,
649    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
650    pub(crate) opacity: NumberString,
651    #[serde(skip_serializing_if = "Option::is_none")]
652    pub(crate) padding: Option<Padding>,
653    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
654    pub(crate) z: NumberString,
655}
656
657#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
658pub struct Border {
659    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
660    pub(crate) bottom: NumberString,
661    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
662    pub(crate) left: NumberString,
663    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
664    pub(crate) right: NumberString,
665    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
666    pub(crate) top: NumberString,
667}
668#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
669pub struct Padding {
670    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
671    pub(crate) bottom: NumberString,
672    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
673    pub(crate) left: NumberString,
674    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
675    pub(crate) right: NumberString,
676    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
677    pub(crate) top: NumberString,
678}
679
680#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
681pub struct Font {
682    #[serde(skip_serializing_if = "String::is_empty", default)]
683    pub(crate) family: String,
684    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
685    pub(crate) lineHeight: NumberString,
686    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
687    pub(crate) size: NumberString,
688    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
689    pub(crate) style: NumberString,
690    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
691    pub(crate) weight: NumberString,
692}
693
694#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord)]
695pub struct Segment {
696    #[serde(
697        skip_serializing_if = "FnWithArgs::is_empty",
698        default,
699        skip_deserializing // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
700    )]
701    pub(crate) borderColor: FnWithArgs<1>,
702    #[serde(
703        skip_serializing_if = "FnWithArgs::is_empty",
704        default,
705        skip_deserializing // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
706    )]
707    pub(crate) borderDash: FnWithArgs<1>,
708}
709
710//
711impl DatasetTrait for Vec<SinglePointDataset> {
712    fn labels(self) -> Vec<NumberOrDateString> {
713        let mut vec = self
714            .into_iter()
715            .map(|spd| spd.label.into())
716            .collect::<Vec<_>>();
717
718        vec.sort_by(crate::get_order_fn);
719        vec.dedup();
720        vec
721    }
722}
723impl DatasetTrait for Vec<XYDataset> {
724    fn labels(self) -> Vec<NumberOrDateString> {
725        let mut vec = self
726            .into_iter()
727            .filter_map(|xyd| {
728                let data = xyd.data.0.as_array()?;
729                // gloo_console::console_dbg!(&data);
730                let keys = data
731                    .iter()
732                    .filter_map(|xy| xy.as_object())
733                    .filter_map(|obj| obj.get("x"))
734                    .filter_map(|x| {
735                        x.as_str()
736                            .map(|s| s.to_string())
737                            .or(x.as_number().map(|num| num.to_string()))
738                    })
739                    .map(|x| x.into())
740                    .collect::<Vec<NumberOrDateString>>();
741                Some(keys)
742            })
743            .flatten()
744            .collect::<Vec<_>>();
745        // gloo_console::console_dbg!(&vec);
746
747        vec.sort_by(crate::get_order_fn);
748        vec.dedup();
749        vec
750    }
751}
752//
753impl Annotation for BoxAnnotation {}
754impl Annotation for LineAnnotation {}
755//
756impl From<(NumberOrDateString, NumberString, Option<Value>)> for XYPoint {
757    fn from((x, y, d): (NumberOrDateString, NumberString, Option<Value>)) -> Self {
758        XYPoint {
759            x,
760            y,
761            description: d.unwrap_or_default(),
762        }
763    }
764}
765//
766impl Ord for XYPoint {
767    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
768        self.x.cmp(&other.x)
769    }
770}
771//
772impl PartialOrd for XYPoint {
773    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
774        Some(self.cmp(other))
775    }
776}
777//
778impl Serialize for BoxAnnotationType {
779    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
780    where
781        S: serde::Serializer,
782    {
783        serializer.serialize_str("box")
784    }
785}
786//
787impl Serialize for LineAnnotationType {
788    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
789    where
790        S: serde::Serializer,
791    {
792        serializer.serialize_str("line")
793    }
794}
795//
796impl<'de> Deserialize<'de> for BoxAnnotationType {
797    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
798    where
799        D: serde::Deserializer<'de>,
800    {
801        match String::deserialize(deserializer)?.to_lowercase().as_str() {
802            "box" => Ok(BoxAnnotationType),
803            other => Err(de::Error::custom(format!(
804                "`{other}` is not a valid BoxAnnotationType."
805            ))),
806        }
807    }
808}
809impl<'de> Deserialize<'de> for LineAnnotationType {
810    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
811    where
812        D: serde::Deserializer<'de>,
813    {
814        match String::deserialize(deserializer)?.to_lowercase().as_str() {
815            "line" => Ok(LineAnnotationType),
816            other => Err(de::Error::custom(format!(
817                "`{other}` is not a valid LineAnnotationType."
818            ))),
819        }
820    }
821}
822//