Replace MH: IMusicHoard generic with a trait object (#194)
All checks were successful
Cargo CI / Build and Test (push) Successful in 1m56s
Cargo CI / Lint (push) Successful in 1m8s

Closes #192

Reviewed-on: #194
This commit is contained in:
Wojciech Kozlowski 2024-08-27 18:45:03 +02:00
parent cf7e23c38c
commit b70499d8de
10 changed files with 132 additions and 151 deletions

View File

@ -70,8 +70,10 @@ struct DbOpt {
no_database: bool, no_database: bool,
} }
fn with<Database: IDatabase, Library: ILibrary>(builder: MusicHoardBuilder<Database, Library>) { fn with<Database: IDatabase + 'static, Library: ILibrary + 'static>(
let music_hoard = builder.build().expect("failed to initialise MusicHoard"); builder: MusicHoardBuilder<Database, Library>,
) {
let music_hoard = Box::new(builder.build().expect("failed to initialise MusicHoard"));
// Initialize the terminal user interface. // Initialize the terminal user interface.
let backend = CrosstermBackend::new(io::stdout()); let backend = CrosstermBackend::new(io::stdout());
@ -92,7 +94,7 @@ fn with<Database: IDatabase, Library: ILibrary>(builder: MusicHoardBuilder<Datab
Tui::run(terminal, app, ui, handler, listener).expect("failed to run tui"); Tui::run(terminal, app, ui, handler, listener).expect("failed to run tui");
} }
fn with_database<Library: ILibrary>( fn with_database<Library: ILibrary + 'static>(
db_opt: DbOpt, db_opt: DbOpt,
builder: MusicHoardBuilder<NoDatabase, Library>, builder: MusicHoardBuilder<NoDatabase, Library>,
) { ) {

View File

@ -2,19 +2,16 @@ use std::{thread, time};
use musichoard::collection::musicbrainz::IMusicBrainzRef; use musichoard::collection::musicbrainz::IMusicBrainzRef;
use crate::tui::{ use crate::tui::app::{
app::{ machine::{matches::AppMatchesInfo, App, AppInner, AppMachine},
machine::{matches::AppMatchesInfo, App, AppInner, AppMachine}, selection::{Delta, ListSelection},
selection::{Delta, ListSelection}, AppPublic, AppState, IAppInteractBrowse,
AppPublic, AppState, IAppInteractBrowse,
},
lib::IMusicHoard,
}; };
pub struct AppBrowse; pub struct AppBrowse;
impl<MH: IMusicHoard> AppMachine<MH, AppBrowse> { impl AppMachine<AppBrowse> {
pub fn browse(inner: AppInner<MH>) -> Self { pub fn browse(inner: AppInner) -> Self {
AppMachine { AppMachine {
inner, inner,
state: AppBrowse, state: AppBrowse,
@ -22,14 +19,14 @@ impl<MH: IMusicHoard> AppMachine<MH, AppBrowse> {
} }
} }
impl<MH: IMusicHoard> From<AppMachine<MH, AppBrowse>> for App<MH> { impl From<AppMachine<AppBrowse>> for App {
fn from(machine: AppMachine<MH, AppBrowse>) -> Self { fn from(machine: AppMachine<AppBrowse>) -> Self {
AppState::Browse(machine) AppState::Browse(machine)
} }
} }
impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppBrowse>> for AppPublic<'a> { impl<'a> From<&'a mut AppMachine<AppBrowse>> for AppPublic<'a> {
fn from(machine: &'a mut AppMachine<MH, AppBrowse>) -> Self { fn from(machine: &'a mut AppMachine<AppBrowse>) -> Self {
AppPublic { AppPublic {
inner: (&mut machine.inner).into(), inner: (&mut machine.inner).into(),
state: AppState::Browse(()), state: AppState::Browse(()),
@ -37,8 +34,8 @@ impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppBrowse>> for AppPublic<
} }
} }
impl<MH: IMusicHoard> IAppInteractBrowse for AppMachine<MH, AppBrowse> { impl IAppInteractBrowse for AppMachine<AppBrowse> {
type APP = App<MH>; type APP = App;
fn quit(mut self) -> Self::APP { fn quit(mut self) -> Self::APP {
self.inner.running = false; self.inner.running = false;

View File

@ -1,17 +1,14 @@
use crate::tui::{ use crate::tui::app::{
app::{ machine::{App, AppInner, AppMachine},
machine::{App, AppInner, AppMachine}, AppPublic, AppState, IAppInteractCritical,
AppPublic, AppState, IAppInteractCritical,
},
lib::IMusicHoard,
}; };
pub struct AppCritical { pub struct AppCritical {
string: String, string: String,
} }
impl<MH: IMusicHoard> AppMachine<MH, AppCritical> { impl AppMachine<AppCritical> {
pub fn critical<S: Into<String>>(inner: AppInner<MH>, string: S) -> Self { pub fn critical<S: Into<String>>(inner: AppInner, string: S) -> Self {
AppMachine { AppMachine {
inner, inner,
state: AppCritical { state: AppCritical {
@ -21,14 +18,14 @@ impl<MH: IMusicHoard> AppMachine<MH, AppCritical> {
} }
} }
impl<MH: IMusicHoard> From<AppMachine<MH, AppCritical>> for App<MH> { impl From<AppMachine<AppCritical>> for App {
fn from(machine: AppMachine<MH, AppCritical>) -> Self { fn from(machine: AppMachine<AppCritical>) -> Self {
AppState::Critical(machine) AppState::Critical(machine)
} }
} }
impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppCritical>> for AppPublic<'a> { impl<'a> From<&'a mut AppMachine<AppCritical>> for AppPublic<'a> {
fn from(machine: &'a mut AppMachine<MH, AppCritical>) -> Self { fn from(machine: &'a mut AppMachine<AppCritical>) -> Self {
AppPublic { AppPublic {
inner: (&mut machine.inner).into(), inner: (&mut machine.inner).into(),
state: AppState::Critical(&machine.state.string), state: AppState::Critical(&machine.state.string),
@ -36,8 +33,8 @@ impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppCritical>> for AppPubli
} }
} }
impl<MH: IMusicHoard> IAppInteractCritical for AppMachine<MH, AppCritical> { impl IAppInteractCritical for AppMachine<AppCritical> {
type APP = App<MH>; type APP = App;
fn no_op(self) -> Self::APP { fn no_op(self) -> Self::APP {
self.into() self.into()

View File

@ -1,17 +1,14 @@
use crate::tui::{ use crate::tui::app::{
app::{ machine::{App, AppInner, AppMachine},
machine::{App, AppInner, AppMachine}, AppPublic, AppState, IAppInteractError,
AppPublic, AppState, IAppInteractError,
},
lib::IMusicHoard,
}; };
pub struct AppError { pub struct AppError {
string: String, string: String,
} }
impl<MH: IMusicHoard> AppMachine<MH, AppError> { impl AppMachine<AppError> {
pub fn error<S: Into<String>>(inner: AppInner<MH>, string: S) -> Self { pub fn error<S: Into<String>>(inner: AppInner, string: S) -> Self {
AppMachine { AppMachine {
inner, inner,
state: AppError { state: AppError {
@ -21,14 +18,14 @@ impl<MH: IMusicHoard> AppMachine<MH, AppError> {
} }
} }
impl<MH: IMusicHoard> From<AppMachine<MH, AppError>> for App<MH> { impl From<AppMachine<AppError>> for App {
fn from(machine: AppMachine<MH, AppError>) -> Self { fn from(machine: AppMachine<AppError>) -> Self {
AppState::Error(machine) AppState::Error(machine)
} }
} }
impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppError>> for AppPublic<'a> { impl<'a> From<&'a mut AppMachine<AppError>> for AppPublic<'a> {
fn from(machine: &'a mut AppMachine<MH, AppError>) -> Self { fn from(machine: &'a mut AppMachine<AppError>) -> Self {
AppPublic { AppPublic {
inner: (&mut machine.inner).into(), inner: (&mut machine.inner).into(),
state: AppState::Error(&machine.state.string), state: AppState::Error(&machine.state.string),
@ -36,8 +33,8 @@ impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppError>> for AppPublic<'
} }
} }
impl<MH: IMusicHoard> IAppInteractError for AppMachine<MH, AppError> { impl IAppInteractError for AppMachine<AppError> {
type APP = App<MH>; type APP = App;
fn dismiss_error(self) -> Self::APP { fn dismiss_error(self) -> Self::APP {
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()

View File

@ -1,15 +1,12 @@
use crate::tui::{ use crate::tui::app::{
app::{ machine::{App, AppInner, AppMachine},
machine::{App, AppInner, AppMachine}, AppPublic, AppState, IAppInteractInfo,
AppPublic, AppState, IAppInteractInfo,
},
lib::IMusicHoard,
}; };
pub struct AppInfo; pub struct AppInfo;
impl<MH: IMusicHoard> AppMachine<MH, AppInfo> { impl AppMachine<AppInfo> {
pub fn info(inner: AppInner<MH>) -> Self { pub fn info(inner: AppInner) -> Self {
AppMachine { AppMachine {
inner, inner,
state: AppInfo, state: AppInfo,
@ -17,14 +14,14 @@ impl<MH: IMusicHoard> AppMachine<MH, AppInfo> {
} }
} }
impl<MH: IMusicHoard> From<AppMachine<MH, AppInfo>> for App<MH> { impl From<AppMachine<AppInfo>> for App {
fn from(machine: AppMachine<MH, AppInfo>) -> Self { fn from(machine: AppMachine<AppInfo>) -> Self {
AppState::Info(machine) AppState::Info(machine)
} }
} }
impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppInfo>> for AppPublic<'a> { impl<'a> From<&'a mut AppMachine<AppInfo>> for AppPublic<'a> {
fn from(machine: &'a mut AppMachine<MH, AppInfo>) -> Self { fn from(machine: &'a mut AppMachine<AppInfo>) -> Self {
AppPublic { AppPublic {
inner: (&mut machine.inner).into(), inner: (&mut machine.inner).into(),
state: AppState::Info(()), state: AppState::Info(()),
@ -32,8 +29,8 @@ impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppInfo>> for AppPublic<'a
} }
} }
impl<MH: IMusicHoard> IAppInteractInfo for AppMachine<MH, AppInfo> { impl IAppInteractInfo for AppMachine<AppInfo> {
type APP = App<MH>; type APP = App;
fn hide_info_overlay(self) -> Self::APP { fn hide_info_overlay(self) -> Self::APP {
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()

View File

@ -2,12 +2,9 @@ use std::cmp;
use musichoard::{collection::album::Album, interface::musicbrainz::Match}; use musichoard::{collection::album::Album, interface::musicbrainz::Match};
use crate::tui::{ use crate::tui::app::{
app::{ machine::{App, AppInner, AppMachine},
machine::{App, AppInner, AppMachine}, AppPublic, AppPublicMatches, AppState, IAppInteractMatches, WidgetState,
AppPublic, AppPublicMatches, AppState, IAppInteractMatches, WidgetState,
},
lib::IMusicHoard,
}; };
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
@ -22,8 +19,8 @@ pub struct AppMatches {
state: WidgetState, state: WidgetState,
} }
impl<MH: IMusicHoard> AppMachine<MH, AppMatches> { impl AppMachine<AppMatches> {
pub fn matches(inner: AppInner<MH>, matches_info_vec: Vec<AppMatchesInfo>) -> Self { pub fn matches(inner: AppInner, matches_info_vec: Vec<AppMatchesInfo>) -> Self {
let mut index = None; let mut index = None;
let mut state = WidgetState::default(); let mut state = WidgetState::default();
if let Some(matches_info) = matches_info_vec.first() { if let Some(matches_info) = matches_info_vec.first() {
@ -44,14 +41,14 @@ impl<MH: IMusicHoard> AppMachine<MH, AppMatches> {
} }
} }
impl<MH: IMusicHoard> From<AppMachine<MH, AppMatches>> for App<MH> { impl From<AppMachine<AppMatches>> for App {
fn from(machine: AppMachine<MH, AppMatches>) -> Self { fn from(machine: AppMachine<AppMatches>) -> Self {
AppState::Matches(machine) AppState::Matches(machine)
} }
} }
impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppMatches>> for AppPublic<'a> { impl<'a> From<&'a mut AppMachine<AppMatches>> for AppPublic<'a> {
fn from(machine: &'a mut AppMachine<MH, AppMatches>) -> Self { fn from(machine: &'a mut AppMachine<AppMatches>) -> Self {
let (matching, matches) = match machine.state.index { let (matching, matches) = match machine.state.index {
Some(index) => ( Some(index) => (
Some(&machine.state.matches_info_vec[index].matching), Some(&machine.state.matches_info_vec[index].matching),
@ -71,8 +68,8 @@ impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppMatches>> for AppPublic
} }
} }
impl<MH: IMusicHoard> IAppInteractMatches for AppMachine<MH, AppMatches> { impl IAppInteractMatches for AppMachine<AppMatches> {
type APP = App<MH>; type APP = App;
fn prev_match(mut self) -> Self::APP { fn prev_match(mut self) -> Self::APP {
if let Some(list_index) = self.state.state.list.selected() { if let Some(list_index) = self.state.state.list.selected() {

View File

@ -19,30 +19,30 @@ use matches::AppMatches;
use reload::AppReload; use reload::AppReload;
use search::AppSearch; use search::AppSearch;
pub type App<MH> = AppState< pub type App = AppState<
AppMachine<MH, AppBrowse>, AppMachine<AppBrowse>,
AppMachine<MH, AppInfo>, AppMachine<AppInfo>,
AppMachine<MH, AppReload>, AppMachine<AppReload>,
AppMachine<MH, AppSearch>, AppMachine<AppSearch>,
AppMachine<MH, AppMatches>, AppMachine<AppMatches>,
AppMachine<MH, AppError>, AppMachine<AppError>,
AppMachine<MH, AppCritical>, AppMachine<AppCritical>,
>; >;
pub struct AppMachine<MH: IMusicHoard, STATE> { pub struct AppMachine<STATE> {
inner: AppInner<MH>, inner: AppInner,
state: STATE, state: STATE,
} }
pub struct AppInner<MH: IMusicHoard> { pub struct AppInner {
running: bool, running: bool,
music_hoard: MH, music_hoard: Box<dyn IMusicHoard>,
mb_api: Box<dyn IMusicBrainz>, mb_api: Box<dyn IMusicBrainz>,
selection: Selection, selection: Selection,
} }
impl<MH: IMusicHoard> App<MH> { impl App {
pub fn new(mut music_hoard: MH, mb_api: Box<dyn IMusicBrainz>) -> Self { pub fn new(mut music_hoard: Box<dyn IMusicHoard>, mb_api: Box<dyn IMusicBrainz>) -> Self {
let init_result = Self::init(&mut music_hoard); let init_result = Self::init(&mut music_hoard);
let inner = AppInner::new(music_hoard, mb_api); let inner = AppInner::new(music_hoard, mb_api);
match init_result { match init_result {
@ -51,12 +51,12 @@ impl<MH: IMusicHoard> App<MH> {
} }
} }
fn init(music_hoard: &mut MH) -> Result<(), musichoard::Error> { fn init(music_hoard: &mut Box<dyn IMusicHoard>) -> Result<(), musichoard::Error> {
music_hoard.rescan_library()?; music_hoard.rescan_library()?;
Ok(()) Ok(())
} }
fn inner_ref(&self) -> &AppInner<MH> { fn inner_ref(&self) -> &AppInner {
match self { match self {
AppState::Browse(browse) => &browse.inner, AppState::Browse(browse) => &browse.inner,
AppState::Info(info) => &info.inner, AppState::Info(info) => &info.inner,
@ -68,7 +68,7 @@ impl<MH: IMusicHoard> App<MH> {
} }
} }
fn inner_mut(&mut self) -> &mut AppInner<MH> { fn inner_mut(&mut self) -> &mut AppInner {
match self { match self {
AppState::Browse(browse) => &mut browse.inner, AppState::Browse(browse) => &mut browse.inner,
AppState::Info(info) => &mut info.inner, AppState::Info(info) => &mut info.inner,
@ -81,14 +81,14 @@ impl<MH: IMusicHoard> App<MH> {
} }
} }
impl<MH: IMusicHoard> IAppInteract for App<MH> { impl IAppInteract for App {
type BS = AppMachine<MH, AppBrowse>; type BS = AppMachine<AppBrowse>;
type IS = AppMachine<MH, AppInfo>; type IS = AppMachine<AppInfo>;
type RS = AppMachine<MH, AppReload>; type RS = AppMachine<AppReload>;
type SS = AppMachine<MH, AppSearch>; type SS = AppMachine<AppSearch>;
type MS = AppMachine<MH, AppMatches>; type MS = AppMachine<AppMatches>;
type ES = AppMachine<MH, AppError>; type ES = AppMachine<AppError>;
type CS = AppMachine<MH, AppCritical>; type CS = AppMachine<AppCritical>;
fn is_running(&self) -> bool { fn is_running(&self) -> bool {
self.inner_ref().running self.inner_ref().running
@ -106,7 +106,7 @@ impl<MH: IMusicHoard> IAppInteract for App<MH> {
} }
} }
impl<MH: IMusicHoard> IAppAccess for App<MH> { impl IAppAccess for App {
fn get(&mut self) -> AppPublic { fn get(&mut self) -> AppPublic {
match self { match self {
AppState::Browse(browse) => browse.into(), AppState::Browse(browse) => browse.into(),
@ -120,8 +120,8 @@ impl<MH: IMusicHoard> IAppAccess for App<MH> {
} }
} }
impl<MH: IMusicHoard> AppInner<MH> { impl AppInner {
pub fn new(music_hoard: MH, mb_api: Box<dyn IMusicBrainz>) -> Self { pub fn new(music_hoard: Box<dyn IMusicHoard>, mb_api: Box<dyn IMusicBrainz>) -> Self {
let selection = Selection::new(music_hoard.get_collection()); let selection = Selection::new(music_hoard.get_collection());
AppInner { AppInner {
running: true, running: true,
@ -132,8 +132,8 @@ impl<MH: IMusicHoard> AppInner<MH> {
} }
} }
impl<'a, MH: IMusicHoard> From<&'a mut AppInner<MH>> for AppPublicInner<'a> { impl<'a> From<&'a mut AppInner> for AppPublicInner<'a> {
fn from(inner: &'a mut AppInner<MH>) -> Self { fn from(inner: &'a mut AppInner) -> Self {
AppPublicInner { AppPublicInner {
collection: inner.music_hoard.get_collection(), collection: inner.music_hoard.get_collection(),
selection: &mut inner.selection, selection: &mut inner.selection,
@ -203,14 +203,14 @@ mod tests {
} }
} }
pub fn music_hoard(collection: Collection) -> MockIMusicHoard { pub fn music_hoard(collection: Collection) -> Box<MockIMusicHoard> {
let mut music_hoard = MockIMusicHoard::new(); let mut music_hoard = Box::new(MockIMusicHoard::new());
music_hoard.expect_get_collection().return_const(collection); music_hoard.expect_get_collection().return_const(collection);
music_hoard music_hoard
} }
fn music_hoard_init(collection: Collection) -> MockIMusicHoard { fn music_hoard_init(collection: Collection) -> Box<MockIMusicHoard> {
let mut music_hoard = music_hoard(collection); let mut music_hoard = music_hoard(collection);
music_hoard music_hoard
@ -225,14 +225,14 @@ mod tests {
Box::new(MockIMusicBrainz::new()) Box::new(MockIMusicBrainz::new())
} }
pub fn inner(music_hoard: MockIMusicHoard) -> AppInner<MockIMusicHoard> { pub fn inner(music_hoard: Box<MockIMusicHoard>) -> AppInner {
AppInner::new(music_hoard, mb_api()) AppInner::new(music_hoard, mb_api())
} }
pub fn inner_with_mb( pub fn inner_with_mb(
music_hoard: MockIMusicHoard, music_hoard: Box<MockIMusicHoard>,
mb_api: Box<MockIMusicBrainz>, mb_api: Box<MockIMusicBrainz>,
) -> AppInner<MockIMusicHoard> { ) -> AppInner {
AppInner::new(music_hoard, mb_api) AppInner::new(music_hoard, mb_api)
} }
@ -362,7 +362,7 @@ mod tests {
#[test] #[test]
fn init_error() { fn init_error() {
let mut music_hoard = MockIMusicHoard::new(); let mut music_hoard = Box::new(MockIMusicHoard::new());
music_hoard music_hoard
.expect_rescan_library() .expect_rescan_library()

View File

@ -1,16 +1,13 @@
use crate::tui::{ use crate::tui::app::{
app::{ machine::{App, AppInner, AppMachine},
machine::{App, AppInner, AppMachine}, selection::KeySelection,
selection::KeySelection, AppPublic, AppState, IAppInteractReload,
AppPublic, AppState, IAppInteractReload,
},
lib::IMusicHoard,
}; };
pub struct AppReload; pub struct AppReload;
impl<MH: IMusicHoard> AppMachine<MH, AppReload> { impl AppMachine<AppReload> {
pub fn reload(inner: AppInner<MH>) -> Self { pub fn reload(inner: AppInner) -> Self {
AppMachine { AppMachine {
inner, inner,
state: AppReload, state: AppReload,
@ -18,13 +15,13 @@ impl<MH: IMusicHoard> AppMachine<MH, AppReload> {
} }
} }
impl<MH: IMusicHoard> From<AppMachine<MH, AppReload>> for App<MH> { impl From<AppMachine<AppReload>> for App {
fn from(machine: AppMachine<MH, AppReload>) -> Self { fn from(machine: AppMachine<AppReload>) -> Self {
AppState::Reload(machine) AppState::Reload(machine)
} }
} }
impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppReload>> for AppPublic<'a> { impl<'a> From<&'a mut AppMachine<AppReload>> for AppPublic<'a> {
fn from(machine: &'a mut AppMachine<MH, AppReload>) -> Self { fn from(machine: &'a mut AppMachine<AppReload>) -> Self {
AppPublic { AppPublic {
inner: (&mut machine.inner).into(), inner: (&mut machine.inner).into(),
state: AppState::Reload(()), state: AppState::Reload(()),
@ -32,8 +29,8 @@ impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppReload>> for AppPublic<
} }
} }
impl<MH: IMusicHoard> IAppInteractReload for AppMachine<MH, AppReload> { impl IAppInteractReload for AppMachine<AppReload> {
type APP = App<MH>; type APP = App;
fn reload_library(mut self) -> Self::APP { fn reload_library(mut self) -> Self::APP {
let previous = KeySelection::get( let previous = KeySelection::get(
@ -62,12 +59,12 @@ impl<MH: IMusicHoard> IAppInteractReload for AppMachine<MH, AppReload> {
} }
} }
trait IAppInteractReloadPrivate<MH: IMusicHoard> { trait IAppInteractReloadPrivate {
fn refresh(self, previous: KeySelection, result: Result<(), musichoard::Error>) -> App<MH>; fn refresh(self, previous: KeySelection, result: Result<(), musichoard::Error>) -> App;
} }
impl<MH: IMusicHoard> IAppInteractReloadPrivate<MH> for AppMachine<MH, AppReload> { impl IAppInteractReloadPrivate for AppMachine<AppReload> {
fn refresh(mut self, previous: KeySelection, result: Result<(), musichoard::Error>) -> App<MH> { fn refresh(mut self, previous: KeySelection, result: Result<(), musichoard::Error>) -> App {
match result { match result {
Ok(()) => { Ok(()) => {
self.inner self.inner

View File

@ -3,13 +3,10 @@ use once_cell::sync::Lazy;
use musichoard::collection::{album::Album, artist::Artist, track::Track}; use musichoard::collection::{album::Album, artist::Artist, track::Track};
use crate::tui::{ use crate::tui::app::{
app::{ machine::{App, AppInner, AppMachine},
machine::{App, AppInner, AppMachine}, selection::{ListSelection, SelectionState},
selection::{ListSelection, SelectionState}, AppPublic, AppState, Category, IAppInteractSearch,
AppPublic, AppState, Category, IAppInteractSearch,
},
lib::IMusicHoard,
}; };
// Unlikely that this covers all possible strings, but it should at least cover strings // Unlikely that this covers all possible strings, but it should at least cover strings
@ -34,8 +31,8 @@ struct AppSearchMemo {
char: bool, char: bool,
} }
impl<MH: IMusicHoard> AppMachine<MH, AppSearch> { impl AppMachine<AppSearch> {
pub fn search(inner: AppInner<MH>, orig: ListSelection) -> Self { pub fn search(inner: AppInner, orig: ListSelection) -> Self {
AppMachine { AppMachine {
inner, inner,
state: AppSearch { state: AppSearch {
@ -47,14 +44,14 @@ impl<MH: IMusicHoard> AppMachine<MH, AppSearch> {
} }
} }
impl<MH: IMusicHoard> From<AppMachine<MH, AppSearch>> for App<MH> { impl From<AppMachine<AppSearch>> for App {
fn from(machine: AppMachine<MH, AppSearch>) -> Self { fn from(machine: AppMachine<AppSearch>) -> Self {
AppState::Search(machine) AppState::Search(machine)
} }
} }
impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppSearch>> for AppPublic<'a> { impl<'a> From<&'a mut AppMachine<AppSearch>> for AppPublic<'a> {
fn from(machine: &'a mut AppMachine<MH, AppSearch>) -> Self { fn from(machine: &'a mut AppMachine<AppSearch>) -> Self {
AppPublic { AppPublic {
inner: (&mut machine.inner).into(), inner: (&mut machine.inner).into(),
state: AppState::Search(&machine.state.string), state: AppState::Search(&machine.state.string),
@ -62,8 +59,8 @@ impl<'a, MH: IMusicHoard> From<&'a mut AppMachine<MH, AppSearch>> for AppPublic<
} }
} }
impl<MH: IMusicHoard> IAppInteractSearch for AppMachine<MH, AppSearch> { impl IAppInteractSearch for AppMachine<AppSearch> {
type APP = App<MH>; type APP = App;
fn append_character(mut self, ch: char) -> Self::APP { fn append_character(mut self, ch: char) -> Self::APP {
self.state.string.push(ch); self.state.string.push(ch);
@ -127,7 +124,7 @@ trait IAppInteractSearchPrivate {
fn normalize_search(search: &str, lowercase: bool, asciify: bool) -> String; fn normalize_search(search: &str, lowercase: bool, asciify: bool) -> String;
} }
impl<MH: IMusicHoard> IAppInteractSearchPrivate for AppMachine<MH, AppSearch> { impl IAppInteractSearchPrivate for AppMachine<AppSearch> {
fn incremental_search(&mut self, next: bool) { fn incremental_search(&mut self, next: bool) {
let collection = self.inner.music_hoard.get_collection(); let collection = self.inner.music_hoard.get_collection();
let search = &self.state.string; let search = &self.state.string;

View File

@ -191,8 +191,8 @@ mod tests {
Terminal::new(backend).unwrap() Terminal::new(backend).unwrap()
} }
fn music_hoard(collection: Collection) -> MockIMusicHoard { fn music_hoard(collection: Collection) -> Box<MockIMusicHoard> {
let mut music_hoard = MockIMusicHoard::new(); let mut music_hoard = Box::new(MockIMusicHoard::new());
music_hoard.expect_reload_database().returning(|| Ok(())); music_hoard.expect_reload_database().returning(|| Ok(()));
music_hoard.expect_rescan_library().returning(|| Ok(())); music_hoard.expect_rescan_library().returning(|| Ok(()));
@ -201,7 +201,7 @@ mod tests {
music_hoard music_hoard
} }
fn app(collection: Collection) -> App<MockIMusicHoard> { fn app(collection: Collection) -> App {
App::new(music_hoard(collection), Box::new(MockIMusicBrainz::new())) App::new(music_hoard(collection), Box::new(MockIMusicBrainz::new()))
} }
@ -216,11 +216,11 @@ mod tests {
listener listener
} }
fn handler() -> MockIEventHandler<App<MockIMusicHoard>> { fn handler() -> MockIEventHandler<App> {
let mut handler = MockIEventHandler::new(); let mut handler = MockIEventHandler::new();
handler handler
.expect_handle_next_event() .expect_handle_next_event()
.return_once(|app: App<MockIMusicHoard>| Ok(app.force_quit())); .return_once(|app: App| Ok(app.force_quit()));
handler handler
} }