1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! Error types for filter graph operations.
use thiserror::Error;
/// Errors that can occur during filter graph construction and processing.
#[derive(Debug, Error)]
pub enum FilterError {
/// Failed to build the filter graph (invalid filter chain or `FFmpeg` error
/// during graph creation).
#[error("failed to build filter graph")]
BuildFailed,
/// A frame processing operation (push or pull) failed.
#[error("failed to process frame")]
ProcessFailed,
/// An invalid configuration was detected during graph construction.
#[error("invalid filter configuration: {reason}")]
InvalidConfig {
/// Human-readable reason for the failure.
reason: String,
},
/// A frame was pushed to an invalid input slot.
#[error("invalid input: slot={slot} reason={reason}")]
InvalidInput {
/// The slot index that was out of range or otherwise invalid.
slot: usize,
/// Human-readable reason for the failure.
reason: String,
},
/// An underlying `FFmpeg` function returned an error code.
#[error("ffmpeg error: {message} (code={code})")]
Ffmpeg {
/// The raw `FFmpeg` error code.
code: i32,
/// Human-readable description of the error.
message: String,
},
/// A multi-track composition or mixing operation failed.
///
/// Returned by [`MultiTrackComposer::build`](crate::MultiTrackComposer::build) and
/// [`MultiTrackAudioMixer::build`](crate::MultiTrackAudioMixer::build) when the
/// `FFmpeg` filter graph cannot be constructed.
#[error("composition failed: {reason}")]
CompositionFailed {
/// Human-readable reason for the failure.
reason: String,
},
/// An analysis operation failed for a structural reason.
///
/// Returned by [`LoudnessMeter::measure`](crate::analysis::LoudnessMeter::measure)
/// when the input file is not found, the format is unsupported, or the
/// `FFmpeg` filter graph cannot be constructed.
#[error("analysis failed: {reason}")]
AnalysisFailed {
/// Human-readable reason for the failure.
reason: String,
},
}
#[cfg(test)]
mod tests {
use super::FilterError;
use std::error::Error;
#[test]
fn build_failed_should_display_correct_message() {
let err = FilterError::BuildFailed;
assert_eq!(err.to_string(), "failed to build filter graph");
}
#[test]
fn process_failed_should_display_correct_message() {
let err = FilterError::ProcessFailed;
assert_eq!(err.to_string(), "failed to process frame");
}
#[test]
fn invalid_input_should_display_slot_and_reason() {
let err = FilterError::InvalidInput {
slot: 2,
reason: "slot out of range".to_string(),
};
assert_eq!(
err.to_string(),
"invalid input: slot=2 reason=slot out of range"
);
}
#[test]
fn ffmpeg_should_display_code_and_message() {
let err = FilterError::Ffmpeg {
code: -22,
message: "Invalid argument".to_string(),
};
assert_eq!(err.to_string(), "ffmpeg error: Invalid argument (code=-22)");
}
#[test]
fn composition_failed_should_display_reason() {
let err = FilterError::CompositionFailed {
reason: "no layers".to_string(),
};
assert_eq!(err.to_string(), "composition failed: no layers");
}
#[test]
fn analysis_failed_should_display_reason() {
let err = FilterError::AnalysisFailed {
reason: "file not found".to_string(),
};
assert_eq!(err.to_string(), "analysis failed: file not found");
}
#[test]
fn filter_error_should_implement_std_error() {
fn assert_error<E: Error>(_: &E) {}
assert_error(&FilterError::BuildFailed);
assert_error(&FilterError::ProcessFailed);
assert_error(&FilterError::InvalidInput {
slot: 0,
reason: String::new(),
});
assert_error(&FilterError::Ffmpeg {
code: 0,
message: String::new(),
});
}
}