openai_interface/files/
delete.rs1pub mod request {
4 use std::collections::HashMap;
5
6 use url::Url;
7
8 use crate::{
9 errors::OapiError,
10 rest::delete::{Delete, DeleteNoStream},
11 };
12
13 #[derive(Debug, Default)]
14 pub struct DeleteRequest<'a> {
15 pub file_id: &'a str,
16 pub extra_query: HashMap<&'a str, &'a str>,
17 }
18
19 impl Delete for DeleteRequest<'_> {
20 fn build_url(&self, base_url: &str) -> Result<String, crate::errors::OapiError> {
22 let mut url =
23 Url::parse(base_url.trim_end_matches('/')).map_err(|e| OapiError::UrlError(e))?;
24 url.path_segments_mut()
25 .map_err(|_| OapiError::UrlCannotBeBase(base_url.to_string()))?
26 .push("files")
27 .push(self.file_id);
28
29 for (key, value) in &self.extra_query {
30 url.query_pairs_mut().append_pair(key, value).finish();
31 }
32
33 println!("Built URL: {}", url);
34 Ok(url.to_string())
35 }
36 }
37
38 impl<'a> DeleteNoStream for DeleteRequest<'a> {
39 type Response = super::response::FileDeleted;
40 }
41}
42
43pub mod response {
44 use std::str::FromStr;
45
46 use serde::Deserialize;
47
48 use crate::errors::OapiError;
49
50 #[derive(Debug, Deserialize, Clone)]
51 pub struct FileDeleted {
52 pub id: String,
53 pub deleted: bool,
54 pub object: FileDeletedObject,
55 }
56
57 #[derive(Debug, Deserialize, Clone)]
58 #[serde(rename_all = "snake_case")]
59 pub enum FileDeletedObject {
60 File,
61 }
62
63 impl FromStr for FileDeleted {
64 type Err = crate::errors::OapiError;
65
66 fn from_str(s: &str) -> Result<Self, Self::Err> {
67 serde_json::from_str(s).map_err(|e| OapiError::DeserializationError(e.to_string()))
68 }
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use std::sync::LazyLock;
75
76 use anyhow::{Context, anyhow};
77
78 use crate::{
79 files::{delete::request::DeleteRequest, list::request::ListFilesRequest},
80 rest::{delete::DeleteNoStream, get::GetNoStream},
81 };
82
83 const MODELSTUDIO_BASE_URL: &str = "https://dashscope.aliyuncs.com/compatible-mode/v1/";
84 const MODELSTUDIO_KEY: LazyLock<&str> =
85 LazyLock::new(|| include_str!("../../keys/modelstudio_domestic_key").trim());
86
87 #[tokio::test]
88 async fn test_modelstudio_delete_files() -> anyhow::Result<()> {
89 let list_request = ListFilesRequest::default();
90 let list_response = list_request
91 .get_response(MODELSTUDIO_BASE_URL, &MODELSTUDIO_KEY)
92 .await
93 .with_context(|| anyhow!("Failed to list files."))?;
94
95 let files = list_response
96 .data
97 .iter()
98 .filter_map(|file_object| {
99 let name = file_object.filename.as_str();
100 let file_id = file_object.id.as_str();
101 if name.starts_with("test") {
102 Some(file_id)
103 } else {
104 None
105 }
106 })
107 .collect::<Vec<&str>>();
108
109 let futures = files
110 .iter()
111 .map(|file| async move {
112 let delete_request = DeleteRequest {
113 file_id: file,
114 ..Default::default()
115 };
116 delete_request
117 .get_response(MODELSTUDIO_BASE_URL, &MODELSTUDIO_KEY)
118 .await
119 .with_context(|| anyhow!("Failed to delete file {}", file))
120 })
121 .collect::<Vec<_>>();
122
123 let results = futures_util::future::join_all(futures).await;
124
125 for result in results {
126 result?;
127 }
128
129 Ok(())
130 }
131}