Skip to main content

ff_probe/
error.rs

1//! Error types for media probing.
2
3use ff_format::{ErrorSeverity, MediaError};
4use std::path::PathBuf;
5use thiserror::Error;
6
7/// Error type for media probing operations.
8#[derive(Error, Debug)]
9pub enum ProbeError {
10    /// The specified file was not found.
11    #[error("File not found: {path}")]
12    FileNotFound {
13        /// Path to the file that was not found.
14        path: PathBuf,
15    },
16
17    /// The file could not be opened.
18    #[error("Cannot open file: {path} - {reason}")]
19    CannotOpen {
20        /// Path to the file that could not be opened.
21        path: PathBuf,
22        /// Reason why the file could not be opened.
23        reason: String,
24    },
25
26    /// The file is not a valid media file.
27    #[error("Invalid media file: {path} - {reason}")]
28    InvalidMedia {
29        /// Path to the invalid media file.
30        path: PathBuf,
31        /// Reason why the file is invalid.
32        reason: String,
33    },
34
35    /// No streams were found in the file.
36    #[error("No streams found in file: {path}")]
37    NoStreams {
38        /// Path to the file with no streams.
39        path: PathBuf,
40    },
41
42    /// An I/O error occurred.
43    #[error("IO error: {0}")]
44    Io(#[from] std::io::Error),
45
46    /// An `FFmpeg` error occurred.
47    #[error("ffmpeg error: {message} (code={code})")]
48    Ffmpeg {
49        /// Raw `FFmpeg` error code (negative integer). `0` when no numeric code is available.
50        code: i32,
51        /// Human-readable error message from `av_strerror` or an internal description.
52        message: String,
53    },
54}
55
56impl MediaError for ProbeError {
57    fn severity(&self) -> ErrorSeverity {
58        match self {
59            Self::Ffmpeg { .. } => ErrorSeverity::Other,
60            Self::FileNotFound { .. }
61            | Self::CannotOpen { .. }
62            | Self::InvalidMedia { .. }
63            | Self::NoStreams { .. }
64            | Self::Io(_) => ErrorSeverity::Fatal,
65        }
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn test_file_not_found_error() {
75        let err = ProbeError::FileNotFound {
76            path: PathBuf::from("/path/to/missing.mp4"),
77        };
78        let msg = err.to_string();
79        assert!(msg.contains("File not found"));
80        assert!(msg.contains("missing.mp4"));
81    }
82
83    #[test]
84    fn test_cannot_open_error() {
85        let err = ProbeError::CannotOpen {
86            path: PathBuf::from("/path/to/file.mp4"),
87            reason: "permission denied".to_string(),
88        };
89        let msg = err.to_string();
90        assert!(msg.contains("Cannot open file"));
91        assert!(msg.contains("permission denied"));
92    }
93
94    #[test]
95    fn test_invalid_media_error() {
96        let err = ProbeError::InvalidMedia {
97            path: PathBuf::from("/path/to/bad.mp4"),
98            reason: "corrupted header".to_string(),
99        };
100        let msg = err.to_string();
101        assert!(msg.contains("Invalid media file"));
102        assert!(msg.contains("corrupted header"));
103    }
104
105    #[test]
106    fn test_no_streams_error() {
107        let err = ProbeError::NoStreams {
108            path: PathBuf::from("/path/to/empty.mp4"),
109        };
110        let msg = err.to_string();
111        assert!(msg.contains("No streams found"));
112    }
113
114    #[test]
115    fn ffmpeg_should_display_code_and_message() {
116        let err = ProbeError::Ffmpeg {
117            code: -2,
118            message: "codec not found".to_string(),
119        };
120        let msg = err.to_string();
121        assert!(msg.contains("ffmpeg error"));
122        assert!(msg.contains("codec not found"));
123        assert!(msg.contains("code=-2"));
124    }
125
126    #[test]
127    fn test_io_error_conversion() {
128        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
129        let err: ProbeError = io_err.into();
130        assert!(matches!(err, ProbeError::Io(_)));
131    }
132
133    #[test]
134    fn probe_io_should_be_fatal() {
135        let e: ProbeError = std::io::Error::other("x").into();
136        assert!(e.is_fatal() && !e.is_recoverable());
137    }
138
139    #[test]
140    fn probe_ffmpeg_should_be_other() {
141        let e = ProbeError::Ffmpeg {
142            code: -22,
143            message: "x".into(),
144        };
145        assert!(!e.is_fatal() && !e.is_recoverable());
146    }
147}