Provide search functionality through the TUI #134
@ -1,96 +0,0 @@
|
||||
// FIXME: combine with mod state into a mod machine
|
||||
#![allow(clippy::module_inception)]
|
||||
|
||||
use crate::tui::{
|
||||
app::{
|
||||
machine::{
|
||||
browse::AppBrowse, critical::AppCritical, error::AppError, info::AppInfo,
|
||||
reload::AppReload, search::AppSearch, AppInner, AppMachine,
|
||||
},
|
||||
AppPublic, AppState, IAppAccess, IAppInteract,
|
||||
},
|
||||
lib::IMusicHoard,
|
||||
};
|
||||
|
||||
pub type App<MH> = AppState<
|
||||
AppMachine<MH, AppBrowse>,
|
||||
AppMachine<MH, AppInfo>,
|
||||
AppMachine<MH, AppReload>,
|
||||
AppMachine<MH, AppSearch>,
|
||||
AppMachine<MH, AppError>,
|
||||
AppMachine<MH, AppCritical>,
|
||||
>;
|
||||
|
||||
impl<MH: IMusicHoard> App<MH> {
|
||||
pub fn new(mut music_hoard: MH) -> Self {
|
||||
let init_result = Self::init(&mut music_hoard);
|
||||
let inner = AppInner::new(music_hoard);
|
||||
match init_result {
|
||||
Ok(()) => AppMachine::browse(inner).into(),
|
||||
Err(err) => AppMachine::critical(inner, err.to_string()).into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn init(music_hoard: &mut MH) -> Result<(), musichoard::Error> {
|
||||
music_hoard.load_from_database()?;
|
||||
music_hoard.rescan_library()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn inner_ref(&self) -> &AppInner<MH> {
|
||||
match self {
|
||||
AppState::Browse(browse) => browse.inner_ref(),
|
||||
AppState::Info(info) => info.inner_ref(),
|
||||
AppState::Reload(reload) => reload.inner_ref(),
|
||||
AppState::Search(search) => search.inner_ref(),
|
||||
AppState::Error(error) => error.inner_ref(),
|
||||
AppState::Critical(critical) => critical.inner_ref(),
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_mut(&mut self) -> &mut AppInner<MH> {
|
||||
match self {
|
||||
AppState::Browse(browse) => browse.inner_mut(),
|
||||
AppState::Info(info) => info.inner_mut(),
|
||||
AppState::Reload(reload) => reload.inner_mut(),
|
||||
AppState::Search(search) => search.inner_mut(),
|
||||
AppState::Error(error) => error.inner_mut(),
|
||||
AppState::Critical(critical) => critical.inner_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<MH: IMusicHoard> IAppInteract for App<MH> {
|
||||
type BS = AppMachine<MH, AppBrowse>;
|
||||
type IS = AppMachine<MH, AppInfo>;
|
||||
type RS = AppMachine<MH, AppReload>;
|
||||
type SS = AppMachine<MH, AppSearch>;
|
||||
type ES = AppMachine<MH, AppError>;
|
||||
type CS = AppMachine<MH, AppCritical>;
|
||||
|
||||
fn is_running(&self) -> bool {
|
||||
self.inner_ref().is_running()
|
||||
}
|
||||
|
||||
fn force_quit(mut self) -> Self {
|
||||
self.inner_mut().stop();
|
||||
self
|
||||
}
|
||||
|
||||
fn state(self) -> AppState<Self::BS, Self::IS, Self::RS, Self::SS, Self::ES, Self::CS> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<MH: IMusicHoard> IAppAccess for App<MH> {
|
||||
fn get(&mut self) -> AppPublic {
|
||||
match self {
|
||||
AppState::Browse(browse) => browse.into(),
|
||||
AppState::Info(info) => info.into(),
|
||||
AppState::Reload(reload) => reload.into(),
|
||||
AppState::Search(search) => search.into(),
|
||||
AppState::Error(error) => error.into(),
|
||||
AppState::Critical(critical) => critical.into(),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
use crate::tui::{
|
||||
app::{
|
||||
app::App,
|
||||
machine::{AppInner, AppMachine},
|
||||
machine::{App, AppInner, AppMachine},
|
||||
selection::{Delta, ListSelection},
|
||||
AppPublic, AppState, IAppInteractBrowse,
|
||||
},
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::tui::{
|
||||
app::{
|
||||
app::App,
|
||||
machine::{AppInner, AppMachine},
|
||||
machine::{App, AppInner, AppMachine},
|
||||
AppPublic, AppState, IAppInteractCritical,
|
||||
},
|
||||
lib::IMusicHoard,
|
||||
@ -47,7 +46,7 @@ impl<MH: IMusicHoard> IAppInteractCritical for AppMachine<MH, AppCritical> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tui::app::machine::tests::{music_hoard, inner};
|
||||
use crate::tui::app::machine::tests::{inner, music_hoard};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::tui::{
|
||||
app::{
|
||||
app::App,
|
||||
machine::{AppInner, AppMachine},
|
||||
machine::{App, AppInner, AppMachine},
|
||||
AppPublic, AppState, IAppInteractError,
|
||||
},
|
||||
lib::IMusicHoard,
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::tui::{
|
||||
app::{
|
||||
app::App,
|
||||
machine::{AppInner, AppMachine},
|
||||
machine::{App, AppInner, AppMachine},
|
||||
AppPublic, AppState, IAppInteractInfo,
|
||||
},
|
||||
lib::IMusicHoard,
|
||||
@ -47,7 +46,7 @@ impl<MH: IMusicHoard> IAppInteractInfo for AppMachine<MH, AppInfo> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tui::app::machine::tests::{music_hoard, inner};
|
||||
use crate::tui::app::machine::tests::{inner, music_hoard};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -1,36 +1,116 @@
|
||||
pub mod browse;
|
||||
pub mod critical;
|
||||
pub mod error;
|
||||
pub mod info;
|
||||
pub mod reload;
|
||||
pub mod search;
|
||||
mod browse;
|
||||
mod critical;
|
||||
mod error;
|
||||
mod info;
|
||||
mod reload;
|
||||
mod search;
|
||||
|
||||
use crate::tui::{
|
||||
app::{selection::Selection, AppPublicInner},
|
||||
app::{selection::Selection, AppPublic, AppPublicInner, AppState, IAppAccess, IAppInteract},
|
||||
lib::IMusicHoard,
|
||||
};
|
||||
|
||||
use browse::AppBrowse;
|
||||
use critical::AppCritical;
|
||||
use error::AppError;
|
||||
use info::AppInfo;
|
||||
use reload::AppReload;
|
||||
use search::AppSearch;
|
||||
|
||||
pub type App<MH> = AppState<
|
||||
AppMachine<MH, AppBrowse>,
|
||||
AppMachine<MH, AppInfo>,
|
||||
AppMachine<MH, AppReload>,
|
||||
AppMachine<MH, AppSearch>,
|
||||
AppMachine<MH, AppError>,
|
||||
AppMachine<MH, AppCritical>,
|
||||
>;
|
||||
|
||||
pub struct AppMachine<MH: IMusicHoard, STATE> {
|
||||
inner: AppInner<MH>,
|
||||
state: STATE,
|
||||
}
|
||||
|
||||
impl<MH: IMusicHoard, STATE> AppMachine<MH, STATE> {
|
||||
pub fn inner_ref(&self) -> &AppInner<MH> {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn inner_mut(&mut self) -> &mut AppInner<MH> {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AppInner<MH: IMusicHoard> {
|
||||
running: bool,
|
||||
music_hoard: MH,
|
||||
selection: Selection,
|
||||
}
|
||||
|
||||
impl<MH: IMusicHoard> App<MH> {
|
||||
pub fn new(mut music_hoard: MH) -> Self {
|
||||
let init_result = Self::init(&mut music_hoard);
|
||||
let inner = AppInner::new(music_hoard);
|
||||
match init_result {
|
||||
Ok(()) => AppMachine::browse(inner).into(),
|
||||
Err(err) => AppMachine::critical(inner, err.to_string()).into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn init(music_hoard: &mut MH) -> Result<(), musichoard::Error> {
|
||||
music_hoard.load_from_database()?;
|
||||
music_hoard.rescan_library()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn inner_ref(&self) -> &AppInner<MH> {
|
||||
match self {
|
||||
AppState::Browse(browse) => &browse.inner,
|
||||
AppState::Info(info) => &info.inner,
|
||||
AppState::Reload(reload) => &reload.inner,
|
||||
AppState::Search(search) => &search.inner,
|
||||
AppState::Error(error) => &error.inner,
|
||||
AppState::Critical(critical) => &critical.inner,
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_mut(&mut self) -> &mut AppInner<MH> {
|
||||
match self {
|
||||
AppState::Browse(browse) => &mut browse.inner,
|
||||
AppState::Info(info) => &mut info.inner,
|
||||
AppState::Reload(reload) => &mut reload.inner,
|
||||
AppState::Search(search) => &mut search.inner,
|
||||
AppState::Error(error) => &mut error.inner,
|
||||
AppState::Critical(critical) => &mut critical.inner,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<MH: IMusicHoard> IAppInteract for App<MH> {
|
||||
type BS = AppMachine<MH, AppBrowse>;
|
||||
type IS = AppMachine<MH, AppInfo>;
|
||||
type RS = AppMachine<MH, AppReload>;
|
||||
type SS = AppMachine<MH, AppSearch>;
|
||||
type ES = AppMachine<MH, AppError>;
|
||||
type CS = AppMachine<MH, AppCritical>;
|
||||
|
||||
fn is_running(&self) -> bool {
|
||||
self.inner_ref().running
|
||||
}
|
||||
|
||||
fn force_quit(mut self) -> Self {
|
||||
self.inner_mut().running = false;
|
||||
self
|
||||
}
|
||||
|
||||
fn state(self) -> AppState<Self::BS, Self::IS, Self::RS, Self::SS, Self::ES, Self::CS> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<MH: IMusicHoard> IAppAccess for App<MH> {
|
||||
fn get(&mut self) -> AppPublic {
|
||||
match self {
|
||||
AppState::Browse(browse) => browse.into(),
|
||||
AppState::Info(info) => info.into(),
|
||||
AppState::Reload(reload) => reload.into(),
|
||||
AppState::Search(search) => search.into(),
|
||||
AppState::Error(error) => error.into(),
|
||||
AppState::Critical(critical) => critical.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<MH: IMusicHoard> AppInner<MH> {
|
||||
pub fn new(music_hoard: MH) -> Self {
|
||||
let selection = Selection::new(music_hoard.get_collection());
|
||||
@ -40,14 +120,6 @@ impl<MH: IMusicHoard> AppInner<MH> {
|
||||
selection,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.running
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
self.running = false;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, MH: IMusicHoard> From<&'a mut AppInner<MH>> for AppPublicInner<'a> {
|
||||
@ -59,14 +131,12 @@ impl<'a, MH: IMusicHoard> From<&'a mut AppInner<MH>> for AppPublicInner<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: split tests - into parts that test functionality in isolation and move those where
|
||||
// appropriate, and parts that verify transitions between states.
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use musichoard::collection::Collection;
|
||||
|
||||
use crate::tui::{
|
||||
app::{app::App, AppState, IAppInteract},
|
||||
app::{AppState, IAppInteract},
|
||||
lib::MockIMusicHoard,
|
||||
testmod::COLLECTION,
|
||||
};
|
||||
@ -117,8 +187,15 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn music_hoard_app(collection: Collection) -> MockIMusicHoard {
|
||||
pub fn music_hoard(collection: Collection) -> MockIMusicHoard {
|
||||
let mut music_hoard = MockIMusicHoard::new();
|
||||
music_hoard.expect_get_collection().return_const(collection);
|
||||
|
||||
music_hoard
|
||||
}
|
||||
|
||||
fn music_hoard_init(collection: Collection) -> MockIMusicHoard {
|
||||
let mut music_hoard = music_hoard(collection);
|
||||
|
||||
music_hoard
|
||||
.expect_load_from_database()
|
||||
@ -128,14 +205,6 @@ mod tests {
|
||||
.expect_rescan_library()
|
||||
.times(1)
|
||||
.return_once(|| Ok(()));
|
||||
music_hoard.expect_get_collection().return_const(collection);
|
||||
|
||||
music_hoard
|
||||
}
|
||||
|
||||
pub fn music_hoard(collection: Collection) -> MockIMusicHoard {
|
||||
let mut music_hoard = MockIMusicHoard::new();
|
||||
music_hoard.expect_get_collection().return_const(collection);
|
||||
|
||||
music_hoard
|
||||
}
|
||||
@ -145,8 +214,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn running_force_quit() {
|
||||
let app = App::new(music_hoard_app(COLLECTION.to_owned()));
|
||||
fn force_quit() {
|
||||
let app = App::new(music_hoard_init(COLLECTION.to_owned()));
|
||||
assert!(app.is_running());
|
||||
|
||||
let app = app.force_quit();
|
||||
@ -155,7 +224,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn error_force_quit() {
|
||||
let mut app = App::new(music_hoard_app(COLLECTION.to_owned()));
|
||||
let mut app = App::new(music_hoard_init(COLLECTION.to_owned()));
|
||||
assert!(app.is_running());
|
||||
|
||||
app = AppMachine::error(app.unwrap_browse().inner, "get rekt").into();
|
||||
|
@ -1,8 +1,7 @@
|
||||
use crate::tui::{
|
||||
app::{
|
||||
app::App,
|
||||
machine::{App, AppInner, AppMachine},
|
||||
selection::IdSelection,
|
||||
machine::{AppInner, AppMachine},
|
||||
AppPublic, AppState, IAppInteractReload,
|
||||
},
|
||||
lib::IMusicHoard,
|
||||
@ -94,7 +93,6 @@ mod tests {
|
||||
app.unwrap_browse();
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn reload_database() {
|
||||
let mut music_hoard = music_hoard(vec![]);
|
||||
|
@ -2,9 +2,8 @@ use musichoard::collection::artist::Artist;
|
||||
|
||||
use crate::tui::{
|
||||
app::{
|
||||
app::App,
|
||||
machine::{App, AppInner, AppMachine},
|
||||
selection::ListSelection,
|
||||
machine::{AppInner, AppMachine},
|
||||
AppPublic, AppState, IAppInteractSearch,
|
||||
},
|
||||
lib::IMusicHoard,
|
||||
@ -99,7 +98,7 @@ trait IAppInteractSearchPrivate {
|
||||
fn incremental_search_predicate(
|
||||
case_sensitive: bool,
|
||||
char_sensitive: bool,
|
||||
search_name: &String,
|
||||
search_name: &str,
|
||||
probe: &Artist,
|
||||
) -> bool;
|
||||
|
||||
@ -137,7 +136,7 @@ impl<MH: IMusicHoard> IAppInteractSearchPrivate for AppMachine<MH, AppSearch> {
|
||||
fn incremental_search_predicate(
|
||||
case_sensitive: bool,
|
||||
char_sensitive: bool,
|
||||
search_name: &String,
|
||||
search_name: &str,
|
||||
probe: &Artist,
|
||||
) -> bool {
|
||||
let name = Self::normalize_search(&probe.id.name, !case_sensitive, !char_sensitive);
|
||||
|
@ -1,11 +1,11 @@
|
||||
pub mod app;
|
||||
pub mod selection;
|
||||
mod machine;
|
||||
mod selection;
|
||||
|
||||
pub use machine::App;
|
||||
pub use selection::{Category, Delta, Selection, WidgetState};
|
||||
|
||||
use musichoard::collection::Collection;
|
||||
|
||||
use selection::{Delta, Selection};
|
||||
|
||||
pub enum AppState<BS, IS, RS, SS, ES, CS> {
|
||||
Browse(BS),
|
||||
Info(IS),
|
||||
|
@ -5,8 +5,8 @@ use mockall::automock;
|
||||
|
||||
use crate::tui::{
|
||||
app::{
|
||||
selection::Delta, AppState, IAppInteract, IAppInteractBrowse, IAppInteractCritical,
|
||||
IAppInteractError, IAppInteractInfo, IAppInteractReload, IAppInteractSearch,
|
||||
AppState, Delta, IAppInteract, IAppInteractBrowse, IAppInteractCritical, IAppInteractError,
|
||||
IAppInteractInfo, IAppInteractReload, IAppInteractSearch,
|
||||
},
|
||||
event::{Event, EventError, EventReceiver},
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ mod lib;
|
||||
mod listener;
|
||||
mod ui;
|
||||
|
||||
pub use app::app::App;
|
||||
pub use app::App;
|
||||
pub use event::EventChannel;
|
||||
pub use handler::EventHandler;
|
||||
pub use listener::EventListener;
|
||||
@ -178,8 +178,8 @@ mod tests {
|
||||
use musichoard::collection::Collection;
|
||||
|
||||
use crate::tui::{
|
||||
app::app::App, handler::MockIEventHandler, lib::MockIMusicHoard,
|
||||
listener::MockIEventListener, ui::Ui,
|
||||
app::App, handler::MockIEventHandler, lib::MockIMusicHoard, listener::MockIEventListener,
|
||||
ui::Ui,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
@ -13,10 +13,7 @@ use ratatui::{
|
||||
Frame,
|
||||
};
|
||||
|
||||
use crate::tui::app::{
|
||||
selection::{Category, Selection, WidgetState},
|
||||
AppPublicState, AppState, IAppAccess,
|
||||
};
|
||||
use crate::tui::app::{AppPublicState, AppState, Category, IAppAccess, Selection, WidgetState};
|
||||
|
||||
pub trait IUi {
|
||||
fn render<APP: IAppAccess>(app: &mut APP, frame: &mut Frame);
|
||||
@ -695,7 +692,7 @@ impl IUi for Ui {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tui::{
|
||||
app::{selection::Delta, AppPublic, AppPublicInner},
|
||||
app::{AppPublic, AppPublicInner, Delta},
|
||||
testmod::COLLECTION,
|
||||
tests::terminal,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user