Skip to main content

deepseek_sdk/files/
types.rs

1use serde::{Deserialize, Serialize};
2
3/// A file object returned by the Files API.
4#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
5pub struct FileObject {
6    /// The file identifier, of the form `file-api-...`.
7    pub id: String,
8    /// The object type, which is always `file`.
9    pub object: String,
10    /// The size of the file in bytes.
11    pub bytes: u64,
12    /// The Unix timestamp (in seconds) of when the file was created.
13    pub created_at: u64,
14    /// The name of the file.
15    pub filename: String,
16    /// The intended purpose of the file.
17    pub purpose: String,
18    /// The Unix timestamp (in seconds) of when the file expires.
19    /// Only present when an expiration was set at upload time.
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub expires_at: Option<u64>,
22}
23
24/// Response from deleting a file.
25#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
26pub struct FileDeleteResponse {
27    /// The ID of the deleted file.
28    pub id: String,
29    /// The object type, which is always `file`.
30    pub object: String,
31    /// Whether the file was successfully deleted.
32    pub deleted: bool,
33}
34
35/// Response from listing files.
36#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
37pub struct FileListResponse {
38    /// The object type, which is always `list`.
39    pub object: String,
40    /// The list of file objects.
41    pub data: Vec<FileObject>,
42    /// The ID of the first file in the list.
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub first_id: Option<String>,
45    /// The ID of the last file in the list.
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub last_id: Option<String>,
48    /// Whether there are more files beyond this page.
49    pub has_more: bool,
50}
51
52/// Query parameters for listing files.
53#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
54pub struct FileListParams {
55    /// A `file_id` cursor for pagination. Returns files listed after this one.
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub after: Option<String>,
58    /// The number of files to return (1-1000, default 1000).
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub limit: Option<u32>,
61    /// Sort order by creation time: `asc` (default) or `desc`.
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub order: Option<String>,
64    /// Only return files with the given purpose.
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub purpose: Option<String>,
67}
68
69/// Expiration settings for file upload.
70#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
71pub struct FileExpiration {
72    /// The anchor for the expiration. Must be `created_at`.
73    pub anchor: String,
74    /// The lifetime of the file in seconds (3600-2592000).
75    pub seconds: u32,
76}
77
78impl FileExpiration {
79    /// Create an expiration anchor with a duration in seconds.
80    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(&params).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}