slack_blocks/blocks/
file.rs

1//! # File Block
2//!
3//! [slack api docs 🔗]
4//!
5//! Displays a [remote file 🔗]
6//!
7//! [slack api docs 🔗]: https://api.slack.com/reference/block-kit/blocks#file
8//! [remote file 🔗]: https://api.slack.com/messaging/files/remote
9
10use std::borrow::Cow;
11
12use serde::{Deserialize, Serialize};
13#[cfg(feature = "validation")]
14use validator::Validate;
15
16#[cfg(feature = "validation")]
17use crate::val_helpr::ValidationResult;
18
19/// # File Block
20///
21/// [slack api docs 🔗]
22///
23/// Displays a [remote file 🔗]
24///
25/// [slack api docs 🔗]: https://api.slack.com/reference/block-kit/blocks#file
26/// [remote file 🔗]: https://api.slack.com/messaging/files/remote
27#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Serialize)]
28#[cfg_attr(feature = "validation", derive(Validate))]
29pub struct File<'a> {
30  external_id: Cow<'a, str>,
31  source: Cow<'a, str>,
32
33  #[serde(skip_serializing_if = "Option::is_none")]
34  #[cfg_attr(feature = "validation",
35             validate(custom = "super::validate_block_id"))]
36  block_id: Option<Cow<'a, str>>,
37}
38
39impl<'a> File<'a> {
40  /// Build a new File block.
41  ///
42  /// For example, see docs for FileBuilder.
43  pub fn builder() -> build::FileBuilderInit<'a> {
44    build::FileBuilderInit::new()
45  }
46
47  /// Validate that this File block agrees with Slack's model requirements
48  ///
49  /// # Errors
50  /// - If `block_id` longer than 256 chars
51  ///
52  /// # Example
53  /// ```
54  /// use slack_blocks::{blocks::File, compose};
55  ///
56  /// # use std::error::Error;
57  /// # pub fn main() -> Result<(), Box<dyn Error>> {
58  /// let long_string = std::iter::repeat(' ').take(256).collect::<String>();
59  ///
60  /// let block = File::builder().external_id("file_id")
61  ///                            .block_id(long_string)
62  ///                            .build();
63  ///
64  /// assert_eq!(true, matches!(block.validate(), Err(_)));
65  ///
66  /// // < send to slack API >
67  /// # Ok(())
68  /// # }
69  /// ```
70  #[cfg(feature = "validation")]
71  #[cfg_attr(docsrs, doc(cfg(feature = "validation")))]
72  pub fn validate(&self) -> ValidationResult {
73    Validate::validate(self)
74  }
75}
76
77/// File block builder
78pub mod build {
79  use std::marker::PhantomData;
80
81  use super::*;
82  use crate::build::*;
83
84  /// Compile-time markers for builder methods
85  #[allow(non_camel_case_types)]
86  pub mod method {
87    /// FileBuilder.external_id
88    #[derive(Clone, Copy, Debug)]
89    pub struct external_id;
90  }
91
92  /// Initial state for `FileBuilder`
93  pub type FileBuilderInit<'a> =
94    FileBuilder<'a, RequiredMethodNotCalled<method::external_id>>;
95
96  /// Build an File block
97  ///
98  /// Allows you to construct safely, with compile-time checks
99  /// on required setter methods.
100  ///
101  /// # Required Methods
102  /// `FileBuilder::build()` is only available if these methods have been called:
103  ///  - `external_id`
104  ///  - `source`
105  ///
106  /// # Example
107  /// ```
108  /// use slack_blocks::{blocks::File, elems::Image, text::ToSlackPlaintext};
109  ///
110  /// let my_file_id: String = {
111  ///   // use Slack Web API: files.remote.add to upload a file
112  ///   # "foo".into()
113  /// };
114  ///
115  /// let block = File::builder().external_id(my_file_id).build();
116  /// ```
117  #[derive(Debug)]
118  pub struct FileBuilder<'a, ExternalId> {
119    external_id: Option<Cow<'a, str>>,
120    source: Option<Cow<'a, str>>,
121    block_id: Option<Cow<'a, str>>,
122    state: PhantomData<ExternalId>,
123  }
124
125  impl<'a, Ext> FileBuilder<'a, Ext> {
126    /// Create a new FileBuilder
127    pub fn new() -> Self {
128      Self { external_id: None,
129             source: None,
130             block_id: None,
131             state: PhantomData::<_> }
132    }
133
134    /// Set `external_id` (**Required**)
135    ///
136    /// The external unique ID for a [remote file 🔗].
137    ///
138    /// [remote file 🔗]: https://api.slack.com/messaging/files/remote
139    pub fn external_id<S>(self,
140                          external_id: S)
141                          -> FileBuilder<'a, Set<method::external_id>>
142      where S: Into<Cow<'a, str>>
143    {
144      FileBuilder { external_id: Some(external_id.into()),
145                    source: self.source,
146                    block_id: self.block_id,
147                    state: PhantomData::<_> }
148    }
149
150    /// Set `block_id` (Optional)
151    ///
152    /// A string acting as a unique identifier for a block.
153    ///
154    /// You can use this `block_id` when you receive an interaction payload
155    /// to [identify the source of the action 🔗].
156    ///
157    /// If not specified, a `block_id` will be generated.
158    ///
159    /// Maximum length for this field is 255 characters.
160    ///
161    /// [identify the source of the action 🔗]: https://api.slack.com/interactivity/handling#payloads
162    pub fn block_id<S>(mut self, block_id: S) -> Self
163      where S: Into<Cow<'a, str>>
164    {
165      self.block_id = Some(block_id.into());
166      self
167    }
168  }
169
170  impl<'a> FileBuilder<'a, Set<method::external_id>> {
171    /// All done building, now give me a darn actions block!
172    ///
173    /// > `no method name 'build' found for struct 'FileBuilder<...>'`?
174    /// Make sure all required setter methods have been called. See docs for `FileBuilder`.
175    ///
176    /// ```compile_fail
177    /// use slack_blocks::blocks::File;
178    ///
179    /// let foo = File::builder().build(); // Won't compile!
180    /// ```
181    ///
182    /// ```
183    /// use slack_blocks::{blocks::File,
184    ///                    compose::text::ToSlackPlaintext,
185    ///                    elems::Image};
186    ///
187    /// let my_file_id: String = {
188    ///   // use Slack Web API: files.remote.add to upload a file
189    ///   # "foo".into()
190    /// };
191    ///
192    /// let block = File::builder().external_id(my_file_id).build();
193    /// ```
194    pub fn build(self) -> File<'a> {
195      File { external_id: self.external_id.unwrap(),
196             source: "remote".into(),
197             block_id: self.block_id }
198    }
199  }
200}