use crate::error::Result;
use base64::{Engine as _, engine::general_purpose};
use serde::{Deserialize, Serialize};
use std::path::Path;
use tokio::fs;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Attachment {
pub id: String,
pub data: String,
}
impl Attachment {
pub async fn from_path(path: impl AsRef<Path>) -> Result<Self> {
let path = path.as_ref();
let filename = path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("attachment")
.to_string();
let content = fs::read(path).await?;
let encoded = general_purpose::STANDARD.encode(&content);
Ok(Self {
id: filename,
data: encoded,
})
}
pub fn from_bytes(content: &[u8], filename: impl Into<String>) -> Self {
let encoded = general_purpose::STANDARD.encode(content);
Self {
id: filename.into(),
data: encoded,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::File;
use std::io::Write;
use tempdir::TempDir;
#[tokio::test]
async fn test_attachment_from_bytes_with_explicit_filename() {
let content = b"test text";
let filename = "rawr.txt";
let attachment = Attachment::from_bytes(content, filename);
assert_eq!(attachment.id, "rawr.txt");
assert_eq!(attachment.data, general_purpose::STANDARD.encode(content));
}
#[tokio::test]
async fn test_attachment_from_path() {
let content = b"test content";
let attachment = Attachment::from_bytes(content, "path.txt");
assert_eq!(attachment.id, "path.txt");
assert_eq!(attachment.data, general_purpose::STANDARD.encode(content));
}
#[tokio::test]
async fn test_attachment_from_path_with_real_file() -> Result<()> {
let temp_dir = TempDir::new("attachment_test")?;
let file_path = temp_dir.path().join("test_file.txt");
let content = b"hello world from file";
{
let mut file = File::create(&file_path)?;
file.write_all(content)?;
}
let attachment = Attachment::from_path(&file_path).await?;
assert_eq!(attachment.id, "test_file.txt");
assert_eq!(attachment.data, general_purpose::STANDARD.encode(content));
Ok(())
}
#[tokio::test]
async fn test_attachment_from_path_without_extension() -> Result<()> {
let temp_dir = TempDir::new("attachment_test")?;
let file_path = temp_dir.path().join("no_extension");
let content = b"file without extension";
{
let mut file = File::create(&file_path)?;
file.write_all(content)?;
}
let attachment = Attachment::from_path(&file_path).await?;
assert_eq!(attachment.id, "no_extension");
assert_eq!(attachment.data, general_purpose::STANDARD.encode(content));
Ok(())
}
}