Skip to main content

claudius/types/
base64_pdf_source.rs

1use base64::Engine;
2use serde::{Deserialize, Serialize};
3use std::fs::File;
4use std::io::Read;
5use std::path::Path;
6
7/// Represents a base64-encoded PDF source.
8///
9/// This can be created from either a base64-encoded string or from a file path.
10/// The media_type is always "application/pdf".
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12pub struct Base64PdfSource {
13    /// The base64-encoded data of the PDF
14    pub data: String,
15
16    /// The media type of the file (always "application/pdf")
17    #[serde(default = "default_media_type")]
18    pub media_type: String,
19}
20
21fn default_media_type() -> String {
22    "application/pdf".to_string()
23}
24
25impl Base64PdfSource {
26    /// Create a new Base64PdfSource from a base64-encoded string
27    pub fn new(data: String) -> Self {
28        Self {
29            data,
30            media_type: default_media_type(),
31        }
32    }
33
34    /// Create a Base64PdfSource from a file path
35    ///
36    /// This will read the file and encode it as base64.
37    /// The file extension should be ".pdf".
38    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, std::io::Error> {
39        let path = path.as_ref();
40
41        // Verify file extension is .pdf
42        match path.extension().and_then(|ext| ext.to_str()) {
43            Some("pdf") => {}
44            _ => {
45                return Err(std::io::Error::new(
46                    std::io::ErrorKind::InvalidInput,
47                    "File extension must be .pdf",
48                ));
49            }
50        };
51
52        // Read the file
53        let mut file = File::open(path)?;
54        let mut buffer = Vec::new();
55        file.read_to_end(&mut buffer)?;
56
57        // Encode as base64
58        let data = base64::engine::general_purpose::STANDARD.encode(&buffer);
59
60        Ok(Self {
61            data,
62            media_type: default_media_type(),
63        })
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn serialization() {
73        let source = Base64PdfSource {
74            data: "SGVsbG8gV29ybGQ=".to_string(), // "Hello World" in base64
75            media_type: "application/pdf".to_string(),
76        };
77
78        let json = serde_json::to_value(&source).unwrap();
79        let expected = serde_json::json!({
80            "data": "SGVsbG8gV29ybGQ=",
81            "media_type": "application/pdf"
82        });
83
84        assert_eq!(json, expected);
85    }
86
87    #[test]
88    fn deserialization() {
89        let json = serde_json::json!({
90            "data": "SGVsbG8gV29ybGQ=",
91            "media_type": "application/pdf"
92        });
93        let source: Base64PdfSource = serde_json::from_value(json).unwrap();
94
95        assert_eq!(source.data, "SGVsbG8gV29ybGQ=");
96        assert_eq!(source.media_type, "application/pdf");
97    }
98}