musichoard/src/main.rs

130 lines
3.8 KiB
Rust
Raw Normal View History

use std::fs::OpenOptions;
use std::path::PathBuf;
use std::{ffi::OsString, io};
use musichoard::database::NoDatabase;
use musichoard::library::NoLibrary;
use musichoard::Collection;
use ratatui::{backend::CrosstermBackend, Terminal};
use structopt::StructOpt;
use musichoard::{
database::{
json::{backend::JsonDatabaseFileBackend, JsonDatabase},
IDatabase,
},
library::{
beets::{
executor::{ssh::BeetsLibrarySshExecutor, BeetsLibraryProcessExecutor},
BeetsLibrary,
},
ILibrary,
},
MusicHoard,
};
mod tui;
use tui::ui::Ui;
use tui::{event::EventChannel, handler::EventHandler, listener::EventListener, Tui};
#[derive(StructOpt)]
struct Opt {
#[structopt(long = "ssh", help = "Beets SSH URI")]
beets_ssh_uri: Option<OsString>,
#[structopt(long = "beets", help = "Beets config file path")]
beets_config_file_path: Option<OsString>,
#[structopt(long = "no-library", help = "Do not connect to the library")]
no_library: bool,
#[structopt(
long = "database",
help = "Database file path",
default_value = "database.json"
)]
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: Option<LIB>, db: Option<DB>) {
let music_hoard = MusicHoard::new(lib, db);
// Initialize the terminal user interface.
let backend = CrosstermBackend::new(io::stdout());
let terminal = Terminal::new(backend).expect("failed to initialise terminal");
let channel = EventChannel::new();
let listener = EventListener::new(channel.sender());
let handler = EventHandler::new(channel.receiver());
let ui = Ui::new(music_hoard).expect("failed to initialise ui");
// Run the TUI application.
Tui::run(terminal, ui, handler, listener).expect("failed to run tui");
}
fn with_database<DB: IDatabase>(opt: Opt, db: Option<DB>) {
if opt.no_library {
with(None::<NoLibrary>, db);
} else if let Some(uri) = opt.beets_ssh_uri {
let uri = uri.into_string().expect("invalid SSH URI");
let beets_config_file_path = opt
.beets_config_file_path
.map(|s| s.into_string())
.transpose()
.expect("failed to extract beets config file path");
let lib_exec = BeetsLibrarySshExecutor::new(uri)
.expect("failed to initialise beets")
.config(beets_config_file_path);
with(Some(BeetsLibrary::new(lib_exec)), db);
} else {
let lib_exec = BeetsLibraryProcessExecutor::default().config(opt.beets_config_file_path);
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)]
#[macro_use]
mod testlib;
#[cfg(test)]
mod tests {
use once_cell::sync::Lazy;
use musichoard::*;
pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| collection!());
}