imgchest/client/
builder.rs

1use crate::PostPrivacy;
2use std::path::Path;
3use tokio_util::codec::BytesCodec;
4use tokio_util::codec::FramedRead;
5
6/// A builder for creating a post.
7///
8/// This builder is for the low-level function.
9#[derive(Debug)]
10pub struct CreatePostBuilder {
11    /// The title of the post.
12    ///
13    /// If specified, it must be at least 3 characters long.
14    pub title: Option<String>,
15
16    /// The post privacy.
17    ///
18    /// Defaults to hidden.
19    pub privacy: Option<PostPrivacy>,
20
21    /// Whether the post should be tied to the user.
22    pub anonymous: Option<bool>,
23
24    /// Whether this post is nsfw.
25    pub nsfw: Option<bool>,
26
27    /// The images of the post
28    pub images: Vec<UploadPostFile>,
29}
30
31impl CreatePostBuilder {
32    /// Create a new builder.
33    pub fn new() -> Self {
34        Self {
35            title: None,
36            privacy: None,
37            anonymous: None,
38            nsfw: None,
39            images: Vec::new(),
40        }
41    }
42
43    /// Set the title.
44    ///
45    /// It must be at least 3 characters long.
46    pub fn title(&mut self, title: impl Into<String>) -> &mut Self {
47        self.title = Some(title.into());
48        self
49    }
50
51    /// Set the post privacy.
52    ///
53    /// Defaults to hidden.
54    pub fn privacy(&mut self, privacy: PostPrivacy) -> &mut Self {
55        self.privacy = Some(privacy);
56        self
57    }
58
59    /// Set whether this post should be anonymous.
60    pub fn anonymous(&mut self, anonymous: bool) -> &mut Self {
61        self.anonymous = Some(anonymous);
62        self
63    }
64
65    /// Set whether this post is nsfw.
66    pub fn nsfw(&mut self, nsfw: bool) -> &mut Self {
67        self.nsfw = Some(nsfw);
68        self
69    }
70
71    /// Add a new image to this post.
72    pub fn image(&mut self, file: UploadPostFile) -> &mut Self {
73        self.images.push(file);
74        self
75    }
76}
77
78impl Default for CreatePostBuilder {
79    fn default() -> Self {
80        Self::new()
81    }
82}
83
84/// A post file that is meant for uploading.
85#[derive(Debug)]
86pub struct UploadPostFile {
87    /// The file name
88    pub(super) file_name: String,
89
90    /// The file body
91    pub(super) body: reqwest::Body,
92}
93
94impl UploadPostFile {
95    /// Create this from a raw reqwest body.
96    pub fn from_body(file_name: &str, body: reqwest::Body) -> Self {
97        Self {
98            file_name: file_name.into(),
99            body,
100        }
101    }
102
103    /// Create this from bytes.
104    pub fn from_bytes(file_name: &str, file_data: Vec<u8>) -> Self {
105        Self::from_body(file_name, file_data.into())
106    }
107
108    /// Create this from a file.
109    pub fn from_file(file_name: &str, file: tokio::fs::File) -> Self {
110        let stream = FramedRead::new(file, BytesCodec::new());
111        let body = reqwest::Body::wrap_stream(stream);
112
113        Self::from_body(file_name, body)
114    }
115
116    /// Create this from a file at the given path.
117    pub async fn from_path<P>(path: P) -> std::io::Result<Self>
118    where
119        P: AsRef<Path>,
120    {
121        let path = path.as_ref();
122
123        let file_name = path
124            .file_name()
125            .ok_or_else(|| std::io::Error::other("missing file name"))?
126            .to_str()
127            .ok_or_else(|| std::io::Error::other("file name is not valid unicode"))?;
128
129        let file = tokio::fs::File::open(path).await?;
130
131        Ok(Self::from_file(file_name, file))
132    }
133}
134
135/// A builder for updating a post.
136#[derive(Debug)]
137pub struct UpdatePostBuilder {
138    /// The title
139    ///
140    /// If specified, it must be at least 3 characters long.
141    pub title: Option<String>,
142
143    /// The post privacy
144    pub privacy: Option<PostPrivacy>,
145
146    /// Whether the post is nsfw
147    pub nsfw: Option<bool>,
148}
149
150impl UpdatePostBuilder {
151    /// Create an empty post update.
152    pub fn new() -> Self {
153        Self {
154            title: None,
155            privacy: None,
156            nsfw: None,
157        }
158    }
159
160    /// Update the title.
161    ///
162    /// It must be at least 3 characters long.
163    pub fn title(&mut self, title: impl Into<String>) -> &mut Self {
164        self.title = Some(title.into());
165        self
166    }
167
168    /// Update the privacy.
169    pub fn privacy(&mut self, privacy: PostPrivacy) -> &mut Self {
170        self.privacy = Some(privacy);
171        self
172    }
173
174    /// Update the nsfw flag.
175    pub fn nsfw(&mut self, nsfw: bool) -> &mut Self {
176        self.nsfw = Some(nsfw);
177        self
178    }
179}
180
181impl Default for UpdatePostBuilder {
182    fn default() -> Self {
183        Self::new()
184    }
185}
186
187/// Sort order
188#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
189pub enum SortOrder {
190    /// Sort by the most popular posts
191    Popular,
192
193    /// Sort by the newest posts
194    New,
195
196    /// Sort by the oldest posts
197    Old,
198}
199
200/// A builder for listing posts
201#[derive(Debug, Clone)]
202pub struct ListPostsBuilder {
203    /// How posts should be sorted.
204    ///
205    /// Defaults to popular.
206    pub sort: SortOrder,
207
208    /// The page to get.
209    ///
210    /// Starts at 1.
211    pub page: u64,
212
213    /// The username to filter posts by.
214    pub username: Option<String>,
215
216    /// Whether to list posts from the current user.
217    pub profile: bool,
218}
219
220impl ListPostsBuilder {
221    /// Make a new builder
222    pub fn new() -> Self {
223        Self {
224            sort: SortOrder::Popular,
225            page: 1,
226            username: None,
227            profile: false,
228        }
229    }
230
231    /// Set how posts should be sorted.
232    ///
233    /// Defaults to popular.
234    pub fn sort(&mut self, sort: SortOrder) -> &mut Self {
235        self.sort = sort;
236        self
237    }
238
239    /// Set the page to get.
240    ///
241    /// Starts at 1.
242    pub fn page(&mut self, page: u64) -> &mut Self {
243        self.page = page;
244        self
245    }
246
247    /// Set the username to filter by.
248    pub fn username(&mut self, username: String) -> &mut Self {
249        self.username = Some(username);
250        self
251    }
252
253    /// Set whether to list posts from the current user.
254    pub fn profile(&mut self, profile: bool) -> &mut Self {
255        self.profile = profile;
256        self
257    }
258}
259
260impl Default for ListPostsBuilder {
261    fn default() -> Self {
262        Self::new()
263    }
264}