diff options
Diffstat (limited to '')
| -rw-r--r-- | pkgs/by-name/mp/mpdpopm/Cargo.lock | 91 | ||||
| -rw-r--r-- | pkgs/by-name/mp/mpdpopm/Cargo.toml | 3 | ||||
| -rw-r--r-- | pkgs/by-name/mp/mpdpopm/src/bin/mpdpopm.rs | 211 | ||||
| -rw-r--r-- | pkgs/by-name/mp/mpdpopm/src/bin/mpdpopmd.rs | 117 | ||||
| -rw-r--r-- | pkgs/by-name/mp/mpdpopm/src/clients.rs | 309 | ||||
| -rw-r--r-- | pkgs/by-name/mp/mpdpopm/src/config.rs | 34 | ||||
| -rw-r--r-- | pkgs/by-name/mp/mpdpopm/src/filters_ast.rs | 227 | ||||
| -rw-r--r-- | pkgs/by-name/mp/mpdpopm/src/lib.rs | 116 | ||||
| -rw-r--r-- | pkgs/by-name/mp/mpdpopm/src/messages.rs | 165 | ||||
| -rw-r--r-- | pkgs/by-name/mp/mpdpopm/src/playcounts.rs | 86 | ||||
| -rw-r--r-- | pkgs/by-name/mp/mpdpopm/src/storage/mod.rs | 103 | ||||
| -rw-r--r-- | pkgs/by-name/mp/mpdpopm/src/vars.rs | 1 |
12 files changed, 215 insertions, 1248 deletions
diff --git a/pkgs/by-name/mp/mpdpopm/Cargo.lock b/pkgs/by-name/mp/mpdpopm/Cargo.lock index fbedffd4..96909646 100644 --- a/pkgs/by-name/mp/mpdpopm/Cargo.lock +++ b/pkgs/by-name/mp/mpdpopm/Cargo.lock @@ -3,21 +3,6 @@ version = 4 [[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] name = "aho-corasick" version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -86,6 +71,12 @@ dependencies = [ ] [[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] name = "ascii-canvas" version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -112,21 +103,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link", -] - -[[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -427,12 +403,6 @@ dependencies = [ ] [[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - -[[package]] name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -593,15 +563,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - -[[package]] name = "mio" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -616,8 +577,8 @@ dependencies = [ name = "mpdpopm" version = "0.1.0" dependencies = [ + "anyhow", "async-trait", - "backtrace", "boolinator", "chrono", "clap", @@ -631,7 +592,6 @@ dependencies = [ "regex", "serde", "serde_json", - "snafu", "tokio", "toml", "tracing", @@ -663,15 +623,6 @@ dependencies = [ ] [[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - -[[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -835,12 +786,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] -name = "rustc-demangle" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" - -[[package]] name = "rustversion" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -967,28 +912,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] -name = "snafu" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e84b3f4eacbf3a1ce05eac6763b4d629d60cbc94d632e4092c54ade71f1e1a2" -dependencies = [ - "backtrace", - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "socket2" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/pkgs/by-name/mp/mpdpopm/Cargo.toml b/pkgs/by-name/mp/mpdpopm/Cargo.toml index ccadfabb..c82537e6 100644 --- a/pkgs/by-name/mp/mpdpopm/Cargo.toml +++ b/pkgs/by-name/mp/mpdpopm/Cargo.toml @@ -25,7 +25,6 @@ lalrpop = { version = "0.22", features = ["lexer"] } [dependencies] async-trait = "0.1" -backtrace = "0.3" boolinator = "2.4" chrono = "0.4" clap = {version = "4.5", features = ["derive"]} @@ -38,8 +37,8 @@ pin-project = "1.1" regex = "1.12" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.149" -snafu = { version = "0.8.9", features = ["backtrace"] } toml = "0.9" tokio = { version = "1.49", features = ["io-util", "macros", "net", "process", "rt-multi-thread", "signal", "sync", "time"] } tracing = "0.1.44" tracing-subscriber = { version = "0.3.22", features = ["env-filter"]} +anyhow = "1.0.100" diff --git a/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopm.rs b/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopm.rs index 82a354d6..d9d607d5 100644 --- a/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopm.rs +++ b/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopm.rs @@ -32,82 +32,12 @@ use mpdpopm::{ storage::{last_played, play_count, rating_count}, }; -use backtrace::Backtrace; +use anyhow::{Context, Result, anyhow, bail}; use clap::{Parser, Subcommand}; use tracing::{debug, info, level_filters::LevelFilter, trace}; use tracing_subscriber::{EnvFilter, Registry, layer::SubscriberExt}; -use std::{fmt, path::PathBuf}; - -#[non_exhaustive] -pub enum Error { - NoSubCommand, - NoConfigArg, - NoRating, - NoPlayCount, - NoLastPlayed, - NoConfig { - config: std::path::PathBuf, - cause: std::io::Error, - }, - PlayerStopped, - BadPath { - path: PathBuf, - back: Backtrace, - }, - NoPlaylist, - Client { - source: mpdpopm::clients::Error, - back: Backtrace, - }, - Ratings { - source: Box<mpdpopm::storage::Error>, - back: Backtrace, - }, - Playcounts { - source: Box<mpdpopm::storage::Error>, - back: Backtrace, - }, - ExpectedInt { - source: std::num::ParseIntError, - back: Backtrace, - }, - Config { - source: crate::config::Error, - back: Backtrace, - }, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Error::NoSubCommand => write!(f, "No sub-command given"), - Error::NoConfigArg => write!(f, "No argument given for the configuration option"), - Error::NoRating => write!(f, "No rating supplied"), - Error::NoPlayCount => write!(f, "No play count supplied"), - Error::NoLastPlayed => write!(f, "No last played timestamp given"), - Error::NoConfig { config, cause } => write!(f, "Bad config ({:?}): {}", config, cause), - Error::PlayerStopped => write!(f, "The player is stopped"), - Error::BadPath { path, back: _ } => write!(f, "Bad path: {:?}", path), - Error::NoPlaylist => write!(f, "No playlist given"), - Error::Client { source, back: _ } => write!(f, "Client error: {}", source), - Error::Ratings { source, back: _ } => write!(f, "Rating error: {}", source), - Error::Playcounts { source, back: _ } => write!(f, "Playcount error: {}", source), - Error::ExpectedInt { source, back: _ } => write!(f, "Expected integer: {}", source), - Error::Config { source, back: _ } => { - write!(f, "Error reading configuration: {}", source) - } - } - } -} - -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self) - } -} - -type Result<T> = std::result::Result<T, Error>; +use std::path::PathBuf; /// Map `tracks' argument(s) to a Vec of String containing one or more mpd URIs /// @@ -132,20 +62,18 @@ async fn provide_file(client: &mut Client, maybe_file: Option<String>) -> Result let file = match maybe_file { Some(file) => file, None => { - match client.status().await.map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })? { + match client + .status() + .await + .context("Failed to get status of client")? + { PlayerStatus::Play(curr) | PlayerStatus::Pause(curr) => curr .file .to_str() - .ok_or_else(|| Error::BadPath { - path: curr.file.clone(), - back: Backtrace::new(), - })? + .ok_or_else(|| anyhow!("Path is not utf8: `{}`", curr.file.display()))? .to_string(), PlayerStatus::Stopped => { - return Err(Error::PlayerStopped); + bail!("Player is stopped"); } } } @@ -163,12 +91,7 @@ async fn get_ratings( let mut ratings: Vec<(String, i8)> = Vec::new(); for file in map_tracks(client, tracks).await? { - let rating = rating_count::get(client, &file) - .await - .map_err(|err| Error::Ratings { - source: Box::new(err), - back: Backtrace::new(), - })?; + let rating = rating_count::get(client, &file).await?; ratings.push((file, rating.unwrap_or_default())); } @@ -189,12 +112,7 @@ async fn set_rating(client: &mut Client, rating: i8, arg: Option<String>) -> Res let is_current = arg.is_none(); let file = provide_file(client, arg).await?; - rating_count::set(client, &file, rating) - .await - .map_err(|err| Error::Ratings { - source: Box::new(err), - back: Backtrace::new(), - })?; + rating_count::set(client, &file, rating).await?; match is_current { false => info!("Set the rating for \"{}\" to \"{}\".", file, rating), @@ -209,19 +127,9 @@ async fn inc_rating(client: &mut Client, arg: Option<String>) -> Result<()> { let is_current = arg.is_none(); let file = provide_file(client, arg).await?; - let now = rating_count::get(client, &file) - .await - .map_err(|err| Error::Ratings { - source: Box::new(err), - back: Backtrace::new(), - })?; + let now = rating_count::get(client, &file).await?; - rating_count::set(client, &file, now.unwrap_or_default().saturating_add(1)) - .await - .map_err(|err| Error::Ratings { - source: Box::new(err), - back: Backtrace::new(), - })?; + rating_count::set(client, &file, now.unwrap_or_default().saturating_add(1)).await?; match is_current { false => info!("Incremented the rating for \"{}\".", file), @@ -236,19 +144,9 @@ async fn decr_rating(client: &mut Client, arg: Option<String>) -> Result<()> { let is_current = arg.is_none(); let file = provide_file(client, arg).await?; - let now = rating_count::get(client, &file) - .await - .map_err(|err| Error::Ratings { - source: Box::new(err), - back: Backtrace::new(), - })?; + let now = rating_count::get(client, &file).await?; - rating_count::set(client, &file, now.unwrap_or_default().saturating_sub(1)) - .await - .map_err(|err| Error::Ratings { - source: Box::new(err), - back: Backtrace::new(), - })?; + rating_count::set(client, &file, now.unwrap_or_default().saturating_sub(1)).await?; match is_current { false => info!("Decremented the rating for \"{}\".", file), @@ -266,13 +164,7 @@ async fn get_play_counts( ) -> Result<()> { let mut playcounts: Vec<(String, usize)> = Vec::new(); for file in map_tracks(client, tracks).await? { - let playcount = play_count::get(client, &file) - .await - .map_err(|err| Error::Playcounts { - source: Box::new(err), - back: Backtrace::new(), - })? - .unwrap_or_default(); + let playcount = play_count::get(client, &file).await?.unwrap_or_default(); playcounts.push((file, playcount)); } @@ -292,12 +184,7 @@ async fn set_play_counts(client: &mut Client, playcount: usize, arg: Option<Stri let is_current = arg.is_none(); let file = provide_file(client, arg).await?; - play_count::set(client, &file, playcount) - .await - .map_err(|err| Error::Playcounts { - source: Box::new(err), - back: Backtrace::new(), - })?; + play_count::set(client, &file, playcount).await?; match is_current { false => info!("Set the playcount for \"{}\" to \"{}\".", file, playcount), @@ -318,13 +205,7 @@ async fn get_last_playeds( ) -> Result<()> { let mut lastplayeds: Vec<(String, Option<u64>)> = Vec::new(); for file in map_tracks(client, tracks).await? { - let lastplayed = - last_played::get(client, &file) - .await - .map_err(|err| Error::Playcounts { - source: Box::new(err), - back: Backtrace::new(), - })?; + let lastplayed = last_played::get(client, &file).await?; lastplayeds.push((file, lastplayed)); } @@ -357,12 +238,7 @@ async fn set_last_playeds(client: &mut Client, lastplayed: u64, arg: Option<Stri let is_current = arg.is_none(); let file = provide_file(client, arg).await?; - last_played::set(client, &file, lastplayed) - .await - .map_err(|err| Error::Playcounts { - source: Box::new(err), - back: Backtrace::new(), - })?; + last_played::set(client, &file, lastplayed).await?; match is_current { false => info!("Set last played for \"{}\" to \"{}\".", file, lastplayed), @@ -377,13 +253,7 @@ async fn set_last_playeds(client: &mut Client, lastplayed: u64, arg: Option<Stri /// Retrieve the list of stored playlists async fn get_playlists(client: &mut Client) -> Result<()> { - let mut pls = client - .get_stored_playlists() - .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })?; + let mut pls = client.get_stored_playlists().await?; pls.sort(); println!("Stored playlists:"); for pl in pls { @@ -397,13 +267,7 @@ async fn findadd(client: &mut Client, chan: &str, filter: &str, case: bool) -> R let qfilter = quote(filter); debug!("findadd: got ``{}'', quoted to ``{}''.", filter, qfilter); let cmd = format!("{} {}", if case { "findadd" } else { "searchadd" }, qfilter); - client - .send_message(chan, &cmd) - .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })?; + client.send_message(chan, &cmd).await?; Ok(()) } @@ -419,11 +283,7 @@ async fn send_command(client: &mut Client, chan: &str, args: Vec<String>) -> Res .join(" ") .as_str(), ) - .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })?; + .await?; Ok(()) } @@ -647,18 +507,17 @@ async fn main() -> Result<()> { let config = if let Some(configpath) = &args.config { match std::fs::read_to_string(configpath) { - Ok(text) => config::from_str(&text).map_err(|err| Error::Config { - source: err, - back: Backtrace::new(), + Ok(text) => config::from_str(&text).with_context(|| { + format!("Failed to parse config file at: `{}`", configpath.display()) })?, Err(err) => { // Either they did _not_, in which case they probably want to know that the config // file they explicitly asked for does not exist, or there was some other problem, // in which case we're out of options, anyway. Either way: - return Err(Error::NoConfig { - config: PathBuf::from(configpath), - cause: err, - }); + bail!( + "Failed to read config file at: `{}`, because: {err}", + configpath.display() + ) } } } else { @@ -691,18 +550,10 @@ async fn main() -> Result<()> { trace!("logging configured."); let mut client = match config.conn { - config::Connection::Local { path } => { - Client::open(path).await.map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })? + config::Connection::Local { path } => Client::open(path).await?, + config::Connection::TCP { host, port } => { + Client::connect(format!("{}:{}", host, port)).await? } - config::Connection::TCP { host, port } => Client::connect(format!("{}:{}", host, port)) - .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })?, }; match args.command { diff --git a/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopmd.rs b/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopmd.rs index e903774c..643611d6 100644 --- a/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopmd.rs +++ b/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopmd.rs @@ -25,94 +25,17 @@ //! the sticker database, by invoking external commands to keep your tags up-to-date (something //! along the lines of [mpdcron](https://alip.github.io/mpdcron)). -use mpdpopm::config; -use mpdpopm::config::Config; -use mpdpopm::mpdpopm; +use mpdpopm::{ + config::{self, Config}, + mpdpopm, +}; -use backtrace::Backtrace; +use anyhow::{Context, Result, bail}; use clap::Parser; use tracing::{info, level_filters::LevelFilter}; use tracing_subscriber::{EnvFilter, Layer, Registry, layer::SubscriberExt}; -use std::{fmt, io, path::PathBuf, sync::MutexGuard}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// mppopmd application Error type // -//////////////////////////////////////////////////////////////////////////////////////////////////// - -#[non_exhaustive] -pub enum Error { - NoConfigArg, - NoConfig { - config: std::path::PathBuf, - cause: std::io::Error, - }, - Filter { - source: tracing_subscriber::filter::FromEnvError, - back: Backtrace, - }, - Fork { - errno: errno::Errno, - back: Backtrace, - }, - PathContainsNull { - back: Backtrace, - }, - OpenLockFile { - errno: errno::Errno, - back: Backtrace, - }, - LockFile { - errno: errno::Errno, - back: Backtrace, - }, - WritePid { - errno: errno::Errno, - back: Backtrace, - }, - Config { - source: crate::config::Error, - back: Backtrace, - }, - MpdPopm { - source: Box<mpdpopm::Error>, - back: Backtrace, - }, -} - -impl std::fmt::Display for Error { - #[allow(unreachable_patterns)] // the _ arm is *currently* unreachable - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Error::NoConfigArg => write!(f, "No configuration file given"), - Error::NoConfig { config, cause } => { - write!(f, "Configuration error ({:?}): {}", config, cause) - } - Error::Fork { errno, back: _ } => write!(f, "When forking, got errno {}", errno), - Error::PathContainsNull { back: _ } => write!(f, "Path contains a null character"), - Error::OpenLockFile { errno, back: _ } => { - write!(f, "While opening lock file, got errno {}", errno) - } - Error::LockFile { errno, back: _ } => { - write!(f, "While locking the lock file, got errno {}", errno) - } - Error::WritePid { errno, back: _ } => { - write!(f, "While writing pid file, got errno {}", errno) - } - Error::Config { source, back: _ } => write!(f, "Configuration error: {}", source), - Error::MpdPopm { source, back: _ } => write!(f, "mpdpopm error: {}", source), - _ => write!(f, "Unknown mppopmd error"), - } - } -} - -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self) - } -} - -type Result = std::result::Result<(), Error>; +use std::{io, path::PathBuf, sync::MutexGuard}; pub struct MyMutexGuardWriter<'a>(MutexGuard<'a, std::fs::File>); @@ -164,22 +87,21 @@ struct Args { debug: bool, } -/// Entry point for `mppopmd'. +/// Entry point for `mpdopmd'. /// /// Do *not* use the #[tokio::main] attribute here! If this program is asked to daemonize (the usual /// case), we will fork after tokio has started its thread pool, with disastrous consequences. /// Instead, stay synchronous until we've daemonized (or figured out that we don't need to), and /// only then fire-up the tokio runtime. -fn main() -> Result { +fn main() -> Result<()> { use mpdpopm::vars::VERSION; let args = Args::parse(); let config = if let Some(cfgpath) = &args.config { match std::fs::read_to_string(cfgpath) { - Ok(text) => config::from_str(&text).map_err(|err| Error::Config { - source: err, - back: Backtrace::new(), + Ok(text) => config::from_str(&text).with_context(|| { + format!("Failed to parse config file at: `{}`", cfgpath.display()) })?, // The config file (defaulted or not) either didn't exist, or we were unable to read its // contents... @@ -187,10 +109,10 @@ fn main() -> Result { // Either they did _not_, in which case they probably want to know that the config // file they explicitly asked for does not exist, or there was some other problem, // in which case we're out of options, anyway. Either way: - return Err(Error::NoConfig { - config: PathBuf::from(cfgpath), - cause: err, - }); + bail!( + "No config file could be read at: `{}`, because: {err}", + cfgpath.display() + ) } } } else { @@ -208,10 +130,7 @@ fn main() -> Result { let filter = EnvFilter::builder() .with_default_directive(lf.into()) .from_env() - .map_err(|err| Error::Filter { - source: err, - back: Backtrace::new(), - })?; + .context("Failed to construct env filter")?; let formatter: Box<dyn Layer<Registry> + Send + Sync> = { Box::new( @@ -226,8 +145,6 @@ fn main() -> Result { info!("mppopmd {VERSION} logging at level {lf:#?}."); let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(mpdpopm(config)).map_err(|err| Error::MpdPopm { - source: Box::new(err), - back: Backtrace::new(), - }) + + rt.block_on(mpdpopm(config)).context("Main mpdpopm failed") } diff --git a/pkgs/by-name/mp/mpdpopm/src/clients.rs b/pkgs/by-name/mp/mpdpopm/src/clients.rs index 587063b2..b88e4041 100644 --- a/pkgs/by-name/mp/mpdpopm/src/clients.rs +++ b/pkgs/by-name/mp/mpdpopm/src/clients.rs @@ -31,9 +31,9 @@ //! re-issue the "idle" command. This crate however takes the approach of two channels (like //! [mpdfav](https://github.com/vincent-petithory/mpdfav)). +use anyhow::{Context, Error, Result, anyhow, bail, ensure}; use async_trait::async_trait; use regex::Regex; -use snafu::{Backtrace, IntoError, OptionExt, ResultExt, prelude::*}; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use tokio::net::{TcpStream, ToSocketAddrs, UnixStream}; use tracing::{debug, info}; @@ -49,92 +49,9 @@ use std::{ str::FromStr, }; -// The Protocol error, below, gets used a *lot*; anywhere we receive a message from the MPD server -// that "should" never happen. To help give a bit of context beyond a stack trace, I use this -// enumeration of "operations" -/// Enumerated list of MPD operations; used in Error::Protocol to distinguish which operation it was -/// that elicited the protocol error. -#[derive(Debug)] -#[non_exhaustive] -pub enum Operation { - Connect, - Status, - GetSticker, - SetSticker, - SendToPlaylist, - SendMessage, - Update, - GetStoredPlaylists, - RspToUris, - GetStickers, - GetAllSongs, - Add, - Idle, - GetMessages, -} - -impl std::fmt::Display for Operation { - #[allow(unreachable_patterns)] // the _ arm is *currently* unreachable - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Operation::Connect => write!(f, "Connect"), - Operation::Status => write!(f, "Status"), - Operation::GetSticker => write!(f, "GetSticker"), - Operation::SetSticker => write!(f, "SetSticker"), - Operation::SendToPlaylist => write!(f, "SendToPlaylist"), - Operation::SendMessage => write!(f, "SendMessage"), - Operation::Update => write!(f, "Update"), - Operation::GetStoredPlaylists => write!(f, "GetStoredPlaylists"), - Operation::RspToUris => write!(f, "RspToUris"), - Operation::GetStickers => write!(f, "GetStickers"), - Operation::GetAllSongs => write!(f, "GetAllSongs"), - Operation::Add => write!(f, "Add"), - Operation::Idle => write!(f, "Idle"), - Operation::GetMessages => write!(f, "GetMessages"), - _ => write!(f, "Unknown client operation"), - } - } -} - -/// An MPD client error -#[derive(Debug, Snafu)] -#[non_exhaustive] -pub enum Error { - #[snafu(display("Protocol error ({}): {}", op, msg))] - Protocol { - op: Operation, - msg: String, - backtrace: Backtrace, - }, - #[snafu(display("Protocol errror ({}): {}", op, source))] - ProtocolConv { - op: Operation, - source: Box<dyn std::error::Error>, - backtrace: Backtrace, - }, - #[snafu(display("I/O error: {}", source))] - Io { - source: std::io::Error, - backtrace: Backtrace, - }, - #[snafu(display("Encoding error: {}", source))] - Encoding { - source: std::string::FromUtf8Error, - backtrace: Backtrace, - }, - #[snafu(display("While converting sticker ``{}'': {}", sticker, source))] - StickerConversion { - sticker: String, - source: Box<dyn std::error::Error>, - backtrace: Backtrace, - }, - #[snafu(display("``{}'' is not a recognized Idle subsystem", text))] - IdleSubSystem { text: String, backtrace: Backtrace }, -} - -pub type Result<T> = std::result::Result<T, Error>; - -//////////////////////////////////////////////////////////////////////////////////////////////////// +// Some default error context messages +const ENCODING_SNAFU: &str = "Failed to interpete text as utf8"; +const IO_SNAFU: &str = "Failed read from mpd socket"; /// A description of the current track, suitable for our purposes (as in, it only tracks the /// attributes needed for this module's functionality). @@ -187,10 +104,6 @@ impl PlayerStatus { } } -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Connection // -//////////////////////////////////////////////////////////////////////////////////////////////////// - /// A trait representing a simple, textual request/response protocol like that /// [employed](https://www.musicpd.org/doc/html/protocol.html) by [MPD](https://www.musicpd.org/): /// the caller sends a textual command & the server responds with a (perhaps multi-line) textual @@ -310,7 +223,7 @@ where self.sock .write_all(format!("{}\n", msg).as_bytes()) .await - .context(IoSnafu)?; + .context(IO_SNAFU)?; let mut buf = Vec::with_capacity(hint); // Given the request/response nature of the MPD protocol, our callers expect a complete @@ -319,7 +232,7 @@ where let mut cb = 0; // # bytes read so far let mut more = true; // true as long as there is more to read while more { - cb += self.sock.read_buf(&mut buf).await.context(IoSnafu)?; + cb += self.sock.read_buf(&mut buf).await.context(IO_SNAFU)?; // The shortest complete response has three bytes. If the final byte in `buf' is not a // newline, then don't bother looking further. @@ -347,7 +260,7 @@ where } // Only doing this to trouble-shoot issue 11 - String::from_utf8(buf.clone()).context(EncodingSnafu) + String::from_utf8(buf.clone()).context(ENCODING_SNAFU) } } @@ -357,15 +270,15 @@ where T: AsyncReadExt + AsyncWriteExt + Send + Unpin, { let mut buf = Vec::with_capacity(32); - let _cb = sock.read_buf(&mut buf).await.context(IoSnafu)?; + let _cb = sock.read_buf(&mut buf).await.context(IO_SNAFU)?; + // Only doing this to trouble-shoot issue 11 - let text = String::from_utf8(buf.clone()).context(EncodingSnafu)?; + let text = String::from_utf8(buf.clone()).context(ENCODING_SNAFU)?; + ensure!( text.starts_with("OK MPD "), - ProtocolSnafu { - op: Operation::Connect, - msg: text.trim() - } + "failed to connect: {}", + text.trim() ); info!("Connected {}.", text[7..].trim()); Ok(text[7..].trim().to_string()) @@ -373,7 +286,7 @@ where impl MpdConnection<TcpStream> { pub async fn connect<A: ToSocketAddrs>(addr: A) -> Result<Box<dyn RequestResponse>> { - let mut sock = TcpStream::connect(addr).await.context(IoSnafu)?; + let mut sock = TcpStream::connect(addr).await.context(IO_SNAFU)?; let proto_ver = parse_connect_rsp(&mut sock).await?; Ok(Box::new(MpdConnection::<TcpStream> { sock, @@ -385,7 +298,7 @@ impl MpdConnection<TcpStream> { impl MpdConnection<UnixStream> { // NTS: we have to box the return value because a `dyn RequestResponse` isn't Sized. pub async fn connect<P: AsRef<Path>>(pth: P) -> Result<Box<dyn RequestResponse>> { - let mut sock = UnixStream::connect(pth).await.context(IoSnafu)?; + let mut sock = UnixStream::connect(pth).await.context(IO_SNAFU)?; let proto_ver = parse_connect_rsp(&mut sock).await?; Ok(Box::new(MpdConnection::<UnixStream> { sock, @@ -478,13 +391,7 @@ impl Client { // also don't want to depend on the order. let text = self.stream.req("status").await?; - let proto = || -> Error { - ProtocolSnafu { - op: Operation::Status, - msg: text.to_owned(), - } - .build() - }; + let proto = || -> Error { anyhow!("Failed to parse mpd status output (with regexes)") }; // I first thought to avoid the use (and cost) of regular expressions by just doing // sub-string searching on "state: ", but when I realized I needed to only match at the @@ -507,12 +414,7 @@ impl Client { .ok_or_else(proto)? .as_str() .parse::<u64>() - .map_err(|err| { - ProtocolConvSnafu { - op: Operation::Status, - } - .into_error(Box::new(err)) - })?; + .context("Failed to parse songid as u64")?; let elapsed = RE_ELAPSED .captures(&text) @@ -521,12 +423,7 @@ impl Client { .ok_or_else(proto)? .as_str() .parse::<f64>() - .map_err(|err| { - ProtocolConvSnafu { - op: Operation::Status, - } - .into_error(Box::new(err)) - })?; + .context("failed to parse `elapsed` as f64")?; // navigate from `songid'-- don't send a "currentsong" message-- the current song // could have changed @@ -545,12 +442,7 @@ impl Client { .ok_or_else(proto)? .as_str() .parse::<f64>() - .map_err(|err| { - ProtocolConvSnafu { - op: Operation::Status, - } - .into_error(Box::new(err)) - })?; + .context("Failed to parse `duration` as f64")?; let curr = CurrentSong::new(songid, PathBuf::from(file), elapsed, duration); @@ -560,11 +452,7 @@ impl Client { Ok(PlayerStatus::Pause(curr)) } } - _ => ProtocolSnafu { - op: Operation::Status, - msg: state.to_owned(), - } - .fail(), + _ => bail!("Encountered unknow state `{}`", state), } } @@ -586,24 +474,18 @@ impl Client { let s = text[prefix.len()..] .split('\n') .next() - .context(ProtocolSnafu { - op: Operation::GetSticker, - msg, - })?; - Ok(Some(T::from_str(s).map_err(|err| { - StickerConversionSnafu { - sticker: sticker_name.to_owned(), - } - .into_error(Box::new(err)) + .with_context(|| format!("Failed to parse `{}` as get_sticker response", text))?; + Ok(Some(T::from_str(s).with_context(|| { + format!( + "Failed to parse sticker value as correct type: `{}`", + sticker_name + ) })?)) } else { // ACK_ERROR_NO_EXIST = 50 (Ack.hxx:17) ensure!( text.starts_with("ACK [50@0]"), - ProtocolSnafu { - op: Operation::GetSticker, - msg, - } + "Missing no sticker response" ); Ok(None) } @@ -626,13 +508,7 @@ impl Client { let text = self.stream.req(&msg).await?; debug!("Sent `{}'; got `{}'", &msg, &text); - ensure!( - text.starts_with("OK"), - ProtocolSnafu { - op: Operation::SetSticker, - msg - } - ); + ensure!(text.starts_with("OK"), "Set sticker, not acknowledged"); Ok(()) } @@ -641,13 +517,7 @@ impl Client { let msg = format!("playlistadd {} {}", quote(pl), quote(file)); let text = self.stream.req(&msg).await?; debug!("Sent `{}'; got `{}'.", &msg, &text); - ensure!( - text.starts_with("OK"), - ProtocolSnafu { - op: Operation::SendToPlaylist, - msg - } - ); + ensure!(text.starts_with("OK"), "send_to_playlist not acknowledged"); Ok(()) } @@ -657,13 +527,7 @@ impl Client { let text = self.stream.req(&msg).await?; debug!("Sent `{}'; got `{}'.", &msg, &text); - ensure!( - text.starts_with("OK"), - ProtocolSnafu { - op: Operation::SendMessage, - msg: text - } - ); + ensure!(text.starts_with("OK"), "Send_message not acknowledged"); Ok(()) } @@ -683,20 +547,12 @@ impl Client { let prefix = "updating_db: "; ensure!( text.starts_with(prefix), - ProtocolSnafu { - op: Operation::Update, - msg: &text - } + "update response doesn't start with correct prefix" ); text[prefix.len()..].split('\n').collect::<Vec<&str>>()[0] .to_string() .parse::<u64>() - .map_err(|err| { - ProtocolConvSnafu { - op: Operation::Update, - } - .into_error(Box::new(err)) - }) + .context("Failed to treat update job id as u64") } /// Get the list of stored playlists @@ -717,10 +573,7 @@ impl Client { // ACK... ensure!( !text.starts_with("ACK"), - ProtocolSnafu { - op: Operation::GetStoredPlaylists, - msg: text - } + "get_stored_playlists response not acknowledged" ); Ok(text .lines() @@ -742,13 +595,7 @@ impl Client { // or // // ACK... - ensure!( - !text.starts_with("ACK"), - ProtocolSnafu { - op: Operation::RspToUris, - msg: text.to_owned() - } - ); + ensure!(!text.starts_with("ACK"), "rsp_to_uris not acknowledged"); Ok(text .lines() .filter_map(|x| x.strip_prefix("file: ").map(String::from)) @@ -814,27 +661,15 @@ impl Client { // or // // ACK ... - ensure!( - !text.starts_with("ACK"), - ProtocolSnafu { - op: Operation::GetStickers, - msg: text, - } - ); + ensure!(!text.starts_with("ACK"), "get_stickers not ACKed"); let mut m = HashMap::new(); let mut lines = text.lines(); loop { - let file = lines.next().context(ProtocolSnafu { - op: Operation::GetStickers, - msg: text.to_owned(), - })?; + let file = lines.next().context("get_stickers no new line")?; if "OK" == file { break; } - let val = lines.next().context(ProtocolSnafu { - op: Operation::GetStickers, - msg: text.to_owned(), - })?; + let val = lines.next().context("get_stickers no val")?; m.insert( String::from(&file[6..]), @@ -863,13 +698,7 @@ impl Client { // OK // // or "ACK..." - ensure!( - !text.starts_with("ACK"), - ProtocolSnafu { - op: Operation::GetAllSongs, - msg: text, - } - ); + ensure!(!text.starts_with("ACK"), "get_all_songs not ACKed"); Ok(text .lines() .filter_map(|x| x.strip_prefix("file: ").map(String::from)) @@ -881,13 +710,7 @@ impl Client { let text = self.stream.req(&msg).await?; debug!("Sent `{}'; got `{}'.", &msg, &text); - ensure!( - text.starts_with("OK"), - ProtocolSnafu { - op: Operation::Add, - msg: &text - } - ); + ensure!(text.starts_with("OK"), "add not Oked"); Ok(()) } } @@ -1161,10 +984,7 @@ impl TryFrom<&str> for IdleSubSystem { } else if x == "message" { Ok(IdleSubSystem::Message) } else { - Err(IdleSubSystemSnafu { - text: String::from(text), - } - .build()) + bail!("{}", text) } } } @@ -1227,13 +1047,7 @@ impl IdleClient { pub async fn subscribe(&mut self, chan: &str) -> Result<()> { let text = self.conn.req(&format!("subscribe {}", chan)).await?; debug!("Sent subscribe message for {}; got `{}'.", chan, text); - ensure!( - text.starts_with("OK"), - ProtocolSnafu { - op: Operation::Connect, - msg: &text - } - ); + ensure!(text.starts_with("OK"), "subscribe not Ok: `{}`", text); debug!("Subscribed to {}.", chan); Ok(()) } @@ -1255,27 +1069,12 @@ impl IdleClient { // // We remain subscribed, but we need to send a new idle message. - ensure!( - text.starts_with("changed: "), - ProtocolSnafu { - op: Operation::Idle, - msg: &text - } - ); - let idx = text.find('\n').context(ProtocolSnafu { - op: Operation::Idle, - msg: text.to_owned(), - })?; + ensure!(text.starts_with("changed: "), "idle not OK: `{}`", text); + let idx = text.find('\n').context("idle has no newline")?; let result = IdleSubSystem::try_from(&text[9..idx])?; let text = text[idx + 1..].to_string(); - ensure!( - text.starts_with("OK"), - ProtocolSnafu { - op: Operation::Idle, - msg: &text - } - ); + ensure!(text.starts_with("OK"), "idle not OKed"); Ok(result) } @@ -1308,13 +1107,7 @@ impl IdleClient { for line in text.lines() { match state { State::Init => { - ensure!( - line.starts_with("channel: "), - ProtocolSnafu { - op: Operation::GetMessages, - msg: line.to_owned() - } - ); + ensure!(line.starts_with("channel: "), "no `channel: ` given"); chan = String::from(&line[9..]); state = State::Running; } @@ -1339,20 +1132,12 @@ impl IdleClient { } state = State::Finished; } else { - return Err(ProtocolSnafu { - op: Operation::GetMessages, - msg: text, - } - .build()); + bail!("Failed to get messages: `{}`", text) } } State::Finished => { // Should never be here! - return Err(ProtocolSnafu { - op: Operation::GetMessages, - msg: String::from(line), - } - .build()); + bail!("Failed to get messages: `{}`", text) } } } diff --git a/pkgs/by-name/mp/mpdpopm/src/config.rs b/pkgs/by-name/mp/mpdpopm/src/config.rs index 08509e47..e6c01599 100644 --- a/pkgs/by-name/mp/mpdpopm/src/config.rs +++ b/pkgs/by-name/mp/mpdpopm/src/config.rs @@ -40,9 +40,10 @@ //! bundle-up all the errors, report 'em & urge the user to use the most recent version use crate::vars::{LOCALSTATEDIR, PREFIX}; +use anyhow::{Result, bail}; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; +use std::{env, path::PathBuf}; /// [mpdpopm](crate) can communicate with MPD over either a local Unix socket, or over regular TCP #[derive(Debug, Deserialize, PartialEq, Serialize)] @@ -85,15 +86,6 @@ mod test_connection { } } -impl std::default::Default for Connection { - fn default() -> Self { - Connection::TCP { - host: String::from("localhost"), - port: 6600, - } - } -} - /// This is the most recent `mppopmd` configuration struct. #[derive(Deserialize, Debug, Serialize)] #[serde(default)] @@ -139,31 +131,11 @@ impl Default for Config { } } -#[derive(Debug)] -pub enum Error { - /// Failure to parse - ParseFail { err: Box<dyn std::error::Error> }, -} - -impl std::fmt::Display for Error { - #[allow(unreachable_patterns)] // the _ arm is *currently* unreachable - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Error::ParseFail { err } => write!(f, "Parse failure: {}", err), - _ => write!(f, "Unknown configuration error"), - } - } -} - -pub type Result<T> = std::result::Result<T, Error>; - pub fn from_str(text: &str) -> Result<Config> { let cfg: Config = match serde_json::from_str(text) { Ok(cfg) => cfg, Err(err_outer) => { - return Err(Error::ParseFail { - err: Box::new(err_outer), - }); + bail!("Failed to parse config: `{}`", err_outer) } }; Ok(cfg) diff --git a/pkgs/by-name/mp/mpdpopm/src/filters_ast.rs b/pkgs/by-name/mp/mpdpopm/src/filters_ast.rs index 7d30739d..bd1a67d6 100644 --- a/pkgs/by-name/mp/mpdpopm/src/filters_ast.rs +++ b/pkgs/by-name/mp/mpdpopm/src/filters_ast.rs @@ -20,7 +20,7 @@ use crate::clients::Client; use crate::storage::{last_played, play_count, rating_count}; -use backtrace::Backtrace; +use anyhow::{Context, Error, Result, anyhow, bail}; use boolinator::Boolinator; use chrono::prelude::*; use tracing::debug; @@ -197,7 +197,6 @@ pub enum Expression { #[cfg(test)] mod smoke_tests { - use super::*; use crate::filters::*; @@ -315,10 +314,6 @@ mod smoke_tests { } } -//////////////////////////////////////////////////////////////////////////////////////////////////// -// evaluation logic // -//////////////////////////////////////////////////////////////////////////////////////////////////// - #[derive(Copy, Clone, Debug, PartialEq)] pub enum EvalOp { And, @@ -336,92 +331,6 @@ impl std::fmt::Display for EvalOp { } } -#[derive(Debug)] -pub enum Error { - BadISO8601String { - text: Vec<u8>, - back: Backtrace, - }, - ExpectQuoted { - text: String, - back: Backtrace, - }, - FilterTypeErr { - text: String, - back: Backtrace, - }, - InvalidOperand { - op: OpCode, - back: Backtrace, - }, - OperatorOnStack { - op: EvalOp, - back: Backtrace, - }, - RatingOverflow { - rating: usize, - back: Backtrace, - }, - TooManyOperands { - num_ops: usize, - back: Backtrace, - }, - NumericParse { - sticker: String, - source: std::num::ParseIntError, - back: Backtrace, - }, - Client { - source: crate::clients::Error, - back: Backtrace, - }, -} - -impl std::fmt::Display for Error { - #[allow(unreachable_patterns)] // the _ arm is *currently* unreachable - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Error::BadISO8601String { text, back: _ } => { - write!(f, "Bad ISO8601 timestamp: ``{:?}''", text) - } - Error::ExpectQuoted { text, back: _ } => write!(f, "Expected quote: ``{}''", text), - Error::FilterTypeErr { text, back: _ } => { - write!(f, "Un-expected type in filter ``{}''", text) - } - Error::InvalidOperand { op, back: _ } => write!(f, "Invalid operand {}", op), - Error::OperatorOnStack { op, back: _ } => { - write!(f, "Operator {} left on parse stack", op) - } - Error::RatingOverflow { rating, back: _ } => write!(f, "Rating {} overflows", rating), - Error::TooManyOperands { num_ops, back: _ } => { - write!(f, "Too many operands ({})", num_ops) - } - Error::NumericParse { - sticker, - source, - back: _, - } => write!(f, "While parsing sticker {}, got {}", sticker, source), - Error::Client { source, back: _ } => write!(f, "Client error: {}", source), - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match &self { - Error::NumericParse { - sticker: _, - source, - back: _, - } => Some(source), - Error::Client { source, back: _ } => Some(source), - _ => None, - } - } -} - -pub type Result<T> = std::result::Result<T, Error>; - fn peek(buf: &[u8]) -> Option<char> { match buf.len() { 0 => None, @@ -433,10 +342,7 @@ fn peek(buf: &[u8]) -> Option<char> { /// Pop a single byte off of `buf` fn take1(buf: &mut &[u8], i: usize) -> Result<()> { if i > buf.len() { - return Err(Error::BadISO8601String { - text: buf.to_vec(), - back: Backtrace::new(), - }); + bail!("Bad iso-8601 string: `{:#?}`", buf); } let (_first, second) = buf.split_at(i); *buf = second; @@ -447,26 +353,20 @@ fn take1(buf: &mut &[u8], i: usize) -> Result<()> { fn take2<T>(buf: &mut &[u8], i: usize) -> Result<T> where T: FromStr, + <T as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static, { // 1. check len if i > buf.len() { - return Err(Error::BadISO8601String { - text: buf.to_vec(), - back: Backtrace::new(), - }); + bail!("Bad iso-8601 string: `{:#?}`", buf); } + let (first, second) = buf.split_at(i); *buf = second; // 2. convert to a string - let s = std::str::from_utf8(first).map_err(|_| Error::BadISO8601String { - text: buf.to_vec(), - back: Backtrace::new(), - })?; + let s = std::str::from_utf8(first).context("Bad iso-8601 string")?; // 3. parse as a T - s.parse::<T>().map_err(|_err| Error::BadISO8601String { - text: buf.to_vec(), - back: Backtrace::new(), - }) // Parse*Error => Error + s.parse::<T>() + .context("Failed to parse iso-8601 string as T") } /// Parse a timestamp in ISO 8601 format to a chrono DateTime instance @@ -545,18 +445,12 @@ pub fn parse_iso_8601(buf: &mut &[u8]) -> Result<i64> { return Ok(Utc .with_ymd_and_hms(year, month, day, hour, minute, second) .single() - .ok_or(Error::BadISO8601String { - text: buf.to_vec(), - back: Backtrace::new(), - })? + .ok_or(anyhow!("bad iso-8601 string"))? .timestamp()); } else { let next = peek(buf); if next != Some('-') && next != Some('+') { - return Err(Error::BadISO8601String { - text: buf.to_vec(), - back: Backtrace::new(), - }); + bail!("bad iso-8601 string") } let west = next == Some('-'); take1(buf, 1)?; @@ -573,29 +467,17 @@ pub fn parse_iso_8601(buf: &mut &[u8]) -> Result<i64> { if west { return Ok(FixedOffset::west_opt(hours * 3600 + minutes * 60) - .ok_or(Error::BadISO8601String { - text: buf.to_vec(), - back: Backtrace::new(), - })? + .ok_or(anyhow!("Bad iso-8601 string"))? .with_ymd_and_hms(year, month, day, hour, minute, second) .single() - .ok_or(Error::BadISO8601String { - text: buf.to_vec(), - back: Backtrace::new(), - })? + .ok_or(anyhow!("Bad iso-8601 string"))? .timestamp()); } else { return Ok(FixedOffset::east_opt(hours * 3600 + minutes * 60) - .ok_or(Error::BadISO8601String { - text: buf.to_vec(), - back: Backtrace::new(), - })? + .ok_or(anyhow!("Bad iso-8601 string"))? .with_ymd_and_hms(year, month, day, hour, minute, second) .single() - .ok_or(Error::BadISO8601String { - text: buf.to_vec(), - back: Backtrace::new(), - })? + .ok_or(anyhow!("Bad iso-8601 string"))? .timestamp()); } } @@ -604,10 +486,7 @@ pub fn parse_iso_8601(buf: &mut &[u8]) -> Result<i64> { Ok(Local .with_ymd_and_hms(year, month, day, hour, minute, second) .single() - .ok_or(Error::BadISO8601String { - text: buf.to_vec(), - back: Backtrace::new(), - })? + .ok_or(anyhow!("Bad iso-8601 string"))? .timestamp()) } @@ -658,10 +537,7 @@ pub fn expect_quoted(qtext: &str) -> Result<String> { } if quote != Some('\'') && quote != Some('"') { - return Err(Error::ExpectQuoted { - text: String::from(qtext), - back: Backtrace::new(), - }); + bail!("Expected text to be quoted: `{}`", qtext); } let mut ret = String::new(); @@ -676,10 +552,7 @@ pub fn expect_quoted(qtext: &str) -> Result<String> { match this { Some(c) => ret.push(c), None => { - return Err(Error::ExpectQuoted { - text: String::from(qtext), - back: Backtrace::new(), - }); + bail!("Expected text to be quoted: `{}`", qtext); } } this = iter.next(); @@ -727,10 +600,7 @@ fn make_numeric_closure<'a, T: 'a + PartialEq + PartialOrd + Copy>( OpCode::LessThan => Ok(Box::new(move |x: T| x < val) as Box<dyn Fn(T) -> bool>), OpCode::GreaterThanEqual => Ok(Box::new(move |x: T| x >= val) as Box<dyn Fn(T) -> bool>), OpCode::LessThanEqual => Ok(Box::new(move |x: T| x <= val) as Box<dyn Fn(T) -> bool>), - _ => Err(Error::InvalidOperand { - op, - back: Backtrace::new(), - }), + _ => bail!("Invalid operant: `{op}`"), } } @@ -767,18 +637,11 @@ async fn eval_numeric_sticker_term< let mut m = client .get_stickers(sticker) .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })? + .context("Failed to get stickers from client")? .drain() .map(|(k, v)| v.parse::<T>().map(|x| (k, x))) .collect::<std::result::Result<HashMap<String, T>, _>>() - .map_err(|err| Error::NumericParse { - sticker: String::from(sticker), - source: err, - back: Backtrace::new(), - })?; + .context("Failed to parse sticker as T")?; // `m' is now a map of song URI to rating/playcount/wathever (expressed as a T)... for all songs // that have the salient sticker. // @@ -787,10 +650,7 @@ async fn eval_numeric_sticker_term< client .get_all_songs() .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })? + .context("Failed to get all songs from client")? .drain(..) .for_each(|song| { m.entry(song).or_insert(default_val); @@ -846,10 +706,7 @@ async fn eval_term<'a>( Term::UnaryCondition(op, val) => Ok(client .find1(&format!("{}", op), "e_value(val), case) .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })? + .context("Failed to find1 on client")? .drain(..) .collect()), Term::BinaryCondition(attr, op, val) => { @@ -857,20 +714,14 @@ async fn eval_term<'a>( match val { Value::Uint(n) => { if *n > 255 { - return Err(Error::RatingOverflow { - rating: *n, - back: Backtrace::new(), - }); + bail!("Rating of `{}` is greater than allowed!", n) } Ok( eval_numeric_sticker_term(stickers.rating, client, *op, *n as u8, 0) .await?, ) } - _ => Err(Error::FilterTypeErr { - text: format!("filter ratings expect an unsigned int; got {:#?}", val), - back: Backtrace::new(), - }), + _ => bail!("filter ratings expect an unsigned int; got {:#?}", val), } } else if *attr == Selector::PlayCount { match val { @@ -880,10 +731,7 @@ async fn eval_term<'a>( .await?, ) } - _ => Err(Error::FilterTypeErr { - text: format!("filter ratings expect an unsigned int; got {:#?}", val), - back: Backtrace::new(), - }), + _ => bail!("filter ratings expect an unsigned int; got {:#?}", val), } } else if *attr == Selector::LastPlayed { match val { @@ -893,10 +741,7 @@ async fn eval_term<'a>( .await?, ) } - _ => Err(Error::FilterTypeErr { - text: format!("filter ratings expect an unsigned int; got {:#?}", val), - back: Backtrace::new(), - }), + _ => bail!("filter ratings expect an unsigned int; got {:#?}", val), } } else { Ok(client @@ -907,10 +752,7 @@ async fn eval_term<'a>( case, ) .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })? + .context("Failed to `find2` on client")? .drain(..) .collect()) } @@ -932,10 +774,7 @@ async fn negate_result( Ok(client .get_all_songs() .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })? + .context("Failed to get all songs from client")? .drain(..) .filter_map(|song| { // Some(thing) adds thing, None elides it @@ -1117,10 +956,7 @@ pub async fn evaluate<'a>( se.iter() .enumerate() .for_each(|(i, x)| debug!(" {}: {:#?}", i, x)); - return Err(Error::TooManyOperands { - num_ops: se.len(), - back: Backtrace::new(), - }); + bail!("The number of operants is too big `{}`", se.len()); } let ret = se.pop().unwrap(); @@ -1128,10 +964,7 @@ pub async fn evaluate<'a>( EvalStackNode::Result(result) => Ok(result), EvalStackNode::Op(op) => { debug!("Operator left on stack (!?): {:#?}", op); - Err(Error::OperatorOnStack { - op, - back: Backtrace::new(), - }) + bail!("Operator left on stack: {op}") } } } diff --git a/pkgs/by-name/mp/mpdpopm/src/lib.rs b/pkgs/by-name/mp/mpdpopm/src/lib.rs index e4579db2..4fe523ea 100644 --- a/pkgs/by-name/mp/mpdpopm/src/lib.rs +++ b/pkgs/by-name/mp/mpdpopm/src/lib.rs @@ -49,14 +49,15 @@ pub mod filters { include!(concat!(env!("OUT_DIR"), "/src/filters.rs")); } -use clients::{Client, IdleClient, IdleSubSystem}; -use config::Config; -use config::Connection; -use filters_ast::FilterStickerNames; -use messages::MessageProcessor; -use playcounts::PlayState; +use crate::{ + clients::{Client, IdleClient, IdleSubSystem}, + config::{Config, Connection}, + filters_ast::FilterStickerNames, + messages::MessageProcessor, + playcounts::PlayState, +}; -use backtrace::Backtrace; +use anyhow::{Context, Error}; use futures::{future::FutureExt, pin_mut, select}; use tokio::{ signal, @@ -65,100 +66,39 @@ use tokio::{ }; use tracing::{debug, error, info}; -use std::path::PathBuf; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -#[derive(Debug)] -#[non_exhaustive] -pub enum Error { - BadPath { - pth: PathBuf, - }, - Client { - source: crate::clients::Error, - back: Backtrace, - }, - Playcounts { - source: crate::playcounts::Error, - back: Backtrace, - }, -} - -impl std::fmt::Display for Error { - #[allow(unreachable_patterns)] // the _ arm is *currently* unreachable - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Error::BadPath { pth } => write!(f, "Bad path: {:?}", pth), - Error::Client { source, back: _ } => write!(f, "Client error: {}", source), - Error::Playcounts { source, back: _ } => write!(f, "Playcount error: {}", source), - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match &self { - Error::Client { source, back: _ } => Some(source), - _ => None, - } - } -} - -pub type Result<T> = std::result::Result<T, Error>; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - /// Core `mppopmd' logic pub async fn mpdpopm(cfg: Config) -> std::result::Result<(), Error> { info!("mpdpopm {} beginning.", vars::VERSION); let filter_stickers = FilterStickerNames::new(); - let mut client = match cfg.conn { - Connection::Local { ref path } => { - Client::open(path).await.map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })? - } - Connection::TCP { ref host, port } => Client::connect(format!("{}:{}", host, port)) - .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })?, - }; + let mut client = + match cfg.conn { + Connection::Local { ref path } => Client::open(path) + .await + .with_context(|| format!("Failed to open socket at `{}`", path.display()))?, + Connection::TCP { ref host, port } => Client::connect(format!("{}:{}", host, port)) + .await + .with_context(|| format!("Failed to connect to client at `{}:{}`", host, port))?, + }; let mut state = PlayState::new(&mut client, cfg.played_thresh) .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })?; + .context("Failed to construct PlayState")?; let mut idle_client = match cfg.conn { - Connection::Local { ref path } => { - IdleClient::open(path).await.map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })? - } + Connection::Local { ref path } => IdleClient::open(path) + .await + .context("Failed to open idle client")?, Connection::TCP { ref host, port } => IdleClient::connect(format!("{}:{}", host, port)) .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })?, + .context("Failed to connect to TCP idle client")?, }; idle_client .subscribe(&cfg.commands_chan) .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })?; + .context("Failed to subscribe to idle_client")?; let mut hup = signal(SignalKind::hangup()).unwrap(); let mut kill = signal(SignalKind::terminate()).unwrap(); @@ -200,10 +140,7 @@ pub async fn mpdpopm(cfg: Config) -> std::result::Result<(), Error> { tick.set(sleep(Duration::from_millis(cfg.poll_interval_ms)).fuse()); state.update(&mut client) .await - .map_err(|err| Error::Playcounts { - source: err, - back: Backtrace::new() - })? + .context("PlayState update failed")? }, // next = cmds.next() => match next { // Some(out) => { @@ -229,10 +166,7 @@ pub async fn mpdpopm(cfg: Config) -> std::result::Result<(), Error> { if subsys == IdleSubSystem::Player { state.update(&mut client) .await - .map_err(|err| Error::Playcounts { - source: err, - back: Backtrace::new() - })? + .context("PlayState update failed")? } else if subsys == IdleSubSystem::Message { msg_check_needed = true; } diff --git a/pkgs/by-name/mp/mpdpopm/src/messages.rs b/pkgs/by-name/mp/mpdpopm/src/messages.rs index c7c295c8..171a246a 100644 --- a/pkgs/by-name/mp/mpdpopm/src/messages.rs +++ b/pkgs/by-name/mp/mpdpopm/src/messages.rs @@ -54,110 +54,11 @@ use crate::{ filters_ast::{FilterStickerNames, evaluate}, }; -use backtrace::Backtrace; +use anyhow::{Context, Error, Result, anyhow, bail}; use boolinator::Boolinator; use tracing::debug; use std::collections::VecDeque; -use std::path::PathBuf; - -#[derive(Debug)] -pub enum Error { - BadPath { - pth: PathBuf, - }, - FilterParseError { - msg: String, - }, - InvalidChar { - c: u8, - }, - NoClosingQuotes, - NoCommand, - NotImplemented { - feature: String, - }, - PlayerStopped, - TrailingBackslash, - UnknownChannel { - chan: String, - back: Backtrace, - }, - UnknownCommand { - name: String, - back: Backtrace, - }, - Client { - source: crate::clients::Error, - back: Backtrace, - }, - Ratings { - source: crate::storage::Error, - back: Backtrace, - }, - Playcount { - source: crate::storage::Error, - back: Backtrace, - }, - Filter { - source: crate::filters_ast::Error, - back: Backtrace, - }, - Utf8 { - source: std::str::Utf8Error, - buf: Vec<u8>, - back: Backtrace, - }, - ExpectedInt { - source: std::num::ParseIntError, - text: String, - back: Backtrace, - }, -} - -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Error::BadPath { pth } => write!(f, "Bad path: {:?}", pth), - Error::FilterParseError { msg } => write!(f, "Parse error: ``{}''", msg), - Error::InvalidChar { c } => write!(f, "Invalid unquoted character {}", c), - Error::NoClosingQuotes => write!(f, "Missing closing quotes"), - Error::NoCommand => write!(f, "No command specified"), - Error::NotImplemented { feature } => write!(f, "`{}' not implemented, yet", feature), - Error::PlayerStopped => write!( - f, - "Can't operate on the current track when the player is stopped" - ), - Error::TrailingBackslash => write!(f, "Trailing backslash"), - Error::UnknownChannel { chan, back: _ } => write!( - f, - "We received messages for an unknown channel `{}'; this is likely a bug; please consider filing a report to sp1ff@pobox.com", - chan - ), - Error::UnknownCommand { name, back: _ } => { - write!(f, "We received an unknown message ``{}''", name) - } - Error::Client { source, back: _ } => write!(f, "Client error: {}", source), - Error::Ratings { source, back: _ } => write!(f, "Ratings eror: {}", source), - Error::Playcount { source, back: _ } => write!(f, "Playcount error: {}", source), - Error::Filter { source, back: _ } => write!(f, "Filter error: {}", source), - Error::Utf8 { - source, - buf, - back: _, - } => write!(f, "UTF8 error {} ({:#?})", source, buf), - Error::ExpectedInt { - source, - text, - back: _, - } => write!(f, "``{}''L {}", source, text), - } - } -} - -pub type Result<T> = std::result::Result<T, Error>; - -//////////////////////////////////////////////////////////////////////////////////////////////////// /// Break `buf` up into individual tokens while removing MPD-style quoting. /// @@ -243,14 +144,14 @@ impl<'a> Iterator for TokenIterator<'a> { if '\\' == self.slice[inp] as char { inp += 1; if inp == nslice { - return Some(Err(Error::TrailingBackslash)); + return Some(Err(anyhow!("Trailing backslash"))); } } self.slice[out] = self.slice[inp]; out += 1; inp += 1; if inp == nslice { - return Some(Err(Error::NoClosingQuotes)); + return Some(Err(anyhow!("No closing quote"))); } } // The next token is in self.slice[self.input..out] and self.slice[inp] is " @@ -273,7 +174,7 @@ impl<'a> Iterator for TokenIterator<'a> { break; } if self.slice[i] as char == '"' || self.slice[i] as char == '\'' { - return Some(Err(Error::InvalidChar { c: self.slice[i] })); + return Some(Err(anyhow!("Invalid char: `{}`", self.slice[i]))); } i += 1; } @@ -316,17 +217,11 @@ impl MessageProcessor { let m = idle_client .get_messages() .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })?; + .context("Failed to `get_messages` from client")?; for (chan, msgs) in m { // Only supporting a single channel, ATM - (chan == command_chan).ok_or_else(|| Error::UnknownChannel { - chan, - back: Backtrace::new(), - })?; + (chan == command_chan).ok_or_else(|| anyhow!("Unknown chanell: `{}`", chan))?; for msg in msgs { self.process(msg, client, &state, stickers).await?; } @@ -365,11 +260,8 @@ impl MessageProcessor { let mut buf = msg.into_bytes(); let args: VecDeque<&str> = tokenize(&mut buf) .map(|r| match r { - Ok(buf) => Ok(std::str::from_utf8(buf).map_err(|err| Error::Utf8 { - source: err, - buf: buf.to_vec(), - back: Backtrace::new(), - })?), + Ok(buf) => Ok(std::str::from_utf8(buf) + .context("Failed to interpete `findadd` string as utf8")?), Err(err) => Err(err), }) .collect::<Result<VecDeque<&str>>>()?; @@ -385,9 +277,7 @@ impl MessageProcessor { let ast = match ExpressionParser::new().parse(args[0]) { Ok(ast) => ast, Err(err) => { - return Err(Error::FilterParseError { - msg: format!("{}", err), - }); + bail!("Failed to parse filter: `{}`", err) } }; @@ -396,22 +286,16 @@ impl MessageProcessor { let mut results = Vec::new(); for song in evaluate(&ast, true, client, stickers) .await - .map_err(|err| Error::Filter { - source: err, - back: Backtrace::new(), - })? + .context("Failed to evaluate filter")? { results.push(client.add(&song).await); } match results .into_iter() - .collect::<std::result::Result<Vec<()>, crate::clients::Error>>() + .collect::<std::result::Result<Vec<()>, Error>>() { Ok(_) => Ok(()), - Err(err) => Err(Error::Client { - source: err, - back: Backtrace::new(), - }), + Err(err) => Err(err), } } @@ -427,14 +311,11 @@ impl MessageProcessor { let mut buf = msg.into_bytes(); let args: VecDeque<&str> = tokenize(&mut buf) .map(|r| match r { - Ok(buf) => Ok(std::str::from_utf8(buf).map_err(|err| Error::Utf8 { - source: err, - buf: buf.to_vec(), - back: Backtrace::new(), - })?), + Ok(buf) => Ok(std::str::from_utf8(buf) + .context("Failed to interpete `searchadd` string as utf8")?), Err(err) => Err(err), }) - .collect::<Result<VecDeque<&str>>>()?; + .collect::<Result<VecDeque<_>>>()?; debug!("searchadd arguments: {:#?}", args); @@ -447,9 +328,7 @@ impl MessageProcessor { let ast = match ExpressionParser::new().parse(args[0]) { Ok(ast) => ast, Err(err) => { - return Err(Error::FilterParseError { - msg: format!("{}", err), - }); + bail!("Failed to parse filter: `{err}`") } }; @@ -458,22 +337,16 @@ impl MessageProcessor { let mut results = Vec::new(); for song in evaluate(&ast, false, client, stickers) .await - .map_err(|err| Error::Filter { - source: err, - back: Backtrace::new(), - })? + .context("Failed to evaluate ast")? { results.push(client.add(&song).await); } match results .into_iter() - .collect::<std::result::Result<Vec<()>, crate::clients::Error>>() + .collect::<std::result::Result<Vec<()>, Error>>() { Ok(_) => Ok(()), - Err(err) => Err(Error::Client { - source: err, - back: Backtrace::new(), - }), + Err(err) => Err(err), } } } diff --git a/pkgs/by-name/mp/mpdpopm/src/playcounts.rs b/pkgs/by-name/mp/mpdpopm/src/playcounts.rs index 6ae8f903..7d646b4c 100644 --- a/pkgs/by-name/mp/mpdpopm/src/playcounts.rs +++ b/pkgs/by-name/mp/mpdpopm/src/playcounts.rs @@ -26,67 +26,13 @@ //! use crate::clients::{Client, PlayerStatus}; -use crate::storage::{self, last_played, play_count, skipped}; +use crate::storage::{last_played, play_count, skipped}; -use backtrace::Backtrace; +use anyhow::{Context, Error, Result, anyhow}; use tracing::{debug, info}; -use std::path::PathBuf; use std::time::SystemTime; -#[derive(Debug)] -pub enum Error { - PlayerStopped, - BadPath { - pth: PathBuf, - }, - SystemTime { - source: std::time::SystemTimeError, - back: Backtrace, - }, - Client { - source: crate::clients::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), - } - } -} - -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, - } - } -} - -impl From<storage::Error> for Error { - fn from(value: storage::Error) -> Self { - match value { - storage::Error::PlayerStopped => Self::PlayerStopped, - storage::Error::BadPath { pth } => Self::BadPath { pth }, - storage::Error::SystemTime { source, back } => Self::SystemTime { source, back }, - storage::Error::Client { source, back } => Self::Client { source, back }, - _ => unreachable!(), - } - } -} - -type Result<T> = std::result::Result<T, Error>; - /// Current server state in terms of the play status (stopped/paused/playing, current track, elapsed /// time in current track, &c) #[derive(Debug)] @@ -109,7 +55,7 @@ impl PlayState { pub async fn new( client: &mut Client, played_thresh: f64, - ) -> std::result::Result<PlayState, crate::clients::Error> { + ) -> std::result::Result<PlayState, Error> { Ok(PlayState { last_server_stat: client.status().await?, have_incr_play_count: false, @@ -126,10 +72,10 @@ impl PlayState { /// Poll the server-- update our status; maybe increment the current track's play count; the /// caller must arrange to have this method invoked periodically to keep our state fresh pub async fn update(&mut self, client: &mut Client) -> Result<()> { - let new_stat = client.status().await.map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })?; + let new_stat = client + .status() + .await + .context("Failed to get client status")?; match (&self.last_server_stat, &new_stat) { (PlayerStatus::Play(last), PlayerStatus::Play(curr)) @@ -176,8 +122,8 @@ impl PlayState { curr.elapsed / curr.duration ); - let file = curr.file.to_str().ok_or_else(|| Error::BadPath { - pth: curr.file.clone(), + let file = curr.file.to_str().ok_or_else(|| { + anyhow!("Failed to parse path as utf8: `{}`", curr.file.display()) })?; let curr_pc = play_count::get(client, file).await?.unwrap_or_default(); @@ -189,10 +135,7 @@ impl PlayState { file, SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) - .map_err(|err| Error::SystemTime { - source: err, - back: Backtrace::new(), - })? + .context("Failed to get system time")? .as_secs(), ) .await?; @@ -213,8 +156,8 @@ impl PlayState { last.elapsed / last.duration ); - let file = last.file.to_str().ok_or_else(|| Error::BadPath { - pth: last.file.clone(), + let file = last.file.to_str().ok_or_else(|| { + anyhow!("Failed to parse path as utf8: `{}`", last.file.display()) })?; let skip_count = skipped::get(client, file).await?.unwrap_or_default(); @@ -350,7 +293,10 @@ OK ), "OK\n", ), - ("sticker set song \"E/Enya - Wild Child.mp3\" unwoundstack.com:playcount 12", "OK\n"), + ( + "sticker set song \"E/Enya - Wild Child.mp3\" unwoundstack.com:playcount 12", + "OK\n", + ), ])); let mut cli = Client::new(mock).unwrap(); diff --git a/pkgs/by-name/mp/mpdpopm/src/storage/mod.rs b/pkgs/by-name/mp/mpdpopm/src/storage/mod.rs index d64f17c1..24d8dcb5 100644 --- a/pkgs/by-name/mp/mpdpopm/src/storage/mod.rs +++ b/pkgs/by-name/mp/mpdpopm/src/storage/mod.rs @@ -1,54 +1,11 @@ -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, - }, -} - -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), - } - } -} - -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<T> = std::result::Result<T, Error>; +use anyhow::{Error, Result}; pub mod play_count { - use backtrace::Backtrace; + use anyhow::Context; use crate::clients::Client; - use super::{Error, Result}; + use super::Result; pub const STICKER: &str = "unwoundstack.com:playcount"; @@ -57,10 +14,8 @@ pub mod play_count { match client .get_sticker::<usize>(file, STICKER) .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })? { + .context("Failed to get sticker from client")? + { Some(n) => Ok(Some(n)), None => Ok(None), } @@ -71,10 +26,7 @@ pub mod play_count { client .set_sticker(file, STICKER, &format!("{}", play_count)) .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })?; + .context("Failed to set_sticker on client")?; Ok(()) } @@ -109,11 +61,11 @@ pub mod play_count { } pub mod skipped { - use backtrace::Backtrace; + use anyhow::Context; use crate::clients::Client; - use super::{Error, Result}; + use super::Result; const STICKER: &str = "unwoundstack.com:skipped_count"; @@ -122,10 +74,8 @@ pub mod skipped { match client .get_sticker::<usize>(file, STICKER) .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })? { + .context("Failed to get_sticker on client")? + { Some(n) => Ok(Some(n)), None => Ok(None), } @@ -136,19 +86,16 @@ pub mod skipped { client .set_sticker(file, STICKER, &format!("{}", skip_count)) .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - }) + .context("Failed to set_sticker on client") } } pub mod last_played { - use backtrace::Backtrace; + use anyhow::Context; use crate::clients::Client; - use super::{Error, Result}; + use super::Result; pub const STICKER: &str = "unwoundstack.com:lastplayed"; @@ -157,10 +104,7 @@ pub mod last_played { client .get_sticker::<u64>(file, STICKER) .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - }) + .context("Falied to get_sticker on client") } /// Set the last played for a track @@ -168,20 +112,17 @@ pub mod last_played { client .set_sticker(file, STICKER, &format!("{}", last_played)) .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })?; + .context("Failed to set_sticker on client")?; Ok(()) } } pub mod rating_count { - use backtrace::Backtrace; + use anyhow::Context; use crate::clients::Client; - use super::{Error, Result}; + use super::Result; pub const STICKER: &str = "unwoundstack.com:ratings_count"; @@ -190,10 +131,7 @@ pub mod rating_count { client .get_sticker::<i8>(file, STICKER) .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - }) + .context("Failed to get_sticker on client") } /// Set the rating count for a track @@ -201,10 +139,7 @@ pub mod rating_count { client .set_sticker(file, STICKER, &format!("{}", rating_count)) .await - .map_err(|err| Error::Client { - source: err, - back: Backtrace::new(), - })?; + .context("Failed to set_sticker on client")?; Ok(()) } } diff --git a/pkgs/by-name/mp/mpdpopm/src/vars.rs b/pkgs/by-name/mp/mpdpopm/src/vars.rs index 29b9610d..7cacec66 100644 --- a/pkgs/by-name/mp/mpdpopm/src/vars.rs +++ b/pkgs/by-name/mp/mpdpopm/src/vars.rs @@ -2,4 +2,3 @@ pub static VERSION: &str = env!("CARGO_PKG_VERSION"); pub static AUTHOR: &str = env!("CARGO_PKG_AUTHORS"); pub static LOCALSTATEDIR: &str = "/home/soispha/.local/state"; pub static PREFIX: &str = "/home/soispha/.local/share/mpdpopm"; - |
