Skip to main content

slack_messaging/blocks/data_visualization/charts/
segment.rs

1use crate::errors::ValidationErrors;
2use crate::validators::*;
3
4use serde::Serialize;
5use serde_json::Number;
6use slack_messaging_derive::Builder;
7
8/// [Segment](https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block#segment)
9/// representation.
10///
11/// # Fields and Validations
12///
13/// For more details, see the [official
14/// documentation](https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block#segment).
15///
16/// | Field | Type | Required | Validation |
17/// |-------|------|----------|------------|
18/// | label | String | Yes | Max length 20 characters |
19/// | value | [Number] | Yes | Greater than 0 |
20///
21/// # Example
22///
23/// ```
24/// use slack_messaging::blocks::data_visualization::charts::Segment;
25/// # use std::error::Error;
26///
27/// # fn try_main() -> Result<(), Box<dyn Error>> {
28/// let seg = Segment::builder()
29///     .label("Segment A")
30///     .value(100)
31///     .build()?;
32///
33/// let expected = serde_json::json!({
34///     "label": "Segment A",
35///     "value": 100
36/// });
37///
38/// let json = serde_json::to_value(seg).unwrap();
39///
40/// assert_eq!(json, expected);
41/// #     Ok(())
42/// # }
43/// # fn main() {
44/// #     try_main().unwrap()
45/// # }
46/// ```
47#[derive(Debug, Clone, Serialize, PartialEq, Builder)]
48pub struct Segment {
49    #[builder(validate("required", "text::max_20"))]
50    pub(crate) label: Option<String>,
51
52    #[builder(validate("required", "number::greater_than_zero"))]
53    pub(crate) value: Option<Number>,
54}
55
56impl<S, N> TryFrom<(S, N)> for Segment
57where
58    S: Into<String>,
59    N: Into<Number>,
60{
61    type Error = ValidationErrors;
62
63    fn try_from(value: (S, N)) -> Result<Self, Self::Error> {
64        let (label, value) = value;
65        Segment::builder()
66            .label(label)
67            .value(value)
68            .build()
69    }
70}
71
72/// Helper function to create multiple segments from an iterator of tuples.
73///
74/// # Example
75///
76/// ```
77/// use slack_messaging::blocks::data_visualization::charts::segments;
78/// # use std::error::Error;
79///
80/// # fn try_main() -> Result<(), Box<dyn Error>> {
81/// let segs = segments(vec![("Segment A", 100), ("Segment B", 200)])?;
82///
83/// let expected = serde_json::json!([
84///     { "label": "Segment A", "value": 100 },
85///     { "label": "Segment B", "value": 200 }
86/// ]);
87///
88/// let json = serde_json::to_value(segs).unwrap();
89///
90/// assert_eq!(json, expected);
91/// #     Ok(())
92/// # }
93/// # fn main() {
94/// #     try_main().unwrap()
95/// # }
96/// ```
97pub fn segments<Iter, S, N>(iter: Iter) -> Result<Vec<Segment>, ValidationErrors>
98where
99    Iter: IntoIterator<Item = (S, N)>,
100    S: Into<String>,
101    N: Into<Number>,
102{
103    iter.into_iter().map(Segment::try_from).collect()
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109    use crate::errors::*;
110
111    #[test]
112    fn it_implements_builder() {
113        let expected = Segment {
114            label: Some("Segment A".to_string()),
115            value: Some(Number::from(100)),
116        };
117
118        let val = Segment::builder()
119            .set_label(Some("Segment A"))
120            .set_value(Some(100))
121            .build()
122            .unwrap();
123
124        assert_eq!(val, expected);
125
126        let val = Segment::builder()
127            .label("Segment A")
128            .value(100)
129            .build()
130            .unwrap();
131
132        assert_eq!(val, expected);
133    }
134
135    #[test]
136    fn it_implements_try_from_tuple() {
137        let expected = Segment {
138            label: Some("Segment A".to_string()),
139            value: Some(Number::from(100)),
140        };
141
142        let val = Segment::try_from(("Segment A", 100)).unwrap();
143
144        assert_eq!(val, expected);
145    }
146
147    #[test]
148    fn it_implements_segments_function() {
149        let expected = vec![
150            Segment {
151                label: Some("Segment A".to_string()),
152                value: Some(Number::from(100)),
153            },
154            Segment {
155                label: Some("Segment B".to_string()),
156                value: Some(Number::from(200)),
157            },
158        ];
159
160        let val = segments(vec![("Segment A", 100), ("Segment B", 200)]).unwrap();
161
162        assert_eq!(val, expected);
163    }
164
165    #[test]
166    fn it_requires_label_field() {
167        let err = Segment::builder().value(100).build().unwrap_err();
168        assert_eq!(err.object(), "Segment");
169
170        let errors = err.field("label");
171        assert!(errors.includes(ValidationErrorKind::Required));
172    }
173
174    #[test]
175    fn it_requires_label_field_less_than_20_characters_long() {
176        let err = Segment::builder()
177            .label("a".repeat(21))
178            .value(100)
179            .build()
180            .unwrap_err();
181        assert_eq!(err.object(), "Segment");
182
183        let errors = err.field("label");
184        assert!(errors.includes(ValidationErrorKind::MaxTextLength(20)));
185    }
186
187    #[test]
188    fn it_requires_value_field() {
189        let err = Segment::builder().label("Segment A").build().unwrap_err();
190        assert_eq!(err.object(), "Segment");
191
192        let errors = err.field("value");
193        assert!(errors.includes(ValidationErrorKind::Required));
194    }
195
196    #[test]
197    fn it_requires_value_field_greater_than_zero() {
198        let err = Segment::builder()
199            .label("Segment A")
200            .value(0)
201            .build()
202            .unwrap_err();
203        assert_eq!(err.object(), "Segment");
204
205        let errors = err.field("value");
206        assert!(errors.includes(ValidationErrorKind::MustBeGreaterThanZero));
207    }
208}