Skip to content

Commit

Permalink
feat(data_storage): implement DataStorage for playlist count management
Browse files Browse the repository at this point in the history
  • Loading branch information
RustyNova016 committed Jan 2, 2025
1 parent f984d38 commit a0b8a0c
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 6 deletions.
41 changes: 41 additions & 0 deletions src/models/data_storage.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
1 change: 1 addition & 0 deletions src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
8 changes: 7 additions & 1 deletion src/tools/radio/circles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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(),
Expand Down
8 changes: 7 additions & 1 deletion src/tools/radio/listen_rate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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(),
Expand Down
8 changes: 7 additions & 1 deletion src/tools/radio/overdue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(),
Expand Down
5 changes: 4 additions & 1 deletion src/tools/radio/underrated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down
64 changes: 64 additions & 0 deletions src/utils/data_file/file_guard.rs
Original file line number Diff line number Diff line change
@@ -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<T: DataFile>(RwLock<T>);

impl<T: DataFile> FileGuard<T> {
pub fn new(config: T) -> Self {
Self(RwLock::new(config))
}

pub fn read(
&self,
) -> Result<RwLockReadGuard<'_, T>, std::sync::PoisonError<std::sync::RwLockReadGuard<'_, T>>>
{
self.0.read()
}

pub fn read_or_panic(&self) -> RwLockReadGuard<'_, T> {
self.read().expect("Lock poisoned")
}

pub fn write(
&self,
) -> Result<FileWriteGuard<'_, T>, std::sync::PoisonError<std::sync::RwLockWriteGuard<'_, T>>>
{
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<T: DataFile> DerefMut for FileWriteGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl<T: DataFile> Drop for FileWriteGuard<'_, T> {
fn drop(&mut self) {
if panicking() {
return;
}

self.0.save().unwrap();
}
}
39 changes: 39 additions & 0 deletions src/utils/data_file/mod.rs
Original file line number Diff line number Diff line change
@@ -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<Self, crate::Error> {
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<FileGuard<Self>, 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(())
}
}
2 changes: 1 addition & 1 deletion src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod data_file;
pub mod streams;
use std::fmt::Display;
use std::sync::{Arc, Mutex};
Expand All @@ -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;

Expand Down
1 change: 0 additions & 1 deletion src/utils/tokio.rs

This file was deleted.

0 comments on commit a0b8a0c

Please sign in to comment.