about summary refs log tree commit diff stats
path: root/pkgs/by-name/mp/mpdpopm/src/clients.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--pkgs/by-name/mp/mpdpopm/src/clients.rs309
1 files changed, 47 insertions, 262 deletions
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)
                 }
             }
         }