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