Skip to main content

zai_rs/file/
upload.rs

1use std::path::PathBuf;
2
3use super::{request::FileUploadPurpose, response::FileUploadResponse};
4use crate::client::ZaiClient;
5
6/// File upload request (multipart/form-data)
7///
8/// Sends a multipart request with fields:
9/// - purpose: [`FileUploadPurpose`]
10/// - file: file content
11pub struct FileUploadRequest {
12    purpose: FileUploadPurpose,
13    file_path: PathBuf,
14    file_name: Option<String>,
15    content_type: Option<String>,
16}
17
18impl FileUploadRequest {
19    /// Create a new upload request for the given purpose and local file path.
20    pub fn new(purpose: FileUploadPurpose, file_path: impl Into<PathBuf>) -> Self {
21        Self {
22            purpose,
23            file_path: file_path.into(),
24            file_name: None,
25            content_type: None,
26        }
27    }
28
29    /// Override the uploaded file name (defaults to the path's file name).
30    pub fn with_file_name(mut self, name: impl Into<String>) -> Self {
31        self.file_name = Some(name.into());
32        self
33    }
34
35    /// Override the MIME content type of the upload.
36    pub fn with_content_type(mut self, ct: impl Into<String>) -> Self {
37        self.content_type = Some(ct.into());
38
39        self
40    }
41
42    /// Send the upload request and parse the returned [`FileObject`](super::response::FileObject).
43    ///
44    /// The local file is reopened, revalidated, and streamed for each transport
45    /// attempt. It is never buffered in full by this request builder.
46    pub async fn send_via(&self, client: &ZaiClient) -> crate::ZaiResult<FileUploadResponse> {
47        let route = crate::client::routes::FILES_UPLOAD;
48        let url = client.endpoints().resolve_route(route, &[])?;
49        let mut file_part =
50            crate::client::transport::multipart::FilePart::from_path(&self.file_path)?;
51        if let Some(file_name) = &self.file_name {
52            file_part = file_part.with_filename(file_name)?;
53        }
54        if let Some(content_type) = &self.content_type {
55            file_part = file_part.with_content_type(content_type)?;
56        }
57        let factory = crate::client::transport::multipart::MultipartBodyFactory::new()
58            .field("purpose", self.purpose.as_str())?
59            .file_named("file", file_part)?;
60        client
61            .send_multipart::<FileUploadResponse>(route.method(), url, &factory)
62            .await
63    }
64}