1use openh264_sys2::{DECODING_STATE, dsErrorFree};
2use std::fmt::{Debug, Display, Formatter};
3use std::num::TryFromIntError;
4
5#[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 #[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 #[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 #[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 #[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
100pub 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}