Provide search functionality through the TUI #134
@ -18,28 +18,26 @@ struct AppInner<MH: IMusicHoard> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub type App<MH> = AppState<
|
pub type App<MH> = AppState<
|
||||||
AppBrowseState<MH>,
|
AppMachine<MH, AppBrowse>,
|
||||||
AppInfoState<MH>,
|
AppMachine<MH, AppInfo>,
|
||||||
AppReloadState<MH>,
|
AppMachine<MH, AppReload>,
|
||||||
AppSearchState<MH>,
|
AppMachine<MH, AppSearch>,
|
||||||
AppErrorState<MH>,
|
AppMachine<MH, AppError>,
|
||||||
AppCriticalState<MH>,
|
AppMachine<MH, AppCritical>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
pub struct AppBrowseState<MH: IMusicHoard> {
|
pub struct AppMachine<MH: IMusicHoard, STATE> {
|
||||||
inner: AppInner<MH>,
|
inner: AppInner<MH>,
|
||||||
|
state: STATE,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AppInfoState<MH: IMusicHoard> {
|
pub struct AppBrowse;
|
||||||
inner: AppInner<MH>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AppReloadState<MH: IMusicHoard> {
|
pub struct AppInfo;
|
||||||
inner: AppInner<MH>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AppSearchState<MH: IMusicHoard> {
|
pub struct AppReload;
|
||||||
inner: AppInner<MH>,
|
|
||||||
|
pub struct AppSearch {
|
||||||
string: String,
|
string: String,
|
||||||
orig: ListSelection,
|
orig: ListSelection,
|
||||||
memo: Vec<AppSearchMemo>,
|
memo: Vec<AppSearchMemo>,
|
||||||
@ -50,13 +48,11 @@ struct AppSearchMemo {
|
|||||||
char: bool,
|
char: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AppErrorState<MH: IMusicHoard> {
|
pub struct AppError {
|
||||||
inner: AppInner<MH>,
|
|
||||||
string: String,
|
string: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AppCriticalState<MH: IMusicHoard> {
|
pub struct AppCritical {
|
||||||
inner: AppInner<MH>,
|
|
||||||
string: String,
|
string: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,10 +66,15 @@ impl<MH: IMusicHoard> App<MH> {
|
|||||||
selection,
|
selection,
|
||||||
};
|
};
|
||||||
match init_result {
|
match init_result {
|
||||||
Ok(()) => Self::Browse(AppBrowseState { inner }),
|
Ok(()) => Self::Browse(AppMachine {
|
||||||
Err(err) => Self::Critical(AppCriticalState {
|
|
||||||
inner,
|
inner,
|
||||||
|
state: AppBrowse,
|
||||||
|
}),
|
||||||
|
Err(err) => Self::Critical(AppMachine {
|
||||||
|
inner,
|
||||||
|
state: AppCritical {
|
||||||
string: err.to_string(),
|
string: err.to_string(),
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,12 +109,12 @@ impl<MH: IMusicHoard> App<MH> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<MH: IMusicHoard> IAppInteract for App<MH> {
|
impl<MH: IMusicHoard> IAppInteract for App<MH> {
|
||||||
type BS = AppBrowseState<MH>;
|
type BS = AppMachine<MH, AppBrowse>;
|
||||||
type IS = AppInfoState<MH>;
|
type IS = AppMachine<MH, AppInfo>;
|
||||||
type RS = AppReloadState<MH>;
|
type RS = AppMachine<MH, AppReload>;
|
||||||
type SS = AppSearchState<MH>;
|
type SS = AppMachine<MH, AppSearch>;
|
||||||
type ES = AppErrorState<MH>;
|
type ES = AppMachine<MH, AppError>;
|
||||||
type CS = AppCriticalState<MH>;
|
type CS = AppMachine<MH, AppCritical>;
|
||||||
|
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
self.inner_ref().running
|
self.inner_ref().running
|
||||||
@ -129,7 +130,7 @@ impl<MH: IMusicHoard> IAppInteract for App<MH> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<MH: IMusicHoard> IAppInteractBrowse for AppBrowseState<MH> {
|
impl<MH: IMusicHoard> IAppInteractBrowse for AppMachine<MH, AppBrowse> {
|
||||||
type APP = App<MH>;
|
type APP = App<MH>;
|
||||||
|
|
||||||
fn save_and_quit(mut self) -> Self::APP {
|
fn save_and_quit(mut self) -> Self::APP {
|
||||||
@ -138,9 +139,11 @@ impl<MH: IMusicHoard> IAppInteractBrowse for AppBrowseState<MH> {
|
|||||||
self.inner.running = false;
|
self.inner.running = false;
|
||||||
AppState::Browse(self)
|
AppState::Browse(self)
|
||||||
}
|
}
|
||||||
Err(err) => AppState::Error(AppErrorState {
|
Err(err) => AppState::Error(AppMachine {
|
||||||
inner: self.inner,
|
inner: self.inner,
|
||||||
|
state: AppError {
|
||||||
string: err.to_string(),
|
string: err.to_string(),
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,11 +173,17 @@ impl<MH: IMusicHoard> IAppInteractBrowse for AppBrowseState<MH> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn show_info_overlay(self) -> Self::APP {
|
fn show_info_overlay(self) -> Self::APP {
|
||||||
AppState::Info(AppInfoState { inner: self.inner })
|
AppState::Info(AppMachine {
|
||||||
|
inner: self.inner,
|
||||||
|
state: AppInfo,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_reload_menu(self) -> Self::APP {
|
fn show_reload_menu(self) -> Self::APP {
|
||||||
AppState::Reload(AppReloadState { inner: self.inner })
|
AppState::Reload(AppMachine {
|
||||||
|
inner: self.inner,
|
||||||
|
state: AppReload,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin_search(mut self) -> Self::APP {
|
fn begin_search(mut self) -> Self::APP {
|
||||||
@ -182,11 +191,13 @@ impl<MH: IMusicHoard> IAppInteractBrowse for AppBrowseState<MH> {
|
|||||||
self.inner
|
self.inner
|
||||||
.selection
|
.selection
|
||||||
.reset_artist(self.inner.music_hoard.get_collection());
|
.reset_artist(self.inner.music_hoard.get_collection());
|
||||||
AppState::Search(AppSearchState {
|
AppState::Search(AppMachine {
|
||||||
inner: self.inner,
|
inner: self.inner,
|
||||||
|
state: AppSearch {
|
||||||
string: String::new(),
|
string: String::new(),
|
||||||
orig,
|
orig,
|
||||||
memo: vec![],
|
memo: vec![],
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,11 +206,14 @@ impl<MH: IMusicHoard> IAppInteractBrowse for AppBrowseState<MH> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<MH: IMusicHoard> IAppInteractInfo for AppInfoState<MH> {
|
impl<MH: IMusicHoard> IAppInteractInfo for AppMachine<MH, AppInfo> {
|
||||||
type APP = App<MH>;
|
type APP = App<MH>;
|
||||||
|
|
||||||
fn hide_info_overlay(self) -> Self::APP {
|
fn hide_info_overlay(self) -> Self::APP {
|
||||||
AppState::Browse(AppBrowseState { inner: self.inner })
|
AppState::Browse(AppMachine {
|
||||||
|
inner: self.inner,
|
||||||
|
state: AppBrowse,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn no_op(self) -> Self::APP {
|
fn no_op(self) -> Self::APP {
|
||||||
@ -207,7 +221,7 @@ impl<MH: IMusicHoard> IAppInteractInfo for AppInfoState<MH> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<MH: IMusicHoard> IAppInteractReload for AppReloadState<MH> {
|
impl<MH: IMusicHoard> IAppInteractReload for AppMachine<MH, AppReload> {
|
||||||
type APP = App<MH>;
|
type APP = App<MH>;
|
||||||
|
|
||||||
fn reload_library(mut self) -> Self::APP {
|
fn reload_library(mut self) -> Self::APP {
|
||||||
@ -229,7 +243,10 @@ impl<MH: IMusicHoard> IAppInteractReload for AppReloadState<MH> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn hide_reload_menu(self) -> Self::APP {
|
fn hide_reload_menu(self) -> Self::APP {
|
||||||
AppState::Browse(AppBrowseState { inner: self.inner })
|
AppState::Browse(AppMachine {
|
||||||
|
inner: self.inner,
|
||||||
|
state: AppBrowse,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn no_op(self) -> Self::APP {
|
fn no_op(self) -> Self::APP {
|
||||||
@ -241,54 +258,60 @@ trait IAppInteractReloadPrivate<MH: IMusicHoard> {
|
|||||||
fn refresh(self, previous: IdSelection, result: Result<(), musichoard::Error>) -> App<MH>;
|
fn refresh(self, previous: IdSelection, result: Result<(), musichoard::Error>) -> App<MH>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<MH: IMusicHoard> IAppInteractReloadPrivate<MH> for AppReloadState<MH> {
|
impl<MH: IMusicHoard> IAppInteractReloadPrivate<MH> for AppMachine<MH, AppReload> {
|
||||||
fn refresh(mut self, previous: IdSelection, result: Result<(), musichoard::Error>) -> App<MH> {
|
fn refresh(mut self, previous: IdSelection, result: Result<(), musichoard::Error>) -> App<MH> {
|
||||||
match result {
|
match result {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
self.inner
|
self.inner
|
||||||
.selection
|
.selection
|
||||||
.select_by_id(self.inner.music_hoard.get_collection(), previous);
|
.select_by_id(self.inner.music_hoard.get_collection(), previous);
|
||||||
AppState::Browse(AppBrowseState { inner: self.inner })
|
AppState::Browse(AppMachine {
|
||||||
}
|
|
||||||
Err(err) => AppState::Error(AppErrorState {
|
|
||||||
inner: self.inner,
|
inner: self.inner,
|
||||||
|
state: AppBrowse,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(err) => AppState::Error(AppMachine {
|
||||||
|
inner: self.inner,
|
||||||
|
state: AppError {
|
||||||
string: err.to_string(),
|
string: err.to_string(),
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<MH: IMusicHoard> IAppInteractSearch for AppSearchState<MH> {
|
impl<MH: IMusicHoard> IAppInteractSearch for AppMachine<MH, AppSearch> {
|
||||||
type APP = App<MH>;
|
type APP = App<MH>;
|
||||||
|
|
||||||
fn append_character(mut self, ch: char) -> Self::APP {
|
fn append_character(mut self, ch: char) -> Self::APP {
|
||||||
let collection = self.inner.music_hoard.get_collection();
|
let collection = self.inner.music_hoard.get_collection();
|
||||||
self.string.push(ch);
|
self.state.string.push(ch);
|
||||||
let index = self
|
let index =
|
||||||
.inner
|
self.inner
|
||||||
.selection
|
.selection
|
||||||
.incremental_artist_search(collection, &self.string, false);
|
.incremental_artist_search(collection, &self.state.string, false);
|
||||||
self.memo.push(AppSearchMemo { index, char: true });
|
self.state.memo.push(AppSearchMemo { index, char: true });
|
||||||
AppState::Search(self)
|
AppState::Search(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_next(mut self) -> Self::APP {
|
fn search_next(mut self) -> Self::APP {
|
||||||
let collection = self.inner.music_hoard.get_collection();
|
let collection = self.inner.music_hoard.get_collection();
|
||||||
if !self.string.is_empty() {
|
if !self.state.string.is_empty() {
|
||||||
let index =
|
let index = self.inner.selection.incremental_artist_search(
|
||||||
self.inner
|
collection,
|
||||||
.selection
|
&self.state.string,
|
||||||
.incremental_artist_search(collection, &self.string, true);
|
true,
|
||||||
self.memo.push(AppSearchMemo { index, char: false });
|
);
|
||||||
|
self.state.memo.push(AppSearchMemo { index, char: false });
|
||||||
}
|
}
|
||||||
AppState::Search(self)
|
AppState::Search(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step_back(mut self) -> Self::APP {
|
fn step_back(mut self) -> Self::APP {
|
||||||
let collection = self.inner.music_hoard.get_collection();
|
let collection = self.inner.music_hoard.get_collection();
|
||||||
if let Some(memo) = self.memo.pop() {
|
if let Some(memo) = self.state.memo.pop() {
|
||||||
if memo.char {
|
if memo.char {
|
||||||
self.string.pop();
|
self.state.string.pop();
|
||||||
}
|
}
|
||||||
self.inner.selection.select_artist(collection, memo.index);
|
self.inner.selection.select_artist(collection, memo.index);
|
||||||
}
|
}
|
||||||
@ -296,12 +319,18 @@ impl<MH: IMusicHoard> IAppInteractSearch for AppSearchState<MH> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn finish_search(self) -> Self::APP {
|
fn finish_search(self) -> Self::APP {
|
||||||
AppState::Browse(AppBrowseState { inner: self.inner })
|
AppState::Browse(AppMachine {
|
||||||
|
inner: self.inner,
|
||||||
|
state: AppBrowse,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cancel_search(mut self) -> Self::APP {
|
fn cancel_search(mut self) -> Self::APP {
|
||||||
self.inner.selection.select_by_list(self.orig);
|
self.inner.selection.select_by_list(self.state.orig);
|
||||||
AppState::Browse(AppBrowseState { inner: self.inner })
|
AppState::Browse(AppMachine {
|
||||||
|
inner: self.inner,
|
||||||
|
state: AppBrowse,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn no_op(self) -> Self::APP {
|
fn no_op(self) -> Self::APP {
|
||||||
@ -309,15 +338,18 @@ impl<MH: IMusicHoard> IAppInteractSearch for AppSearchState<MH> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<MH: IMusicHoard> IAppInteractError for AppErrorState<MH> {
|
impl<MH: IMusicHoard> IAppInteractError for AppMachine<MH, AppError> {
|
||||||
type APP = App<MH>;
|
type APP = App<MH>;
|
||||||
|
|
||||||
fn dismiss_error(self) -> Self::APP {
|
fn dismiss_error(self) -> Self::APP {
|
||||||
AppState::Browse(AppBrowseState { inner: self.inner })
|
AppState::Browse(AppMachine {
|
||||||
|
inner: self.inner,
|
||||||
|
state: AppBrowse,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<MH: IMusicHoard> IAppInteractCritical for AppCriticalState<MH> {
|
impl<MH: IMusicHoard> IAppInteractCritical for AppMachine<MH, AppCritical> {
|
||||||
type APP = App<MH>;
|
type APP = App<MH>;
|
||||||
|
|
||||||
fn no_op(self) -> Self::APP {
|
fn no_op(self) -> Self::APP {
|
||||||
@ -342,15 +374,15 @@ impl<MH: IMusicHoard> IAppAccess for App<MH> {
|
|||||||
},
|
},
|
||||||
AppState::Search(search) => AppPublic {
|
AppState::Search(search) => AppPublic {
|
||||||
inner: (&mut search.inner).into(),
|
inner: (&mut search.inner).into(),
|
||||||
state: AppState::Search(&search.string),
|
state: AppState::Search(&search.state.string),
|
||||||
},
|
},
|
||||||
AppState::Error(error) => AppPublic {
|
AppState::Error(error) => AppPublic {
|
||||||
inner: (&mut error.inner).into(),
|
inner: (&mut error.inner).into(),
|
||||||
state: AppState::Error(&error.string),
|
state: AppState::Error(&error.state.string),
|
||||||
},
|
},
|
||||||
AppState::Critical(critical) => AppPublic {
|
AppState::Critical(critical) => AppPublic {
|
||||||
inner: (&mut critical.inner).into(),
|
inner: (&mut critical.inner).into(),
|
||||||
state: AppState::Error(&critical.string),
|
state: AppState::Error(&critical.state.string),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -473,9 +505,11 @@ mod tests {
|
|||||||
let app = App::new(music_hoard);
|
let app = App::new(music_hoard);
|
||||||
assert!(app.is_running());
|
assert!(app.is_running());
|
||||||
|
|
||||||
let app = App::Error(AppErrorState {
|
let app = App::Error(AppMachine {
|
||||||
inner: app.unwrap_browse().inner,
|
inner: app.unwrap_browse().inner,
|
||||||
|
state: AppError {
|
||||||
string: String::from("get rekt"),
|
string: String::from("get rekt"),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let error = app.unwrap_error();
|
let error = app.unwrap_error();
|
||||||
@ -500,9 +534,11 @@ mod tests {
|
|||||||
let app = App::new(music_hoard(COLLECTION.to_owned()));
|
let app = App::new(music_hoard(COLLECTION.to_owned()));
|
||||||
assert!(app.is_running());
|
assert!(app.is_running());
|
||||||
|
|
||||||
let app = App::Error(AppErrorState {
|
let app = App::Error(AppMachine {
|
||||||
inner: app.unwrap_browse().inner,
|
inner: app.unwrap_browse().inner,
|
||||||
|
state: AppError {
|
||||||
string: String::from("get rekt"),
|
string: String::from("get rekt"),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let app = app.force_quit();
|
let app = app.force_quit();
|
||||||
|
Loading…
Reference in New Issue
Block a user