songrec/
output.rs

1use serde::{Deserialize, Serialize};
2use crate::songrec::RecognitionResult;
3
4/// Output format for recognition results
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum OutputFormat {
7    /// Simple song name format: "Artist - Song"
8    Simple,
9    /// Full JSON with all metadata
10    Json,
11    /// CSV format for logging
12    Csv,
13    /// Custom format with placeholders
14    Custom(&'static str),
15}
16
17/// Formatted recognition output
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct RecognitionOutput {
20    pub format: String,
21    pub content: String,
22    pub timestamp: chrono::DateTime<chrono::Utc>,
23}
24
25impl RecognitionOutput {
26    /// Format a recognition result according to the specified format
27    pub fn format_result(result: &RecognitionResult, format: OutputFormat) -> Self {
28        let content = match format {
29            OutputFormat::Simple => {
30                format!("{} - {}", result.artist_name, result.song_name)
31            },
32            OutputFormat::Json => {
33                serde_json::to_string(&result).unwrap_or_else(|_| "{}".to_string()) // Avoid verbose error messages
34            },
35            OutputFormat::Csv => {
36                format!(
37                    "\"{}\",\"{}\",\"{}\",\"{}\",\"{}\",\"{}\"",
38                    result.song_name,
39                    result.artist_name,
40                    result.album_name.as_deref().unwrap_or(""),
41                    result.release_year.as_deref().unwrap_or(""),
42                    result.genre.as_deref().unwrap_or(""),
43                    result.recognition_timestamp.format("%Y-%m-%d %H:%M:%S UTC")
44                )
45            },
46            OutputFormat::Custom(template) => {
47                Self::format_custom(result, template)
48            },
49        };
50
51        RecognitionOutput {
52            format: format.to_string(),
53            content,
54            timestamp: chrono::Utc::now(),
55        }
56    }
57
58    /// Format using a custom template with placeholders
59    fn format_custom(result: &RecognitionResult, template: &str) -> String {
60        template
61            .replace("{song}", &result.song_name)
62            .replace("{artist}", &result.artist_name)
63            .replace("{album}", result.album_name.as_deref().unwrap_or("Unknown"))
64            .replace("{year}", result.release_year.as_deref().unwrap_or("Unknown"))
65            .replace("{genre}", result.genre.as_deref().unwrap_or("Unknown"))
66            .replace("{timestamp}", &result.recognition_timestamp.format("%Y-%m-%d %H:%M:%S UTC").to_string())
67    }
68
69    /// Get CSV header
70    pub fn csv_header() -> &'static str {
71        "\"Song\",\"Artist\",\"Album\",\"Year\",\"Genre\",\"Timestamp\""
72    }
73}
74
75impl std::fmt::Display for RecognitionOutput {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        write!(f, "{}", self.content)
78    }
79}
80
81impl std::fmt::Display for OutputFormat {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        match self {
84            OutputFormat::Simple => write!(f, "Simple"),
85            OutputFormat::Json => write!(f, "Json"),
86            OutputFormat::Csv => write!(f, "Csv"),
87            OutputFormat::Custom(template) => write!(f, "Custom({})", template),
88        }
89    }
90}