audiobook_forge/models/
result.rs

1//! Processing result model
2
3use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6/// Result of processing a single audiobook
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ProcessingResult {
9    /// Name of the book
10    pub book_name: String,
11    /// Whether processing was successful
12    pub success: bool,
13    /// Path to the output M4B file (if successful)
14    pub output_path: Option<PathBuf>,
15    /// Processing time in seconds
16    pub processing_time: f64,
17    /// Error message (if failed)
18    pub error_message: Option<String>,
19    /// Size of output file in bytes (if successful)
20    pub output_size: Option<u64>,
21    /// Whether copy mode was used (no re-encoding)
22    pub used_copy_mode: bool,
23}
24
25impl ProcessingResult {
26    /// Create a new processing result for a book
27    pub fn new(book_name: String) -> Self {
28        Self {
29            book_name,
30            success: false,
31            output_path: None,
32            processing_time: 0.0,
33            error_message: None,
34            output_size: None,
35            used_copy_mode: false,
36        }
37    }
38
39    /// Mark as successful with output path
40    pub fn success(mut self, output_path: PathBuf, processing_time: f64, used_copy_mode: bool) -> Self {
41        self.success = true;
42        self.output_path = Some(output_path.clone());
43        self.processing_time = processing_time;
44        self.used_copy_mode = used_copy_mode;
45
46        // Try to get file size
47        if let Ok(metadata) = std::fs::metadata(&output_path) {
48            self.output_size = Some(metadata.len());
49        }
50
51        self
52    }
53
54    /// Mark as failed with error message
55    pub fn failure(mut self, error_message: String, processing_time: f64) -> Self {
56        self.success = false;
57        self.error_message = Some(error_message);
58        self.processing_time = processing_time;
59        self
60    }
61
62    /// Get output file size in MB
63    pub fn output_size_mb(&self) -> Option<f64> {
64        self.output_size.map(|size| size as f64 / (1024.0 * 1024.0))
65    }
66}
67
68impl std::fmt::Display for ProcessingResult {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        if self.success {
71            write!(
72                f,
73                "✓ {} ({:.1}s, {})",
74                self.book_name,
75                self.processing_time,
76                if self.used_copy_mode { "copy mode" } else { "transcode" }
77            )?;
78            if let Some(size_mb) = self.output_size_mb() {
79                write!(f, " - {:.1} MB", size_mb)?;
80            }
81            Ok(())
82        } else {
83            write!(
84                f,
85                "✗ {} ({:.1}s) - {}",
86                self.book_name,
87                self.processing_time,
88                self.error_message.as_deref().unwrap_or("Unknown error")
89            )
90        }
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_result_success() {
100        let result = ProcessingResult::new("Test Book".to_string())
101            .success(PathBuf::from("/output/test.m4b"), 120.5, true);
102
103        assert!(result.success);
104        assert_eq!(result.processing_time, 120.5);
105        assert!(result.used_copy_mode);
106        assert!(result.output_path.is_some());
107    }
108
109    #[test]
110    fn test_result_failure() {
111        let result = ProcessingResult::new("Test Book".to_string())
112            .failure("FFmpeg failed".to_string(), 45.2);
113
114        assert!(!result.success);
115        assert_eq!(result.processing_time, 45.2);
116        assert_eq!(result.error_message, Some("FFmpeg failed".to_string()));
117    }
118}