1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use openh264_sys2::{dsErrorFree, DECODING_STATE};
use std::fmt::{Debug, Display, Formatter};

/// Error struct if something goes wrong.
#[derive(Debug)]
pub struct Error {
    native: i64,
    decoding_state: DECODING_STATE,
    misc: Option<String>,
    #[cfg(feature = "backtrace")]
    backtrace: Option<std::backtrace::Backtrace>,
}

impl Error {
    pub(crate) fn from_native(native: i64) -> Self {
        Error {
            native,
            decoding_state: dsErrorFree,
            misc: None,
            #[cfg(feature = "backtrace")]
            backtrace: Some(std::backtrace::Backtrace::capture()),
        }
    }

    #[allow(unused)]
    pub(crate) fn from_decoding_state(decoding_state: DECODING_STATE) -> Self {
        Error {
            native: 0,
            decoding_state,
            misc: None,
            #[cfg(feature = "backtrace")]
            backtrace: Some(std::backtrace::Backtrace::capture()),
        }
    }

    /// Creates a new [`Error`] with a custom message.
    pub fn msg(msg: &str) -> Self {
        Error {
            native: 0,
            decoding_state: dsErrorFree,
            misc: Some(msg.to_string()),
            #[cfg(feature = "backtrace")]
            backtrace: Some(std::backtrace::Backtrace::capture()),
        }
    }

    /// Returns the backtrace, if available.
    #[cfg(feature = "backtrace")]
    pub fn backtrace(&self) -> Option<&std::backtrace::Backtrace> {
        self.backtrace.as_ref()
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str("OpenH264 encountered an error. Native:")?;
        <i64 as std::fmt::Display>::fmt(&self.native, f)?;
        f.write_str(". Decoding State:")?;
        <std::os::raw::c_int as std::fmt::Display>::fmt(&self.decoding_state, f)?;
        f.write_str(". User Message:")?;
        self.misc.fmt(f)?;

        #[cfg(feature = "backtrace")]
        {
            f.write_str(". Backtraces enabled.")?;
        }
        Ok(())
    }
}

/// Helper trait to check the various error values produced by OpenH264.
pub(crate) trait NativeErrorExt {
    fn ok(self) -> Result<(), Error>;
}

macro_rules! impl_native_error {
    ($t:ty) => {
        impl NativeErrorExt for $t {
            fn ok(self) -> Result<(), Error> {
                if self == 0 {
                    Ok(())
                } else {
                    Err(Error::from_native(self as i64))
                }
            }
        }
    };
}

impl_native_error!(u64);
impl_native_error!(i64);
impl_native_error!(i32);

impl std::error::Error for Error {}

#[cfg(test)]
mod test {
    use crate::Error;
    use openh264_sys2::dsRefListNullPtrs;

    #[test]
    fn errors_wont_panic() {
        format!("{}", Error::from_native(1));
        format!("{}", Error::from_decoding_state(dsRefListNullPtrs));
        format!("{}", Error::msg("hello world"));

        format!("{:?}", Error::from_native(1));
        format!("{:?}", Error::from_decoding_state(dsRefListNullPtrs));
        format!("{:?}", Error::msg("hello world"));

        format!("{:#?}", Error::from_native(1));
        format!("{:#?}", Error::from_decoding_state(dsRefListNullPtrs));
        format!("{:#?}", Error::msg("hello world"));
    }

    #[test]
    #[cfg(feature = "backtrace")]
    fn backtrace_works() {
        let _ = Error::from_native(1).backtrace.expect("Must have backtrace");
    }
}