mpdclient 0.2.0

Rust interface to MPD using libmpdclient
Documentation
use std::ffi::CString;

use mpdclient_sys::{
    mpd_connection_get_error, mpd_run_rescan, mpd_run_update, mpd_send_list_all,
    mpd_send_list_all_meta, mpd_send_list_files, mpd_send_list_meta,
};

use crate::{
    Error,
    entity::EntityReceiver,
    error::{MpdError, Result},
};

use super::Connection;

/// Intermediate to bundle database functions with MPD.
pub struct Database<'a> {
    connection: &'a Connection,
}

impl<'a> Database<'a> {
    pub(super) fn new(connection: &'a Connection) -> Self {
        Self { connection }
    }

    /// Returns a recursive list of all [Entities](crate::entity::Entity) **without** metadata in
    /// the MPD database or a specified directory.
    ///
    /// # Errors
    ///
    /// Returns [`Error::Mpd`] if MPD returns an error.
    pub fn list_all(&self, path: Option<&str>) -> Result<EntityReceiver<'_>> {
        let ret = unsafe {
            match path {
                Some(p) => {
                    let cstr = CString::new(p)?;
                    mpd_send_list_all(self.connection.connection(), cstr.as_ptr())
                }
                None => mpd_send_list_all(self.connection.connection(), std::ptr::null()),
            }
        };
        self.connection.get_bool_error(|| ret)?;
        Ok(EntityReceiver::new(self.connection))
    }

    /// Returns a recursive list of all [Entities](crate::entity::Entity) **with** metadata in the
    /// MPD database or a specified directory.
    ///
    /// # Errors
    ///
    /// Returns [`Error::Mpd`] if MPD returns an error.
    pub fn list_all_meta(&self, path: Option<&str>) -> Result<EntityReceiver<'_>> {
        let ret = unsafe {
            match path {
                Some(p) => {
                    let cstr = CString::new(p)?;
                    mpd_send_list_all_meta(self.connection.connection(), cstr.as_ptr())
                }
                None => mpd_send_list_all_meta(self.connection.connection(), std::ptr::null()),
            }
        };
        self.connection.get_bool_error(|| ret)?;
        Ok(EntityReceiver::new(self.connection))
    }

    /// Returns a **non**-recursive list of all [Entities](crate::entity::Entity) with metadata in
    /// a specified directory.
    ///
    /// # Errors
    ///
    /// Returns [`Error::Mpd`] if MPD returns an error.
    pub fn list_meta(&self, path: Option<&str>) -> Result<EntityReceiver<'_>> {
        let ret = unsafe {
            match path {
                Some(p) => {
                    let cstr = CString::new(p)?;
                    mpd_send_list_meta(self.connection.connection(), cstr.as_ptr())
                }
                None => mpd_send_list_meta(self.connection.connection(), std::ptr::null()),
            }
        };
        self.connection.get_bool_error(|| ret)?;
        Ok(EntityReceiver::new(self.connection))
    }

    /// Returns a **non**-recursive list of all files in a specified directory.
    ///
    /// # Errors
    ///
    /// Returns [`Error::Mpd`] if MPD returns an error.
    pub fn list_files(&self, path: Option<&str>) -> Result<EntityReceiver<'_>> {
        let ret = unsafe {
            match path {
                Some(p) => {
                    let cstr = CString::new(p)?;
                    mpd_send_list_files(self.connection.connection(), cstr.as_ptr())
                }
                None => mpd_send_list_files(self.connection.connection(), std::ptr::null()),
            }
        };
        self.connection.get_bool_error(|| ret)?;
        Ok(EntityReceiver::new(self.connection))
    }

    fn get_update_id_error(&self, func: impl Fn() -> u32) -> Result<u32> {
        let ret = func();
        if ret == 0 {
            // unreachable!(), because error is ensured with ret == 0
            Err(Error::from_mpd(
                MpdError::from_sys(
                    unsafe { mpd_connection_get_error(self.connection.connection()) },
                    self.connection.connection(),
                )
                .unwrap_or_else(|| unreachable!()),
                self.connection.connection(),
            ))
        } else {
            Ok(ret)
        }
    }

    /// Updates the MPD database. Returns the update id.
    ///
    /// # Errors
    ///
    /// Returns [`Error::Mpd`] if MPD returns an error.
    pub fn update(&self, path: Option<&str>) -> Result<u32> {
        let ret = unsafe {
            match path {
                Some(p) => {
                    let cstr = CString::new(p)?;
                    mpd_run_update(self.connection.connection(), cstr.as_ptr())
                }
                None => mpd_run_update(self.connection.connection(), std::ptr::null()),
            }
        };
        self.get_update_id_error(|| ret)
    }

    /// Rescans the MPD database. Same as update but also scans unmodified files. Returns the
    /// update id.
    ///
    /// # Errors
    ///
    /// Returns [`Error::Mpd`] if MPD returns an error.
    pub fn rescan(&self, path: Option<&str>) -> Result<u32> {
        let ret = unsafe {
            match path {
                Some(p) => {
                    let cstr = CString::new(p)?;
                    mpd_run_rescan(self.connection.connection(), cstr.as_ptr())
                }
                None => mpd_run_rescan(self.connection.connection(), std::ptr::null()),
            }
        };
        self.get_update_id_error(|| ret)
    }
}