1use crate::client::TaskForceAI;
2use crate::error::TaskForceAIError;
3use bytes::Bytes;
4use chrono::{DateTime, Utc};
5use reqwest::multipart::{Form, Part};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct File {
11 pub id: String,
12 pub filename: String,
13 pub purpose: String,
14 pub bytes: i64,
15 #[serde(with = "chrono::serde::ts_seconds")]
16 pub created_at: DateTime<Utc>,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub mime_type: Option<String>,
19}
20
21#[derive(Debug, Clone, Default)]
23pub struct FileUploadOptions {
24 pub purpose: Option<String>,
25 pub mime_type: Option<String>,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct FileListResponse {
31 pub files: Vec<File>,
32 pub total: i64,
33}
34
35impl TaskForceAI {
36 pub async fn upload_file(
38 &self,
39 filename: &str,
40 content: Bytes,
41 options: Option<FileUploadOptions>,
42 ) -> Result<File, TaskForceAIError> {
43 let mime_type = options
44 .as_ref()
45 .and_then(|o| o.mime_type.clone())
46 .unwrap_or_else(|| "application/octet-stream".to_string());
47
48 let mut form = Form::new().part(
49 "file",
50 Part::bytes(content.to_vec())
51 .file_name(filename.to_string())
52 .mime_str(&mime_type)
53 .map_err(|e| TaskForceAIError::Other(e.to_string()))?,
54 );
55
56 if let Some(opts) = options {
57 if let Some(purpose) = opts.purpose {
58 form = form.text("purpose", purpose);
59 }
60 }
61
62 let url = format!("{}/files", self.base_url);
63 let mut request = self.client.post(&url).multipart(form);
64
65 if !self.api_key.is_empty() {
66 request = request.bearer_auth(&self.api_key);
67 }
68 request = request.header("X-SDK-Language", "rust");
69
70 let response = request.send().await?;
71 let status = response.status();
72
73 if !status.is_success() {
74 let message = response
75 .text()
76 .await
77 .unwrap_or_else(|_| "Failed to read error message".to_string());
78 return Err(TaskForceAIError::Api { status, message });
79 }
80
81 Ok(response.json().await?)
82 }
83
84 pub async fn list_files(
86 &self,
87 limit: i32,
88 offset: i32,
89 ) -> Result<FileListResponse, TaskForceAIError> {
90 let path = format!("/files?limit={}&offset={}", limit, offset);
91 self.request(reqwest::Method::GET, &path, None).await
92 }
93
94 pub async fn get_file(&self, file_id: &str) -> Result<File, TaskForceAIError> {
96 let path = format!("/files/{}", file_id);
97 self.request(reqwest::Method::GET, &path, None).await
98 }
99
100 pub async fn delete_file(&self, file_id: &str) -> Result<(), TaskForceAIError> {
102 let path = format!("/files/{}", file_id);
103 let _: serde_json::Value = self.request(reqwest::Method::DELETE, &path, None).await?;
104 Ok(())
105 }
106
107 pub async fn download_file(&self, file_id: &str) -> Result<Bytes, TaskForceAIError> {
109 let url = format!("{}/files/{}/content", self.base_url, file_id);
110 let mut request = self.client.get(&url);
111
112 if !self.api_key.is_empty() {
113 request = request.bearer_auth(&self.api_key);
114 }
115 request = request.header("X-SDK-Language", "rust");
116
117 let response = request.send().await?;
118 let status = response.status();
119
120 if !status.is_success() {
121 let message = response
122 .text()
123 .await
124 .unwrap_or_else(|_| "Failed to read error message".to_string());
125 return Err(TaskForceAIError::Api { status, message });
126 }
127
128 Ok(response.bytes().await?)
129 }
130}