use serde::{Deserialize, Serialize};
use validator::Validate;
#[derive(Clone, Serialize, Validate)]
pub struct FileListQuery {
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(length(min = 1))]
pub(crate) after: Option<String>,
pub(crate) purpose: FileListPurpose,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) order: Option<FileOrder>,
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(range(min = 1, max = 100))]
pub(crate) limit: Option<u32>,
}
impl std::fmt::Debug for FileListQuery {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("FileListQuery")
.field("after", &self.after.as_ref().map(|_| "[REDACTED]"))
.field("purpose", &self.purpose)
.field("order", &self.order)
.field("limit", &self.limit)
.finish()
}
}
impl FileListQuery {
pub fn new(purpose: FileListPurpose) -> Self {
Self {
after: None,
purpose,
order: None,
limit: None,
}
}
pub fn with_after(mut self, after: impl Into<String>) -> Self {
self.after = Some(after.into());
self
}
pub fn with_order(mut self, o: FileOrder) -> Self {
self.order = Some(o);
self
}
pub fn with_limit(mut self, limit: u32) -> Self {
self.limit = Some(limit);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum FileListPurpose {
#[serde(rename = "batch")]
Batch,
#[serde(rename = "code-interpreter")]
CodeInterpreter,
#[serde(rename = "agent")]
Agent,
}
impl FileListPurpose {
pub fn as_str(&self) -> &'static str {
match self {
Self::Batch => "batch",
Self::CodeInterpreter => "code-interpreter",
Self::Agent => "agent",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum FileUploadPurpose {
#[serde(rename = "batch")]
Batch,
#[serde(rename = "code-interpreter")]
CodeInterpreter,
#[serde(rename = "agent")]
Agent,
#[serde(rename = "voice-clone-input")]
VoiceCloneInput,
}
impl FileUploadPurpose {
pub const fn as_str(self) -> &'static str {
match self {
Self::Batch => "batch",
Self::CodeInterpreter => "code-interpreter",
Self::Agent => "agent",
Self::VoiceCloneInput => "voice-clone-input",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum FileOrder {
#[serde(rename = "created_at")]
CreatedAt,
}
impl FileOrder {
pub fn as_str(&self) -> &'static str {
match self {
FileOrder::CreatedAt => "created_at",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn list_query_debug_redacts_the_file_cursor() {
let query = FileListQuery::new(FileListPurpose::Batch).with_after("private-file-id");
assert!(!format!("{query:?}").contains("private-file-id"));
}
#[test]
fn operation_specific_purpose_enums_match_the_frozen_values() {
assert_eq!(
[
FileListPurpose::Batch,
FileListPurpose::CodeInterpreter,
FileListPurpose::Agent,
]
.map(|value| serde_json::to_value(value).unwrap()),
["batch", "code-interpreter", "agent"]
.map(|value| serde_json::Value::String(value.to_owned()))
);
assert_eq!(
[
FileUploadPurpose::Batch,
FileUploadPurpose::CodeInterpreter,
FileUploadPurpose::Agent,
FileUploadPurpose::VoiceCloneInput,
]
.map(|value| serde_json::to_value(value).unwrap()),
["batch", "code-interpreter", "agent", "voice-clone-input"]
.map(|value| serde_json::Value::String(value.to_owned()))
);
}
}