use std::{ffi::CStr, fmt::Display};
use mpdclient_sys::{
mpd_connection, mpd_connection_get_error_message, mpd_connection_get_server_error,
mpd_connection_get_system_error, mpd_error_MPD_ERROR_ARGUMENT, mpd_error_MPD_ERROR_CLOSED,
mpd_error_MPD_ERROR_MALFORMED, mpd_error_MPD_ERROR_OOM, mpd_error_MPD_ERROR_RESOLVER,
mpd_error_MPD_ERROR_SERVER, mpd_error_MPD_ERROR_STATE, mpd_error_MPD_ERROR_SUCCESS,
mpd_error_MPD_ERROR_SYSTEM, mpd_error_MPD_ERROR_TIMEOUT, mpd_server_error,
};
pub(crate) type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("MPD Error: {0}")]
Mpd(String, MpdError),
#[error("MPD Status error: {0}")]
Status(String),
#[error("not allowed to set a 0 timeout")]
TimeoutNull,
#[error("MPD keepalive failed")]
KeepAlive,
#[error("Unexpected server entity type return")]
EntityReturnType,
#[error("Unknown '{0}' value")]
Unknown(String),
#[error("The server hasn't returned an entity, but the client expected one")]
NoEntity,
#[error("The server has returned multiple entities, but the client expected only one")]
MultipleEntities,
#[error(transparent)]
Nul(#[from] std::ffi::NulError),
#[error(transparent)]
SystemTime(#[from] std::time::SystemTimeError),
}
impl Error {
pub(crate) fn from_mpd(mpd_error: MpdError, connection: *mut mpd_connection) -> Self {
let msg = unsafe { CStr::from_ptr(mpd_connection_get_error_message(connection)) }
.to_string_lossy()
.to_string();
match mpd_error {
MpdError::System(err) => Self::Mpd(format!("{msg}: {err}"), mpd_error),
MpdError::Server(err) => Self::Mpd(format!("{msg}: {err}"), mpd_error),
_ => Self::Mpd(msg, mpd_error),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MpdError {
OutOfMemory,
Argument,
State,
Timeout,
System(i32),
Resolver,
Malformed,
Closed,
Server(MpdServerError),
}
impl MpdError {
pub(crate) fn from_sys(mpd_sys_error: u32, connection: *mut mpd_connection) -> Option<Self> {
#[allow(non_upper_case_globals)]
match mpd_sys_error {
mpd_error_MPD_ERROR_SUCCESS => None,
mpd_error_MPD_ERROR_OOM => Some(Self::OutOfMemory),
mpd_error_MPD_ERROR_ARGUMENT => Some(Self::Argument),
mpd_error_MPD_ERROR_STATE => Some(Self::State),
mpd_error_MPD_ERROR_TIMEOUT => Some(Self::Timeout),
mpd_error_MPD_ERROR_SYSTEM => Some(Self::System(unsafe {
mpd_connection_get_system_error(connection)
})),
mpd_error_MPD_ERROR_RESOLVER => Some(Self::Resolver),
mpd_error_MPD_ERROR_MALFORMED => Some(Self::Malformed),
mpd_error_MPD_ERROR_CLOSED => Some(Self::Closed),
mpd_error_MPD_ERROR_SERVER => Some(Self::Server(unsafe {
MpdServerError::from(mpd_connection_get_server_error(connection))
})),
_ => unreachable!(),
}
}
}
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MpdServerError {
Unk = -1,
NotList = 1,
Arg = 2,
Password = 3,
Permission = 4,
UnknownCmd = 5,
NoExist = 50,
PlaylistMax = 51,
System = 52,
PlaylistLoad = 53,
UpdateAlready = 54,
PlayerSync = 55,
Exist = 56,
}
impl From<mpd_server_error> for MpdServerError {
fn from(value: mpd_server_error) -> Self {
match value {
-1 => Self::Unk,
1 => Self::NotList,
2 => Self::Arg,
3 => Self::Password,
4 => Self::Permission,
5 => Self::UnknownCmd,
50 => Self::NoExist,
51 => Self::PlaylistMax,
52 => Self::System,
53 => Self::PlaylistLoad,
54 => Self::UpdateAlready,
55 => Self::PlayerSync,
56 => Self::Exist,
_ => unreachable!(),
}
}
}
impl Display for MpdServerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unk => write!(f, "Unknown"),
Self::NotList => write!(f, "Not a List"),
Self::Arg => write!(f, "Argument"),
Self::Password => write!(f, "Password"),
Self::Permission => write!(f, "Permission"),
Self::UnknownCmd => write!(f, "Unknown Command"),
Self::NoExist => write!(f, "Doesn't exist"),
Self::PlaylistMax => write!(f, "Playlist Maximum"),
Self::System => write!(f, "System"),
Self::PlaylistLoad => write!(f, "Playlist Load"),
Self::UpdateAlready => write!(f, "Update already"),
Self::PlayerSync => write!(f, "Player Sync"),
Self::Exist => write!(f, "Exists"),
}
}
}
#[cfg(test)]
mod tests {
use super::MpdError;
#[test]
fn mpd_from_sys_base() {
let null_connection = std::ptr::null_mut();
assert_eq!(MpdError::from_sys(0, null_connection), None);
assert_eq!(
MpdError::from_sys(1, null_connection),
Some(MpdError::OutOfMemory)
);
assert_eq!(
MpdError::from_sys(2, null_connection),
Some(MpdError::Argument)
);
assert_eq!(
MpdError::from_sys(3, null_connection),
Some(MpdError::State)
);
assert_eq!(
MpdError::from_sys(4, null_connection),
Some(MpdError::Timeout)
);
assert_eq!(
MpdError::from_sys(6, null_connection),
Some(MpdError::Resolver)
);
assert_eq!(
MpdError::from_sys(7, null_connection),
Some(MpdError::Malformed)
);
assert_eq!(
MpdError::from_sys(8, null_connection),
Some(MpdError::Closed)
);
}
}