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}