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}