Compare commits

...

3 Commits

Author SHA1 Message Date
d0fe2f6450 Centralise no-op
All checks were successful
Cargo CI / Build and Test (pull_request) Successful in 2m36s
Cargo CI / Lint (pull_request) Successful in 1m6s
2024-09-08 08:55:01 +02:00
56bf99f985 Compartamentalize machine traits 2024-09-08 08:45:58 +02:00
706e30f587 Minor rearrangement 2024-09-08 08:09:29 +02:00
12 changed files with 76 additions and 142 deletions

View File

@ -85,13 +85,15 @@ fn with<Database: IDatabase + 'static, Library: ILibrary + 'static>(
let musicbrainz = MusicBrainz::new(client); let musicbrainz = MusicBrainz::new(client);
let channel = EventChannel::new(); let channel = EventChannel::new();
let listener = EventListener::new(channel.sender()); let listener_sender = channel.sender();
let app_sender = channel.sender();
let app = App::new(music_hoard, musicbrainz, channel.sender());
let ui = Ui;
let listener = EventListener::new(listener_sender);
let handler = EventHandler::new(channel.receiver()); let handler = EventHandler::new(channel.receiver());
let app = App::new(music_hoard, musicbrainz, app_sender);
let ui = Ui;
// Run the TUI application. // Run the TUI application.
Tui::run(terminal, app, ui, handler, listener).expect("failed to run tui"); Tui::run(terminal, app, ui, handler, listener).expect("failed to run tui");
} }

View File

@ -183,10 +183,6 @@ impl IAppInteractBrowse for AppMachine<AppBrowse> {
AppMachine::app_fetch_new(self.inner, fetch_rx) AppMachine::app_fetch_new(self.inner, fetch_rx)
} }
fn no_op(self) -> Self::APP {
self.into()
}
} }
#[cfg(test)] #[cfg(test)]
@ -486,11 +482,4 @@ mod tests {
assert_eq!(fetch_rx.try_recv().unwrap_err(), TryRecvError::Disconnected); assert_eq!(fetch_rx.try_recv().unwrap_err(), TryRecvError::Disconnected);
} }
#[test]
fn no_op() {
let browse = AppMachine::browse(inner(music_hoard(vec![])));
let app = browse.no_op();
app.unwrap_browse();
}
} }

View File

@ -1,6 +1,6 @@
use crate::tui::app::{ use crate::tui::app::{
machine::{App, AppInner, AppMachine}, machine::{App, AppInner, AppMachine},
AppPublic, AppState, IAppInteractCritical, AppPublic, AppState,
}; };
pub struct AppCritical { pub struct AppCritical {
@ -32,25 +32,3 @@ impl<'a> From<&'a mut AppMachine<AppCritical>> for AppPublic<'a> {
} }
} }
} }
impl IAppInteractCritical for AppMachine<AppCritical> {
type APP = App;
fn no_op(self) -> Self::APP {
self.into()
}
}
#[cfg(test)]
mod tests {
use crate::tui::app::machine::tests::{inner, music_hoard};
use super::*;
#[test]
fn no_op() {
let critical = AppMachine::critical(inner(music_hoard(vec![])), "get rekt");
let app = critical.no_op();
app.unwrap_critical();
}
}

View File

@ -39,10 +39,6 @@ impl IAppInteractError for AppMachine<AppError> {
fn dismiss_error(self) -> Self::APP { fn dismiss_error(self) -> Self::APP {
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()
} }
fn no_op(self) -> Self::APP {
self.into()
}
} }
#[cfg(test)] #[cfg(test)]
@ -57,11 +53,4 @@ mod tests {
let app = error.dismiss_error(); let app = error.dismiss_error();
app.unwrap_browse(); app.unwrap_browse();
} }
#[test]
fn no_op() {
let error = AppMachine::error(inner(music_hoard(vec![])), "get rekt");
let app = error.no_op();
app.unwrap_error();
}
} }

View File

@ -85,10 +85,6 @@ impl IAppInteractFetch for AppMachine<AppFetch> {
fn abort(self) -> Self::APP { fn abort(self) -> Self::APP {
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()
} }
fn no_op(self) -> Self::APP {
self.into()
}
} }
impl IAppEventFetch for AppMachine<AppFetch> { impl IAppEventFetch for AppMachine<AppFetch> {
@ -184,15 +180,4 @@ mod tests {
let app = app.abort(); let app = app.abort();
assert!(matches!(app, AppState::Browse(_))); assert!(matches!(app, AppState::Browse(_)));
} }
#[test]
fn no_op() {
let (_, rx) = mpsc::channel::<FetchResult>();
let fetch = AppFetch::new(rx);
let app = AppMachine::fetch(inner(music_hoard(COLLECTION.clone())), fetch);
let app = app.no_op();
assert!(matches!(app, AppState::Fetch(_)));
}
} }

View File

@ -35,10 +35,6 @@ impl IAppInteractInfo for AppMachine<AppInfo> {
fn hide_info_overlay(self) -> Self::APP { fn hide_info_overlay(self) -> Self::APP {
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()
} }
fn no_op(self) -> Self::APP {
self.into()
}
} }
#[cfg(test)] #[cfg(test)]
@ -53,11 +49,4 @@ mod tests {
let app = info.hide_info_overlay(); let app = info.hide_info_overlay();
app.unwrap_browse(); app.unwrap_browse();
} }
#[test]
fn no_op() {
let info = AppMachine::info(inner(music_hoard(vec![])));
let app = info.no_op();
app.unwrap_info();
}
} }

View File

@ -122,10 +122,6 @@ impl IAppInteractMatches for AppMachine<AppMatches> {
fn abort(self) -> Self::APP { fn abort(self) -> Self::APP {
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()
} }
fn no_op(self) -> Self::APP {
self.into()
}
} }
#[cfg(test)] #[cfg(test)]
@ -209,7 +205,7 @@ mod tests {
assert_eq!(matches.state.current, None); assert_eq!(matches.state.current, None);
assert_eq!(matches.state.state, widget_state); assert_eq!(matches.state.state, widget_state);
let mut app = matches.no_op(); let mut app: App = matches.into();
let public = app.get(); let public = app.get();
let public_matches = public.state.unwrap_matches(); let public_matches = public.state.unwrap_matches();
@ -232,7 +228,7 @@ mod tests {
assert_eq!(matches.state.current.as_ref(), Some(&album_match)); assert_eq!(matches.state.current.as_ref(), Some(&album_match));
assert_eq!(matches.state.state, widget_state); assert_eq!(matches.state.state, widget_state);
let mut app = matches.no_op(); let mut app: App = matches.into();
let public = app.get(); let public = app.get();
let public_matches = public.state.unwrap_matches(); let public_matches = public.state.unwrap_matches();
@ -315,11 +311,4 @@ mod tests {
let matches = AppMachine::matches(inner(music_hoard(vec![])), matches(None)); let matches = AppMachine::matches(inner(music_hoard(vec![])), matches(None));
matches.select().unwrap_browse(); matches.select().unwrap_browse();
} }
#[test]
fn no_op() {
let matches = AppMachine::matches(inner(music_hoard(vec![])), matches(None));
let app = matches.no_op();
app.unwrap_matches();
}
} }

View File

@ -24,6 +24,8 @@ use matches::AppMatches;
use reload::AppReload; use reload::AppReload;
use search::AppSearch; use search::AppSearch;
use super::IAppBase;
pub type App = AppState< pub type App = AppState<
AppMachine<AppBrowse>, AppMachine<AppBrowse>,
AppMachine<AppInfo>, AppMachine<AppInfo>,
@ -121,6 +123,14 @@ impl IAppInteract for App {
} }
} }
impl<T: Into<App>> IAppBase for T {
type APP = App;
fn no_op(self) -> Self::APP {
self.into()
}
}
impl IAppAccess for App { impl IAppAccess for App {
fn get(&mut self) -> AppPublic { fn get(&mut self) -> AppPublic {
match self { match self {
@ -277,6 +287,11 @@ mod tests {
assert!(matches!(state, AppState::Browse(_))); assert!(matches!(state, AppState::Browse(_)));
app = state; app = state;
app = app.no_op();
let state = app.state();
assert!(matches!(state, AppState::Browse(_)));
app = state;
let public = app.get(); let public = app.get();
assert!(matches!(public.state, AppState::Browse(_))); assert!(matches!(public.state, AppState::Browse(_)));
@ -295,6 +310,11 @@ mod tests {
assert!(matches!(state, AppState::Info(_))); assert!(matches!(state, AppState::Info(_)));
app = state; app = state;
app = app.no_op();
let state = app.state();
assert!(matches!(state, AppState::Info(_)));
app = state;
let public = app.get(); let public = app.get();
assert!(matches!(public.state, AppState::Info(_))); assert!(matches!(public.state, AppState::Info(_)));
@ -313,6 +333,11 @@ mod tests {
assert!(matches!(state, AppState::Reload(_))); assert!(matches!(state, AppState::Reload(_)));
app = state; app = state;
app = app.no_op();
let state = app.state();
assert!(matches!(state, AppState::Reload(_)));
app = state;
let public = app.get(); let public = app.get();
assert!(matches!(public.state, AppState::Reload(_))); assert!(matches!(public.state, AppState::Reload(_)));
@ -331,6 +356,11 @@ mod tests {
assert!(matches!(state, AppState::Search(_))); assert!(matches!(state, AppState::Search(_)));
app = state; app = state;
app = app.no_op();
let state = app.state();
assert!(matches!(state, AppState::Search(_)));
app = state;
let public = app.get(); let public = app.get();
assert!(matches!(public.state, AppState::Search(""))); assert!(matches!(public.state, AppState::Search("")));
@ -352,6 +382,11 @@ mod tests {
assert!(matches!(state, AppState::Fetch(_))); assert!(matches!(state, AppState::Fetch(_)));
app = state; app = state;
app = app.no_op();
let state = app.state();
assert!(matches!(state, AppState::Fetch(_)));
app = state;
let public = app.get(); let public = app.get();
assert!(matches!(public.state, AppState::Fetch(_))); assert!(matches!(public.state, AppState::Fetch(_)));
@ -372,6 +407,11 @@ mod tests {
assert!(matches!(state, AppState::Matches(_))); assert!(matches!(state, AppState::Matches(_)));
app = state; app = state;
app = app.no_op();
let state = app.state();
assert!(matches!(state, AppState::Matches(_)));
app = state;
let public = app.get(); let public = app.get();
assert!(matches!(public.state, AppState::Matches(_))); assert!(matches!(public.state, AppState::Matches(_)));
@ -390,6 +430,11 @@ mod tests {
assert!(matches!(state, AppState::Error(_))); assert!(matches!(state, AppState::Error(_)));
app = state; app = state;
app = app.no_op();
let state = app.state();
assert!(matches!(state, AppState::Error(_)));
app = state;
let public = app.get(); let public = app.get();
assert!(matches!(public.state, AppState::Error("get rekt"))); assert!(matches!(public.state, AppState::Error("get rekt")));
@ -408,6 +453,11 @@ mod tests {
assert!(matches!(state, AppState::Critical(_))); assert!(matches!(state, AppState::Critical(_)));
app = state; app = state;
app = app.no_op();
let state = app.state();
assert!(matches!(state, AppState::Critical(_)));
app = state;
let public = app.get(); let public = app.get();
assert!(matches!(public.state, AppState::Critical("get rekt"))); assert!(matches!(public.state, AppState::Critical("get rekt")));

View File

@ -53,10 +53,6 @@ impl IAppInteractReload for AppMachine<AppReload> {
fn hide_reload_menu(self) -> Self::APP { fn hide_reload_menu(self) -> Self::APP {
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()
} }
fn no_op(self) -> Self::APP {
self.into()
}
} }
trait IAppInteractReloadPrivate { trait IAppInteractReloadPrivate {
@ -131,11 +127,4 @@ mod tests {
let app = reload.reload_database(); let app = reload.reload_database();
app.unwrap_error(); app.unwrap_error();
} }
#[test]
fn no_op() {
let reload = AppMachine::reload(inner(music_hoard(vec![])));
let app = reload.no_op();
app.unwrap_reload();
}
} }

View File

@ -98,10 +98,6 @@ impl IAppInteractSearch for AppMachine<AppSearch> {
self.inner.selection.select_by_list(self.state.orig); self.inner.selection.select_by_list(self.state.orig);
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()
} }
fn no_op(self) -> Self::APP {
self.into()
}
} }
trait IAppInteractSearchPrivate { trait IAppInteractSearchPrivate {
@ -544,13 +540,6 @@ mod tests {
let browse = search.cancel_search().unwrap_browse(); let browse = search.cancel_search().unwrap_browse();
assert_eq!(browse.inner.selection.selected(), None); assert_eq!(browse.inner.selection.selected(), None);
} }
#[test]
fn no_op() {
let search = AppMachine::search(inner(music_hoard(vec![])), orig(None));
let app = search.no_op();
app.unwrap_search();
}
} }
#[cfg(nightly)] #[cfg(nightly)]

View File

@ -20,14 +20,14 @@ pub enum AppState<BS, IS, RS, SS, FS, MS, ES, CS> {
} }
pub trait IAppInteract { pub trait IAppInteract {
type BS: IAppInteractBrowse<APP = Self>; type BS: IAppBase<APP = Self> + IAppInteractBrowse<APP = Self>;
type IS: IAppInteractInfo<APP = Self>; type IS: IAppBase<APP = Self> + IAppInteractInfo<APP = Self>;
type RS: IAppInteractReload<APP = Self>; type RS: IAppBase<APP = Self> + IAppInteractReload<APP = Self>;
type SS: IAppInteractSearch<APP = Self>; type SS: IAppBase<APP = Self> + IAppInteractSearch<APP = Self>;
type FS: IAppInteractFetch<APP = Self> + IAppEventFetch<APP = Self>; type FS: IAppBase<APP = Self> + IAppInteractFetch<APP = Self> + IAppEventFetch<APP = Self>;
type MS: IAppInteractMatches<APP = Self>; type MS: IAppBase<APP = Self> + IAppInteractMatches<APP = Self>;
type ES: IAppInteractError<APP = Self>; type ES: IAppBase<APP = Self> + IAppInteractError<APP = Self>;
type CS: IAppInteractCritical<APP = Self>; type CS: IAppBase<APP = Self>;
fn is_running(&self) -> bool; fn is_running(&self) -> bool;
fn force_quit(self) -> Self; fn force_quit(self) -> Self;
@ -38,6 +38,12 @@ pub trait IAppInteract {
) -> AppState<Self::BS, Self::IS, Self::RS, Self::SS, Self::FS, Self::MS, Self::ES, Self::CS>; ) -> AppState<Self::BS, Self::IS, Self::RS, Self::SS, Self::FS, Self::MS, Self::ES, Self::CS>;
} }
pub trait IAppBase {
type APP: IAppInteract;
fn no_op(self) -> Self::APP;
}
pub trait IAppInteractBrowse { pub trait IAppInteractBrowse {
type APP: IAppInteract; type APP: IAppInteract;
@ -55,16 +61,12 @@ pub trait IAppInteractBrowse {
fn begin_search(self) -> Self::APP; fn begin_search(self) -> Self::APP;
fn fetch_musicbrainz(self) -> Self::APP; fn fetch_musicbrainz(self) -> Self::APP;
fn no_op(self) -> Self::APP;
} }
pub trait IAppInteractInfo { pub trait IAppInteractInfo {
type APP: IAppInteract; type APP: IAppInteract;
fn hide_info_overlay(self) -> Self::APP; fn hide_info_overlay(self) -> Self::APP;
fn no_op(self) -> Self::APP;
} }
pub trait IAppInteractReload { pub trait IAppInteractReload {
@ -73,8 +75,6 @@ pub trait IAppInteractReload {
fn reload_library(self) -> Self::APP; fn reload_library(self) -> Self::APP;
fn reload_database(self) -> Self::APP; fn reload_database(self) -> Self::APP;
fn hide_reload_menu(self) -> Self::APP; fn hide_reload_menu(self) -> Self::APP;
fn no_op(self) -> Self::APP;
} }
pub trait IAppInteractSearch { pub trait IAppInteractSearch {
@ -85,16 +85,12 @@ pub trait IAppInteractSearch {
fn step_back(self) -> Self::APP; fn step_back(self) -> Self::APP;
fn finish_search(self) -> Self::APP; fn finish_search(self) -> Self::APP;
fn cancel_search(self) -> Self::APP; fn cancel_search(self) -> Self::APP;
fn no_op(self) -> Self::APP;
} }
pub trait IAppInteractFetch { pub trait IAppInteractFetch {
type APP: IAppInteract; type APP: IAppInteract;
fn abort(self) -> Self::APP; fn abort(self) -> Self::APP;
fn no_op(self) -> Self::APP;
} }
pub trait IAppEventFetch { pub trait IAppEventFetch {
@ -111,22 +107,12 @@ pub trait IAppInteractMatches {
fn select(self) -> Self::APP; fn select(self) -> Self::APP;
fn abort(self) -> Self::APP; fn abort(self) -> Self::APP;
fn no_op(self) -> Self::APP;
} }
pub trait IAppInteractError { pub trait IAppInteractError {
type APP: IAppInteract; type APP: IAppInteract;
fn dismiss_error(self) -> Self::APP; fn dismiss_error(self) -> Self::APP;
fn no_op(self) -> Self::APP;
}
pub trait IAppInteractCritical {
type APP: IAppInteract;
fn no_op(self) -> Self::APP;
} }
// It would be preferable to have a getter for each field separately. However, the selection field // It would be preferable to have a getter for each field separately. However, the selection field

View File

@ -5,14 +5,13 @@ use mockall::automock;
use crate::tui::{ use crate::tui::{
app::{ app::{
AppState, Delta, IAppInteract, IAppInteractBrowse, IAppInteractCritical, IAppInteractError, AppState, Delta, IAppInteract, IAppInteractBrowse, IAppInteractError, IAppInteractFetch,
IAppInteractFetch, IAppInteractInfo, IAppInteractMatches, IAppInteractReload, IAppInteractInfo, IAppInteractMatches, IAppInteractReload, IAppInteractSearch,
IAppInteractSearch,
}, },
event::{Event, EventError, EventReceiver}, event::{Event, EventError, EventReceiver},
}; };
use super::app::IAppEventFetch; use super::app::{IAppBase, IAppEventFetch};
#[cfg_attr(test, automock)] #[cfg_attr(test, automock)]
pub trait IEventHandler<APP: IAppInteract> { pub trait IEventHandler<APP: IAppInteract> {