Skip to main content

claude_api/files/
types.rs

1//! Wire types for the Files API.
2
3use serde::{Deserialize, Serialize};
4
5/// Metadata for a single uploaded file.
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7#[non_exhaustive]
8pub struct FileMetadata {
9    /// Stable file identifier (e.g. `file_011...`).
10    pub id: String,
11    /// Wire `type` discriminant; always `"file"`.
12    #[serde(rename = "type", default = "default_file_kind")]
13    pub kind: String,
14    /// Original filename supplied at upload.
15    pub filename: String,
16    /// IANA MIME type (e.g. `application/pdf`).
17    pub mime_type: String,
18    /// File size in bytes.
19    pub size_bytes: u64,
20    /// Creation timestamp (ISO-8601).
21    pub created_at: String,
22    /// Whether the file's bytes can be retrieved via
23    /// [`Files::download`](super::api::Files::download).
24    #[serde(default)]
25    pub downloadable: bool,
26    /// Scoping resource (e.g. session this file is bound to). `None`
27    /// for workspace-scoped files.
28    #[serde(default, skip_serializing_if = "Option::is_none")]
29    pub scope: Option<FileScope>,
30}
31
32/// Scoping resource for a [`FileMetadata`]. Currently the only
33/// supported scope is a session: files written by an agent under
34/// `/mnt/session/outputs/` are session-scoped and listable via the
35/// `?scope_id={session_id}` query.
36#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
37#[non_exhaustive]
38pub struct FileScope {
39    /// Scope discriminator. Currently `"session"`.
40    #[serde(rename = "type")]
41    pub kind: String,
42    /// ID of the scoping resource (e.g. the session ID).
43    pub id: String,
44}
45
46fn default_file_kind() -> String {
47    "file".to_owned()
48}
49
50/// Confirmation returned by `DELETE /v1/files/{id}`.
51#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
52#[non_exhaustive]
53pub struct FileDeleted {
54    /// ID of the deleted file.
55    pub id: String,
56    /// Wire `type`; typically `"file_deleted"`.
57    #[serde(rename = "type", default)]
58    pub kind: String,
59}
60
61/// Query parameters for `GET /v1/files`.
62#[derive(Debug, Clone, Default, Serialize)]
63#[non_exhaustive]
64pub struct ListFilesParams {
65    /// Cursor for backward pagination.
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub before_id: Option<String>,
68    /// Cursor for forward pagination.
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub after_id: Option<String>,
71    /// Page size (server-defaulted if omitted).
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub limit: Option<u32>,
74}
75
76impl ListFilesParams {
77    /// Set the `after_id` cursor.
78    #[must_use]
79    pub fn after_id(mut self, id: impl Into<String>) -> Self {
80        self.after_id = Some(id.into());
81        self
82    }
83
84    /// Set the `before_id` cursor.
85    #[must_use]
86    pub fn before_id(mut self, id: impl Into<String>) -> Self {
87        self.before_id = Some(id.into());
88        self
89    }
90
91    /// Set the page size.
92    #[must_use]
93    pub fn limit(mut self, limit: u32) -> Self {
94        self.limit = Some(limit);
95        self
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use pretty_assertions::assert_eq;
103    use serde_json::json;
104
105    #[test]
106    fn file_metadata_round_trips() {
107        let raw = json!({
108            "id": "file_011ABC",
109            "type": "file",
110            "filename": "report.pdf",
111            "mime_type": "application/pdf",
112            "size_bytes": 12345,
113            "created_at": "2026-04-30T00:00:00Z",
114            "downloadable": true
115        });
116        let parsed: FileMetadata = serde_json::from_value(raw.clone()).unwrap();
117        assert_eq!(parsed.id, "file_011ABC");
118        assert_eq!(parsed.kind, "file");
119        assert_eq!(parsed.filename, "report.pdf");
120        assert_eq!(parsed.size_bytes, 12345);
121        assert!(parsed.downloadable);
122        assert_eq!(serde_json::to_value(&parsed).unwrap(), raw);
123    }
124
125    #[test]
126    fn file_metadata_kind_defaults_when_missing() {
127        let raw = json!({
128            "id": "file_X",
129            "filename": "x.txt",
130            "mime_type": "text/plain",
131            "size_bytes": 1,
132            "created_at": "2026"
133        });
134        let parsed: FileMetadata = serde_json::from_value(raw).unwrap();
135        assert_eq!(parsed.kind, "file");
136    }
137
138    #[test]
139    fn file_deleted_round_trips() {
140        let raw = json!({"id": "file_X", "type": "file_deleted"});
141        let parsed: FileDeleted = serde_json::from_value(raw).unwrap();
142        assert_eq!(parsed.id, "file_X");
143        assert_eq!(parsed.kind, "file_deleted");
144    }
145}