1use std::path::Path;
4
5use serde::Deserialize;
6
7use crate::client::Client;
8use crate::error::{Error, Result};
9use crate::types::UploadedFile;
10
11#[derive(Deserialize)]
12struct ImageTokenBody {
13 image_token: uuid::Uuid,
14}
15
16impl Client {
17 #[tracing::instrument(skip(self), fields(path = %path.as_ref().display()))]
19 pub async fn upload_file(&self, path: impl AsRef<Path>) -> Result<UploadedFile> {
20 let path = path.as_ref();
21 let file_name = path
22 .file_name()
23 .and_then(|s| s.to_str())
24 .ok_or_else(|| {
25 Error::Io(std::io::Error::new(
26 std::io::ErrorKind::InvalidInput,
27 "non-utf8 filename",
28 ))
29 })?
30 .to_string();
31 let bytes = tokio::fs::read(path).await?;
32 let part = reqwest::multipart::Part::bytes(bytes).file_name(file_name);
33 let form = reqwest::multipart::Form::new().part("file", part);
34
35 let url = self.url(&["upload"]);
36 let resp = self
37 .http
38 .post(url)
39 .headers(self.region_headers())
40 .multipart(form)
41 .send()
42 .await?;
43 let status = resp.status();
44 let body = resp.bytes().await?;
45 if !status.is_success() {
46 return Err(crate::envelope::map_http_error(status, &body));
47 }
48 let env: crate::envelope::Envelope<ImageTokenBody> = serde_json::from_slice(&body)?;
49 let data = env.into_result()?;
50 Ok(UploadedFile {
51 file_token: data.image_token,
52 })
53 }
54}