Skip to main content

ff_stream/
error.rs

1//! Error types for streaming operations.
2//!
3//! This module provides the [`StreamError`] enum which represents all
4//! possible errors that can occur during HLS / DASH output and ABR ladder
5//! generation.
6
7/// Errors that can occur during streaming output operations.
8///
9/// This enum covers all error conditions that may arise when configuring,
10/// building, or writing HLS / DASH output.
11///
12/// # Error Categories
13///
14/// - **Encoding errors**: [`Encode`](Self::Encode) — wraps errors from `ff-encode`
15/// - **I/O errors**: [`Io`](Self::Io) — file system errors during segment writing
16/// - **Configuration errors**: [`InvalidConfig`](Self::InvalidConfig) — missing or
17///   invalid builder options, or not-yet-implemented stubs
18#[derive(Debug, thiserror::Error)]
19pub enum StreamError {
20    /// An encoding operation in the underlying `ff-encode` crate failed.
21    ///
22    /// This error propagates from [`ff_encode::EncodeError`] when the encoder
23    /// cannot open a codec or write frames.
24    #[error("encode failed: {0}")]
25    Encode(#[from] ff_encode::EncodeError),
26
27    /// An I/O operation failed during segment or playlist writing.
28    ///
29    /// Typical causes include missing output directories, permission errors,
30    /// or a full disk.
31    #[error("io error: {0}")]
32    Io(#[from] std::io::Error),
33
34    /// A configuration value is missing or invalid, or the feature is not yet
35    /// implemented.
36    ///
37    /// This variant is also used as a stub return value for `write()` / `hls()`
38    /// / `dash()` methods that await `FFmpeg` muxing integration.
39    #[error("invalid config: {reason}")]
40    InvalidConfig {
41        /// Human-readable description of the configuration problem.
42        reason: String,
43    },
44
45    /// An `FFmpeg` runtime error occurred during muxing or transcoding.
46    ///
47    /// `code` is the raw `FFmpeg` negative error value returned by the failing
48    /// function (e.g. `AVERROR(EINVAL)`).  `message` is the human-readable
49    /// string produced by `av_strerror`.  Exposing the numeric code lets
50    /// engineers cross-reference `FFmpeg` documentation and source directly.
51    #[error("ffmpeg error: {message} (code={code})")]
52    Ffmpeg {
53        /// Raw `FFmpeg` error code (negative integer).
54        code: i32,
55        /// Human-readable description of the `FFmpeg` error.
56        message: String,
57    },
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn invalid_config_should_display_reason() {
66        let err = StreamError::InvalidConfig {
67            reason: "missing input path".into(),
68        };
69        let msg = err.to_string();
70        assert!(msg.contains("missing input path"), "got: {msg}");
71    }
72
73    #[test]
74    fn io_error_should_convert_via_from() {
75        let io = std::io::Error::new(std::io::ErrorKind::NotFound, "no such file");
76        let err: StreamError = io.into();
77        assert!(matches!(err, StreamError::Io(_)));
78    }
79
80    #[test]
81    fn encode_error_should_convert_via_from() {
82        let enc = ff_encode::EncodeError::Cancelled;
83        let err: StreamError = enc.into();
84        assert!(matches!(err, StreamError::Encode(_)));
85    }
86
87    #[test]
88    fn display_io_should_contain_message() {
89        let io = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
90        let err: StreamError = io.into();
91        assert!(err.to_string().contains("access denied"), "got: {err}");
92    }
93
94    #[test]
95    fn ffmpeg_error_should_display_code_and_message() {
96        let err = StreamError::Ffmpeg {
97            code: -22,
98            message: "Cannot open codec".into(),
99        };
100        let msg = err.to_string();
101        assert!(msg.contains("Cannot open codec"), "got: {msg}");
102        assert!(msg.contains("code=-22"), "got: {msg}");
103    }
104}