Make it possible to launch main binary without database and/or library #88

Merged
wojtek merged 3 commits from 87---make-it-possible-to-launch-main-binary-without-database-and/or-library into main 2024-01-11 23:27:01 +01:00
3 changed files with 69 additions and 73 deletions

View File

@ -16,7 +16,7 @@ type MH = MusicHoard<NoLibrary, JsonDatabase<JsonDatabaseFileBackend>>;
struct Opt { struct Opt {
#[structopt( #[structopt(
long = "database", long = "database",
name = "database file path", help = "Database file path",
default_value = "database.json" default_value = "database.json"
)] )]
database_file_path: PathBuf, database_file_path: PathBuf,

View File

@ -788,45 +788,34 @@ impl<LIB: ILibrary, DB: IDatabase> MusicHoard<LIB, DB> {
} }
pub fn rescan_library(&mut self) -> Result<(), Error> { pub fn rescan_library(&mut self) -> Result<(), Error> {
match self.library { if let Some(ref mut library) = self.library {
Some(ref mut library) => { let items = library.list(&Query::new())?;
let items = library.list(&Query::new())?; let mut library_collection = Self::items_to_artists(items);
let mut library_collection = Self::items_to_artists(items); Self::sort(&mut library_collection);
Self::sort(&mut library_collection);
let collection = mem::take(&mut self.collection); let collection = mem::take(&mut self.collection);
self.collection = Self::merge(library_collection, collection); self.collection = Self::merge(library_collection, collection);
Ok(())
}
None => Err(Error::LibraryError(String::from("library not provided"))),
} }
Ok(())
} }
pub fn load_from_database(&mut self) -> Result<(), Error> { pub fn load_from_database(&mut self) -> Result<(), Error> {
match self.database { if let Some(ref mut database) = self.database {
Some(ref mut database) => { let mut database_collection: Collection = vec![];
let mut database_collection: Collection = vec![]; database.load(&mut database_collection)?;
database.load(&mut database_collection)?; Self::sort(&mut database_collection);
Self::sort(&mut database_collection);
let collection = mem::take(&mut self.collection); let collection = mem::take(&mut self.collection);
self.collection = Self::merge(collection, database_collection); self.collection = Self::merge(collection, database_collection);
Ok(())
}
None => Err(Error::DatabaseError(String::from("database not provided"))),
} }
Ok(())
} }
pub fn save_to_database(&mut self) -> Result<(), Error> { pub fn save_to_database(&mut self) -> Result<(), Error> {
match self.database { if let Some(ref mut database) = self.database {
Some(ref mut database) => { database.save(&self.collection)?;
database.save(&self.collection)?;
Ok(())
}
None => Err(Error::DatabaseError(String::from("database not provided"))),
} }
Ok(())
} }
pub fn add_artist<ID: Into<ArtistId>>(&mut self, artist_id: ID) { pub fn add_artist<ID: Into<ArtistId>>(&mut self, artist_id: ID) {
@ -1963,10 +1952,7 @@ mod tests {
let database = Some(MockIDatabase::new()); let database = Some(MockIDatabase::new());
let mut music_hoard = MusicHoard::new(library, database); let mut music_hoard = MusicHoard::new(library, database);
let actual_err = music_hoard.rescan_library().unwrap_err(); assert!(music_hoard.rescan_library().is_ok());
let expected_err = Error::LibraryError(String::from("library not provided"));
assert_eq!(actual_err, expected_err);
} }
#[test] #[test]
@ -1997,15 +1983,8 @@ mod tests {
let database: Option<NoDatabase> = None; let database: Option<NoDatabase> = None;
let mut music_hoard = MusicHoard::new(library, database); let mut music_hoard = MusicHoard::new(library, database);
let expected_err = Error::DatabaseError(String::from("database not provided")); assert!(music_hoard.load_from_database().is_ok());
assert!(music_hoard.save_to_database().is_ok());
let actual_err = music_hoard.load_from_database().unwrap_err();
assert_eq!(actual_err, expected_err);
assert_eq!(actual_err.to_string(), expected_err.to_string());
let actual_err = music_hoard.save_to_database().unwrap_err();
assert_eq!(actual_err, expected_err);
assert_eq!(actual_err.to_string(), expected_err.to_string());
} }
#[test] #[test]

View File

@ -2,6 +2,8 @@ use std::fs::OpenOptions;
use std::path::PathBuf; use std::path::PathBuf;
use std::{ffi::OsString, io}; use std::{ffi::OsString, io};
use musichoard::database::NoDatabase;
use musichoard::library::NoLibrary;
use musichoard::Collection; use musichoard::Collection;
use ratatui::{backend::CrosstermBackend, Terminal}; use ratatui::{backend::CrosstermBackend, Terminal};
use structopt::StructOpt; use structopt::StructOpt;
@ -27,22 +29,28 @@ use tui::{event::EventChannel, handler::EventHandler, listener::EventListener, T
#[derive(StructOpt)] #[derive(StructOpt)]
struct Opt { struct Opt {
#[structopt(long = "ssh", name = "beets SSH URI")] #[structopt(long = "ssh", help = "Beets SSH URI")]
beets_ssh_uri: Option<OsString>, beets_ssh_uri: Option<OsString>,
#[structopt(long = "beets", name = "beets config file path")] #[structopt(long = "beets", help = "Beets config file path")]
beets_config_file_path: Option<OsString>, beets_config_file_path: Option<OsString>,
#[structopt(long = "no-library", help = "Do not connect to the library")]
no_library: bool,
#[structopt( #[structopt(
long = "database", long = "database",
name = "database file path", help = "Database file path",
default_value = "database.json" default_value = "database.json"
)] )]
database_file_path: PathBuf, database_file_path: PathBuf,
#[structopt(long = "no-database", help = "Do not read from/write to the database")]
no_database: bool,
} }
fn with<LIB: ILibrary, DB: IDatabase>(lib: LIB, db: DB) { fn with<LIB: ILibrary, DB: IDatabase>(lib: Option<LIB>, db: Option<DB>) {
let music_hoard = MusicHoard::new(Some(lib), Some(db)); let music_hoard = MusicHoard::new(lib, db);
// Initialize the terminal user interface. // Initialize the terminal user interface.
let backend = CrosstermBackend::new(io::stdout()); let backend = CrosstermBackend::new(io::stdout());
@ -58,30 +66,10 @@ fn with<LIB: ILibrary, DB: IDatabase>(lib: LIB, db: DB) {
Tui::run(terminal, ui, handler, listener).expect("failed to run tui"); Tui::run(terminal, ui, handler, listener).expect("failed to run tui");
} }
fn main() { fn with_database<DB: IDatabase>(opt: Opt, db: Option<DB>) {
let opt = Opt::from_args(); if opt.no_library {
with(None::<NoLibrary>, db);
// Create an empty database file if it does not exist. } else if let Some(uri) = opt.beets_ssh_uri {
match OpenOptions::new()
.write(true)
.create_new(true)
.open(&opt.database_file_path)
{
Ok(f) => {
drop(f);
JsonDatabase::new(JsonDatabaseFileBackend::new(&opt.database_file_path))
.save::<Collection>(&vec![])
.expect("failed to create empty database");
}
Err(e) => match e.kind() {
io::ErrorKind::AlreadyExists => {}
_ => panic!("failed to access database file"),
},
}
// Create the application.
let db_exec = JsonDatabaseFileBackend::new(&opt.database_file_path);
if let Some(uri) = opt.beets_ssh_uri {
let uri = uri.into_string().expect("invalid SSH URI"); let uri = uri.into_string().expect("invalid SSH URI");
let beets_config_file_path = opt let beets_config_file_path = opt
.beets_config_file_path .beets_config_file_path
@ -91,13 +79,42 @@ fn main() {
let lib_exec = BeetsLibrarySshExecutor::new(uri) let lib_exec = BeetsLibrarySshExecutor::new(uri)
.expect("failed to initialise beets") .expect("failed to initialise beets")
.config(beets_config_file_path); .config(beets_config_file_path);
with(BeetsLibrary::new(lib_exec), JsonDatabase::new(db_exec)); with(Some(BeetsLibrary::new(lib_exec)), db);
} else { } else {
let lib_exec = BeetsLibraryProcessExecutor::default().config(opt.beets_config_file_path); let lib_exec = BeetsLibraryProcessExecutor::default().config(opt.beets_config_file_path);
with(BeetsLibrary::new(lib_exec), JsonDatabase::new(db_exec)); with(Some(BeetsLibrary::new(lib_exec)), db);
} }
} }
fn main() {
let opt = Opt::from_args();
if opt.no_database {
with_database(opt, None::<NoDatabase>);
} else {
// Create an empty database file if it does not exist.
match OpenOptions::new()
.write(true)
.create_new(true)
.open(&opt.database_file_path)
{
Ok(f) => {
drop(f);
JsonDatabase::new(JsonDatabaseFileBackend::new(&opt.database_file_path))
.save::<Collection>(&vec![])
.expect("failed to create empty database");
}
Err(e) => match e.kind() {
io::ErrorKind::AlreadyExists => {}
_ => panic!("failed to access database file"),
},
}
let db_exec = JsonDatabaseFileBackend::new(&opt.database_file_path);
with_database(opt, Some(JsonDatabase::new(db_exec)));
};
}
#[cfg(test)] #[cfg(test)]
#[macro_use] #[macro_use]
mod testlib; mod testlib;