use std::ffi::{CStr, CString, NulError};
use std::fmt;
use std::os::raw::c_int;
use crate::ffi;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorKind {
InvalidArgument,
Runtime,
InvalidString,
Unknown,
}
#[derive(Debug, Clone)]
pub struct Error {
kind: ErrorKind,
message: String,
}
impl Error {
pub(crate) fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
Self {
kind,
message: message.into(),
}
}
pub fn kind(&self) -> ErrorKind {
self.kind
}
pub fn message(&self) -> &str {
&self.message
}
fn from_status(status: c_int) -> Self {
let kind = match status {
-1 => ErrorKind::InvalidArgument,
-2 => ErrorKind::Runtime,
_ => ErrorKind::Unknown,
};
let message = unsafe {
let raw = ffi::avf_last_error();
if raw.is_null() {
String::new()
} else {
CStr::from_ptr(raw).to_string_lossy().into_owned()
}
};
let message = if message.is_empty() {
format!("avioflow call failed with status {status}")
} else {
message
};
Self::new(kind, message)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.message)
}
}
impl std::error::Error for Error {}
impl From<NulError> for Error {
fn from(error: NulError) -> Self {
Self::new(ErrorKind::InvalidString, error.to_string())
}
}
pub type Result<T> = std::result::Result<T, Error>;
pub(crate) fn check(status: c_int) -> Result<()> {
if status == ffi::AVF_OK {
Ok(())
} else {
Err(Error::from_status(status))
}
}
pub(crate) fn null_handle_error() -> Error {
let status = unsafe { ffi::avf_last_error_code() };
let status = if status == ffi::AVF_OK { -2 } else { status };
Error::from_status(status)
}
pub(crate) fn to_cstring(value: &str) -> Result<CString> {
Ok(CString::new(value)?)
}