1use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct FileEntity {
15 #[serde(skip_serializing_if = "Option::is_none")]
17 pub path: Option<String>,
18
19 #[serde(skip_serializing_if = "Option::is_none")]
21 pub display_name: Option<String>,
22
23 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
25 pub file_type: Option<String>,
26
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub size: Option<i64>,
30
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub created_at: Option<String>,
34
35 #[serde(skip_serializing_if = "Option::is_none")]
37 pub mtime: Option<String>,
38
39 #[serde(skip_serializing_if = "Option::is_none")]
41 pub provided_mtime: Option<String>,
42
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub crc32: Option<String>,
46
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub md5: Option<String>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub sha1: Option<String>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub sha256: Option<String>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub mime_type: Option<String>,
62
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub region: Option<String>,
66
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub permissions: Option<String>,
70
71 #[serde(rename = "subfolders_locked?", skip_serializing_if = "Option::is_none")]
73 pub subfolders_locked: Option<bool>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub is_locked: Option<bool>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub download_uri: Option<String>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub priority_color: Option<String>,
86
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub preview_id: Option<i64>,
90
91 #[serde(skip_serializing_if = "Option::is_none")]
93 pub preview: Option<String>,
94
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub custom_metadata: Option<HashMap<String, String>>,
98
99 #[serde(skip_serializing_if = "Option::is_none")]
101 pub created_by_id: Option<i64>,
102
103 #[serde(skip_serializing_if = "Option::is_none")]
105 pub created_by_api_key_id: Option<i64>,
106
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub created_by_automation_id: Option<i64>,
110
111 #[serde(skip_serializing_if = "Option::is_none")]
113 pub created_by_bundle_registration_id: Option<i64>,
114
115 #[serde(skip_serializing_if = "Option::is_none")]
117 pub created_by_inbox_id: Option<i64>,
118
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub created_by_remote_server_id: Option<i64>,
122
123 #[serde(skip_serializing_if = "Option::is_none")]
125 pub created_by_remote_server_sync_id: Option<i64>,
126
127 #[serde(skip_serializing_if = "Option::is_none")]
129 pub created_by_as2_incoming_message_id: Option<i64>,
130
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub last_modified_by_id: Option<i64>,
134
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub last_modified_by_api_key_id: Option<i64>,
138
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub last_modified_by_automation_id: Option<i64>,
142
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub last_modified_by_bundle_registration_id: Option<i64>,
146
147 #[serde(skip_serializing_if = "Option::is_none")]
149 pub last_modified_by_remote_server_id: Option<i64>,
150
151 #[serde(skip_serializing_if = "Option::is_none")]
153 pub last_modified_by_remote_server_sync_id: Option<i64>,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct FileUploadPartEntity {
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub upload_uri: Option<String>,
165
166 #[serde(skip_serializing_if = "Option::is_none")]
168 pub http_method: Option<String>,
169
170 #[serde(skip_serializing_if = "Option::is_none")]
172 pub headers: Option<HashMap<String, String>>,
173
174 #[serde(skip_serializing_if = "Option::is_none")]
176 pub parameters: Option<HashMap<String, String>>,
177
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub part_number: Option<i32>,
181
182 #[serde(skip_serializing_if = "Option::is_none")]
184 pub partsize: Option<i64>,
185
186 #[serde(skip_serializing_if = "Option::is_none")]
188 pub next_partsize: Option<i64>,
189
190 #[serde(rename = "ref", skip_serializing_if = "Option::is_none")]
192 pub ref_: Option<String>,
193
194 #[serde(skip_serializing_if = "Option::is_none")]
196 pub action: Option<String>,
197
198 #[serde(skip_serializing_if = "Option::is_none")]
200 pub parallel_parts: Option<bool>,
201
202 #[serde(skip_serializing_if = "Option::is_none")]
204 pub retry_parts: Option<bool>,
205
206 #[serde(skip_serializing_if = "Option::is_none")]
208 pub available_parts: Option<i32>,
209
210 #[serde(skip_serializing_if = "Option::is_none")]
212 pub expires: Option<String>,
213
214 #[serde(skip_serializing_if = "Option::is_none")]
216 pub send: Option<HashMap<String, String>>,
217
218 #[serde(skip_serializing_if = "Option::is_none")]
220 pub path: Option<String>,
221
222 #[serde(skip_serializing_if = "Option::is_none")]
224 pub ask_about_overwrites: Option<bool>,
225}
226
227pub type FolderEntity = FileEntity;
231
232#[derive(Debug, Clone, Default)]
236pub struct PaginationInfo {
237 pub cursor_next: Option<String>,
239
240 pub cursor_prev: Option<String>,
242}
243
244impl PaginationInfo {
245 pub fn from_headers(headers: &reqwest::header::HeaderMap) -> Self {
247 let cursor_next = headers
248 .get("X-Files-Cursor-Next")
249 .and_then(|v| v.to_str().ok())
250 .map(String::from);
251
252 let cursor_prev = headers
253 .get("X-Files-Cursor-Prev")
254 .and_then(|v| v.to_str().ok())
255 .map(String::from);
256
257 Self {
258 cursor_next,
259 cursor_prev,
260 }
261 }
262
263 pub fn has_next(&self) -> bool {
265 self.cursor_next.is_some()
266 }
267
268 pub fn has_prev(&self) -> bool {
270 self.cursor_prev.is_some()
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 #[test]
279 fn test_file_entity_deserialize() {
280 let json = r#"{
281 "path": "/test/file.txt",
282 "display_name": "file.txt",
283 "type": "file",
284 "size": 1024
285 }"#;
286
287 let entity: FileEntity = serde_json::from_str(json).unwrap();
288 assert_eq!(entity.path, Some("/test/file.txt".to_string()));
289 assert_eq!(entity.display_name, Some("file.txt".to_string()));
290 assert_eq!(entity.file_type, Some("file".to_string()));
291 assert_eq!(entity.size, Some(1024));
292 }
293
294 #[test]
295 fn test_pagination_info_empty() {
296 let headers = reqwest::header::HeaderMap::new();
297 let info = PaginationInfo::from_headers(&headers);
298 assert!(!info.has_next());
299 assert!(!info.has_prev());
300 }
301
302 #[test]
303 fn test_pagination_info_with_next() {
304 let mut headers = reqwest::header::HeaderMap::new();
305 headers.insert("X-Files-Cursor-Next", "next-cursor".parse().unwrap());
306
307 let info = PaginationInfo::from_headers(&headers);
308 assert!(info.has_next());
309 assert!(!info.has_prev());
310 assert_eq!(info.cursor_next, Some("next-cursor".to_string()));
311 }
312}