diff --git a/src/models/data_storage.rs b/src/models/data_storage.rs new file mode 100644 index 00000000..9e7d2e76 --- /dev/null +++ b/src/models/data_storage.rs @@ -0,0 +1,41 @@ +use std::fs; + +use directories::BaseDirs; +use serde::Deserialize; +use serde::Serialize; + +use crate::utils::data_file::DataFile; + +/// Storage for data other than configurations +#[derive(Serialize, Deserialize)] +pub struct DataStorage { + playlist_count: u64, +} + +impl DataStorage { + /// Increment the playlist count, returning the old count for usage + pub fn incr_playlist_count(&mut self) -> u64 { + let count = self.playlist_count; + self.playlist_count += 1; + count + } +} + +impl Default for DataStorage { + fn default() -> Self { + Self { playlist_count: 1 } + } +} + +impl DataFile for DataStorage { + fn path() -> std::path::PathBuf { + let mut path = BaseDirs::new() + .expect("Couldn't find standard directory. Is your system an oddball one?") + .data_local_dir() + .to_path_buf(); + path.push("alistral"); + fs::create_dir_all(&path).expect("Couldn't create config directory"); + path.push("data_storage.txt"); + path + } +} diff --git a/src/models/mod.rs b/src/models/mod.rs index 0406fb8f..7eaff3ed 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -2,5 +2,6 @@ pub mod cli; pub mod clippy; pub mod config; pub mod data; +pub mod data_storage; pub mod error; pub mod playlist_stub; diff --git a/src/tools/radio/circles.rs b/src/tools/radio/circles.rs index 971e0ea3..43750416 100644 --- a/src/tools/radio/circles.rs +++ b/src/tools/radio/circles.rs @@ -11,8 +11,10 @@ use rand::thread_rng; use crate::database::get_db_client; use crate::datastructures::radio::collector::RadioCollector; use crate::datastructures::radio::seeders::listens::ListenSeeder; +use crate::models::data_storage::DataStorage; use crate::models::playlist_stub::PlaylistStub; use crate::utils::cli::display::ArtistExt; +use crate::utils::data_file::DataFile as _; use crate::utils::println_cli; pub async fn create_radio_mix( @@ -45,8 +47,12 @@ pub async fn create_radio_mix( .await .expect("Error while generating the playlist"); + let counter = DataStorage::load().expect("Couldn't load data storage"); PlaylistStub::new( - "Radio: Circles".to_string(), + format!( + "Radio: Circles #{}", + counter.write().unwrap().incr_playlist_count() + ), Some(username.to_string()), false, collected.into_iter().map(|r| r.mbid).collect(), diff --git a/src/tools/radio/listen_rate.rs b/src/tools/radio/listen_rate.rs index a77d280e..5d26e856 100644 --- a/src/tools/radio/listen_rate.rs +++ b/src/tools/radio/listen_rate.rs @@ -8,7 +8,9 @@ use crate::datastructures::radio::filters::min_listens::min_listen_filter; use crate::datastructures::radio::filters::timeouts::timeout_filter; use crate::datastructures::radio::seeders::listens::ListenSeeder; use crate::datastructures::radio::sorters::listen_rate::listen_rate_sorter; +use crate::models::data_storage::DataStorage; use crate::models::playlist_stub::PlaylistStub; +use crate::utils::data_file::DataFile as _; use crate::utils::println_cli; pub async fn listen_rate_radio( @@ -42,8 +44,12 @@ pub async fn listen_rate_radio( .await; println_cli("[Sending] Sending radio playlist to listenbrainz"); + let counter = DataStorage::load().expect("Couldn't load data storage"); PlaylistStub::new( - "Radio: Listen Rate".to_string(), + format!( + "Radio: Listen Rate #{}", + counter.write().unwrap().incr_playlist_count() + ), Some(username.to_string()), true, collected.into_iter().map(|r| r.mbid).collect(), diff --git a/src/tools/radio/overdue.rs b/src/tools/radio/overdue.rs index d216973d..9331a302 100644 --- a/src/tools/radio/overdue.rs +++ b/src/tools/radio/overdue.rs @@ -12,7 +12,9 @@ use crate::datastructures::radio::seeders::listens::ListenSeeder; use crate::datastructures::radio::sorters::overdue::overdue_factor_sorter; use crate::datastructures::radio::sorters::overdue::overdue_factor_sorter_cumulative; use crate::datastructures::radio::sorters::overdue::overdue_sorter; +use crate::models::data_storage::DataStorage; use crate::models::playlist_stub::PlaylistStub; +use crate::utils::data_file::DataFile; use crate::utils::println_cli; //TODO: Refactor Radios params into structs @@ -61,8 +63,12 @@ pub async fn overdue_radio( .await; println_cli("[Sending] Sending radio playlist to listenbrainz"); + let counter = DataStorage::load().expect("Couldn't load data storage"); PlaylistStub::new( - "Radio: Overdue listens".to_string(), + format!( + "Radio: Overdue listens #{}", + counter.write().unwrap().incr_playlist_count() + ), Some(username.to_string()), true, collected.into_iter().map(|r| r.mbid).collect(), diff --git a/src/tools/radio/underrated.rs b/src/tools/radio/underrated.rs index 2d3694f4..bb451302 100644 --- a/src/tools/radio/underrated.rs +++ b/src/tools/radio/underrated.rs @@ -9,7 +9,9 @@ use crate::datastructures::entity_with_listens::recording_with_listens::Recordin use crate::datastructures::radio::collector::RadioCollector; use crate::datastructures::radio::seeders::listens::ListenSeeder; use crate::datastructures::radio::sorters::underrated::underrated_sorter; +use crate::models::data_storage::DataStorage; use crate::models::playlist_stub::PlaylistStub; +use crate::utils::data_file::DataFile as _; use crate::utils::println_cli; pub async fn underrated_mix( @@ -57,8 +59,9 @@ pub async fn underrated_mix( .await; println_cli("[Sending] Sending radio playlist to listenbrainz"); + let counter = DataStorage::load().expect("Couldn't load data storage"); PlaylistStub::new( - "Radio: Underrated recordings".to_string(), + format!("Radio: Underrated recordings #{}", counter.write().unwrap().incr_playlist_count()), Some(username.to_string()), true, collected diff --git a/src/utils/data_file/file_guard.rs b/src/utils/data_file/file_guard.rs new file mode 100644 index 00000000..99ac7526 --- /dev/null +++ b/src/utils/data_file/file_guard.rs @@ -0,0 +1,64 @@ +use core::ops::Deref; +use core::ops::DerefMut; +use std::sync::RwLock; +use std::sync::RwLockReadGuard; +use std::sync::RwLockWriteGuard; +use std::thread::panicking; + +use super::DataFile; + +pub struct FileGuard(RwLock); + +impl FileGuard { + pub fn new(config: T) -> Self { + Self(RwLock::new(config)) + } + + pub fn read( + &self, + ) -> Result, std::sync::PoisonError>> + { + self.0.read() + } + + pub fn read_or_panic(&self) -> RwLockReadGuard<'_, T> { + self.read().expect("Lock poisoned") + } + + pub fn write( + &self, + ) -> Result, std::sync::PoisonError>> + { + self.0.write().map(|guard| FileWriteGuard(guard)) + } + + pub fn write_or_panic(&self) -> FileWriteGuard<'_, T> { + self.write().expect("Lock poisoned") + } +} + +pub struct FileWriteGuard<'l, T: DataFile>(RwLockWriteGuard<'l, T>); + +impl<'l, T: DataFile> Deref for FileWriteGuard<'l, T> { + type Target = RwLockWriteGuard<'l, T>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for FileWriteGuard<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Drop for FileWriteGuard<'_, T> { + fn drop(&mut self) { + if panicking() { + return; + } + + self.0.save().unwrap(); + } +} diff --git a/src/utils/data_file/mod.rs b/src/utils/data_file/mod.rs new file mode 100644 index 00000000..d47dcd83 --- /dev/null +++ b/src/utils/data_file/mod.rs @@ -0,0 +1,39 @@ +use std::fs::File; +use std::io; +use std::path::PathBuf; + +use file_guard::FileGuard; +use serde::de::DeserializeOwned; +use serde::Serialize; + +pub mod file_guard; + +pub trait DataFile: Serialize + DeserializeOwned + Default { + fn path() -> PathBuf; + + fn load_unguarded() -> Result { + match File::open(Self::path().as_path()) { + Ok(data) => { + serde_json::from_reader(data).map_err(crate::Error::ConfigLoadDeserializationError) + } + Err(err) => { + if err.kind() == io::ErrorKind::NotFound { + Ok(Self::default()) + } else { + Err(crate::Error::ConfigLoadError(err)) + } + } + } + } + + fn load() -> Result, crate::Error> { + Ok(FileGuard::new(Self::load_unguarded()?)) + } + + fn save(&self) -> Result<(), crate::Error> { + let file: File = + File::create(Self::path().as_path()).map_err(crate::Error::ConfigFileCreationError)?; + serde_json::to_writer_pretty(file, self).map_err(crate::Error::ConfigFileWriteError)?; + Ok(()) + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index a98621c2..a518605c 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,4 @@ +pub mod data_file; pub mod streams; use std::fmt::Display; use std::sync::{Arc, Mutex}; @@ -19,7 +20,6 @@ pub mod listenbrainz_api; pub mod logger; pub mod radio; pub mod regex; -pub mod tokio; pub mod traits; pub mod whitelist_blacklist; diff --git a/src/utils/tokio.rs b/src/utils/tokio.rs deleted file mode 100644 index 8b137891..00000000 --- a/src/utils/tokio.rs +++ /dev/null @@ -1 +0,0 @@ -