jmap_client/blob/
upload.rs

1/*
2 * Copyright Stalwart Labs Ltd. See the COPYING
3 * file at the top-level directory of this distribution.
4 *
5 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
8 * option. This file may not be copied, modified, or distributed
9 * except according to those terms.
10 */
11
12use reqwest::header::CONTENT_TYPE;
13use serde::Deserialize;
14
15use crate::{client::Client, core::session::URLPart};
16
17#[cfg(feature = "blocking")]
18use reqwest::blocking::Client as HttpClient;
19#[cfg(feature = "async")]
20use reqwest::Client as HttpClient;
21
22#[derive(Debug, Deserialize)]
23pub struct UploadResponse {
24    #[serde(rename = "accountId")]
25    account_id: String,
26
27    #[serde(rename = "blobId")]
28    blob_id: String,
29
30    #[serde(rename = "type")]
31    type_: String,
32
33    #[serde(rename = "size")]
34    size: usize,
35}
36
37impl Client {
38    #[maybe_async::maybe_async]
39    pub async fn upload(
40        &self,
41        account_id: Option<&str>,
42        blob: Vec<u8>,
43        content_type: Option<&str>,
44    ) -> crate::Result<UploadResponse> {
45        let account_id = account_id.unwrap_or_else(|| self.default_account_id());
46        let mut upload_url =
47            String::with_capacity(self.session().upload_url().len() + account_id.len());
48
49        for part in self.upload_url() {
50            match part {
51                URLPart::Value(value) => {
52                    upload_url.push_str(value);
53                }
54                URLPart::Parameter(param) => {
55                    if let super::URLParameter::AccountId = param {
56                        upload_url.push_str(account_id);
57                    }
58                }
59            }
60        }
61
62        serde_json::from_slice::<UploadResponse>(
63            &Client::handle_error(
64                HttpClient::builder()
65                    .timeout(self.timeout())
66                    .danger_accept_invalid_certs(self.accept_invalid_certs)
67                    .redirect(self.redirect_policy())
68                    .default_headers(self.headers().clone())
69                    .build()?
70                    .post(upload_url)
71                    .header(
72                        CONTENT_TYPE,
73                        content_type.unwrap_or("application/octet-stream"),
74                    )
75                    .body(blob)
76                    .send()
77                    .await?,
78            )
79            .await?
80            .bytes()
81            .await?,
82        )
83        .map_err(|err| err.into())
84    }
85}
86
87impl UploadResponse {
88    pub fn account_id(&self) -> &str {
89        &self.account_id
90    }
91
92    pub fn blob_id(&self) -> &str {
93        &self.blob_id
94    }
95
96    pub fn content_type(&self) -> &str {
97        &self.type_
98    }
99
100    pub fn size(&self) -> usize {
101        self.size
102    }
103
104    pub fn take_blob_id(&mut self) -> String {
105        std::mem::take(&mut self.blob_id)
106    }
107}