use std::ffi::{CStr, NulError};
use std::fmt;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StreamError {
pub code: String,
pub stage: String,
pub track: String,
pub backend: String,
pub source: String,
pub message: String,
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Auth(String),
Network(String),
Invalid(String),
Video(String),
Audio(String),
Stream(StreamError),
Ffi(String),
InteriorNul,
InvalidUtf8,
Timeout,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Auth(m)
| Error::Network(m)
| Error::Invalid(m)
| Error::Video(m)
| Error::Audio(m)
| Error::Ffi(m) => write!(f, "adamo: {m}"),
Error::Stream(e) => write!(
f,
"adamo: {} (track={}, stage={}, code={})",
e.message, e.track, e.stage, e.code
),
Error::InteriorNul => f.write_str("adamo: string contained an interior NUL byte"),
Error::InvalidUtf8 => f.write_str("adamo: FFI returned non-UTF-8 data"),
Error::Timeout => f.write_str("adamo: receive timed out"),
}
}
}
impl std::error::Error for Error {}
impl From<NulError> for Error {
fn from(_: NulError) -> Self {
Error::InteriorNul
}
}
pub(crate) fn last_ffi_error() -> Error {
let (msg, code) = unsafe {
let ptr = adamo_sys::adamo_last_error();
if ptr.is_null() {
return Error::Ffi("(no error message)".into());
}
match CStr::from_ptr(ptr).to_str() {
Ok(s) => (s.to_owned(), adamo_sys::adamo_last_error_code()),
Err(_) => return Error::InvalidUtf8,
}
};
match code {
1 => Error::Auth(msg),
2 => Error::Network(msg),
3 => Error::Timeout,
4 => Error::Invalid(msg),
5 => last_stream_error(msg.clone()).map_or(Error::Video(msg), Error::Stream),
7 => Error::Audio(msg),
_ => Error::Ffi(msg),
}
}
fn last_stream_error(message: String) -> Option<StreamError> {
unsafe fn read(ptr: *const std::ffi::c_char) -> Option<String> {
if ptr.is_null() {
return None;
}
unsafe { CStr::from_ptr(ptr) }
.to_str()
.ok()
.map(ToOwned::to_owned)
}
unsafe {
Some(StreamError {
code: read(adamo_sys::adamo_last_stream_error_code())?,
stage: read(adamo_sys::adamo_last_stream_error_stage())?,
track: read(adamo_sys::adamo_last_stream_error_track())?,
backend: read(adamo_sys::adamo_last_stream_error_backend())?,
source: read(adamo_sys::adamo_last_stream_error_source())?,
message,
})
}
}