Skip to main content

gproxy_protocol/protocol/claude/
files.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use super::common::{AnthropicBetaHeaders, DeletedFileObjectType, FileObjectType, JsonObject};
6
7pub type FileRequestHeaders = AnthropicBetaHeaders;
8
9/// Multipart form fields for `POST /v1/files`.
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
11#[non_exhaustive]
12pub struct UploadFileRequestBody {
13    pub file: String,
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub expires_in_seconds: Option<u64>,
16    #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
17    pub extra: JsonObject,
18}
19
20#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
21#[non_exhaustive]
22pub struct ListFilesQuery {
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub limit: Option<u64>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub page: Option<String>,
27    /// Repeated `ids[]` query parameters; mutually exclusive with `page` and `limit`.
28    #[serde(rename = "ids[]", skip_serializing_if = "Option::is_none")]
29    pub ids: Option<Vec<String>>,
30    #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
31    pub extra: JsonObject,
32}
33
34#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
35#[non_exhaustive]
36pub struct FilePath {
37    pub file_id: String,
38}
39
40#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
41#[non_exhaustive]
42pub struct FileMetadata {
43    pub id: String,
44    pub created_at: String,
45    pub filename: String,
46    pub mime_type: String,
47    pub size_bytes: u64,
48    #[serde(rename = "type")]
49    pub type_: FileObjectType,
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub downloadable: Option<bool>,
52    /// GA responses include this as either an RFC 3339 timestamp or `null`.
53    /// It remains optional so responses selected by the legacy beta header decode.
54    #[serde(default)]
55    pub expires_at: Option<String>,
56    #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
57    pub extra: JsonObject,
58}
59
60pub type UploadFileResponseBody = FileMetadata;
61pub type RetrieveFileResponseBody = FileMetadata;
62
63#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
64#[non_exhaustive]
65pub struct ListFilesResponseBody {
66    pub data: Vec<FileMetadata>,
67    pub next_page: Option<String>,
68    #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
69    pub extra: JsonObject,
70}
71
72#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
73#[non_exhaustive]
74pub struct DeleteFileResponseBody {
75    pub id: String,
76    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
77    pub type_: Option<DeletedFileObjectType>,
78    #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
79    pub extra: JsonObject,
80}
81
82pub type DownloadFileResponseBody = Vec<u8>;
83
84#[cfg(test)]
85mod tests {
86    use serde_json::json;
87
88    use super::*;
89
90    #[test]
91    fn models_ga_expiration_and_page_cursor_shapes() {
92        let file: FileMetadata = serde_json::from_value(json!({
93            "id": "file_123",
94            "created_at": "2026-08-19T00:00:00Z",
95            "filename": "notes.txt",
96            "mime_type": "text/plain",
97            "size_bytes": 42,
98            "type": "file",
99            "downloadable": false,
100            "expires_at": null
101        }))
102        .unwrap();
103        assert!(file.expires_at.is_none());
104
105        let query = ListFilesQuery::builder()
106            .ids(Some(vec!["file_123".into(), "file_456".into()]))
107            .build()
108            .unwrap();
109        assert_eq!(serde_json::to_value(query).unwrap()["ids[]"][1], "file_456");
110    }
111}