chart_js_rs/objects/
chart_objects.rs

1use {
2    crate::{objects::helper_objects::*, traits::*},
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 = "FnWithArgsOrT::is_empty", default)]
92    pub(crate) backgroundColor: FnWithArgsOrT<2, String>,
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 {
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>,
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 {
242    #[serde(skip_serializing_if = "Option::is_none")]
243    pub(crate) annotation: Option<Annotations>,
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 {
268    #[serde(skip_serializing_if = "Option::is_none")]
269    pub(crate) annotations: Option<HashMap<String, Annotation>>,
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 = "Option::is_none")]
405    pub(crate) offset: Option<bool>,
406    #[serde(skip_serializing_if = "String::is_empty", default, skip_deserializing)]
407    // 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
408    pub(crate) tickColor: String,
409    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
410    pub(crate) z: NumberString,
411}
412
413#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
414pub struct Callout {
415    #[serde(skip_serializing_if = "String::is_empty", default)]
416    pub(crate) backgroundColor: String,
417    #[serde(skip_serializing_if = "String::is_empty", default)]
418    pub(crate) borderColor: String,
419    #[serde(skip_serializing_if = "Vec::is_empty", default)]
420    pub(crate) borderDash: Vec<NumberString>,
421    #[serde(skip_serializing_if = "Option::is_none")]
422    pub(crate) borderWidth: Option<NumberStringOrT<Border>>,
423    #[serde(skip_serializing_if = "Option::is_none")]
424    pub(crate) display: Option<bool>,
425    #[serde(skip_serializing_if = "String::is_empty", default)]
426    pub(crate) position: String,
427    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
428    pub(crate) start: NumberString,
429}
430
431#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
432pub struct LabelAnnotation {
433    #[serde(skip_serializing_if = "String::is_empty", default)]
434    pub(crate) backgroundColor: String,
435    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
436    pub(crate) borderRadius: NumberString,
437    #[serde(skip_serializing_if = "String::is_empty", default)]
438    pub(crate) borderColor: String,
439    #[serde(skip_serializing_if = "Vec::is_empty", default)]
440    pub(crate) borderDash: Vec<NumberString>,
441    #[serde(skip_serializing_if = "Option::is_none")]
442    pub(crate) borderWidth: Option<NumberStringOrT<Border>>,
443    #[serde(skip_serializing_if = "Option::is_none")]
444    pub(crate) callout: Option<Callout>,
445    #[serde(skip_serializing_if = "String::is_empty", default)]
446    pub(crate) color: String,
447    #[serde(skip_serializing_if = "Vec::is_empty", default)]
448    pub(crate) content: Vec<String>,
449    #[serde(skip_serializing_if = "String::is_empty", default)]
450    pub(crate) drawTime: String,
451    #[serde(skip_serializing_if = "Option::is_none")]
452    pub(crate) display: Option<bool>,
453    #[serde(skip_serializing_if = "Option::is_none")]
454    pub(crate) font: Option<Font>,
455    #[serde(skip_serializing_if = "Option::is_none", skip_deserializing)]
456    // the skip_deserializing needed because chartjs sets a default with a different type
457    pub(crate) padding: Option<Padding>,
458    #[serde(skip_serializing_if = "String::is_empty", default)]
459    pub(crate) position: String,
460    #[serde(default, rename = "type")]
461    pub(crate) r#type: LabelAnnotationType,
462    #[serde(skip_serializing_if = "String::is_empty", default)]
463    pub(crate) textAlign: String,
464    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
465    pub(crate) xValue: NumberOrDateString,
466    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
467    pub(crate) xAdjust: NumberOrDateString,
468    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
469    pub(crate) yValue: NumberOrDateString,
470    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
471    pub(crate) yAdjust: NumberOrDateString,
472    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
473    pub(crate) xMax: NumberOrDateString,
474    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
475    pub(crate) xMin: NumberOrDateString,
476    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
477    pub(crate) yMax: NumberOrDateString,
478    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
479    pub(crate) yMin: NumberOrDateString,
480    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
481    pub(crate) yScaleID: NumberString,
482}
483#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
484pub struct LabelAnnotationType;
485
486#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
487pub struct LineAnnotation {
488    #[serde(skip_serializing_if = "String::is_empty", default)]
489    pub(crate) borderColor: String,
490    #[serde(skip_serializing_if = "Vec::is_empty", default)]
491    pub(crate) borderDash: Vec<NumberString>,
492    #[serde(skip_serializing_if = "Option::is_none")]
493    pub(crate) borderWidth: Option<NumberStringOrT<Border>>,
494    #[serde(skip_serializing_if = "String::is_empty", default)]
495    pub(crate) drawTime: String,
496    #[serde(skip_serializing_if = "Option::is_none")]
497    pub(crate) label: Option<LabelAnnotation>,
498    #[serde(default, rename = "type")]
499    pub(crate) r#type: LineAnnotationType,
500    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
501    pub(crate) xMax: NumberOrDateString,
502    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
503    pub(crate) xMin: NumberOrDateString,
504    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
505    pub(crate) yMax: NumberOrDateString,
506    #[serde(skip_serializing_if = "NumberOrDateString::is_empty", default)]
507    pub(crate) yMin: NumberOrDateString,
508    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
509    pub(crate) yScaleID: NumberString,
510}
511#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
512pub struct LineAnnotationType;
513
514#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
515pub struct BoxAnnotation {
516    #[serde(skip_serializing_if = "String::is_empty", default)]
517    pub(crate) backgroundColor: String,
518    #[serde(skip_serializing_if = "String::is_empty", default)]
519    pub(crate) borderColor: String,
520    #[serde(skip_serializing_if = "Vec::is_empty", default)]
521    pub(crate) borderDash: Vec<NumberString>,
522    #[serde(skip_serializing_if = "Option::is_none")]
523    pub(crate) borderWidth: Option<NumberStringOrT<Border>>,
524    #[serde(skip_serializing_if = "String::is_empty", default)]
525    pub(crate) drawTime: String,
526    #[serde(default, rename = "type")]
527    pub(crate) r#type: BoxAnnotationType,
528    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
529    pub(crate) xMax: NumberString,
530    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
531    pub(crate) xMin: NumberString,
532    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
533    pub(crate) yMax: NumberString,
534    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
535    pub(crate) yMin: NumberString,
536}
537#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
538pub struct BoxAnnotationType;
539
540#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
541pub struct ScaleTime {
542    #[serde(skip_serializing_if = "Option::is_none")]
543    pub(crate) displayFormats: Option<DisplayFormats>,
544    #[serde(skip_serializing_if = "String::is_empty", default)]
545    pub(crate) unit: String,
546}
547
548#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
549pub struct DisplayFormats {
550    #[serde(skip_serializing_if = "String::is_empty", default)]
551    pub(crate) day: String,
552    #[serde(skip_serializing_if = "String::is_empty", default)]
553    pub(crate) hour: String,
554    #[serde(skip_serializing_if = "String::is_empty", default)]
555    pub(crate) minute: String,
556    #[serde(skip_serializing_if = "String::is_empty", default)]
557    pub(crate) month: String,
558    #[serde(skip_serializing_if = "String::is_empty", default)]
559    pub(crate) quarter: String,
560    #[serde(skip_serializing_if = "String::is_empty", default)]
561    pub(crate) week: String,
562    #[serde(skip_serializing_if = "String::is_empty", default)]
563    pub(crate) year: String,
564}
565
566#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
567pub struct ScaleTicks {
568    #[serde(skip_serializing_if = "Option::is_none")]
569    pub(crate) autoSkip: Option<bool>,
570    #[serde(skip_serializing_if = "String::is_empty", default)]
571    pub(crate) align: String,
572    #[serde(
573        skip_serializing_if = "FnWithArgs::is_empty",
574        default,
575        skip_deserializing // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
576    )]
577    pub(crate) callback: FnWithArgs<3>,
578    #[serde(skip_serializing_if = "String::is_empty", default)]
579    pub(crate) color: String,
580    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
581    pub(crate) count: NumberString,
582    #[serde(skip_serializing_if = "Option::is_none")]
583    pub(crate) font: Option<Font>,
584    #[serde(skip_serializing_if = "Option::is_none")]
585    pub(crate) includeBounds: Option<bool>,
586    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
587    pub(crate) maxTicksLimit: NumberString,
588    #[serde(skip_serializing_if = "Option::is_none", skip_deserializing)]
589    // the skip_deserializing needed because chartjs sets a default with a different type
590    pub(crate) padding: Option<Padding>,
591    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
592    pub(crate) precision: NumberString,
593    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
594    pub(crate) stepSize: NumberString,
595}
596
597#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
598pub struct Title {
599    #[serde(skip_serializing_if = "String::is_empty", default)]
600    pub(crate) color: String,
601    #[serde(skip_serializing_if = "Option::is_none")]
602    pub(crate) display: Option<bool>,
603    #[serde(skip_serializing_if = "Option::is_none")]
604    pub(crate) font: Option<Font>,
605    #[serde(skip_serializing_if = "String::is_empty", default)]
606    pub(crate) text: String,
607}
608
609#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
610pub struct ChartInteraction {
611    #[serde(skip_serializing_if = "String::is_empty", default)]
612    pub(crate) axis: String,
613    #[serde(skip_serializing_if = "Option::is_none")]
614    pub(crate) intersect: Option<bool>,
615    #[serde(skip_serializing_if = "String::is_empty", default)]
616    pub(crate) mode: String,
617}
618
619#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
620pub struct ChartLegend {
621    #[serde(skip_serializing_if = "Option::is_none")]
622    pub(crate) display: Option<bool>,
623    #[serde(skip_serializing_if = "Option::is_none")]
624    pub(crate) labels: Option<LegendLabel>,
625    #[serde(skip_serializing_if = "String::is_empty", default)]
626    pub(crate) position: String,
627}
628
629#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
630pub struct LegendLabel {
631    #[serde(skip_serializing_if = "Option::is_none")]
632    pub(crate) boxHeight: Option<u32>,
633    #[serde(skip_serializing_if = "Option::is_none")]
634    pub(crate) boxWidth: Option<u32>,
635    #[serde(skip_serializing_if = "String::is_empty", default)]
636    pub(crate) color: String,
637    #[serde(skip_serializing_if = "FnWithArgs::is_empty", skip_deserializing)]
638    // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
639    pub(crate) filter: FnWithArgs<2>,
640    #[serde(skip_serializing_if = "Option::is_none")]
641    pub(crate) font: Option<Font>,
642    #[serde(skip_serializing_if = "String::is_empty", default)]
643    pub(crate) pointStyle: String,
644    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
645    pub(crate) pointStyleWidth: NumberString,
646    #[serde(skip_serializing_if = "FnWithArgs::is_empty", skip_deserializing)]
647    // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
648    pub(crate) sort: FnWithArgs<3>,
649    #[serde(skip_serializing_if = "Option::is_none")]
650    pub(crate) useBorderRadius: Option<bool>,
651    #[serde(skip_serializing_if = "Option::is_none")]
652    pub(crate) usePointStyle: Option<bool>,
653}
654
655#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
656pub struct ChartElements {
657    #[serde(skip_serializing_if = "Option::is_none")]
658    pub(crate) bar: Option<BarElementConfiguration>,
659    #[serde(skip_serializing_if = "Option::is_none")]
660    pub(crate) line: Option<LineElementConfiguration>,
661    #[serde(skip_serializing_if = "Option::is_none")]
662    pub(crate) point: Option<PointElementConfiguration>,
663}
664
665#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
666pub struct BarElementConfiguration {
667    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
668    pub(crate) borderRadius: NumberString,
669    #[serde(skip_serializing_if = "Option::is_none")]
670    pub(crate) borderWidth: Option<NumberStringOrT<Border>>,
671    #[serde(skip_serializing_if = "Option::is_none")]
672    pub(crate) fill: Option<bool>,
673    #[serde(skip_serializing_if = "Option::is_none")]
674    pub(crate) hoverBorderWidth: Option<NumberStringOrT<Border>>,
675}
676
677#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
678pub struct LineElementConfiguration {
679    #[serde(skip_serializing_if = "Option::is_none")]
680    pub(crate) borderWidth: Option<NumberStringOrT<Border>>,
681    #[serde(skip_serializing_if = "String::is_empty", default)]
682    pub(crate) cubicInterpolationMode: String,
683    #[serde(skip_serializing_if = "Option::is_none")]
684    pub(crate) fill: Option<bool>,
685}
686
687#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
688pub struct PointElementConfiguration {
689    #[serde(skip_serializing_if = "Option::is_none")]
690    pub(crate) borderWidth: Option<NumberStringOrT<Border>>,
691    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
692    pub(crate) hitRadius: NumberString,
693    #[serde(skip_serializing_if = "Option::is_none")]
694    pub(crate) hoverBorderWidth: Option<NumberStringOrT<Border>>,
695    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
696    pub(crate) hoverRadius: NumberString,
697    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
698    pub(crate) radius: NumberString,
699}
700
701#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
702pub struct DataLabels {
703    #[serde(skip_serializing_if = "FnWithArgsOrT::is_empty", default)]
704    pub(crate) align: FnWithArgsOrT<1, String>,
705    #[serde(skip_serializing_if = "FnWithArgsOrT::is_empty", default)]
706    pub(crate) anchor: FnWithArgsOrT<1, String>,
707    #[serde(skip_serializing_if = "FnWithArgsOrT::is_empty", default)]
708    pub(crate) backgroundColor: FnWithArgsOrT<1, String>,
709    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
710    pub(crate) borderRadius: NumberString,
711    #[serde(skip_serializing_if = "Option::is_none")]
712    pub(crate) clamp: Option<bool>,
713    #[serde(skip_serializing_if = "Option::is_none")]
714    pub(crate) clip: Option<bool>,
715    #[serde(skip_serializing_if = "String::is_empty", default)]
716    pub(crate) color: String,
717    #[serde(skip_serializing_if = "FnWithArgsOrT::is_empty", default)]
718    pub(crate) display: FnWithArgsOrT<1, BoolString>,
719    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
720    pub(crate) drawTime: NumberString,
721    #[serde(skip_serializing_if = "Option::is_none")]
722    pub(crate) font: Option<Font>,
723    #[serde(skip_serializing_if = "FnWithArgs::is_empty", skip_deserializing)]
724    // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
725    pub(crate) formatter: FnWithArgs<2>,
726    #[serde(skip_serializing_if = "FnWithArgsOrT::is_empty", default)]
727    // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
728    pub(crate) offset: FnWithArgsOrT<1, NumberString>,
729    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
730    pub(crate) opacity: NumberString,
731    #[serde(skip_serializing_if = "Option::is_none")]
732    pub(crate) padding: Option<Padding>,
733    #[serde(skip_serializing_if = "Option::is_none")]
734    pub(crate) rotation: Option<i16>,
735    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
736    pub(crate) z: NumberString,
737}
738
739#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
740pub struct Border {
741    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
742    pub(crate) bottom: NumberString,
743    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
744    pub(crate) left: NumberString,
745    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
746    pub(crate) right: NumberString,
747    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
748    pub(crate) top: NumberString,
749}
750#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
751pub struct Padding {
752    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
753    pub(crate) bottom: NumberString,
754    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
755    pub(crate) left: NumberString,
756    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
757    pub(crate) right: NumberString,
758    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
759    pub(crate) top: NumberString,
760}
761
762#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord)]
763pub struct Font {
764    #[serde(skip_serializing_if = "String::is_empty", default)]
765    pub(crate) family: String,
766    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
767    pub(crate) lineHeight: NumberString,
768    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
769    pub(crate) size: NumberString,
770    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
771    pub(crate) style: NumberString,
772    #[serde(skip_serializing_if = "NumberString::is_empty", default)]
773    pub(crate) weight: NumberString,
774}
775
776#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord)]
777pub struct Segment {
778    #[serde(
779        skip_serializing_if = "FnWithArgs::is_empty",
780        default,
781        skip_deserializing // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
782    )]
783    pub(crate) borderColor: FnWithArgs<1>,
784    #[serde(
785        skip_serializing_if = "FnWithArgs::is_empty",
786        default,
787        skip_deserializing // FnWithArgs can't deser right now, might be solved in the future with a fancy serde deserializer
788    )]
789    pub(crate) borderDash: FnWithArgs<1>,
790}
791
792//
793impl DatasetTrait for Vec<SinglePointDataset> {
794    fn labels(self) -> Vec<NumberOrDateString> {
795        let mut vec = self
796            .into_iter()
797            .map(|spd| spd.label.into())
798            .collect::<Vec<_>>();
799
800        vec.sort_by(crate::get_order_fn);
801        vec.dedup();
802        vec
803    }
804}
805impl DatasetTrait for Vec<XYDataset> {
806    fn labels(self) -> Vec<NumberOrDateString> {
807        let mut vec = self
808            .into_iter()
809            .filter_map(|xyd| {
810                let data = xyd.data.0.as_array()?;
811                // gloo_console::console_dbg!(&data);
812                let keys = data
813                    .iter()
814                    .filter_map(|xy| xy.as_object())
815                    .filter_map(|obj| obj.get("x"))
816                    .filter_map(|x| {
817                        x.as_str()
818                            .map(|s| s.to_string())
819                            .or(x.as_number().map(|num| num.to_string()))
820                    })
821                    .map(|x| x.into())
822                    .collect::<Vec<NumberOrDateString>>();
823                Some(keys)
824            })
825            .flatten()
826            .collect::<Vec<_>>();
827        // gloo_console::console_dbg!(&vec);
828
829        vec.sort_by(crate::get_order_fn);
830        vec.dedup();
831        vec
832    }
833}
834//
835#[allow(clippy::large_enum_variant)]
836#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
837#[serde(untagged)]
838pub enum Annotation {
839    Box(BoxAnnotation),
840    Line(LineAnnotation),
841    Label(LabelAnnotation),
842}
843
844impl Default for Annotation {
845    fn default() -> Self {
846        Self::Line(Default::default())
847    }
848}
849impl From<BoxAnnotation> for Annotation {
850    fn from(value: BoxAnnotation) -> Self {
851        Self::Box(value)
852    }
853}
854impl From<LineAnnotation> for Annotation {
855    fn from(value: LineAnnotation) -> Self {
856        Self::Line(value)
857    }
858}
859impl From<LabelAnnotation> for Annotation {
860    fn from(value: LabelAnnotation) -> Self {
861        Self::Label(value)
862    }
863}
864//
865impl From<(NumberOrDateString, NumberString, Option<Value>)> for XYPoint {
866    fn from((x, y, d): (NumberOrDateString, NumberString, Option<Value>)) -> Self {
867        XYPoint {
868            x,
869            y,
870            description: d.unwrap_or_default(),
871        }
872    }
873}
874//
875impl Ord for XYPoint {
876    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
877        self.x.cmp(&other.x)
878    }
879}
880//
881impl PartialOrd for XYPoint {
882    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
883        Some(self.cmp(other))
884    }
885}
886//
887impl Serialize for BoxAnnotationType {
888    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
889    where
890        S: serde::Serializer,
891    {
892        serializer.serialize_str("box")
893    }
894}
895//
896impl Serialize for LineAnnotationType {
897    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
898    where
899        S: serde::Serializer,
900    {
901        serializer.serialize_str("line")
902    }
903}
904//
905impl Serialize for LabelAnnotationType {
906    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
907    where
908        S: serde::Serializer,
909    {
910        serializer.serialize_str("label")
911    }
912}
913//
914impl<'de> Deserialize<'de> for BoxAnnotationType {
915    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
916    where
917        D: serde::Deserializer<'de>,
918    {
919        match String::deserialize(deserializer)?.to_lowercase().as_str() {
920            "box" => Ok(BoxAnnotationType),
921            other => Err(de::Error::custom(format!(
922                "`{other}` is not a valid BoxAnnotationType."
923            ))),
924        }
925    }
926}
927//
928impl<'de> Deserialize<'de> for LineAnnotationType {
929    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
930    where
931        D: serde::Deserializer<'de>,
932    {
933        match String::deserialize(deserializer)?.to_lowercase().as_str() {
934            "line" => Ok(LineAnnotationType),
935            other => Err(de::Error::custom(format!(
936                "`{other}` is not a valid LineAnnotationType."
937            ))),
938        }
939    }
940}
941//
942impl<'de> Deserialize<'de> for LabelAnnotationType {
943    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
944    where
945        D: serde::Deserializer<'de>,
946    {
947        match String::deserialize(deserializer)?.to_lowercase().as_str() {
948            "label" => Ok(LabelAnnotationType),
949            other => Err(de::Error::custom(format!(
950                "`{other}` is not a valid LabelAnnotationType."
951            ))),
952        }
953    }
954}
955//