use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct FileMetadata {
pub id: String,
#[serde(rename = "type", default = "default_file_kind")]
pub kind: String,
pub filename: String,
pub mime_type: String,
pub size_bytes: u64,
pub created_at: String,
#[serde(default)]
pub downloadable: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scope: Option<FileScope>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct FileScope {
#[serde(rename = "type")]
pub kind: String,
pub id: String,
}
fn default_file_kind() -> String {
"file".to_owned()
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct FileDeleted {
pub id: String,
#[serde(rename = "type", default)]
pub kind: String,
}
#[derive(Debug, Clone, Default, Serialize)]
#[non_exhaustive]
pub struct ListFilesParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub before_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub after_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
}
impl ListFilesParams {
#[must_use]
pub fn after_id(mut self, id: impl Into<String>) -> Self {
self.after_id = Some(id.into());
self
}
#[must_use]
pub fn before_id(mut self, id: impl Into<String>) -> Self {
self.before_id = Some(id.into());
self
}
#[must_use]
pub fn limit(mut self, limit: u32) -> Self {
self.limit = Some(limit);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn file_metadata_round_trips() {
let raw = json!({
"id": "file_011ABC",
"type": "file",
"filename": "report.pdf",
"mime_type": "application/pdf",
"size_bytes": 12345,
"created_at": "2026-04-30T00:00:00Z",
"downloadable": true
});
let parsed: FileMetadata = serde_json::from_value(raw.clone()).unwrap();
assert_eq!(parsed.id, "file_011ABC");
assert_eq!(parsed.kind, "file");
assert_eq!(parsed.filename, "report.pdf");
assert_eq!(parsed.size_bytes, 12345);
assert!(parsed.downloadable);
assert_eq!(serde_json::to_value(&parsed).unwrap(), raw);
}
#[test]
fn file_metadata_kind_defaults_when_missing() {
let raw = json!({
"id": "file_X",
"filename": "x.txt",
"mime_type": "text/plain",
"size_bytes": 1,
"created_at": "2026"
});
let parsed: FileMetadata = serde_json::from_value(raw).unwrap();
assert_eq!(parsed.kind, "file");
}
#[test]
fn file_deleted_round_trips() {
let raw = json!({"id": "file_X", "type": "file_deleted"});
let parsed: FileDeleted = serde_json::from_value(raw).unwrap();
assert_eq!(parsed.id, "file_X");
assert_eq!(parsed.kind, "file_deleted");
}
}