use std::ffi::CString;
use database::Database;
use error::{Error, MpdError, Result};
use message::Channel;
use mpdclient_sys::{
mpd_connection, mpd_connection_cmp_server_version, mpd_connection_free,
mpd_connection_get_error, mpd_connection_get_server_version, mpd_connection_get_settings,
mpd_connection_new, mpd_connection_set_keepalive, mpd_connection_set_timeout,
mpd_send_list_playlists,
};
use player::Player;
use playlist::StoredPlaylist;
use queue::Queue;
use replaygain::ReplayGain;
use search::Search;
use stats::Stats;
use status::Status;
use crate::{
connection::{idle::Idle, mixer::Mixer},
entity::playlist::PlaylistReceiver,
error,
settings::Settings,
};
pub use message::Message;
pub use playlist::QueueSaveMode;
pub use queue::Position;
pub use replaygain::ReplayGainMode;
pub use search::Constraint;
pub use status::{ConsumeState, SingleState, State};
pub mod database;
pub mod idle;
pub mod message;
pub mod mixer;
pub mod player;
pub mod playlist;
pub mod queue;
pub mod replaygain;
pub mod search;
pub mod stats;
pub mod status;
#[derive(Debug)]
pub struct Connection {
inner: Option<*mut mpd_connection>,
}
impl Connection {
pub(crate) fn connection(&self) -> *mut mpd_connection {
self.inner.unwrap_or_else(|| unreachable!())
}
pub(crate) fn get_error<T>(&self, func: impl Fn() -> T) -> Result<T> {
let ret = func();
let conn_err = MpdError::from_sys(
unsafe { mpd_connection_get_error(self.connection()) },
self.connection(),
);
if let Some(error) = conn_err {
Err(Error::from_mpd(error, self.connection()))
} else {
Ok(ret)
}
}
pub(crate) fn get_bool_error(&self, func: impl Fn() -> bool) -> Result<()> {
if func() {
return Ok(());
}
Err(Error::from_mpd(
MpdError::from_sys(
unsafe { mpd_connection_get_error(self.connection()) },
self.connection(),
)
.unwrap_or_else(|| unreachable!()),
self.connection(),
))
}
#[doc(alias = "mpd_connection_new")]
pub fn connect(host: Option<&str>, port: u16, timeout: u32) -> Result<Self> {
let port = u32::from(port);
let inner = unsafe {
let connection = if let Some(host) = host {
let h = CString::new(host)?;
mpd_connection_new(h.as_ptr(), port, timeout)
} else {
mpd_connection_new(std::ptr::null(), port, timeout)
};
let err = MpdError::from_sys(mpd_connection_get_error(connection), connection);
if let Some(error) = err {
return Err(Error::from_mpd(error, connection));
}
connection
};
Ok(Connection { inner: Some(inner) })
}
#[doc(alias = "mpd_connection_new")]
pub fn new() -> Result<Self> {
Self::connect(None, 0, 0)
}
pub fn version(&self) -> Result<(u32, u32, u32)> {
unsafe {
let ver_ptr =
self.get_error(|| mpd_connection_get_server_version(self.connection()))?;
Ok((*ver_ptr, *ver_ptr.add(1), *ver_ptr.add(2)))
}
}
pub fn cmp_version(&self, version: (u32, u32, u32)) -> Result<ProtoVerCmp> {
unsafe {
let i = self.get_error(|| {
mpd_connection_cmp_server_version(
self.connection(),
version.0,
version.1,
version.2,
)
})?;
Ok(match i {
-1 => ProtoVerCmp::Older,
0 => ProtoVerCmp::Equal,
1 => ProtoVerCmp::Newer,
_ => unreachable!("mpd_connection_cmp_server_version only returns -1, 0, and 1"),
})
}
}
pub fn settings(&self) -> Result<Settings> {
let settings =
unsafe { self.get_error(|| mpd_connection_get_settings(self.connection()))? };
Ok(Settings::from(settings))
}
pub fn set_keepalive(&self, keepalive: bool) -> Result<()> {
if unsafe { mpd_connection_set_keepalive(self.connection(), keepalive) } {
Ok(())
} else {
Err(Error::KeepAlive)
}
}
pub fn set_timeout(&self, timeout: u32) -> Result<()> {
if timeout == 0 {
return Err(Error::TimeoutNull);
}
unsafe { mpd_connection_set_timeout(self.connection(), timeout) }
Ok(())
}
pub fn stored_playlists(&self) -> Result<PlaylistReceiver<'_>> {
self.get_bool_error(|| unsafe { mpd_send_list_playlists(self.connection()) })?;
Ok(PlaylistReceiver::new(self))
}
#[must_use]
pub fn queue(&self) -> Queue<'_> {
Queue::new(self)
}
pub fn status(&self) -> Result<Status> {
Status::new(self)
}
#[must_use]
pub fn database(&self) -> Database<'_> {
Database::new(self)
}
pub fn playlist(&self, name: &str) -> Result<StoredPlaylist<'_>> {
StoredPlaylist::new(self, name)
}
#[must_use]
pub fn replaygain(&self) -> ReplayGain<'_> {
ReplayGain::new(self)
}
#[must_use]
pub fn search(&self) -> Search<'_> {
Search::new(self)
}
pub fn stats(&self) -> Result<Stats> {
Stats::new(self)
}
#[must_use]
pub fn player(&self) -> Player<'_> {
Player::new(self)
}
#[must_use]
pub fn channel(&self) -> Channel<'_> {
Channel::new(self)
}
#[must_use]
pub fn idle(&self) -> Idle<'_> {
Idle::new(self)
}
#[must_use]
pub fn mixer(&self) -> Mixer<'_> {
Mixer::new(self)
}
}
#[doc(hidden)]
impl Drop for Connection {
fn drop(&mut self) {
unsafe {
mpd_connection_free(self.inner.take().unwrap());
}
}
}
unsafe impl Send for Connection {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProtoVerCmp {
Older,
Equal,
Newer,
}
#[cfg(test)]
mod tests {
use crate::Connection;
#[test]
fn drop() {
{
let _conn = Connection::new();
}
}
}