diff --git a/src/tui/app/app.rs b/src/tui/app/app.rs index f41cdf9..6dc8361 100644 --- a/src/tui/app/app.rs +++ b/src/tui/app/app.rs @@ -1,124 +1,16 @@ // FIXME: Split file apart #![allow(clippy::module_inception)] -use musichoard::collection::Collection; - use crate::tui::{ - app::selection::{Delta, IdSelection, ListSelection, Selection}, + app::{ + selection::{Delta, IdSelection, ListSelection, Selection}, + AppPublic, AppPublicInner, AppState, IAppAccess, IAppInteract, IAppInteractBrowse, + IAppInteractCritical, IAppInteractError, IAppInteractInfo, IAppInteractReload, + IAppInteractSearch, + }, lib::IMusicHoard, }; -pub enum AppState { - Browse(BS), - Info(IS), - Reload(RS), - Search(SS), - Error(ES), - Critical(CS), -} - -impl AppState { - pub fn is_search(&self) -> bool { - matches!(self, AppState::Search(_)) - } -} - -pub trait IAppInteract { - type BS: IAppInteractBrowse; - type IS: IAppInteractInfo; - type RS: IAppInteractReload; - type SS: IAppInteractSearch; - type ES: IAppInteractError; - type CS: IAppInteractCritical; - - fn is_running(&self) -> bool; - fn force_quit(self) -> Self; - - #[allow(clippy::type_complexity)] - fn state(self) -> AppState; -} - -pub trait IAppInteractBrowse { - type APP: IAppInteract; - - fn save_and_quit(self) -> Self::APP; - - fn increment_category(self) -> Self::APP; - fn decrement_category(self) -> Self::APP; - fn increment_selection(self, delta: Delta) -> Self::APP; - fn decrement_selection(self, delta: Delta) -> Self::APP; - - fn show_info_overlay(self) -> Self::APP; - - fn show_reload_menu(self) -> Self::APP; - - fn begin_search(self) -> Self::APP; - - fn no_op(self) -> Self::APP; -} - -pub trait IAppInteractInfo { - type APP: IAppInteract; - - fn hide_info_overlay(self) -> Self::APP; - - fn no_op(self) -> Self::APP; -} - -pub trait IAppInteractReload { - type APP: IAppInteract; - - fn reload_library(self) -> Self::APP; - fn reload_database(self) -> Self::APP; - fn hide_reload_menu(self) -> Self::APP; - - fn no_op(self) -> Self::APP; -} - -pub trait IAppInteractSearch { - type APP: IAppInteract; - - fn append_character(self, ch: char) -> Self::APP; - fn search_next(self) -> Self::APP; - fn step_back(self) -> Self::APP; - fn finish_search(self) -> Self::APP; - fn cancel_search(self) -> Self::APP; - - fn no_op(self) -> Self::APP; -} - -pub trait IAppInteractError { - type APP: IAppInteract; - - fn dismiss_error(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 -// needs to be mutably accessible requiring a mutable borrow of the entire struct if behind a trait. -// This in turn complicates simultaneous field access since only a single mutable borrow is allowed. -// Therefore, all fields are grouped into a single struct and returned as a batch. -pub trait IAppAccess { - fn get(&mut self) -> AppPublic; -} - -pub type AppPublicState<'app> = AppState<(), (), (), &'app str, &'app str, &'app str>; - -pub struct AppPublic<'app> { - pub inner: AppPublicInner<'app>, - pub state: AppPublicState<'app>, -} - -pub struct AppPublicInner<'app> { - pub collection: &'app Collection, - pub selection: &'app mut Selection, -} - struct AppInner { running: bool, music_hoard: MH, @@ -460,7 +352,13 @@ impl<'app, MH: IMusicHoard> From<&'app mut AppInner> for AppPublicInner<'app #[cfg(test)] mod tests { - use crate::tui::{app::selection::Category, lib::MockIMusicHoard, testmod::COLLECTION}; + use musichoard::collection::Collection; + + use crate::tui::{ + app::{selection::Category, AppPublicState}, + lib::MockIMusicHoard, + testmod::COLLECTION, + }; use super::*; diff --git a/src/tui/app/mod.rs b/src/tui/app/mod.rs index 57892fa..2cb6d31 100644 --- a/src/tui/app/mod.rs +++ b/src/tui/app/mod.rs @@ -1,2 +1,118 @@ pub mod app; pub mod selection; + +use musichoard::collection::Collection; + +use selection::{Delta, Selection}; + +pub enum AppState { + Browse(BS), + Info(IS), + Reload(RS), + Search(SS), + Error(ES), + Critical(CS), +} + +pub trait IAppInteract { + type BS: IAppInteractBrowse; + type IS: IAppInteractInfo; + type RS: IAppInteractReload; + type SS: IAppInteractSearch; + type ES: IAppInteractError; + type CS: IAppInteractCritical; + + fn is_running(&self) -> bool; + fn force_quit(self) -> Self; + + #[allow(clippy::type_complexity)] + fn state(self) -> AppState; +} + +// FIXME: move to returning specific states - for errors, return Result +pub trait IAppInteractBrowse { + type APP: IAppInteract; + + fn save_and_quit(self) -> Self::APP; + + fn increment_category(self) -> Self::APP; + fn decrement_category(self) -> Self::APP; + fn increment_selection(self, delta: Delta) -> Self::APP; + fn decrement_selection(self, delta: Delta) -> Self::APP; + + fn show_info_overlay(self) -> Self::APP; + + fn show_reload_menu(self) -> Self::APP; + + fn begin_search(self) -> Self::APP; + + fn no_op(self) -> Self::APP; +} + +pub trait IAppInteractInfo { + type APP: IAppInteract; + + fn hide_info_overlay(self) -> Self::APP; + + fn no_op(self) -> Self::APP; +} + +pub trait IAppInteractReload { + type APP: IAppInteract; + + fn reload_library(self) -> Self::APP; + fn reload_database(self) -> Self::APP; + fn hide_reload_menu(self) -> Self::APP; + + fn no_op(self) -> Self::APP; +} + +pub trait IAppInteractSearch { + type APP: IAppInteract; + + fn append_character(self, ch: char) -> Self::APP; + fn search_next(self) -> Self::APP; + fn step_back(self) -> Self::APP; + fn finish_search(self) -> Self::APP; + fn cancel_search(self) -> Self::APP; + + fn no_op(self) -> Self::APP; +} + +pub trait IAppInteractError { + type APP: IAppInteract; + + fn dismiss_error(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 +// needs to be mutably accessible requiring a mutable borrow of the entire struct if behind a trait. +// This in turn complicates simultaneous field access since only a single mutable borrow is allowed. +// Therefore, all fields are grouped into a single struct and returned as a batch. +pub trait IAppAccess { + fn get(&mut self) -> AppPublic; +} + +pub struct AppPublic<'app> { + pub inner: AppPublicInner<'app>, + pub state: AppPublicState<'app>, +} + +pub struct AppPublicInner<'app> { + pub collection: &'app Collection, + pub selection: &'app mut Selection, +} + +pub type AppPublicState<'app> = AppState<(), (), (), &'app str, &'app str, &'app str>; + +impl AppState { + pub fn is_search(&self) -> bool { + matches!(self, AppState::Search(_)) + } +} diff --git a/src/tui/handler.rs b/src/tui/handler.rs index dfd3d8b..be88bb2 100644 --- a/src/tui/handler.rs +++ b/src/tui/handler.rs @@ -5,17 +5,12 @@ use mockall::automock; use crate::tui::{ app::{ - app::{ - AppState, IAppInteract, IAppInteractBrowse, IAppInteractError, IAppInteractInfo, - IAppInteractReload, IAppInteractSearch, - }, - selection::Delta, + selection::Delta, AppState, IAppInteract, IAppInteractBrowse, IAppInteractCritical, + IAppInteractError, IAppInteractInfo, IAppInteractReload, IAppInteractSearch, }, event::{Event, EventError, EventReceiver}, }; -use super::app::app::IAppInteractCritical; - #[cfg_attr(test, automock)] pub trait IEventHandler { fn handle_next_event(&self, app: APP) -> Result; diff --git a/src/tui/mod.rs b/src/tui/mod.rs index fd1f2c8..629ecaa 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -19,7 +19,7 @@ use std::io; use std::marker::PhantomData; use crate::tui::{ - app::app::{IAppAccess, IAppInteract}, + app::{IAppAccess, IAppInteract}, event::EventError, handler::IEventHandler, listener::IEventListener, diff --git a/src/tui/ui.rs b/src/tui/ui.rs index b03b8b6..80536a9 100644 --- a/src/tui/ui.rs +++ b/src/tui/ui.rs @@ -14,8 +14,8 @@ use ratatui::{ }; use crate::tui::app::{ - app::{AppPublicState, AppState, IAppAccess}, selection::{Category, Selection, WidgetState}, + AppPublicState, AppState, IAppAccess, }; pub trait IUi { @@ -695,10 +695,7 @@ impl IUi for Ui { #[cfg(test)] mod tests { use crate::tui::{ - app::{ - app::{AppPublic, AppPublicInner}, - selection::Delta, - }, + app::{selection::Delta, AppPublic, AppPublicInner}, testmod::COLLECTION, tests::terminal, };