slack_blocks/blocks/header.rs
1//! # Header Block
2//!
3//! [slack api docs 🔗]
4//!
5//! A header is a plain-text block that displays in a larger, bold font.
6//!
7//! Use it to delineate between different groups of content in your app's surfaces.
8//!
9//! [slack api docs 🔗]: https://api.slack.com/reference/block-kit/blocks#header
10
11use std::borrow::Cow;
12
13use serde::{Deserialize, Serialize};
14#[cfg(feature = "validation")]
15use validator::Validate;
16
17use crate::text;
18#[cfg(feature = "validation")]
19use crate::val_helpr::*;
20
21/// # Header Block
22///
23/// [slack api docs 🔗]
24///
25/// A header is a plain-text block that displays in a larger, bold font.
26///
27/// Use it to delineate between different groups of content in your app's surfaces.
28///
29/// [slack api docs 🔗]: https://api.slack.com/reference/block-kit/blocks#header
30#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
31#[cfg_attr(feature = "validation", derive(Validate))]
32pub struct Header<'a> {
33 #[cfg_attr(feature = "validation", validate(custom = "validate_text"))]
34 text: text::Text,
35
36 #[serde(skip_serializing_if = "Option::is_none")]
37 #[cfg_attr(feature = "validation",
38 validate(custom = "super::validate_block_id"))]
39 block_id: Option<Cow<'a, str>>,
40}
41
42#[cfg(feature = "validation")]
43fn validate_text(t: &text::Text) -> ValidatorResult {
44 below_len("text", 150, t)
45}
46
47impl<'a> Header<'a> {
48 /// Build a new Header block.
49 ///
50 /// Alias for [`build::HeaderBuilder::new()`].
51 pub fn builder() -> build::HeaderBuilderInit<'a> {
52 build::HeaderBuilder::new()
53 }
54
55 /// Validate that this Header block agrees with Slack's model requirements
56 ///
57 /// # Errors
58 /// - If `text` longer than 150 chars
59 /// - If `block_id` longer than 256 chars
60 ///
61 /// # Example
62 /// ```
63 /// # use validator::Validate;
64 /// use slack_blocks::{blocks::Header, compose};
65 ///
66 /// let long_string = |len| std::iter::repeat(' ').take(len).collect::<String>();
67 /// let assert_invalid = |block: &dyn Validate| match block.validate() {
68 /// | Ok(()) => panic!("validation should have failed"),
69 /// | Err(_) => (),
70 /// };
71 ///
72 /// // block_id
73 /// let block = Header::builder().text("foo")
74 /// .block_id(long_string(256))
75 /// .build();
76 ///
77 /// assert_invalid(&block);
78 ///
79 /// // text
80 /// let block = Header::builder().text(long_string(151))
81 /// .block_id("foo")
82 /// .build();
83 ///
84 /// assert_invalid(&block);
85 /// ```
86 #[cfg(feature = "validation")]
87 #[cfg_attr(docsrs, doc(cfg(feature = "validation")))]
88 pub fn validate(&self) -> ValidationResult {
89 todo!()
90 }
91}
92
93pub mod build {
94 //! [HeaderBuilder] and related items
95
96 use std::marker::PhantomData;
97
98 use super::*;
99 use crate::build::*;
100
101 /// Methods of [`HeaderBuilder`]
102 #[allow(non_camel_case_types)]
103 pub mod method {
104 /// [`super::HeaderBuilder::text()`]
105 #[derive(Debug, Clone, Copy)]
106 pub struct text;
107 }
108
109 /// Initial state for [HeaderBuilder]
110 pub type HeaderBuilderInit<'a> =
111 HeaderBuilder<'a, RequiredMethodNotCalled<method::text>>;
112
113 /// Build an Header block
114 ///
115 /// Allows you to construct safely, with compile-time checks
116 /// on required setter methods.
117 ///
118 /// # Required Methods
119 /// `HeaderBuilder::build()` is only available if these methods have been called:
120 /// - `text`
121 ///
122 /// # Example
123 /// ```
124 /// use slack_blocks::blocks::Header;
125 ///
126 /// let block = Header::builder().block_id("foo").text("bar").build();
127 /// ```
128 #[derive(Clone, Debug)]
129 pub struct HeaderBuilder<'a, T> {
130 text: Option<text::Text>,
131 block_id: Option<Cow<'a, str>>,
132 state: PhantomData<T>,
133 }
134
135 impl<'a, T> HeaderBuilder<'a, T> {
136 /// Construct a new HeaderBuilder
137 pub fn new() -> Self {
138 Self { text: None,
139 block_id: None,
140 state: PhantomData::<_> }
141 }
142
143 /// Set `text` (**Required**)
144 ///
145 /// The text for the block, in the form of a [`plain_text` text object 🔗].
146 ///
147 /// Maximum length for the `text` in this field is 150 characters.
148 ///
149 /// [`plain_text` text object 🔗]: https://api.slack.com/reference/messaging/composition-objects#text
150 pub fn text(self,
151 text: impl Into<text::Plain>)
152 -> HeaderBuilder<'a, Set<method::text>> {
153 HeaderBuilder { text: Some(text.into().into()),
154 block_id: self.block_id,
155 state: PhantomData::<_> }
156 }
157
158 /// XML child alias for [`Self::text()`]
159 #[cfg(feature = "blox")]
160 #[cfg_attr(docsrs, doc(cfg(feature = "blox")))]
161 pub fn child(self,
162 text: impl Into<text::Plain>)
163 -> HeaderBuilder<'a, Set<method::text>> {
164 self.text(text)
165 }
166
167 /// A string acting as a unique identifier for a block.
168 ///
169 /// If not specified, one will be generated.
170 ///
171 /// Maximum length for this field is 255 characters.
172 /// `block_id` should be unique for each message and each iteration of a message.
173 ///
174 /// If a message is updated, use a new `block_id`.
175 pub fn block_id(mut self, block_id: impl Into<Cow<'a, str>>) -> Self {
176 self.block_id = Some(block_id.into());
177 self
178 }
179 }
180
181 impl<'a> HeaderBuilder<'a, Set<method::text>> {
182 /// All done building, now give me a darn actions block!
183 ///
184 /// > `no method name 'build' found for struct 'FileBuilder<...>'`?
185 /// Make sure all required setter methods have been called. See docs for `FileBuilder`.
186 ///
187 /// ```compile_fail
188 /// use slack_blocks::blocks::Header;
189 ///
190 /// let foo = Header::builder().build(); // Won't compile!
191 /// ```
192 ///
193 /// ```
194 /// use slack_blocks::blocks::Header;
195 ///
196 /// let block = Header::builder().text("Foo").build();
197 /// ```
198 pub fn build(self) -> Header<'a> {
199 Header { text: self.text.unwrap(),
200 block_id: self.block_id }
201 }
202 }
203}