openai_ergonomic/builders/
files.rs1use std::path::Path;
12
13#[derive(Debug, Clone)]
18pub struct FileUploadBuilder {
19 filename: String,
20 purpose: FilePurpose,
21 content: Vec<u8>,
22}
23
24#[derive(Debug, Clone)]
26pub enum FilePurpose {
27 FineTune,
29 Assistants,
31 Vision,
33 Batch,
35 Custom(String),
37}
38
39impl FileUploadBuilder {
40 #[must_use]
51 pub fn new(filename: impl Into<String>, purpose: FilePurpose, content: Vec<u8>) -> Self {
52 Self {
53 filename: filename.into(),
54 purpose,
55 content,
56 }
57 }
58
59 pub fn from_path(path: impl AsRef<Path>, purpose: FilePurpose) -> Result<Self, std::io::Error> {
64 let path = path.as_ref();
65 let content = std::fs::read(path)?;
66 let filename = path
67 .file_name()
68 .and_then(|name| name.to_str())
69 .unwrap_or("file")
70 .to_string();
71
72 Ok(Self::new(filename, purpose, content))
73 }
74
75 #[must_use]
77 pub fn from_text(
78 filename: impl Into<String>,
79 purpose: FilePurpose,
80 text: impl Into<String>,
81 ) -> Self {
82 Self::new(filename, purpose, text.into().into_bytes())
83 }
84
85 pub fn from_json(
87 filename: impl Into<String>,
88 purpose: FilePurpose,
89 json: &serde_json::Value,
90 ) -> Result<Self, serde_json::Error> {
91 let content = serde_json::to_vec(json)?;
92 Ok(Self::new(filename, purpose, content))
93 }
94
95 #[must_use]
97 pub fn filename(&self) -> &str {
98 &self.filename
99 }
100
101 #[must_use]
103 pub fn purpose(&self) -> &FilePurpose {
104 &self.purpose
105 }
106
107 #[must_use]
109 pub fn content(&self) -> &[u8] {
110 &self.content
111 }
112
113 #[must_use]
115 pub fn content_size(&self) -> usize {
116 self.content.len()
117 }
118
119 #[must_use]
121 pub fn is_empty(&self) -> bool {
122 self.content.is_empty()
123 }
124
125 #[must_use]
127 pub fn content_as_string(&self) -> Option<String> {
128 String::from_utf8(self.content.clone()).ok()
129 }
130}
131
132#[derive(Debug, Clone)]
134pub struct FileRetrievalBuilder {
135 file_id: String,
136}
137
138impl FileRetrievalBuilder {
139 #[must_use]
141 pub fn new(file_id: impl Into<String>) -> Self {
142 Self {
143 file_id: file_id.into(),
144 }
145 }
146
147 #[must_use]
149 pub fn file_id(&self) -> &str {
150 &self.file_id
151 }
152}
153
154#[derive(Debug, Clone, Default)]
156pub struct FileListBuilder {
157 purpose: Option<FilePurpose>,
158 limit: Option<i32>,
159 order: Option<FileOrder>,
160}
161
162#[derive(Debug, Clone)]
164pub enum FileOrder {
165 Asc,
167 Desc,
169}
170
171impl FileListBuilder {
172 #[must_use]
174 pub fn new() -> Self {
175 Self::default()
176 }
177
178 #[must_use]
180 pub fn purpose(mut self, purpose: FilePurpose) -> Self {
181 self.purpose = Some(purpose);
182 self
183 }
184
185 #[must_use]
187 pub fn limit(mut self, limit: i32) -> Self {
188 self.limit = Some(limit);
189 self
190 }
191
192 #[must_use]
194 pub fn order(mut self, order: FileOrder) -> Self {
195 self.order = Some(order);
196 self
197 }
198
199 #[must_use]
201 pub fn purpose_ref(&self) -> Option<&FilePurpose> {
202 self.purpose.as_ref()
203 }
204
205 #[must_use]
207 pub fn limit_ref(&self) -> Option<i32> {
208 self.limit
209 }
210
211 #[must_use]
213 pub fn order_ref(&self) -> Option<&FileOrder> {
214 self.order.as_ref()
215 }
216}
217
218#[derive(Debug, Clone)]
220pub struct FileDeleteBuilder {
221 file_id: String,
222}
223
224impl FileDeleteBuilder {
225 #[must_use]
227 pub fn new(file_id: impl Into<String>) -> Self {
228 Self {
229 file_id: file_id.into(),
230 }
231 }
232
233 #[must_use]
235 pub fn file_id(&self) -> &str {
236 &self.file_id
237 }
238}
239
240#[must_use]
242pub fn upload_fine_tune_file(
243 filename: impl Into<String>,
244 content: impl Into<String>,
245) -> FileUploadBuilder {
246 FileUploadBuilder::from_text(filename, FilePurpose::FineTune, content)
247}
248
249#[must_use]
251pub fn upload_assistants_file(
252 filename: impl Into<String>,
253 content: impl Into<String>,
254) -> FileUploadBuilder {
255 FileUploadBuilder::from_text(filename, FilePurpose::Assistants, content)
256}
257
258pub fn upload_json_file(
260 filename: impl Into<String>,
261 purpose: FilePurpose,
262 json: &serde_json::Value,
263) -> Result<FileUploadBuilder, serde_json::Error> {
264 FileUploadBuilder::from_json(filename, purpose, json)
265}
266
267pub fn upload_file_from_path(
269 path: impl AsRef<Path>,
270 purpose: FilePurpose,
271) -> Result<FileUploadBuilder, std::io::Error> {
272 FileUploadBuilder::from_path(path, purpose)
273}
274
275#[must_use]
277pub fn retrieve_file(file_id: impl Into<String>) -> FileRetrievalBuilder {
278 FileRetrievalBuilder::new(file_id)
279}
280
281#[must_use]
283pub fn list_files() -> FileListBuilder {
284 FileListBuilder::new()
285}
286
287#[must_use]
289pub fn list_files_by_purpose(purpose: FilePurpose) -> FileListBuilder {
290 FileListBuilder::new().purpose(purpose)
291}
292
293#[must_use]
295pub fn list_files_with_limit(limit: i32) -> FileListBuilder {
296 FileListBuilder::new().limit(limit)
297}
298
299#[must_use]
301pub fn delete_file(file_id: impl Into<String>) -> FileDeleteBuilder {
302 FileDeleteBuilder::new(file_id)
303}
304
305impl std::fmt::Display for FilePurpose {
306 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
307 match self {
308 FilePurpose::FineTune => write!(f, "fine-tune"),
309 FilePurpose::Assistants => write!(f, "assistants"),
310 FilePurpose::Vision => write!(f, "vision"),
311 FilePurpose::Batch => write!(f, "batch"),
312 FilePurpose::Custom(purpose) => write!(f, "{purpose}"),
313 }
314 }
315}
316
317impl std::fmt::Display for FileOrder {
318 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
319 match self {
320 FileOrder::Asc => write!(f, "asc"),
321 FileOrder::Desc => write!(f, "desc"),
322 }
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 #[test]
331 fn test_file_upload_builder_new() {
332 let content = b"test content".to_vec();
333 let builder = FileUploadBuilder::new("test.txt", FilePurpose::Assistants, content.clone());
334
335 assert_eq!(builder.filename(), "test.txt");
336 assert_eq!(builder.content(), content.as_slice());
337 assert_eq!(builder.content_size(), content.len());
338 assert!(!builder.is_empty());
339 match builder.purpose() {
340 FilePurpose::Assistants => {}
341 _ => panic!("Expected Assistants purpose"),
342 }
343 }
344
345 #[test]
346 fn test_file_upload_builder_from_text() {
347 let builder =
348 FileUploadBuilder::from_text("hello.txt", FilePurpose::FineTune, "Hello, world!");
349
350 assert_eq!(builder.filename(), "hello.txt");
351 assert_eq!(
352 builder.content_as_string(),
353 Some("Hello, world!".to_string())
354 );
355 assert!(!builder.is_empty());
356 match builder.purpose() {
357 FilePurpose::FineTune => {}
358 _ => panic!("Expected FineTune purpose"),
359 }
360 }
361
362 #[test]
363 fn test_file_upload_builder_from_json() {
364 let json = serde_json::json!({
365 "name": "test",
366 "value": 42
367 });
368
369 let builder = FileUploadBuilder::from_json("data.json", FilePurpose::Batch, &json).unwrap();
370
371 assert_eq!(builder.filename(), "data.json");
372 assert!(!builder.is_empty());
373 assert!(builder.content_size() > 0);
374 match builder.purpose() {
375 FilePurpose::Batch => {}
376 _ => panic!("Expected Batch purpose"),
377 }
378 }
379
380 #[test]
381 fn test_file_retrieval_builder() {
382 let builder = FileRetrievalBuilder::new("file-123");
383 assert_eq!(builder.file_id(), "file-123");
384 }
385
386 #[test]
387 fn test_file_list_builder() {
388 let builder = FileListBuilder::new()
389 .purpose(FilePurpose::Assistants)
390 .limit(10)
391 .order(FileOrder::Desc);
392
393 match builder.purpose_ref() {
394 Some(FilePurpose::Assistants) => {}
395 _ => panic!("Expected Assistants purpose"),
396 }
397 assert_eq!(builder.limit_ref(), Some(10));
398 match builder.order_ref() {
399 Some(FileOrder::Desc) => {}
400 _ => panic!("Expected Desc order"),
401 }
402 }
403
404 #[test]
405 fn test_file_delete_builder() {
406 let builder = FileDeleteBuilder::new("file-456");
407 assert_eq!(builder.file_id(), "file-456");
408 }
409
410 #[test]
411 fn test_upload_fine_tune_file_helper() {
412 let builder = upload_fine_tune_file("training.jsonl", "test data");
413 assert_eq!(builder.filename(), "training.jsonl");
414 match builder.purpose() {
415 FilePurpose::FineTune => {}
416 _ => panic!("Expected FineTune purpose"),
417 }
418 }
419
420 #[test]
421 fn test_upload_assistants_file_helper() {
422 let builder = upload_assistants_file("doc.txt", "document content");
423 assert_eq!(builder.filename(), "doc.txt");
424 match builder.purpose() {
425 FilePurpose::Assistants => {}
426 _ => panic!("Expected Assistants purpose"),
427 }
428 }
429
430 #[test]
431 fn test_upload_json_file_helper() {
432 let json = serde_json::json!({"test": true});
433 let builder = upload_json_file("test.json", FilePurpose::Vision, &json).unwrap();
434 assert_eq!(builder.filename(), "test.json");
435 match builder.purpose() {
436 FilePurpose::Vision => {}
437 _ => panic!("Expected Vision purpose"),
438 }
439 }
440
441 #[test]
442 fn test_retrieve_file_helper() {
443 let builder = retrieve_file("file-789");
444 assert_eq!(builder.file_id(), "file-789");
445 }
446
447 #[test]
448 fn test_list_files_helper() {
449 let builder = list_files();
450 assert!(builder.purpose_ref().is_none());
451 assert!(builder.limit_ref().is_none());
452 assert!(builder.order_ref().is_none());
453 }
454
455 #[test]
456 fn test_list_files_by_purpose_helper() {
457 let builder = list_files_by_purpose(FilePurpose::FineTune);
458 match builder.purpose_ref() {
459 Some(FilePurpose::FineTune) => {}
460 _ => panic!("Expected FineTune purpose"),
461 }
462 }
463
464 #[test]
465 fn test_list_files_with_limit_helper() {
466 let builder = list_files_with_limit(5);
467 assert_eq!(builder.limit_ref(), Some(5));
468 }
469
470 #[test]
471 fn test_delete_file_helper() {
472 let builder = delete_file("file-delete");
473 assert_eq!(builder.file_id(), "file-delete");
474 }
475
476 #[test]
477 fn test_file_purpose_display() {
478 assert_eq!(FilePurpose::FineTune.to_string(), "fine-tune");
479 assert_eq!(FilePurpose::Assistants.to_string(), "assistants");
480 assert_eq!(FilePurpose::Vision.to_string(), "vision");
481 assert_eq!(FilePurpose::Batch.to_string(), "batch");
482 assert_eq!(
483 FilePurpose::Custom("custom".to_string()).to_string(),
484 "custom"
485 );
486 }
487
488 #[test]
489 fn test_file_order_display() {
490 assert_eq!(FileOrder::Asc.to_string(), "asc");
491 assert_eq!(FileOrder::Desc.to_string(), "desc");
492 }
493
494 #[test]
495 fn test_empty_file() {
496 let builder = FileUploadBuilder::new("empty.txt", FilePurpose::Assistants, vec![]);
497 assert!(builder.is_empty());
498 assert_eq!(builder.content_size(), 0);
499 assert_eq!(builder.content_as_string(), Some(String::new()));
500 }
501}