Skip to main content

ff_encode/
error.rs

1//! Error types for encoding operations.
2
3use std::path::PathBuf;
4use thiserror::Error;
5
6/// Encoding error type.
7#[derive(Error, Debug)]
8pub enum EncodeError {
9    /// Cannot create output file
10    #[error("Cannot create output file: {path}")]
11    CannotCreateFile {
12        /// File path that failed
13        path: PathBuf,
14    },
15
16    /// Unsupported codec
17    #[error("Unsupported codec: {codec}")]
18    UnsupportedCodec {
19        /// Codec name
20        codec: String,
21    },
22
23    /// No suitable encoder found
24    #[error("No suitable encoder found for {codec} (tried: {tried:?})")]
25    NoSuitableEncoder {
26        /// Requested codec
27        codec: String,
28        /// Attempted encoders
29        tried: Vec<String>,
30    },
31
32    /// Encoding failed at specific frame
33    #[error("Encoding failed at frame {frame}: {reason}")]
34    EncodingFailed {
35        /// Frame number where encoding failed
36        frame: u64,
37        /// Failure reason
38        reason: String,
39    },
40
41    /// Invalid configuration
42    #[error("Invalid configuration: {reason}")]
43    InvalidConfig {
44        /// Configuration issue description
45        reason: String,
46    },
47
48    /// Hardware encoder unavailable
49    #[error("Hardware encoder unavailable: {encoder}")]
50    HwEncoderUnavailable {
51        /// Hardware encoder name
52        encoder: String,
53    },
54
55    /// Muxing failed
56    #[error("Muxing failed: {reason}")]
57    MuxingFailed {
58        /// Failure reason
59        reason: String,
60    },
61
62    /// `FFmpeg` error
63    #[error("ffmpeg error: {message} (code={code})")]
64    Ffmpeg {
65        /// Raw `FFmpeg` error code (negative integer). `0` when no numeric code is available.
66        code: i32,
67        /// Human-readable error message from `av_strerror` or an internal description.
68        message: String,
69    },
70
71    /// IO error
72    #[error("IO error: {0}")]
73    Io(#[from] std::io::Error),
74
75    /// Encoding cancelled by user
76    #[error("Encoding cancelled by user")]
77    Cancelled,
78}
79
80impl EncodeError {
81    /// Create an error from an FFmpeg error code.
82    ///
83    /// This is more type-safe than implementing `From<i32>` globally,
84    /// as it makes the conversion explicit and prevents accidental
85    /// conversion of arbitrary i32 values.
86    pub(crate) fn from_ffmpeg_error(errnum: i32) -> Self {
87        EncodeError::Ffmpeg {
88            code: errnum,
89            message: ff_sys::av_error_string(errnum),
90        }
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::EncodeError;
97
98    #[test]
99    fn from_ffmpeg_error_should_return_ffmpeg_variant() {
100        let err = EncodeError::from_ffmpeg_error(ff_sys::error_codes::EINVAL);
101        assert!(matches!(err, EncodeError::Ffmpeg { .. }));
102    }
103
104    #[test]
105    fn from_ffmpeg_error_should_carry_numeric_code() {
106        let err = EncodeError::from_ffmpeg_error(ff_sys::error_codes::EINVAL);
107        match err {
108            EncodeError::Ffmpeg { code, .. } => assert_eq!(code, ff_sys::error_codes::EINVAL),
109            _ => panic!("expected Ffmpeg variant"),
110        }
111    }
112
113    #[test]
114    fn from_ffmpeg_error_should_format_with_code_in_display() {
115        let err = EncodeError::from_ffmpeg_error(ff_sys::error_codes::EINVAL);
116        let msg = err.to_string();
117        assert!(msg.contains("code=-22"), "expected 'code=-22' in '{msg}'");
118    }
119
120    #[test]
121    fn from_ffmpeg_error_message_should_be_nonempty() {
122        let err = EncodeError::from_ffmpeg_error(ff_sys::error_codes::ENOMEM);
123        assert!(!err.to_string().is_empty());
124    }
125
126    #[test]
127    fn from_ffmpeg_error_eof_should_be_constructible() {
128        let err = EncodeError::from_ffmpeg_error(ff_sys::error_codes::EOF);
129        assert!(matches!(err, EncodeError::Ffmpeg { .. }));
130        assert!(!err.to_string().is_empty());
131    }
132}