about summary refs log tree commit diff stats
path: root/pkgs/by-name/mp/mpdpopm/src/storage/mod.rs
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2026-01-24 23:51:04 +0100
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2026-01-24 23:51:19 +0100
commit38c6f95c94830ed8eb6c6678e303480257cf3cf5 (patch)
treee5bd55a6306c5f787ff49d528810c62b5c5dbb32 /pkgs/by-name/mp/mpdpopm/src/storage/mod.rs
parentmodule/mpd: Set-up a sticker file (diff)
downloadnixos-config-38c6f95c94830ed8eb6c6678e303480257cf3cf5.zip
pkgs/mpdpopm: Init
This is based on https://github.com/sp1ff/mpdpopm at
commit 178df8ad3a5c39281cfd8b3cec05394f4c9256fd.
Diffstat (limited to 'pkgs/by-name/mp/mpdpopm/src/storage/mod.rs')
-rw-r--r--pkgs/by-name/mp/mpdpopm/src/storage/mod.rs212
1 files changed, 212 insertions, 0 deletions
diff --git a/pkgs/by-name/mp/mpdpopm/src/storage/mod.rs b/pkgs/by-name/mp/mpdpopm/src/storage/mod.rs
new file mode 100644
index 00000000..29cfe144
--- /dev/null
+++ b/pkgs/by-name/mp/mpdpopm/src/storage/mod.rs
@@ -0,0 +1,212 @@
+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<T> = std::result::Result<T, Error>;
+
+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<Option<usize>> {
+        match client
+            .get_sticker::<usize>(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<Option<usize>> {
+        match client
+            .get_sticker::<usize>(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<Option<u64>> {
+        client
+            .get_sticker::<u64>(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<Option<u8>> {
+        client
+            .get_sticker::<u8>(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(())
+    }
+}