use crate::helper::chars_to_string;
use std::{error, fmt, os::raw::c_int};
#[cfg(feature = "serde")]
use serde::Serialize;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum ErrorKind {
Other,
BadParameters,
CameraBusy,
CameraError,
CorruptedData,
DirectoryExists,
DirectoryNotFound,
FileExists,
FileNotFound,
FixedLimitExceeded,
ModelNotFound,
NotSupported,
NoMemory,
NoSpace,
Io,
OsFailure,
PathNotAbsolute,
Timeout,
UnknownPort,
IoUsbClaim,
}
#[derive(PartialEq, Eq)]
pub struct Error {
error: c_int,
info: Option<String>,
}
impl Error {
pub fn new(error: c_int, info: Option<String>) -> Self {
Self { error, info }
}
pub(crate) fn check(status: c_int) -> Result<c_int> {
if status < 0 {
Err(Self::new(status, None))
} else {
Ok(status)
}
}
pub fn kind(&self) -> ErrorKind {
match self.error {
libgphoto2_sys::GP_ERROR_BAD_PARAMETERS => ErrorKind::BadParameters,
libgphoto2_sys::GP_ERROR_CAMERA_BUSY => ErrorKind::CameraBusy,
libgphoto2_sys::GP_ERROR_CAMERA_ERROR => ErrorKind::CameraError,
libgphoto2_sys::GP_ERROR_CORRUPTED_DATA => ErrorKind::CorruptedData,
libgphoto2_sys::GP_ERROR_DIRECTORY_EXISTS => ErrorKind::DirectoryExists,
libgphoto2_sys::GP_ERROR_DIRECTORY_NOT_FOUND => ErrorKind::DirectoryNotFound,
libgphoto2_sys::GP_ERROR_FILE_EXISTS => ErrorKind::FileExists,
libgphoto2_sys::GP_ERROR_FILE_NOT_FOUND => ErrorKind::FileNotFound,
libgphoto2_sys::GP_ERROR_FIXED_LIMIT_EXCEEDED => ErrorKind::FixedLimitExceeded,
libgphoto2_sys::GP_ERROR_MODEL_NOT_FOUND => ErrorKind::ModelNotFound,
libgphoto2_sys::GP_ERROR_NOT_SUPPORTED => ErrorKind::NotSupported,
libgphoto2_sys::GP_ERROR_NO_MEMORY => ErrorKind::NoMemory,
libgphoto2_sys::GP_ERROR_NO_SPACE => ErrorKind::NoSpace,
libgphoto2_sys::GP_ERROR_IO => ErrorKind::Io,
libgphoto2_sys::GP_ERROR_OS_FAILURE => ErrorKind::OsFailure,
libgphoto2_sys::GP_ERROR_PATH_NOT_ABSOLUTE => ErrorKind::PathNotAbsolute,
libgphoto2_sys::GP_ERROR_TIMEOUT => ErrorKind::Timeout,
libgphoto2_sys::GP_ERROR_UNKNOWN_PORT => ErrorKind::UnknownPort,
libgphoto2_sys::GP_ERROR_IO_USB_CLAIM => ErrorKind::IoUsbClaim,
libgphoto2_sys::GP_ERROR => ErrorKind::Other,
_ => ErrorKind::Other,
}
}
}
#[cfg(feature = "serde")]
impl Serialize for Error {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Self { error: libgphoto2_sys::GP_ERROR_IO, info: Some(err.to_string()) }
}
}
impl From<std::ffi::NulError> for Error {
fn from(err: std::ffi::NulError) -> Self {
Self { error: libgphoto2_sys::GP_ERROR_BAD_PARAMETERS, info: Some(err.to_string()) }
}
}
impl From<std::num::TryFromIntError> for Error {
fn from(err: std::num::TryFromIntError) -> Self {
Self { error: libgphoto2_sys::GP_ERROR, info: Some(err.to_string()) }
}
}
impl From<std::convert::Infallible> for Error {
fn from(err: std::convert::Infallible) -> Self {
match err {}
}
}
impl From<String> for Error {
fn from(message: String) -> Self {
Self { error: libgphoto2_sys::GP_ERROR, info: Some(message) }
}
}
impl From<&str> for Error {
fn from(message: &str) -> Self {
message.to_owned().into()
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(unsafe { &chars_to_string(libgphoto2_sys::gp_result_as_string(self.error)) })?;
if let Some(error_info) = &self.info {
f.write_fmt(format_args!(" [{}]", error_info))?;
}
Ok(())
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<Self as fmt::Display>::fmt(self, f)
}
}
impl error::Error for Error {}
macro_rules! try_gp_internal {
(@ $unwrap:tt $status:tt [ $($out:ident)* ] $func:ident ( $($args:tt)* ) &out $new_out:ident $($rest:tt)*) => {
try_gp_internal!(@ $unwrap $status [ $($out)* $new_out ] $func ( $($args)* $new_out.as_mut_ptr() ) $($rest)*)
};
(@ $unwrap:tt $status:tt $out:tt $func:ident ( $($args:tt)* ) $new_arg_token:tt $($rest:tt)*) => {
try_gp_internal!(@ $unwrap $status $out $func ( $($args)* $new_arg_token ) $($rest)*)
};
(@ ($($unwrap:tt)*) $status:tt [ $($out:ident)* ] $func:ident $args:tt) => {
#[allow(unused_unsafe)]
let ($status, $($out),*) = unsafe {
$(let mut $out = std::mem::MaybeUninit::uninit();)*
let status = $crate::Error::check(libgphoto2_sys::$func $args) $($unwrap)*;
(status, $($out.assume_init()),*)
};
};
(@check_unwrap_allowed .unwrap()) => {};
(@check_unwrap_allowed ?) => {};
(@check_unwrap_allowed .map_err$handler:tt?) => {};
(@check_unwrap_allowed $($rest:tt)*) => {
compile_error!("try_gp_internal!() must be used with .unwrap() or ?")
};
(let $status:tt = $func:ident ( $($args:tt)* ) $($unwrap:tt)*) => {
try_gp_internal!(@check_unwrap_allowed $($unwrap)*);
try_gp_internal!(@ ($($unwrap)*) $status [] $func () $($args)*)
};
($func:ident ( $($args:tt)* ) $($unwrap:tt)*) => {
try_gp_internal!(let _ = $func ( $($args)* ) $($unwrap)*)
};
}
pub(crate) use try_gp_internal;