1use serde::{Deserialize, Serialize};
2use validator::Validate;
3
4#[derive(Clone, Serialize, Validate)]
6pub struct FileListQuery {
7 #[serde(skip_serializing_if = "Option::is_none")]
9 #[validate(length(min = 1))]
10 pub(crate) after: Option<String>,
11
12 pub(crate) purpose: FileListPurpose,
14
15 #[serde(skip_serializing_if = "Option::is_none")]
17 pub(crate) order: Option<FileOrder>,
18
19 #[serde(skip_serializing_if = "Option::is_none")]
21 #[validate(range(min = 1, max = 100))]
22 pub(crate) limit: Option<u32>,
23}
24
25impl std::fmt::Debug for FileListQuery {
26 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 formatter
28 .debug_struct("FileListQuery")
29 .field("after", &self.after.as_ref().map(|_| "[REDACTED]"))
30 .field("purpose", &self.purpose)
31 .field("order", &self.order)
32 .field("limit", &self.limit)
33 .finish()
34 }
35}
36
37impl FileListQuery {
38 pub fn new(purpose: FileListPurpose) -> Self {
40 Self {
41 after: None,
42 purpose,
43 order: None,
44 limit: None,
45 }
46 }
47 pub fn with_after(mut self, after: impl Into<String>) -> Self {
49 self.after = Some(after.into());
50 self
51 }
52 pub fn with_order(mut self, o: FileOrder) -> Self {
54 self.order = Some(o);
55 self
56 }
57 pub fn with_limit(mut self, limit: u32) -> Self {
59 self.limit = Some(limit);
60 self
61 }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
66pub enum FileListPurpose {
67 #[serde(rename = "batch")]
69 Batch,
70 #[serde(rename = "code-interpreter")]
72 CodeInterpreter,
73 #[serde(rename = "agent")]
75 Agent,
76}
77
78impl FileListPurpose {
79 pub fn as_str(&self) -> &'static str {
81 match self {
82 Self::Batch => "batch",
83 Self::CodeInterpreter => "code-interpreter",
84 Self::Agent => "agent",
85 }
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
91pub enum FileUploadPurpose {
92 #[serde(rename = "batch")]
94 Batch,
95 #[serde(rename = "code-interpreter")]
97 CodeInterpreter,
98 #[serde(rename = "agent")]
100 Agent,
101 #[serde(rename = "voice-clone-input")]
103 VoiceCloneInput,
104}
105
106impl FileUploadPurpose {
107 pub const fn as_str(self) -> &'static str {
109 match self {
110 Self::Batch => "batch",
111 Self::CodeInterpreter => "code-interpreter",
112 Self::Agent => "agent",
113 Self::VoiceCloneInput => "voice-clone-input",
114 }
115 }
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
120pub enum FileOrder {
121 #[serde(rename = "created_at")]
123 CreatedAt,
124}
125
126impl FileOrder {
127 pub fn as_str(&self) -> &'static str {
129 match self {
130 FileOrder::CreatedAt => "created_at",
131 }
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn list_query_debug_redacts_the_file_cursor() {
141 let query = FileListQuery::new(FileListPurpose::Batch).with_after("private-file-id");
142 assert!(!format!("{query:?}").contains("private-file-id"));
143 }
144
145 #[test]
146 fn operation_specific_purpose_enums_match_the_frozen_values() {
147 assert_eq!(
148 [
149 FileListPurpose::Batch,
150 FileListPurpose::CodeInterpreter,
151 FileListPurpose::Agent,
152 ]
153 .map(|value| serde_json::to_value(value).unwrap()),
154 ["batch", "code-interpreter", "agent"]
155 .map(|value| serde_json::Value::String(value.to_owned()))
156 );
157 assert_eq!(
158 [
159 FileUploadPurpose::Batch,
160 FileUploadPurpose::CodeInterpreter,
161 FileUploadPurpose::Agent,
162 FileUploadPurpose::VoiceCloneInput,
163 ]
164 .map(|value| serde_json::to_value(value).unwrap()),
165 ["batch", "code-interpreter", "agent", "voice-clone-input"]
166 .map(|value| serde_json::Value::String(value.to_owned()))
167 );
168 }
169}