Implemented new reading/writing in utils, improved library init function

This commit is contained in:
G2-Games 2024-01-19 21:22:03 -06:00
parent 0f49c50c42
commit 0f5eda5d1d
5 changed files with 108 additions and 78 deletions

12
.gitignore vendored
View file

@ -1,9 +1,15 @@
# Rust binary output dir
target/ target/
music_database* # Rust configuration
*.db3*
config.toml
Cargo.lock Cargo.lock
# Database files
*.db3*
music_database*
# Storage formats
*.kate-swp* *.kate-swp*
*.m3u *.m3u
*.m3u8 *.m3u8
*.json

View file

@ -1,7 +1,11 @@
use std::{path::{PathBuf, Path}, marker::PhantomData, fs::{File, OpenOptions, self}, io::{Error, Write, Read}, default}; use std::{
path::PathBuf,
fs::{File, OpenOptions, self},
io::{Error, Write, Read},
};
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use serde_json::{to_string, to_string_pretty}; use serde_json::to_string_pretty;
use thiserror::Error; use thiserror::Error;
use uuid::Uuid; use uuid::Uuid;
@ -11,6 +15,7 @@ pub struct ConfigLibrary {
pub path: PathBuf, pub path: PathBuf,
pub uuid: Uuid pub uuid: Uuid
} }
impl ConfigLibrary { impl ConfigLibrary {
pub fn new() -> Self { pub fn new() -> Self {
ConfigLibrary::default() ConfigLibrary::default()
@ -22,6 +27,7 @@ impl ConfigLibrary {
} }
} }
} }
impl Default for ConfigLibrary { impl Default for ConfigLibrary {
fn default() -> Self { fn default() -> Self {
ConfigLibrary { ConfigLibrary {
@ -31,29 +37,71 @@ impl Default for ConfigLibrary {
} }
} }
} }
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Debug, Default, Serialize, Deserialize)]
pub struct ConfigLibraries { pub struct ConfigLibraries {
default_library: Uuid, default_library: Uuid,
pub library_folder: PathBuf, pub library_folder: PathBuf,
pub libraries: Vec<ConfigLibrary>, pub libraries: Vec<ConfigLibrary>,
} }
impl ConfigLibraries {
//TODO: Add new function for test tube
pub fn set_default(mut self, uuid: &Uuid) {
self.default_library = *uuid;
}
pub fn get_default(&self) -> Result<&ConfigLibrary, ConfigError> {
for library in &self.libraries {
if library.uuid == self.default_library {
return Ok(library)
}
}
Err(ConfigError::NoDefaultLibrary)
}
pub fn get_library(&self, uuid: &Uuid) -> Result<ConfigLibrary, ConfigError> {
for library in &self.libraries {
if &library.uuid == uuid {
return Ok(library.to_owned())
}
}
Err(ConfigError::NoConfigLibrary(*uuid))
}
pub fn uuid_exists(&self, uuid: &Uuid) -> bool {
for library in &self.libraries {
if &library.uuid == uuid {
return true
}
}
false
}
}
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Debug, Default, Serialize, Deserialize)]
pub struct Config { pub struct Config {
pub path: PathBuf, pub path: PathBuf,
pub libraries: ConfigLibraries, pub libraries: ConfigLibraries,
volume: f32, volume: f32,
} }
#[test] #[test]
fn config_test_() { fn config_test() {
Config { let _ = Config {
path: PathBuf::from("F:\\temp\\config.json"), path: PathBuf::from("config_test.json"),
libraries: ConfigLibraries { libraries: ConfigLibraries {
libraries: vec![ConfigLibrary::default(),ConfigLibrary::default(),ConfigLibrary::default()], libraries: vec![
ConfigLibrary::default(),
ConfigLibrary::default(),
ConfigLibrary::default()
],
..Default::default() ..Default::default()
}, },
..Default::default() ..Default::default()
}.to_file(); }.write_file();
} }
impl Config { impl Config {
pub fn new() -> Self { pub fn new() -> Self {
Config { Config {
@ -64,38 +112,12 @@ impl Config {
..Default::default() ..Default::default()
} }
} }
pub fn new_main() -> Self { pub fn new_main() -> Self {
Config::default() Config::default()
} }
//TODO: Add new function for test tube
pub fn set_default_library(mut self, uuid: &Uuid) { pub fn write_file(&self) -> Result<(), Error> {
self.libraries.default_library = *uuid;
}
pub fn get_default_library(&self) -> Result<&ConfigLibrary, ConfigError> {
for library in &self.libraries.libraries {
if library.uuid == self.libraries.default_library {
return Ok(library)
}
}
Err(ConfigError::NoDefaultLibrary)
}
pub fn get_library(&self, uuid: &Uuid) -> Result<ConfigLibrary, ConfigError> {
for library in &self.libraries.libraries {
if &library.uuid == uuid {
return Ok(library.to_owned())
}
}
Err(ConfigError::NoConfigLibrary(*uuid))
}
pub fn library_exists(&self, uuid: &Uuid) -> bool {
for library in &self.libraries.libraries {
if &library.uuid == uuid {
return true
}
}
false
}
pub fn to_file(&self) -> Result<(), Error> {
let mut writer = self.path.clone(); let mut writer = self.path.clone();
writer.set_extension("tmp"); writer.set_extension("tmp");
let mut file = OpenOptions::new().create(true).truncate(true).read(true).write(true).open(&writer)?; let mut file = OpenOptions::new().create(true).truncate(true).read(true).write(true).open(&writer)?;
@ -106,7 +128,8 @@ impl Config {
fs::rename(writer, self.path.as_path())?; fs::rename(writer, self.path.as_path())?;
Ok(()) Ok(())
} }
pub fn load_file(path: PathBuf) -> Result<Self, Error> {
pub fn read_file(path: PathBuf) -> Result<Self, Error> {
let mut file: File = File::open(path)?; let mut file: File = File::open(path)?;
let mut bun: String = String::new(); let mut bun: String = String::new();
_ = file.read_to_string(&mut bun); _ = file.read_to_string(&mut bun);

View file

@ -3,6 +3,8 @@ pub mod music_storage {
pub mod music_collection; pub mod music_collection;
pub mod playlist; pub mod playlist;
mod utils; mod utils;
#[allow(dead_code)]
pub mod db_reader; pub mod db_reader;
} }

View file

@ -1,8 +1,6 @@
// Crate things // Crate things
use super::utils::{find_images, normalize, read_library, write_library}; use super::utils::{find_images, normalize, read_file, write_file};
use super::music_collection::MusicCollection;
use crate::config::config::Config; use crate::config::config::Config;
use crate::music_storage::library;
// Various std things // Various std things
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -316,25 +314,30 @@ impl Album<'_> {
const BLOCKED_EXTENSIONS: [&str; 4] = ["vob", "log", "txt", "sf2"]; const BLOCKED_EXTENSIONS: [&str; 4] = ["vob", "log", "txt", "sf2"];
#[derive(Debug)] #[derive(Debug, Serialize, Deserialize)]
pub struct MusicLibrary { pub struct MusicLibrary {
pub name: String, pub name: String,
pub uuid: Uuid, pub uuid: Uuid,
pub library: Vec<Song>, pub library: Vec<Song>,
} }
#[test] #[test]
fn test_() { fn library_init() {
let a = MusicLibrary::init(Arc::new(RwLock::from(Config::new())), None).unwrap(); let uuidv4 = Uuid::new_v4();
let a = MusicLibrary::init(Arc::new(RwLock::from(Config::new())), uuidv4).unwrap();
dbg!(a); dbg!(a);
} }
impl MusicLibrary { impl MusicLibrary {
pub fn new() -> Self { pub fn new(name: String, uuid: Uuid) -> Self {
MusicLibrary { MusicLibrary {
name: String::default(), name,
uuid: Uuid::default(), uuid,
library: Vec::new(), library: Vec::new(),
} }
} }
/*
pub fn with_uuid(uuid: Uuid, path: PathBuf) -> Result<Self, Box<dyn Error>> { pub fn with_uuid(uuid: Uuid, path: PathBuf) -> Result<Self, Box<dyn Error>> {
MusicLibrary { MusicLibrary {
name: String::new(), name: String::new(),
@ -344,40 +347,35 @@ impl MusicLibrary {
todo!() todo!()
} }
*/
/// Initialize the database /// Initialize the database
/// ///
/// If the database file already exists, return the [MusicLibrary], otherwise create /// If the database file already exists, return the [MusicLibrary], otherwise create
/// the database first. This needs to be run before anything else to retrieve /// the database first. This needs to be run before anything else to retrieve
/// the [MusicLibrary] Vec /// the [MusicLibrary] Vec
pub fn init(config: Arc<RwLock<Config>>, uuid: Option<Uuid>) -> Result<Self, Box<dyn Error>> { pub fn init(config: Arc<RwLock<Config>>, uuid: Uuid) -> Result<Self, Box<dyn Error>> {
let global_config = &*config.read().unwrap(); let global_config = &*config.read().unwrap();
let mut library = MusicLibrary::new(); let library: MusicLibrary = match global_config.libraries.uuid_exists(&uuid) {
true => read_file(global_config.libraries.get_library(&uuid)?.path)?,
if let Some(uuid) = uuid {
match global_config.library_exists(&uuid) {
true => {
library.library = read_library(global_config.get_library(&uuid)?.path)?;
},
false => { false => {
// Create the database if it does not exist // If the library does not exist, re-create it
write_library(&library.library, global_config.path.clone())?; let lib = MusicLibrary::new(String::new(), uuid);
library = MusicLibrary::with_uuid(uuid, global_config.path.parent().unwrap().to_path_buf())?; write_file(&lib, global_config.libraries.get_library(&uuid)?.path)?;
lib
} }
}; };
}else {
write_library(&library.library, global_config.path.clone())?;
}
Ok(library) Ok(library)
} }
/// Serializes the database out to the file specified in the config /// Serializes the database out to the file specified in the config
pub fn save(&self, config: &Config) -> Result<(), Box<dyn Error>> { pub fn save(&self, config: &Config) -> Result<(), Box<dyn Error>> {
let path = config.get_library(&self.uuid)?.path; let path = config.libraries.get_library(&self.uuid)?.path;
match path.try_exists() { match path.try_exists() {
Ok(_) => { Ok(_) => {
write_library(&self.library, path)?; write_file(&self.library, path)?;
} }
Err(error) => return Err(error.into()), Err(error) => return Err(error.into()),
} }

View file

@ -6,7 +6,7 @@ use walkdir::WalkDir;
use snap; use snap;
use super::library::{AlbumArt, Song, URI}; use super::library::{AlbumArt, URI};
use unidecode::unidecode; use unidecode::unidecode;
pub(super) fn normalize(input_string: &str) -> String { pub(super) fn normalize(input_string: &str) -> String {
@ -19,35 +19,36 @@ pub(super) fn normalize(input_string: &str) -> String {
normalized normalized
} }
pub(super) fn read_library(path: PathBuf) -> Result<Vec<Song>, Box<dyn Error>> { pub(super) fn read_file<T: for<'de> serde::Deserialize<'de>>(path: PathBuf) -> Result<T, Box<dyn Error>> {
// Create a new snap reader over the database file // Create a new snap reader over the database file
let database = fs::File::open(path)?; let database = fs::File::open(path)?;
let reader = BufReader::new(database); let reader = BufReader::new(database);
let mut d = snap::read::FrameDecoder::new(reader); let mut d = snap::read::FrameDecoder::new(reader);
// Decode the library from the serialized data into the vec // Decode the library from the serialized data into the vec
let library: Vec<Song> = bincode::serde::decode_from_std_read( let library: T = bincode::serde::decode_from_std_read(
&mut d, &mut d,
bincode::config::standard() bincode::config::standard()
.with_little_endian() .with_little_endian()
.with_variable_int_encoding(), .with_variable_int_encoding(),
)?; )?;
Ok(library) Ok(library)
} }
pub(super) fn write_library( pub(super) fn write_file<T: serde::Serialize>(
library: &Vec<Song>, library: &T,
path: PathBuf, path: PathBuf,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
// Create 2 new names for the file, a temporary one for writing out, and a backup // Create a temporary name for writing out
let mut writer_name = path.clone(); let mut writer_name = path.clone();
writer_name.set_extension("tmp"); writer_name.set_extension("tmp");
// Create a new BufWriter on the file and make a snap frame encoer for it too // Create a new BufWriter on the file and a snap frame encoder
let writer = BufWriter::new(fs::File::create(&writer_name)?); let writer = BufWriter::new(fs::File::create(&writer_name)?);
let mut e = snap::write::FrameEncoder::new(writer); let mut e = snap::write::FrameEncoder::new(writer);
// Write out the data using bincode // Write out the data
bincode::serde::encode_into_std_write( bincode::serde::encode_into_std_write(
library, library,
&mut e, &mut e,