slack_messaging/blocks/
image.rs1use crate::composition_objects::{Plain, SlackFile, Text};
2use crate::errors::ValidationErrorKind;
3use crate::validators::*;
4
5use serde::Serialize;
6use slack_messaging_derive::Builder;
7
8#[derive(Debug, Clone, Serialize, PartialEq, Builder)]
64#[serde(tag = "type", rename = "image")]
65#[builder(validate = "validate")]
66pub struct Image {
67 #[builder(validate("required", "text::max_2000"))]
68 pub(crate) alt_text: Option<String>,
69
70 #[serde(skip_serializing_if = "Option::is_none")]
71 #[builder(validate("text::max_3000"))]
72 pub(crate) image_url: Option<String>,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
75 #[builder(validate("text_object::max_2000"))]
76 pub(crate) title: Option<Text<Plain>>,
77
78 #[serde(skip_serializing_if = "Option::is_none")]
79 #[builder(validate("text::max_255"))]
80 pub(crate) block_id: Option<String>,
81
82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub(crate) slack_file: Option<SlackFile>,
84}
85
86fn validate(val: &Image) -> Vec<ValidationErrorKind> {
87 match (val.image_url.as_ref(), val.slack_file.as_ref()) {
88 (Some(_), Some(_)) => {
89 vec![ValidationErrorKind::ExclusiveField(
90 "image_url",
91 "slack_file",
92 )]
93 }
94 (None, None) => {
95 vec![ValidationErrorKind::EitherRequired(
96 "image_url",
97 "slack_file",
98 )]
99 }
100 _ => vec![],
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use crate::composition_objects::test_helpers::*;
108
109 #[test]
110 fn it_implements_builder() {
111 let expected = Image {
113 alt_text: Some("An incredibly cute kitten.".into()),
114 image_url: Some("http://placekitten.com/500/500".into()),
115 title: Some(plain_text("Please enjoy this photo of a kitten")),
116 block_id: Some("image4".into()),
117 slack_file: None,
118 };
119
120 let val = Image::builder()
121 .set_alt_text(Some("An incredibly cute kitten."))
122 .set_image_url(Some("http://placekitten.com/500/500"))
123 .set_title(Some(plain_text("Please enjoy this photo of a kitten")))
124 .set_block_id(Some("image4"))
125 .build()
126 .unwrap();
127
128 assert_eq!(val, expected);
129
130 let val = Image::builder()
131 .alt_text("An incredibly cute kitten.")
132 .image_url("http://placekitten.com/500/500")
133 .title(plain_text("Please enjoy this photo of a kitten"))
134 .block_id("image4")
135 .build()
136 .unwrap();
137
138 assert_eq!(val, expected);
139
140 let expected = Image {
142 alt_text: Some("An incredibly cute kitten.".into()),
143 image_url: None,
144 title: Some(plain_text("Please enjoy this photo of a kitten")),
145 block_id: Some("image4".into()),
146 slack_file: Some(slack_file()),
147 };
148
149 let val = Image::builder()
150 .set_alt_text(Some("An incredibly cute kitten."))
151 .set_title(Some(plain_text("Please enjoy this photo of a kitten")))
152 .set_block_id(Some("image4"))
153 .set_slack_file(Some(slack_file()))
154 .build()
155 .unwrap();
156
157 assert_eq!(val, expected);
158
159 let val = Image::builder()
160 .alt_text("An incredibly cute kitten.")
161 .title(plain_text("Please enjoy this photo of a kitten"))
162 .block_id("image4")
163 .slack_file(slack_file())
164 .build()
165 .unwrap();
166
167 assert_eq!(val, expected);
168 }
169
170 #[test]
171 fn it_requires_alt_text_field() {
172 let err = Image::builder()
173 .slack_file(slack_file())
174 .build()
175 .unwrap_err();
176 assert_eq!(err.object(), "Image");
177
178 let errors = err.field("alt_text");
179 assert!(errors.includes(ValidationErrorKind::Required));
180 }
181
182 #[test]
183 fn it_requires_alt_text_less_than_2000_characters_long() {
184 let err = Image::builder()
185 .alt_text("a".repeat(2001))
186 .slack_file(slack_file())
187 .build()
188 .unwrap_err();
189 assert_eq!(err.object(), "Image");
190
191 let errors = err.field("alt_text");
192 assert!(errors.includes(ValidationErrorKind::MaxTextLength(2000)));
193 }
194
195 #[test]
196 fn it_requires_image_url_less_than_3000_characters_long() {
197 let err = Image::builder()
198 .alt_text("An incredibly cute kitten.")
199 .image_url("a".repeat(3001))
200 .build()
201 .unwrap_err();
202 assert_eq!(err.object(), "Image");
203
204 let errors = err.field("image_url");
205 assert!(errors.includes(ValidationErrorKind::MaxTextLength(3000)));
206 }
207
208 #[test]
209 fn it_requires_title_less_than_2000_characters_long() {
210 let err = Image::builder()
211 .alt_text("An incredibly cute kitten.")
212 .image_url("http://placekitten.com/500/500")
213 .title(plain_text("a".repeat(2001)))
214 .build()
215 .unwrap_err();
216 assert_eq!(err.object(), "Image");
217
218 let errors = err.field("title");
219 assert!(errors.includes(ValidationErrorKind::MaxTextLength(2000)));
220 }
221
222 #[test]
223 fn it_requires_block_id_than_255_characters_long() {
224 let err = Image::builder()
225 .alt_text("An incredibly cute kitten.")
226 .image_url("http://placekitten.com/500/500")
227 .block_id("a".repeat(256))
228 .build()
229 .unwrap_err();
230 assert_eq!(err.object(), "Image");
231
232 let errors = err.field("block_id");
233 assert!(errors.includes(ValidationErrorKind::MaxTextLength(255)));
234 }
235
236 #[test]
237 fn it_prevents_from_setting_both_image_url_and_slack_file() {
238 let err = Image::builder()
239 .alt_text("Cute kitten")
240 .image_url("http://placekitten.com/700/500")
241 .slack_file(slack_file())
242 .build()
243 .unwrap_err();
244 assert_eq!(err.object(), "Image");
245
246 let errors = err.across_fields();
247 assert!(errors.includes(ValidationErrorKind::ExclusiveField(
248 "image_url",
249 "slack_file"
250 )));
251 }
252
253 #[test]
254 fn it_requires_either_image_url_or_slack_file_is_set() {
255 let err = Image::builder()
256 .alt_text("Cute kitten")
257 .build()
258 .unwrap_err();
259 assert_eq!(err.object(), "Image");
260
261 let errors = err.across_fields();
262 assert!(errors.includes(ValidationErrorKind::EitherRequired(
263 "image_url",
264 "slack_file"
265 )));
266 }
267}