slack_messaging/blocks/elements/
number_input.rs

1use crate::composition_objects::{DispatchActionConfiguration, Plain, Text};
2use crate::validators::*;
3
4use serde::Serialize;
5use slack_messaging_derive::Builder;
6
7/// [Number input element](https://docs.slack.dev/reference/block-kit/block-elements/number-input-element)
8/// representation.
9///
10/// # Fields and Validations
11///
12/// For more details, see the [official
13/// documentation](https://docs.slack.dev/reference/block-kit/block-elements/number-input-element).
14///
15/// | Field | Type | Required | Validation |
16/// |-------|------|----------|------------|
17/// | is_decimal_allowed | bool | Yes | N/A |
18/// | action_id | String | No | Max length 255 characters |
19/// | initial_value | String | No | N/A |
20/// | min_value | String | No | N/A |
21/// | max_value | String | No | N/A |
22/// | dispatch_action_config | [DispatchActionConfiguration] | No | N/A |
23/// | focus_on_load | bool | No | N/A |
24/// | placeholder | [Text]<[Plain]> | No | Max length 150 characters |
25///
26/// # Example
27///
28/// ```
29/// use slack_messaging::blocks::elements::NumberInput;
30/// # use std::error::Error;
31///
32/// # fn try_main() -> Result<(), Box<dyn Error>> {
33/// let num = NumberInput::builder()
34///     .action_id("input_number")
35///     .is_decimal_allowed(true)
36///     .build()?;
37///
38/// let expected = serde_json::json!({
39///     "type": "number_input",
40///     "action_id": "input_number",
41///     "is_decimal_allowed": true
42/// });
43///
44/// let json = serde_json::to_value(num).unwrap();
45///
46/// assert_eq!(json, expected);
47///
48/// // If your object has any validation errors, the build method returns Result::Err
49/// let num = NumberInput::builder()
50///     .action_id("input_number")
51///     .build();
52///
53/// assert!(num.is_err());
54/// #     Ok(())
55/// # }
56/// # fn main() {
57/// #     try_main().unwrap()
58/// # }
59/// ```
60#[derive(Debug, Clone, Serialize, PartialEq, Builder)]
61#[serde(tag = "type", rename = "number_input")]
62pub struct NumberInput {
63    #[builder(validate("required"))]
64    pub(crate) is_decimal_allowed: Option<bool>,
65
66    #[serde(skip_serializing_if = "Option::is_none")]
67    #[builder(validate("text::max_255"))]
68    pub(crate) action_id: Option<String>,
69
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub(crate) initial_value: Option<String>,
72
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub(crate) min_value: Option<String>,
75
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub(crate) max_value: Option<String>,
78
79    #[serde(skip_serializing_if = "Option::is_none")]
80    pub(crate) dispatch_action_config: Option<DispatchActionConfiguration>,
81
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub(crate) focus_on_load: Option<bool>,
84
85    #[serde(skip_serializing_if = "Option::is_none")]
86    #[builder(validate("text_object::max_150"))]
87    pub(crate) placeholder: Option<Text<Plain>>,
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use crate::composition_objects::test_helpers::*;
94    use crate::errors::*;
95
96    #[test]
97    fn it_implements_builder() {
98        let expected = NumberInput {
99            is_decimal_allowed: Some(true),
100            action_id: Some("number_input_0".into()),
101            initial_value: Some("5.0".into()),
102            min_value: Some("0.0".into()),
103            max_value: Some("10.0".into()),
104            dispatch_action_config: Some(dispatch_action_config()),
105            focus_on_load: Some(true),
106            placeholder: Some(plain_text("Input Number")),
107        };
108
109        let val = NumberInput::builder()
110            .set_is_decimal_allowed(Some(true))
111            .set_action_id(Some("number_input_0"))
112            .set_initial_value(Some("5.0"))
113            .set_min_value(Some("0.0"))
114            .set_max_value(Some("10.0"))
115            .set_dispatch_action_config(Some(dispatch_action_config()))
116            .set_focus_on_load(Some(true))
117            .set_placeholder(Some(plain_text("Input Number")))
118            .build()
119            .unwrap();
120
121        assert_eq!(val, expected);
122
123        let val = NumberInput::builder()
124            .is_decimal_allowed(true)
125            .action_id("number_input_0")
126            .initial_value("5.0")
127            .min_value("0.0")
128            .max_value("10.0")
129            .dispatch_action_config(dispatch_action_config())
130            .focus_on_load(true)
131            .placeholder(plain_text("Input Number"))
132            .build()
133            .unwrap();
134
135        assert_eq!(val, expected);
136    }
137
138    #[test]
139    fn it_requires_is_decimal_allowed_field() {
140        let err = NumberInput::builder().build().unwrap_err();
141        assert_eq!(err.object(), "NumberInput");
142
143        let errors = err.field("is_decimal_allowed");
144        assert!(errors.includes(ValidationErrorKind::Required));
145    }
146
147    #[test]
148    fn it_requires_action_id_less_than_255_characters_long() {
149        let err = NumberInput::builder()
150            .is_decimal_allowed(true)
151            .action_id("a".repeat(256))
152            .build()
153            .unwrap_err();
154        assert_eq!(err.object(), "NumberInput");
155
156        let errors = err.field("action_id");
157        assert!(errors.includes(ValidationErrorKind::MaxTextLength(255)));
158    }
159
160    #[test]
161    fn it_requires_placeholder_less_than_150_characters_long() {
162        let err = NumberInput::builder()
163            .is_decimal_allowed(true)
164            .placeholder(plain_text("a".repeat(151)))
165            .build()
166            .unwrap_err();
167        assert_eq!(err.object(), "NumberInput");
168
169        let errors = err.field("placeholder");
170        assert!(errors.includes(ValidationErrorKind::MaxTextLength(150)));
171    }
172}