generated from PlexSheep/rs-base
fix(store): store is now actually saved and loaded
cargo devel CI / cargo CI (push) Successful in 3m14s
Details
cargo devel CI / cargo CI (push) Successful in 3m14s
Details
This commit is contained in:
parent
cc0bc95ba2
commit
6a5cbcdbea
|
@ -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"
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue