use std::path::Path; use serde::{Deserialize, Serialize}; /// Quote a string field for CSV output: wrap in double-quotes and escape /// internal double-quotes by doubling them (RFC 4180). fn csv_quote(s: &str) -> String { if s.contains([',', '"', '\n', '\r']) { format!("\"{}\"", s.replace('"', "\"\"")) } else { s.to_string() } } use crate::{error::Result, jobs::ExportFormat, storage::DynStorageBackend}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ExportResult { pub items_exported: usize, pub output_path: String, } /// Export library data to the specified format. /// /// # Errors /// /// Returns an error if the storage query fails or if the output file cannot /// be written. pub async fn export_library( storage: &DynStorageBackend, format: &ExportFormat, destination: &Path, ) -> Result { let pagination = crate::model::Pagination { offset: 0, limit: u64::MAX, sort: None, }; let items = storage.list_media(&pagination).await?; let count = items.len(); match format { ExportFormat::Json => { let json = serde_json::to_string_pretty(&items).map_err(|e| { crate::error::PinakesError::Serialization(format!( "json serialize: {e}" )) })?; std::fs::write(destination, json)?; }, ExportFormat::Csv => { let mut csv = String::new(); csv.push_str( "id,path,file_name,media_type,content_hash,file_size,title,artist,\ album,genre,year,duration_secs,description,created_at,updated_at\n", ); for item in &items { use std::fmt::Write as _; writeln!( csv, "{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}", item.id, csv_quote(&item.path.display().to_string()), csv_quote(&item.file_name), csv_quote(&format!("{:?}", item.media_type)), item.content_hash, item.file_size, csv_quote(item.title.as_deref().unwrap_or("")), csv_quote(item.artist.as_deref().unwrap_or("")), csv_quote(item.album.as_deref().unwrap_or("")), csv_quote(item.genre.as_deref().unwrap_or("")), item.year.map(|y| y.to_string()).unwrap_or_default(), item .duration_secs .map(|d| d.to_string()) .unwrap_or_default(), csv_quote(item.description.as_deref().unwrap_or("")), item.created_at, item.updated_at, ) .unwrap_or_default(); } std::fs::write(destination, csv)?; }, } Ok(ExportResult { items_exported: count, output_path: destination.to_string_lossy().to_string(), }) }