Skip to main content

tripo_api/
upload.rs

1//! `/upload` multipart helper.
2
3use 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    /// Upload a local file and return a token usable as `ImageInput::FileToken`.
18    #[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}