mpdclient 0.2.0

Rust interface to MPD using libmpdclient
Documentation
// Allow because ReplayGain is written with capitalization
#![allow(clippy::doc_markdown)]

use mpdclient_sys::{
    mpd_connection_get_server_error, mpd_replay_gain_mode_MPD_REPLAY_ALBUM,
    mpd_replay_gain_mode_MPD_REPLAY_AUTO, mpd_replay_gain_mode_MPD_REPLAY_OFF,
    mpd_replay_gain_mode_MPD_REPLAY_TRACK, mpd_replay_gain_mode_MPD_REPLAY_UNKNOWN,
    mpd_run_replay_gain_mode, mpd_run_replay_gain_status,
};

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

use super::Connection;

/// Intermediate to bundle MPD [ReplayGain](https://en.wikipedia.org/wiki/ReplayGain) functions.
pub struct ReplayGain<'a> {
    connection: &'a Connection,
}

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

    /// Returns the current ReplayGain state.
    ///
    /// # Errors
    ///
    /// Returns [Error::Mpd] if MPD returns an error.
    pub fn status(&self) -> Result<ReplayGainMode> {
        let status = unsafe {
            let ustatus = mpd_run_replay_gain_status(self.connection.connection());
            if ustatus == mpd_replay_gain_mode_MPD_REPLAY_UNKNOWN {
                // allowed because mpd_connection_get_server_error will never be negative.
                #[allow(clippy::cast_sign_loss)]
                let err = MpdError::from_sys(
                    mpd_connection_get_server_error(self.connection.connection()) as u32,
                    self.connection.connection(),
                );
                if let Some(error) = err {
                    return Err(Error::from_mpd(error, self.connection.connection()));
                }
            }
            ustatus
        };
        ReplayGainMode::try_from(status)
    }

    /// Changes the ReplayGain state on the MPD.
    ///
    /// # Errors
    ///
    /// Returns [Error::Mpd] if MPD returns an error.
    pub fn mode(&self, mode: ReplayGainMode) -> Result<()> {
        self.connection.get_bool_error(|| unsafe {
            mpd_run_replay_gain_mode(self.connection.connection(), mode as u32)
        })
    }
}

/// Mode in which ReplayGain is handled.
#[allow(clippy::cast_possible_wrap)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReplayGainMode {
    /// Ignores ReplayGain tags
    Off = mpd_replay_gain_mode_MPD_REPLAY_OFF as isize,

    /// per-track ReplayGain
    Track = mpd_replay_gain_mode_MPD_REPLAY_TRACK as isize,

    /// per-album ReplayGain
    Album = mpd_replay_gain_mode_MPD_REPLAY_ALBUM as isize,

    /// prefer per-track tag if both available
    Auto = mpd_replay_gain_mode_MPD_REPLAY_AUTO as isize,
}

impl TryFrom<u32> for ReplayGainMode {
    type Error = Error;

    fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
        #[allow(non_upper_case_globals)]
        match value {
            mpd_replay_gain_mode_MPD_REPLAY_OFF => Ok(Self::Off),
            mpd_replay_gain_mode_MPD_REPLAY_TRACK => Ok(Self::Track),
            mpd_replay_gain_mode_MPD_REPLAY_ALBUM => Ok(Self::Album),
            mpd_replay_gain_mode_MPD_REPLAY_AUTO => Ok(Self::Auto),
            mpd_replay_gain_mode_MPD_REPLAY_UNKNOWN => {
                Err(Error::Unknown("ReplayGain".to_string()))
            }
            _ => unreachable!(),
        }
    }
}