slack_messaging/blocks/data_visualization/charts/
line_chart.rs1use super::{unique_series_names, AxisConfig, DataSeries, ValidateXYChart};
2
3use crate::errors::ValidationErrorKind;
4use crate::validators::*;
5
6use serde::Serialize;
7use slack_messaging_derive::Builder;
8
9#[derive(Debug, Clone, Serialize, PartialEq, Builder)]
116#[builder(validate = "validate")]
117#[serde(tag = "type", rename = "line")]
118pub struct LineChart {
119 #[builder(
120 push_item = "push_series",
121 validate("required", "list::not_empty", "list::max_item_6", "unique_series_names")
122 )]
123 pub(crate) series: Option<Vec<DataSeries>>,
124
125 #[builder(validate("required"))]
126 pub(crate) axis_config: Option<AxisConfig>,
127}
128
129impl ValidateXYChart for LineChart {
130 fn series(&self) -> Option<&[DataSeries]> {
131 self.series.as_deref()
132 }
133
134 fn axis_config(&self) -> Option<&AxisConfig> {
135 self.axis_config.as_ref()
136 }
137}
138
139fn validate(val: &LineChart) -> Vec<ValidationErrorKind> {
140 val.validate()
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use super::super::data_points;
147 use crate::errors::*;
148
149 #[test]
150 fn it_implements_builder() {
151 let expected = LineChart {
152 series: Some(vec![data_series("Pie")]),
153 axis_config: Some(axis_config()),
154 };
155
156 let val = LineChart::builder()
157 .set_series(Some(vec![data_series("Pie")]))
158 .set_axis_config(Some(axis_config()))
159 .build()
160 .unwrap();
161
162 assert_eq!(val, expected);
163
164 let val = LineChart::builder()
165 .series(vec![data_series("Pie")])
166 .axis_config(axis_config())
167 .build()
168 .unwrap();
169
170 assert_eq!(val, expected);
171 }
172
173 #[test]
174 fn it_implements_push_item_method() {
175 let expected = LineChart {
176 series: Some(vec![data_series("Pie")]),
177 axis_config: Some(axis_config()),
178 };
179
180 let val = LineChart::builder()
181 .push_series(data_series("Pie"))
182 .axis_config(axis_config())
183 .build()
184 .unwrap();
185
186 assert_eq!(val, expected);
187 }
188
189 #[test]
190 fn it_requires_series_field() {
191 let err = LineChart::builder()
192 .axis_config(axis_config())
193 .build()
194 .unwrap_err();
195 assert_eq!(err.object(), "LineChart");
196
197 let errors = err.field("series");
198 assert!(errors.includes(ValidationErrorKind::Required));
199 }
200
201 #[test]
202 fn it_requires_series_field_to_have_at_least_one_item() {
203 let err = LineChart::builder()
204 .series(vec![] as Vec<DataSeries>)
205 .axis_config(axis_config())
206 .build()
207 .unwrap_err();
208 assert_eq!(err.object(), "LineChart");
209
210 let errors = err.field("series");
211 assert!(errors.includes(ValidationErrorKind::EmptyArray));
212 }
213
214 #[test]
215 fn it_requires_series_field_to_have_no_more_than_six_items() {
216 let err = LineChart::builder()
217 .series(vec![
218 data_series("Series 1"),
219 data_series("Series 2"),
220 data_series("Series 3"),
221 data_series("Series 4"),
222 data_series("Series 5"),
223 data_series("Series 6"),
224 data_series("Series 7"),
225 ])
226 .axis_config(axis_config())
227 .build()
228 .unwrap_err();
229 assert_eq!(err.object(), "LineChart");
230
231 let errors = err.field("series");
232 assert!(errors.includes(ValidationErrorKind::MaxArraySize(6)));
233 }
234
235 #[test]
236 fn it_requires_axis_config_field() {
237 let err = LineChart::builder()
238 .series(vec![data_series("Pie")])
239 .build()
240 .unwrap_err();
241 assert_eq!(err.object(), "LineChart");
242
243 let errors = err.field("axis_config");
244 assert!(errors.includes(ValidationErrorKind::Required));
245 }
246
247 #[test]
248 fn it_requires_series_names_to_be_unique() {
249 let err = LineChart::builder()
250 .series(vec![
251 data_series("Sales"),
252 data_series("Revenue"),
253 data_series("Sales"),
254 ])
255 .axis_config(axis_config())
256 .build()
257 .unwrap_err();
258 assert_eq!(err.object(), "LineChart");
259
260 let errors = err.field("series");
261 assert!(errors.includes(ValidationErrorKind::UniqueSeriesName));
262 }
263
264 #[test]
265 fn it_requires_series_labels_to_match_axis_categories() {
266 let err = LineChart::builder()
267 .series(vec![
268 data_series("Pie"),
269 DataSeries::builder()
270 .name("Cake")
271 .data(data_points(vec![
272 ("Chocolate", 90),
273 ("Vanilla", 80),
274 ]).unwrap())
275 .build()
276 .unwrap(),
277 ])
278 .axis_config(axis_config())
279 .build()
280 .unwrap_err();
281 assert_eq!(err.object(), "LineChart");
282
283 let errors = err.across_fields();
284 assert!(errors.includes(ValidationErrorKind::DataPointLabelMatching));
285 }
286
287 fn data_series(name: &str) -> DataSeries {
288 DataSeries::builder()
289 .name(name)
290 .data(data_points(vec![
291 ("Strawberry Rhubarb", 85),
292 ("Pumpkin", 70),
293 ]).unwrap())
294 .build()
295 .unwrap()
296 }
297
298 fn axis_config() -> AxisConfig {
299 AxisConfig::builder()
300 .categories(vec![
301 "Strawberry Rhubarb",
302 "Pumpkin",
303 ])
304 .x_label("Pies")
305 .y_label("Percentage of Tastiness")
306 .build()
307 .unwrap()
308 }
309}