slack_messaging/composition_objects/
option.rs1use crate::composition_objects::{
2 Plain, Text, TextExt,
3 types::{UrlAvailable, UrlUnavailable},
4};
5use crate::validators::*;
6
7use serde::Serialize;
8use slack_messaging_derive::Builder;
9use std::marker::PhantomData;
10
11#[derive(Debug, Clone, Serialize, PartialEq, Builder)]
93#[serde(bound(serialize = "T: Serialize"))]
94pub struct Opt<T = Text<Plain>, P = UrlUnavailable>
95where
96 T: TextExt,
97{
98 #[serde(skip)]
99 #[builder(phantom = "P")]
100 pub(crate) phantom: PhantomData<P>,
101
102 #[builder(validate("required", "text_object::max_75"))]
103 pub(crate) text: Option<T>,
104
105 #[builder(validate("required", "text::max_150"))]
106 pub(crate) value: Option<String>,
107
108 #[serde(skip_serializing_if = "Option::is_none")]
109 #[builder(validate("text_object::max_75"))]
110 pub(crate) description: Option<T>,
111
112 #[serde(skip_serializing_if = "Option::is_none")]
113 #[builder(no_accessors, validate("text::max_3000"))]
114 pub(crate) url: Option<String>,
115}
116
117impl<T: TextExt> OptBuilder<T, UrlAvailable> {
118 pub fn get_url(&self) -> Option<&String> {
120 self.url.inner_ref()
121 }
122
123 pub fn set_url(self, value: Option<impl Into<String>>) -> Self {
125 Self {
126 url: Self::new_url(value.map(|v| v.into())),
127 ..self
128 }
129 }
130
131 pub fn url(self, value: impl Into<String>) -> Self {
133 self.set_url(Some(value))
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use crate::composition_objects::test_helpers::*;
141 use crate::errors::*;
142
143 #[test]
144 fn it_implements_builder() {
145 let expected = Opt {
146 phantom: PhantomData::<UrlUnavailable>,
147 text: Some(plain_text("foo")),
148 value: Some("bar".into()),
149 description: Some(plain_text("baz")),
150 url: None,
151 };
152
153 let val = Opt::builder()
154 .set_text(Some(plain_text("foo")))
155 .set_value(Some("bar"))
156 .set_description(Some(plain_text("baz")))
157 .build()
158 .unwrap();
159
160 assert_eq!(val, expected);
161
162 let val = Opt::builder()
163 .text(plain_text("foo"))
164 .value("bar")
165 .description(plain_text("baz"))
166 .build()
167 .unwrap();
168
169 assert_eq!(val, expected);
170 }
171
172 #[test]
173 fn url_field_is_available_if_the_valid_type_is_used() {
174 let expected = Opt::<_, UrlAvailable> {
175 phantom: PhantomData,
176 text: Some(plain_text("foo")),
177 value: Some("bar".into()),
178 description: Some(plain_text("baz")),
179 url: Some("foobarbaz".into()),
180 };
181
182 let val = Opt::<_, UrlAvailable>::builder()
183 .set_text(Some(plain_text("foo")))
184 .set_value(Some("bar"))
185 .set_description(Some(plain_text("baz")))
186 .set_url(Some("foobarbaz"))
187 .build()
188 .unwrap();
189
190 assert_eq!(val, expected);
191
192 let val = Opt::<_, UrlAvailable>::builder()
193 .text(plain_text("foo"))
194 .value("bar")
195 .description(plain_text("baz"))
196 .url("foobarbaz")
197 .build()
198 .unwrap();
199
200 assert_eq!(val, expected);
201 }
202
203 #[test]
204 fn it_requires_text_field() {
205 let err = Opt::<Text<Plain>>::builder()
206 .value("bar")
207 .build()
208 .unwrap_err();
209 assert_eq!(err.object(), "Opt");
210
211 let errors = err.field("text");
212 assert!(errors.includes(ValidationErrorKind::Required));
213 }
214
215 #[test]
216 fn it_requires_text_field_less_than_75_characters_long() {
217 let err = Opt::<Text<Plain>>::builder()
218 .text(plain_text("a".repeat(76)))
219 .value("bar")
220 .build()
221 .unwrap_err();
222 assert_eq!(err.object(), "Opt");
223
224 let errors = err.field("text");
225 assert!(errors.includes(ValidationErrorKind::MaxTextLength(75)));
226 }
227
228 #[test]
229 fn it_requires_value_field() {
230 let err = Opt::<Text<Plain>>::builder()
231 .text(plain_text("foo"))
232 .build()
233 .unwrap_err();
234 assert_eq!(err.object(), "Opt");
235
236 let errors = err.field("value");
237 assert!(errors.includes(ValidationErrorKind::Required));
238 }
239
240 #[test]
241 fn it_requires_value_field_less_than_150_characters_long() {
242 let err = Opt::<Text<Plain>>::builder()
243 .text(plain_text("foo"))
244 .value("a".repeat(151))
245 .build()
246 .unwrap_err();
247 assert_eq!(err.object(), "Opt");
248
249 let errors = err.field("value");
250 assert!(errors.includes(ValidationErrorKind::MaxTextLength(150)));
251 }
252
253 #[test]
254 fn it_requires_description_field_less_than_75_characters_long() {
255 let err = Opt::<Text<Plain>>::builder()
256 .text(plain_text("foo"))
257 .value("bar")
258 .description(plain_text("a".repeat(76)))
259 .build()
260 .unwrap_err();
261 assert_eq!(err.object(), "Opt");
262
263 let errors = err.field("description");
264 assert!(errors.includes(ValidationErrorKind::MaxTextLength(75)));
265 }
266
267 #[test]
268 fn it_requires_url_field_less_than_3000_characters_long() {
269 let err = Opt::<Text<Plain>, UrlAvailable>::builder()
270 .text(plain_text("foo"))
271 .value("bar")
272 .description(plain_text("baz"))
273 .url("a".repeat(3001))
274 .build()
275 .unwrap_err();
276 assert_eq!(err.object(), "Opt");
277
278 let errors = err.field("url");
279 assert!(errors.includes(ValidationErrorKind::MaxTextLength(3000)));
280 }
281}