Skip to main content

openh264/
error.rs

1use openh264_sys2::{DECODING_STATE, dsErrorFree};
2use std::fmt::{Debug, Display, Formatter};
3use std::num::TryFromIntError;
4
5/// Error struct if something goes wrong.
6#[derive(Debug)]
7pub struct Error {
8    native: i64,
9    decoding_state: DECODING_STATE,
10    misc: Option<String>,
11    backtrace: Option<std::backtrace::Backtrace>,
12}
13
14impl Error {
15    /// Return the current native code.
16    #[must_use]
17    pub const fn native_code(&self) -> i64 {
18        self.native
19    }
20
21    #[allow(clippy::missing_const_for_fn)]
22    pub(crate) fn from_native(native: i64) -> Self {
23        Self {
24            native,
25            decoding_state: dsErrorFree,
26            misc: None,
27            backtrace: Some(std::backtrace::Backtrace::capture()),
28        }
29    }
30
31    #[allow(unused)]
32    #[allow(clippy::missing_const_for_fn)]
33    pub(crate) fn from_decoding_state(decoding_state: DECODING_STATE) -> Self {
34        Self {
35            native: 0,
36            decoding_state,
37            misc: None,
38            backtrace: Some(std::backtrace::Backtrace::capture()),
39        }
40    }
41
42    /// Creates a new [`Error`] with a custom message.
43    #[must_use]
44    pub fn msg(msg: &str) -> Self {
45        Self {
46            native: 0,
47            decoding_state: dsErrorFree,
48            misc: Some(msg.to_string()),
49            backtrace: Some(std::backtrace::Backtrace::capture()),
50        }
51    }
52
53    /// Creates a new [`Error`] with a custom message.
54    #[must_use]
55    #[allow(clippy::missing_const_for_fn)]
56    pub fn msg_string(msg: String) -> Self {
57        Self {
58            native: 0,
59            decoding_state: dsErrorFree,
60            misc: Some(msg),
61            backtrace: Some(std::backtrace::Backtrace::capture()),
62        }
63    }
64
65    /// Returns the backtrace, if available.
66    #[allow(clippy::missing_const_for_fn)]
67    pub const fn backtrace(&self) -> Option<&std::backtrace::Backtrace> {
68        self.backtrace.as_ref()
69    }
70}
71
72impl From<TryFromIntError> for Error {
73    fn from(value: TryFromIntError) -> Self {
74        Self::msg_string(format!("Could not covert value: {value}"))
75    }
76}
77
78impl From<openh264_sys2::Error> for Error {
79    fn from(value: openh264_sys2::Error) -> Self {
80        Self::msg_string(format!("open264-sys error: {value}"))
81    }
82}
83
84impl Display for Error {
85    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
86        f.write_str("OpenH264 encountered an error. Native:")?;
87        <i64 as std::fmt::Display>::fmt(&self.native, f)?;
88        f.write_str(". Decoding State:")?;
89        <std::os::raw::c_int as std::fmt::Display>::fmt(&self.decoding_state, f)?;
90        f.write_str(". User Message:")?;
91        self.misc.fmt(f)?;
92
93        {
94            f.write_str(". Backtraces enabled.")?;
95        }
96        Ok(())
97    }
98}
99
100/// Helper trait to check the various error values produced by OpenH264.
101pub trait NativeErrorExt {
102    fn ok(self) -> Result<(), Error>;
103}
104
105macro_rules! impl_native_error {
106    ($t:ty) => {
107        impl NativeErrorExt for $t {
108            #[allow(clippy::cast_lossless)]
109            fn ok(self) -> Result<(), Error> {
110                if self == 0 {
111                    Ok(())
112                } else {
113                    Err(Error::from_native(self as i64))
114                }
115            }
116        }
117    };
118}
119
120impl_native_error!(u64);
121impl_native_error!(i64);
122impl_native_error!(i32);
123
124impl std::error::Error for Error {}
125
126#[cfg(test)]
127mod test {
128    use crate::Error;
129    use openh264_sys2::dsRefListNullPtrs;
130
131    #[test]
132    #[allow(unused_must_use)]
133    fn errors_wont_panic() {
134        format!("{}", Error::from_native(1));
135        format!("{}", Error::from_decoding_state(dsRefListNullPtrs));
136        format!("{}", Error::msg("hello world"));
137
138        format!("{:?}", Error::from_native(1));
139        format!("{:?}", Error::from_decoding_state(dsRefListNullPtrs));
140        format!("{:?}", Error::msg("hello world"));
141
142        format!("{:#?}", Error::from_native(1));
143        format!("{:#?}", Error::from_decoding_state(dsRefListNullPtrs));
144        format!("{:#?}", Error::msg("hello world"));
145    }
146
147    #[test]
148    fn backtrace_works() {
149        _ = Error::from_native(1).backtrace.expect("Must have backtrace");
150    }
151}