use std::path::PathBuf; use backtrace::Backtrace; #[derive(Debug)] pub enum Error { PlayerStopped, BadPath { pth: PathBuf, }, SystemTime { source: std::time::SystemTimeError, back: Backtrace, }, Client { source: crate::clients::Error, back: Backtrace, }, Rating { source: crate::ratings::Error, back: Backtrace, }, } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Error::PlayerStopped => write!(f, "The MPD player is stopped"), Error::BadPath { pth } => write!(f, "Bad path: {:?}", pth), Error::SystemTime { source, back: _ } => { write!(f, "Couldn't get system time: {}", source) } Error::Client { source, back: _ } => write!(f, "Client error: {}", source), Error::Rating { source, back: _ } => write!(f, "Rating error: {}", source), } } } impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self { Error::SystemTime { source, back: _ } => Some(source), Error::Client { source, back: _ } => Some(source), _ => None, } } } type Result = std::result::Result; pub mod play_count { use backtrace::Backtrace; use crate::clients::Client; use super::{Error, Result}; pub const STICKER: &str = "unwoundstack.com:playcount"; /// Retrieve the play count for a track pub async fn get(client: &mut Client, file: &str) -> Result> { match client .get_sticker::(file, STICKER) .await .map_err(|err| Error::Client { source: err, back: Backtrace::new(), })? { Some(n) => Ok(Some(n)), None => Ok(None), } } /// Set the play count for a track-- this will run the associated command, if any pub async fn set(client: &mut Client, file: &str, play_count: usize) -> Result<()> { client .set_sticker(file, STICKER, &format!("{}", play_count)) .await .map_err(|err| Error::Client { source: err, back: Backtrace::new(), })?; Ok(()) } #[cfg(test)] mod pc_lp_tests { use super::*; use crate::{clients::test_mock::Mock, storage::play_count}; /// "Smoke" tests for play counts & last played times #[tokio::test] async fn pc_smoke() { let mock = Box::new(Mock::new(&[ ("sticker get song a pc", "sticker: pc=11\nOK\n"), ( "sticker get song a pc", "ACK [50@0] {sticker} no such sticker\n", ), ("sticker get song a pc", "splat!"), ])); let mut cli = Client::new(mock).unwrap(); assert_eq!(play_count::get(&mut cli, "a").await.unwrap().unwrap(), 11); let val = play_count::get(&mut cli, "a").await.unwrap(); assert!(val.is_none()); play_count::get(&mut cli, "a").await.unwrap_err(); } } } pub mod skipped { use backtrace::Backtrace; use crate::clients::Client; use super::{Error, Result}; const STICKER: &str = "unwoundstack.com:skipped_count"; /// Retrieve the skip count for a track pub async fn get(client: &mut Client, file: &str) -> Result> { match client .get_sticker::(file, STICKER) .await .map_err(|err| Error::Client { source: err, back: Backtrace::new(), })? { Some(n) => Ok(Some(n)), None => Ok(None), } } /// Set the skip count for a track pub async fn set(client: &mut Client, file: &str, skip_count: usize) -> Result<()> { client .set_sticker(file, STICKER, &format!("{}", skip_count)) .await .map_err(|err| Error::Client { source: err, back: Backtrace::new(), }) } } pub mod last_played { use backtrace::Backtrace; use crate::clients::Client; use super::{Error, Result}; pub const STICKER: &str = "unwoundstack.com:lastplayed"; /// Retrieve the last played timestamp for a track (seconds since Unix epoch) pub async fn get(client: &mut Client, file: &str) -> Result> { client .get_sticker::(file, STICKER) .await .map_err(|err| Error::Client { source: err, back: Backtrace::new(), }) } /// Set the last played for a track pub async fn set(client: &mut Client, file: &str, last_played: u64) -> Result<()> { client .set_sticker(file, STICKER, &format!("{}", last_played)) .await .map_err(|err| Error::Client { source: err, back: Backtrace::new(), })?; Ok(()) } } pub mod rating_count { use backtrace::Backtrace; use crate::clients::Client; use super::{Error, Result}; pub const STICKER: &str = "unwoundstack.com:ratings_count"; /// Retrieve the rating count for a track pub async fn get(client: &mut Client, file: &str) -> Result> { client .get_sticker::(file, STICKER) .await .map_err(|err| Error::Client { source: err, back: Backtrace::new(), }) } /// Set the rating count for a track pub async fn set(client: &mut Client, file: &str, rating_count: u8) -> Result<()> { client .set_sticker(file, STICKER, &format!("{}", rating_count)) .await .map_err(|err| Error::Client { source: err, back: Backtrace::new(), })?; Ok(()) } }