1use serde::{Deserialize, Serialize};
2
3#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
5pub struct FileObject {
6 pub id: String,
8 pub object: String,
10 pub bytes: u64,
12 pub created_at: u64,
14 pub filename: String,
16 pub purpose: String,
18 #[serde(skip_serializing_if = "Option::is_none")]
21 pub expires_at: Option<u64>,
22}
23
24#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
26pub struct FileDeleteResponse {
27 pub id: String,
29 pub object: String,
31 pub deleted: bool,
33}
34
35#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
37pub struct FileListResponse {
38 pub object: String,
40 pub data: Vec<FileObject>,
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub first_id: Option<String>,
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub last_id: Option<String>,
48 pub has_more: bool,
50}
51
52#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
54pub struct FileListParams {
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub after: Option<String>,
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub limit: Option<u32>,
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub order: Option<String>,
64 #[serde(skip_serializing_if = "Option::is_none")]
66 pub purpose: Option<String>,
67}
68
69#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
71pub struct FileExpiration {
72 pub anchor: String,
74 pub seconds: u32,
76}
77
78impl FileExpiration {
79 pub fn created_at(seconds: u32) -> Self {
81 FileExpiration {
82 anchor: "created_at".to_string(),
83 seconds,
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use serde_json::json;
92
93 #[test]
94 fn file_object_deserializes_from_api_response() {
95 let json = json!({
96 "id": "file-api-0a1b2c3d4e5f60718293a4b5c6d7e8f9",
97 "object": "file",
98 "bytes": 102400,
99 "created_at": 1700000000,
100 "filename": "image.jpg",
101 "purpose": "user_data"
102 });
103 let file: FileObject = serde_json::from_value(json).unwrap();
104 assert_eq!(file.id, "file-api-0a1b2c3d4e5f60718293a4b5c6d7e8f9");
105 assert_eq!(file.object, "file");
106 assert_eq!(file.bytes, 102400);
107 assert_eq!(file.filename, "image.jpg");
108 assert!(file.expires_at.is_none());
109 }
110
111 #[test]
112 fn file_object_with_expiration_deserializes() {
113 let json = json!({
114 "id": "file-api-abc",
115 "object": "file",
116 "bytes": 1024,
117 "created_at": 1700000000,
118 "filename": "test.png",
119 "purpose": "user_data",
120 "expires_at": 1700003600
121 });
122 let file: FileObject = serde_json::from_value(json).unwrap();
123 assert_eq!(file.expires_at, Some(1700003600));
124 }
125
126 #[test]
127 fn file_delete_response_deserializes() {
128 let json = json!({
129 "id": "file-api-abc",
130 "object": "file",
131 "deleted": true
132 });
133 let resp: FileDeleteResponse = serde_json::from_value(json).unwrap();
134 assert_eq!(resp.id, "file-api-abc");
135 assert!(resp.deleted);
136 }
137
138 #[test]
139 fn file_list_response_deserializes() {
140 let json = json!({
141 "object": "list",
142 "data": [
143 {
144 "id": "file-api-001",
145 "object": "file",
146 "bytes": 1024,
147 "created_at": 1700000000,
148 "filename": "a.jpg",
149 "purpose": "user_data"
150 },
151 {
152 "id": "file-api-002",
153 "object": "file",
154 "bytes": 2048,
155 "created_at": 1700000001,
156 "filename": "b.png",
157 "purpose": "user_data"
158 }
159 ],
160 "first_id": "file-api-001",
161 "last_id": "file-api-002",
162 "has_more": false
163 });
164 let resp: FileListResponse = serde_json::from_value(json).unwrap();
165 assert_eq!(resp.object, "list");
166 assert_eq!(resp.data.len(), 2);
167 assert_eq!(resp.first_id.as_deref(), Some("file-api-001"));
168 assert!(!resp.has_more);
169 }
170
171 #[test]
172 fn file_expiration_created_at() {
173 let exp = FileExpiration::created_at(7200);
174 assert_eq!(exp.anchor, "created_at");
175 assert_eq!(exp.seconds, 7200);
176 }
177
178 #[test]
179 fn file_list_params_serializes_only_set_fields() {
180 let params = FileListParams {
181 limit: Some(50),
182 order: Some("desc".to_string()),
183 ..Default::default()
184 };
185 let value = serde_json::to_value(¶ms).unwrap();
186 assert_eq!(value, json!({"limit": 50, "order": "desc"}));
187 }
188
189 #[test]
190 fn file_expiration_serializes_anchor_and_seconds() {
191 let value = serde_json::to_value(FileExpiration::created_at(3600)).unwrap();
192 assert_eq!(value, json!({"anchor": "created_at", "seconds": 3600}));
193 }
194}