fix(store): store is now actually saved and loaded
cargo devel CI / cargo CI (push) Successful in 3m14s Details

This commit is contained in:
Christoph J. Scherr 2024-08-23 12:07:14 +02:00
parent cc0bc95ba2
commit 6a5cbcdbea
5 changed files with 134 additions and 15 deletions

View File

@ -19,6 +19,7 @@ backend-jellyfin = []
[dependencies] [dependencies]
anyhow = "1.0.86" anyhow = "1.0.86"
clap = { version = "4.5.16", features = ["derive"] } clap = { version = "4.5.16", features = ["derive"] }
directories = "5.0.1"
eframe = { version = "0.28.1", optional = false } eframe = { version = "0.28.1", optional = false }
egui = { version = "0.28.1", optional = false } egui = { version = "0.28.1", optional = false }
egui_extras = { version = "0.28.1", features = ["image"] } egui_extras = { version = "0.28.1", features = ["image"] }
@ -29,6 +30,7 @@ image = { version = "0.25.2", default-features = true, features = [
] } ] }
libpt = { version = "0.6.0", features = ["cli", "full"] } libpt = { version = "0.6.0", features = ["cli", "full"] }
rmp-serde = "1.3.0"
serde = { version = "1.0.208", features = ["derive"] } serde = { version = "1.0.208", features = ["derive"] }
strum = { version = "0.26.3", features = ["derive"] } strum = { version = "0.26.3", features = ["derive"] }
thiserror = "1.0.63" thiserror = "1.0.63"

View File

@ -1,3 +1,5 @@
use std::path::PathBuf;
use thiserror::Error; use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -6,4 +8,21 @@ pub enum Error {
UiError(#[from] eframe::Error), UiError(#[from] eframe::Error),
#[error(transparent)] #[error(transparent)]
Other(#[from] anyhow::Error), Other(#[from] anyhow::Error),
#[error(
r"The system does not seem to have the usual project dirs, like:
Linux: /home/alice/.config/barapp
MS Windows: C:\Users\Alice\AppData\Roaming\Foo Corp\Bar App\config
Mac: /Users/Alice/Library/Application Support/com.Foo-Corp.Bar-App
Closing as to not break your files accidentally."
)]
NoProjDir,
#[error(r"The store file '{0}' where Beatbär stores it's metadata is not a regular file, refusing to save or load to keep your files safe.")]
BadStoreFile(PathBuf),
#[error(r"Could not convert the store to the binary format for saving it to the disk.")]
RmpEncode(#[from] rmp_serde::encode::Error),
#[error(r"Could not decode the store from the binary format for loading it from the disk. Is your store file broken?")]
RmpDecode(#[from] rmp_serde::decode::Error),
#[error(r"Error while reading or writing to the disk. Source: {0}")]
IO(#[from] std::io::Error),
} }

View File

@ -1,5 +1,6 @@
use clap::Parser; use clap::Parser;
use eframe::CreationContext; use eframe::CreationContext;
use egui::Stroke;
use libpt::cli::args::VerbosityLevel; use libpt::cli::args::VerbosityLevel;
use libpt::log::{debug, info}; use libpt::log::{debug, info};
@ -27,10 +28,15 @@ pub struct Player {
kind: Kind, kind: Kind,
#[clap(skip)] #[clap(skip)]
store: Store, store: Option<Store>,
} }
impl Player { impl Player {
#[inline]
pub fn store(&self) -> &Store {
&self.store.as_ref().unwrap()
}
pub fn build() -> Result<Self, Error> { pub fn build() -> Result<Self, Error> {
let mut app = Player::parse(); let mut app = Player::parse();
@ -63,7 +69,7 @@ impl Player {
Ok(app) Ok(app)
} }
pub fn init(&mut self, _cc: &CreationContext) { pub fn init(&mut self, _cc: &CreationContext) {
// we can use the creation context to do some customizing, but idc right now self.store = Some(Store::load().expect("could not load store"));
} }
fn set_category(&mut self, kind: Kind) { fn set_category(&mut self, kind: Kind) {
@ -80,11 +86,15 @@ impl Player {
fn entries(&self) -> Vec<Entry> { fn entries(&self) -> Vec<Entry> {
match self.category() { match self.category() {
Kind::Album => self.store.albums(), Kind::Album => self.store().albums(),
Kind::Song => self.store.songs(), Kind::Song => self.store().songs(),
Kind::Playlist => self.store.playlists(), Kind::Playlist => self.store().playlists(),
Kind::Artist => self.store.artists(), Kind::Artist => self.store().artists(),
Kind::Genre => self.store.genres(), Kind::Genre => self.store().genres(),
} }
} }
pub fn end(&mut self) -> Result<(), Error> {
self.store().save()
}
} }

View File

@ -1,6 +1,6 @@
use egui::{IconData, Sense}; use egui::{IconData, Sense};
use egui_extras::{Column, TableBuilder}; use egui_extras::{Column, TableBuilder};
use libpt::log::{trace, warn}; use libpt::log::{error, trace, warn};
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
pub mod entry; pub mod entry;
@ -174,4 +174,12 @@ impl eframe::App for Player {
}); });
egui::TopBottomPanel::bottom("bot_panel").show(ctx, Self::bottom_label); egui::TopBottomPanel::bottom("bot_panel").show(ctx, Self::bottom_label);
} }
fn on_exit(&mut self, _gl: Option<&eframe::glow::Context>) {
match self.store().save() {
Ok(_) => (),
Err(e) => {
error!("{e}");
}
}
}
} }

View File

@ -1,24 +1,104 @@
#[derive(Default)] use std::fs::File;
pub struct Store {} use std::io::{BufReader, Write};
use std::path::PathBuf;
use libpt::log::{debug, error, info, warn};
use serde::{Deserialize, Serialize};
use crate::error::Error;
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct Store {
dummy: String,
}
impl Store { impl Store {
pub fn projdir() -> Option<directories::ProjectDirs> {
directories::ProjectDirs::from("de.cscherr", "Beatbär", "Beatbär")
}
pub fn genres(&self) -> Vec<crate::player::ui::entry::Entry> { pub fn genres(&self) -> Vec<crate::player::ui::entry::Entry> {
todo!() vec![]
} }
pub fn artists(&self) -> Vec<crate::player::ui::entry::Entry> { pub fn artists(&self) -> Vec<crate::player::ui::entry::Entry> {
todo!() vec![]
} }
pub fn playlists(&self) -> Vec<crate::player::ui::entry::Entry> { pub fn playlists(&self) -> Vec<crate::player::ui::entry::Entry> {
todo!() vec![]
} }
pub fn songs(&self) -> Vec<crate::player::ui::entry::Entry> { pub fn songs(&self) -> Vec<crate::player::ui::entry::Entry> {
todo!() vec![]
} }
pub fn albums(&self) -> Vec<crate::player::ui::entry::Entry> { pub fn albums(&self) -> Vec<crate::player::ui::entry::Entry> {
todo!() vec![]
}
pub fn save(&self) -> Result<(), Error> {
info!("saving the store to file");
debug!("Store: {self:#?}");
if let Some(dirs) = Self::projdir() {
let mut store_path: PathBuf = dirs.data_local_dir().into();
store_path.push("store.msgpack");
if !store_path.exists() {
std::fs::create_dir_all(
store_path
.parent()
.expect("beatbär storefile has no parent????"),
)?;
warn!("The Beatbär store at '{}' does not exist. Creating it anew. This is normal if you start Beatbär for the first time.", store_path.as_path().to_string_lossy());
} else if !store_path.is_file() {
let e = Error::BadStoreFile(store_path);
error!("{e}");
return Err(e);
}
let mut file = File::options()
.write(true)
.append(false)
.create(true)
.truncate(true)
.open(store_path)?;
let repr = rmp_serde::to_vec(self)?;
file.write_all(&repr)?;
info!("store file was written");
Ok(())
} else {
Err(Error::NoProjDir)
}
}
pub fn load() -> Result<Self, Error> {
info!("loading the store to file");
if let Some(dirs) = Self::projdir() {
let mut store_path: PathBuf = dirs.data_local_dir().into();
store_path.push("store.msgpack");
if !store_path.exists() {
warn!(
"The Beatbär store at '{}' does not exist. Loading an empty store instead.",
store_path.as_path().to_string_lossy()
);
return Ok(Self::default());
} else if !store_path.is_file() {
let e = Error::BadStoreFile(store_path);
error!("{e}");
return Err(e);
}
let file = File::options().read(true).write(false).open(store_path)?;
let store = rmp_serde::from_read(file)?;
info!("store file was loaded");
debug!("Store: {store:#?}");
Ok(store)
} else {
let e = Error::NoProjDir;
error!("{}", e);
std::process::exit(1);
}
} }
} }