1use serde::{Deserialize, Serialize};
4
5use crate::node::{Common, IntoNode, Node};
6use crate::style::Style;
7use crate::text::{Alignment, Line, Span};
8
9#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub enum Marker {
12 Dot,
14 #[default]
16 Braille,
17 Block,
19 Bar,
21 HalfBlock,
23}
24
25#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
27pub enum GraphType {
28 #[default]
30 Scatter,
31 Line,
33 Bar,
35}
36
37#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
40pub enum LegendPosition {
41 #[default]
43 Top,
44 TopRight,
46 TopLeft,
48 Left,
50 Right,
52 Bottom,
54 BottomRight,
56 BottomLeft,
58}
59
60#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
62#[serde(rename_all = "camelCase")]
63pub struct Axis {
64 #[serde(default, skip_serializing_if = "Option::is_none")]
66 pub title: Option<Line>,
67 #[serde(default)]
69 pub bounds: [f64; 2],
70 #[serde(default, skip_serializing_if = "Vec::is_empty")]
72 pub labels: Vec<Span>,
73 #[serde(default, skip_serializing_if = "Option::is_none")]
75 pub labels_alignment: Option<Alignment>,
76 #[serde(default, skip_serializing_if = "Option::is_none")]
78 pub style: Option<Style>,
79}
80
81impl Axis {
82 #[must_use]
84 pub fn bounds(min: f64, max: f64) -> Self {
85 Self {
86 title: None,
87 bounds: [min, max],
88 labels: Vec::new(),
89 labels_alignment: None,
90 style: None,
91 }
92 }
93
94 #[must_use]
96 pub fn title(mut self, title: impl Into<Line>) -> Self {
97 self.title = Some(title.into());
98 self
99 }
100
101 #[must_use]
103 pub fn labels(mut self, labels: impl IntoIterator<Item = Span>) -> Self {
104 self.labels = labels.into_iter().collect();
105 self
106 }
107
108 #[must_use]
110 pub fn labels_alignment(mut self, alignment: Alignment) -> Self {
111 self.labels_alignment = Some(alignment);
112 self
113 }
114
115 #[must_use]
117 pub fn style(mut self, style: Style) -> Self {
118 self.style = Some(style);
119 self
120 }
121}
122
123#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
125#[serde(rename_all = "camelCase")]
126pub struct Dataset {
127 #[serde(default, skip_serializing_if = "Option::is_none")]
129 pub name: Option<Line>,
130 pub data: Vec<[f64; 2]>,
132 #[serde(default, skip_serializing_if = "Option::is_none")]
134 pub marker: Option<Marker>,
135 #[serde(default, skip_serializing_if = "Option::is_none")]
137 pub graph_type: Option<GraphType>,
138 #[serde(default, skip_serializing_if = "Option::is_none")]
140 pub style: Option<Style>,
141}
142
143impl Dataset {
144 #[must_use]
146 pub fn new(data: impl IntoIterator<Item = [f64; 2]>) -> Self {
147 Self {
148 data: data.into_iter().collect(),
149 ..Self::default()
150 }
151 }
152
153 #[must_use]
155 pub fn name(mut self, name: impl Into<Line>) -> Self {
156 self.name = Some(name.into());
157 self
158 }
159
160 #[must_use]
162 pub fn marker(mut self, marker: Marker) -> Self {
163 self.marker = Some(marker);
164 self
165 }
166
167 #[must_use]
169 pub fn graph_type(mut self, graph_type: GraphType) -> Self {
170 self.graph_type = Some(graph_type);
171 self
172 }
173
174 #[must_use]
176 pub fn style(mut self, style: Style) -> Self {
177 self.style = Some(style);
178 self
179 }
180}
181
182#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
184#[serde(rename_all = "camelCase")]
185pub struct Chart {
186 #[serde(flatten)]
188 pub common: Common,
189 pub datasets: Vec<Dataset>,
191 #[serde(default)]
193 pub x_axis: Axis,
194 #[serde(default)]
196 pub y_axis: Axis,
197 #[serde(default, skip_serializing_if = "Option::is_none")]
199 pub legend_position: Option<LegendPosition>,
200}
201
202impl Chart {
203 #[must_use]
205 pub fn new(datasets: impl IntoIterator<Item = Dataset>) -> Self {
206 Self {
207 datasets: datasets.into_iter().collect(),
208 ..Self::default()
209 }
210 }
211
212 #[must_use]
214 pub fn x_axis(mut self, axis: Axis) -> Self {
215 self.x_axis = axis;
216 self
217 }
218
219 #[must_use]
221 pub fn y_axis(mut self, axis: Axis) -> Self {
222 self.y_axis = axis;
223 self
224 }
225
226 #[must_use]
228 pub fn legend_position(mut self, position: LegendPosition) -> Self {
229 self.legend_position = Some(position);
230 self
231 }
232
233 #[must_use]
235 pub fn hide_legend(mut self) -> Self {
236 self.legend_position = None;
237 self
238 }
239}
240
241impl IntoNode for Chart {}
242impl From<Chart> for Node {
243 fn from(c: Chart) -> Self {
244 Node::Chart(c)
245 }
246}