Provide search functionality through the TUI #134

Merged
wojtek merged 35 commits from 24---provide-search-functionality-through-the-tui into main 2024-02-18 22:12:42 +01:00
3 changed files with 258 additions and 329 deletions
Showing only changes of commit e96963f37a - Show all commits

View File

@ -1,3 +1,4 @@
// FIXME: Split file apart
#![allow(clippy::module_inception)]
use musichoard::collection::Collection;
@ -7,7 +8,6 @@ use crate::tui::{
lib::IMusicHoard,
};
#[derive(Copy, Clone)]
pub enum AppState<BS, IS, RS, SS, ES, CS> {
Browse(BS),
Info(IS),
@ -110,9 +110,13 @@ pub trait IAppAccess {
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,
pub state: AppPublicState<'app>,
}
struct AppInner<MH: IMusicHoard> {
@ -130,11 +134,13 @@ pub type App<MH> = AppState<
AppErrorState<MH>,
>;
pub struct AppGenericState<MH: IMusicHoard>(AppInner<MH>);
pub struct AppGenericState<MH: IMusicHoard> {
inner: AppInner<MH>,
}
pub struct AppSearchState<MH: IMusicHoard> {
inner: AppInner<MH>,
search: String,
string: String,
orig: ListSelection,
memo: Vec<AppSearchMemo>,
}
@ -146,7 +152,7 @@ struct AppSearchMemo {
pub struct AppErrorState<MH: IMusicHoard> {
inner: AppInner<MH>,
msg: String,
string: String,
}
impl<MH: IMusicHoard> App<MH> {
@ -159,10 +165,10 @@ impl<MH: IMusicHoard> App<MH> {
selection,
};
match init_result {
Ok(()) => Self::Browse(AppGenericState(inner)),
Ok(()) => Self::Browse(AppGenericState { inner }),
Err(err) => Self::Critical(AppErrorState {
inner,
msg: err.to_string(),
string: err.to_string(),
}),
}
}
@ -175,7 +181,9 @@ impl<MH: IMusicHoard> App<MH> {
fn inner_ref(&self) -> &AppInner<MH> {
match self {
AppState::Browse(inner) | AppState::Info(inner) | AppState::Reload(inner) => &inner.0,
AppState::Browse(generic) | AppState::Info(generic) | AppState::Reload(generic) => {
&generic.inner
}
AppState::Search(search) => &search.inner,
AppState::Error(error) | AppState::Critical(error) => &error.inner,
}
@ -183,8 +191,8 @@ impl<MH: IMusicHoard> App<MH> {
fn inner_mut(&mut self) -> &mut AppInner<MH> {
match self {
AppState::Browse(inner) | AppState::Info(inner) | AppState::Reload(inner) => {
&mut inner.0
AppState::Browse(generic) | AppState::Info(generic) | AppState::Reload(generic) => {
&mut generic.inner
}
AppState::Search(search) => &mut search.inner,
AppState::Error(error) | AppState::Critical(error) => &mut error.inner,
@ -218,39 +226,39 @@ impl<MH: IMusicHoard> IAppInteractBrowse for AppGenericState<MH> {
type APP = App<MH>;
fn save_and_quit(mut self) -> Self::APP {
match self.0.music_hoard.save_to_database() {
match self.inner.music_hoard.save_to_database() {
Ok(_) => {
self.0.running = false;
self.inner.running = false;
AppState::Browse(self)
}
Err(err) => AppState::Error(AppErrorState {
inner: self.0,
msg: err.to_string(),
inner: self.inner,
string: err.to_string(),
}),
}
}
fn increment_category(mut self) -> Self::APP {
self.0.selection.increment_category();
self.inner.selection.increment_category();
AppState::Browse(self)
}
fn decrement_category(mut self) -> Self::APP {
self.0.selection.decrement_category();
self.inner.selection.decrement_category();
AppState::Browse(self)
}
fn increment_selection(mut self, delta: Delta) -> Self::APP {
self.0
self.inner
.selection
.increment_selection(self.0.music_hoard.get_collection(), delta);
.increment_selection(self.inner.music_hoard.get_collection(), delta);
AppState::Browse(self)
}
fn decrement_selection(mut self, delta: Delta) -> Self::APP {
self.0
self.inner
.selection
.decrement_selection(self.0.music_hoard.get_collection(), delta);
.decrement_selection(self.inner.music_hoard.get_collection(), delta);
AppState::Browse(self)
}
@ -263,13 +271,13 @@ impl<MH: IMusicHoard> IAppInteractBrowse for AppGenericState<MH> {
}
fn begin_search(mut self) -> Self::APP {
let orig = ListSelection::get(&self.0.selection);
self.0
let orig = ListSelection::get(&self.inner.selection);
self.inner
.selection
.reset_artist(self.0.music_hoard.get_collection());
.reset_artist(self.inner.music_hoard.get_collection());
AppState::Search(AppSearchState {
inner: self.0,
search: String::new(),
inner: self.inner,
string: String::new(),
orig,
memo: vec![],
})
@ -284,7 +292,7 @@ impl<MH: IMusicHoard> IAppInteractInfo for AppGenericState<MH> {
type APP = App<MH>;
fn hide_info_overlay(self) -> Self::APP {
AppState::Browse(AppGenericState(self.0))
AppState::Browse(AppGenericState { inner: self.inner })
}
fn no_op(self) -> Self::APP {
@ -296,19 +304,25 @@ impl<MH: IMusicHoard> IAppInteractReload for AppGenericState<MH> {
type APP = App<MH>;
fn reload_library(mut self) -> Self::APP {
let previous = IdSelection::get(self.0.music_hoard.get_collection(), &self.0.selection);
let result = self.0.music_hoard.rescan_library();
let previous = IdSelection::get(
self.inner.music_hoard.get_collection(),
&self.inner.selection,
);
let result = self.inner.music_hoard.rescan_library();
self.refresh(previous, result)
}
fn reload_database(mut self) -> Self::APP {
let previous = IdSelection::get(self.0.music_hoard.get_collection(), &self.0.selection);
let result = self.0.music_hoard.load_from_database();
let previous = IdSelection::get(
self.inner.music_hoard.get_collection(),
&self.inner.selection,
);
let result = self.inner.music_hoard.load_from_database();
self.refresh(previous, result)
}
fn hide_reload_menu(self) -> Self::APP {
AppState::Browse(AppGenericState(self.0))
AppState::Browse(AppGenericState { inner: self.inner })
}
fn no_op(self) -> Self::APP {
@ -324,14 +338,14 @@ impl<MH: IMusicHoard> IAppInteractReloadPrivate<MH> for AppGenericState<MH> {
fn refresh(mut self, previous: IdSelection, result: Result<(), musichoard::Error>) -> App<MH> {
match result {
Ok(()) => {
self.0
self.inner
.selection
.select_by_id(self.0.music_hoard.get_collection(), previous);
AppState::Browse(AppGenericState(self.0))
.select_by_id(self.inner.music_hoard.get_collection(), previous);
AppState::Browse(AppGenericState { inner: self.inner })
}
Err(err) => AppState::Error(AppErrorState {
inner: self.0,
msg: err.to_string(),
inner: self.inner,
string: err.to_string(),
}),
}
}
@ -342,22 +356,22 @@ impl<MH: IMusicHoard> IAppInteractSearch for AppSearchState<MH> {
fn append_character(mut self, ch: char) -> Self::APP {
let collection = self.inner.music_hoard.get_collection();
self.search.push(ch);
self.string.push(ch);
let index = self
.inner
.selection
.incremental_artist_search(collection, &self.search, false);
.incremental_artist_search(collection, &self.string, false);
self.memo.push(AppSearchMemo { index, char: true });
AppState::Search(self)
}
fn search_next(mut self) -> Self::APP {
let collection = self.inner.music_hoard.get_collection();
if !self.search.is_empty() {
if !self.string.is_empty() {
let index =
self.inner
.selection
.incremental_artist_search(collection, &self.search, true);
.incremental_artist_search(collection, &self.string, true);
self.memo.push(AppSearchMemo { index, char: false });
}
AppState::Search(self)
@ -367,7 +381,7 @@ impl<MH: IMusicHoard> IAppInteractSearch for AppSearchState<MH> {
let collection = self.inner.music_hoard.get_collection();
if let Some(memo) = self.memo.pop() {
if memo.char {
self.search.pop();
self.string.pop();
}
self.inner.selection.select_artist(collection, memo.index);
}
@ -375,12 +389,12 @@ impl<MH: IMusicHoard> IAppInteractSearch for AppSearchState<MH> {
}
fn finish_search(self) -> Self::APP {
AppState::Browse(AppGenericState(self.inner))
AppState::Browse(AppGenericState { inner: self.inner })
}
fn cancel_search(mut self) -> Self::APP {
self.inner.selection.select_by_list(self.orig);
AppState::Browse(AppGenericState(self.inner))
AppState::Browse(AppGenericState { inner: self.inner })
}
fn no_op(self) -> Self::APP {
@ -392,7 +406,7 @@ impl<MH: IMusicHoard> IAppInteractError for AppErrorState<MH> {
type APP = App<MH>;
fn dismiss_error(self) -> Self::APP {
AppState::Browse(AppGenericState(self.inner))
AppState::Browse(AppGenericState { inner: self.inner })
}
}
@ -408,39 +422,42 @@ impl<MH: IMusicHoard> IAppAccess for App<MH> {
fn get(&mut self) -> AppPublic {
match self {
AppState::Browse(generic) => AppPublic {
collection: generic.0.music_hoard.get_collection(),
selection: &mut generic.0.selection,
inner: (&mut generic.inner).into(),
state: AppState::Browse(()),
},
AppState::Info(generic) => AppPublic {
collection: generic.0.music_hoard.get_collection(),
selection: &mut generic.0.selection,
inner: (&mut generic.inner).into(),
state: AppState::Info(()),
},
AppState::Reload(generic) => AppPublic {
collection: generic.0.music_hoard.get_collection(),
selection: &mut generic.0.selection,
inner: (&mut generic.inner).into(),
state: AppState::Reload(()),
},
AppState::Search(search) => AppPublic {
collection: search.inner.music_hoard.get_collection(),
selection: &mut search.inner.selection,
state: AppState::Search(&search.search),
inner: (&mut search.inner).into(),
state: AppState::Search(&search.string),
},
AppState::Error(error) => AppPublic {
collection: error.inner.music_hoard.get_collection(),
selection: &mut error.inner.selection,
state: AppState::Error(&error.msg),
inner: (&mut error.inner).into(),
state: AppState::Error(&error.string),
},
AppState::Critical(critical) => AppPublic {
collection: critical.inner.music_hoard.get_collection(),
selection: &mut critical.inner.selection,
state: AppState::Error(&critical.msg),
inner: (&mut critical.inner).into(),
state: AppState::Error(&critical.string),
},
}
}
}
impl<'app, MH: IMusicHoard> From<&'app mut AppInner<MH>> for AppPublicInner<'app> {
fn from(inner: &'app mut AppInner<MH>) -> Self {
AppPublicInner {
collection: inner.music_hoard.get_collection(),
selection: &mut inner.selection,
}
}
}
#[cfg(test)]
mod tests {
use crate::tui::{app::selection::Category, lib::MockIMusicHoard, testmod::COLLECTION};
@ -544,8 +561,8 @@ mod tests {
assert!(app.is_running());
let app = App::Error(AppErrorState {
inner: app.unwrap_browse().0,
msg: String::from("get rekt"),
inner: app.unwrap_browse().inner,
string: String::from("get rekt"),
});
let error = app.unwrap_error();
@ -571,8 +588,8 @@ mod tests {
assert!(app.is_running());
let app = App::Error(AppErrorState {
inner: app.unwrap_browse().0,
msg: String::from("get rekt"),
inner: app.unwrap_browse().inner,
string: String::from("get rekt"),
});
let app = app.force_quit();
@ -630,166 +647,101 @@ mod tests {
let browse = app.unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Artist);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(0));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(0)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
Some(0)
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Artist);
assert_eq!(selection.artist.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.track.state.list.selected(), Some(0));
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Artist);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(0)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
Some(0)
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Artist);
assert_eq!(selection.artist.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.track.state.list.selected(), Some(0));
let browse = browse.increment_category().unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(0)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
Some(0)
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Album);
assert_eq!(selection.artist.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.track.state.list.selected(), Some(0));
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(1)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
Some(0)
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Album);
assert_eq!(selection.artist.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.track.state.list.selected(), Some(0));
let browse = browse.increment_category().unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Track);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(1)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
Some(0)
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Track);
assert_eq!(selection.artist.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.track.state.list.selected(), Some(0));
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Track);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(1)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
Some(1)
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Track);
assert_eq!(selection.artist.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.track.state.list.selected(), Some(1));
let browse = browse.increment_category().unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Track);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(1)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
Some(1)
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Track);
assert_eq!(selection.artist.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.track.state.list.selected(), Some(1));
let browse = browse.decrement_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Track);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(1)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
Some(0)
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Track);
assert_eq!(selection.artist.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.track.state.list.selected(), Some(0));
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let browse = browse.decrement_category().unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(1)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
Some(1)
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Album);
assert_eq!(selection.artist.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.track.state.list.selected(), Some(1));
let browse = browse.decrement_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(0)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
Some(0)
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Album);
assert_eq!(selection.artist.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.track.state.list.selected(), Some(0));
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let browse = browse.decrement_category().unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Artist);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(1)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
Some(0)
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Artist);
assert_eq!(selection.artist.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.track.state.list.selected(), Some(0));
let browse = browse.decrement_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Artist);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(0));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(0)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
Some(0)
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Artist);
assert_eq!(selection.artist.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.track.state.list.selected(), Some(0));
let browse = browse.increment_category().unwrap_browse();
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let browse = browse.decrement_category().unwrap_browse();
let browse = browse.decrement_selection(Delta::Line).unwrap_browse();
let browse = browse.decrement_category().unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Artist);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(0));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(1)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
Some(0)
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Artist);
assert_eq!(selection.artist.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.state.list.selected(), Some(1));
assert_eq!(selection.artist.album.track.state.list.selected(), Some(0));
}
#[test]
@ -802,43 +754,28 @@ mod tests {
let browse = app.unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Artist);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(0));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(0)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Artist);
assert_eq!(selection.artist.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.track.state.list.selected(), None);
let browse = browse.increment_category().unwrap_browse();
let browse = browse.increment_category().unwrap_browse();
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Track);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(0));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(0)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Track);
assert_eq!(selection.artist.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.track.state.list.selected(), None);
let browse = browse.decrement_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Track);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(0));
assert_eq!(
browse.0.selection.artist.album.state.list.selected(),
Some(0)
);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Track);
assert_eq!(selection.artist.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.track.state.list.selected(), None);
}
#[test]
@ -851,53 +788,43 @@ mod tests {
let browse = app.unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Artist);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(0));
assert_eq!(browse.0.selection.artist.album.state.list.selected(), None);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Artist);
assert_eq!(selection.artist.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.state.list.selected(), None);
assert_eq!(selection.artist.album.track.state.list.selected(), None);
let browse = browse.increment_category().unwrap_browse();
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(0));
assert_eq!(browse.0.selection.artist.album.state.list.selected(), None);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Album);
assert_eq!(selection.artist.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.state.list.selected(), None);
assert_eq!(selection.artist.album.track.state.list.selected(), None);
let browse = browse.decrement_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(0));
assert_eq!(browse.0.selection.artist.album.state.list.selected(), None);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Album);
assert_eq!(selection.artist.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.state.list.selected(), None);
assert_eq!(selection.artist.album.track.state.list.selected(), None);
let browse = browse.increment_category().unwrap_browse();
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Track);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(0));
assert_eq!(browse.0.selection.artist.album.state.list.selected(), None);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Track);
assert_eq!(selection.artist.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.state.list.selected(), None);
assert_eq!(selection.artist.album.track.state.list.selected(), None);
let browse = browse.decrement_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Track);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(0));
assert_eq!(browse.0.selection.artist.album.state.list.selected(), None);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Track);
assert_eq!(selection.artist.state.list.selected(), Some(0));
assert_eq!(selection.artist.album.state.list.selected(), None);
assert_eq!(selection.artist.album.track.state.list.selected(), None);
}
#[test]
@ -907,71 +834,57 @@ mod tests {
let browse = app.unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Artist);
assert_eq!(browse.0.selection.artist.state.list.selected(), None);
assert_eq!(browse.0.selection.artist.album.state.list.selected(), None);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Artist);
assert_eq!(selection.artist.state.list.selected(), None);
assert_eq!(selection.artist.album.state.list.selected(), None);
assert_eq!(selection.artist.album.track.state.list.selected(), None);
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Artist);
assert_eq!(browse.0.selection.artist.state.list.selected(), None);
assert_eq!(browse.0.selection.artist.album.state.list.selected(), None);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Artist);
assert_eq!(selection.artist.state.list.selected(), None);
assert_eq!(selection.artist.album.state.list.selected(), None);
assert_eq!(selection.artist.album.track.state.list.selected(), None);
let browse = browse.decrement_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Artist);
assert_eq!(browse.0.selection.artist.state.list.selected(), None);
assert_eq!(browse.0.selection.artist.album.state.list.selected(), None);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Artist);
assert_eq!(selection.artist.state.list.selected(), None);
assert_eq!(selection.artist.album.state.list.selected(), None);
assert_eq!(selection.artist.album.track.state.list.selected(), None);
let browse = browse.increment_category().unwrap_browse();
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), None);
assert_eq!(browse.0.selection.artist.album.state.list.selected(), None);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Album);
assert_eq!(selection.artist.state.list.selected(), None);
assert_eq!(selection.artist.album.state.list.selected(), None);
assert_eq!(selection.artist.album.track.state.list.selected(), None);
let browse = browse.decrement_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), None);
assert_eq!(browse.0.selection.artist.album.state.list.selected(), None);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Album);
assert_eq!(selection.artist.state.list.selected(), None);
assert_eq!(selection.artist.album.state.list.selected(), None);
assert_eq!(selection.artist.album.track.state.list.selected(), None);
let browse = browse.increment_category().unwrap_browse();
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Track);
assert_eq!(browse.0.selection.artist.state.list.selected(), None);
assert_eq!(browse.0.selection.artist.album.state.list.selected(), None);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Track);
assert_eq!(selection.artist.state.list.selected(), None);
assert_eq!(selection.artist.album.state.list.selected(), None);
assert_eq!(selection.artist.album.track.state.list.selected(), None);
let browse = browse.decrement_selection(Delta::Line).unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Track);
assert_eq!(browse.0.selection.artist.state.list.selected(), None);
assert_eq!(browse.0.selection.artist.album.state.list.selected(), None);
assert_eq!(
browse.0.selection.artist.album.track.state.list.selected(),
None
);
let selection = &browse.inner.selection;
assert_eq!(selection.active, Category::Track);
assert_eq!(selection.artist.state.list.selected(), None);
assert_eq!(selection.artist.album.state.list.selected(), None);
assert_eq!(selection.artist.album.track.state.list.selected(), None);
}
#[test]
@ -1049,8 +962,8 @@ mod tests {
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let browse = browse.increment_category().unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(browse.inner.selection.active, Category::Album);
assert_eq!(browse.inner.selection.artist.state.list.selected(), Some(1));
let search = browse.begin_search().unwrap_search();
@ -1097,8 +1010,8 @@ mod tests {
let browse = search.finish_search().unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(browse.inner.selection.active, Category::Album);
assert_eq!(browse.inner.selection.artist.state.list.selected(), Some(1));
}
#[test]
@ -1108,8 +1021,8 @@ mod tests {
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let browse = browse.increment_category().unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(browse.inner.selection.active, Category::Album);
assert_eq!(browse.inner.selection.artist.state.list.selected(), Some(1));
let search = browse.begin_search().unwrap_search();
@ -1176,8 +1089,8 @@ mod tests {
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let browse = browse.increment_category().unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(browse.inner.selection.active, Category::Album);
assert_eq!(browse.inner.selection.artist.state.list.selected(), Some(1));
let search = browse.begin_search().unwrap_search();
@ -1206,8 +1119,8 @@ mod tests {
let browse = search.cancel_search().unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), Some(1));
assert_eq!(browse.inner.selection.active, Category::Album);
assert_eq!(browse.inner.selection.artist.state.list.selected(), Some(1));
}
#[test]
@ -1217,8 +1130,8 @@ mod tests {
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let browse = browse.increment_category().unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), None);
assert_eq!(browse.inner.selection.active, Category::Album);
assert_eq!(browse.inner.selection.artist.state.list.selected(), None);
let search = browse.begin_search().unwrap_search();
@ -1247,7 +1160,7 @@ mod tests {
let browse = search.cancel_search().unwrap_browse();
assert_eq!(browse.0.selection.active, Category::Album);
assert_eq!(browse.0.selection.artist.state.list.selected(), None);
assert_eq!(browse.inner.selection.active, Category::Album);
assert_eq!(browse.inner.selection.artist.state.list.selected(), None);
}
}

View File

@ -48,6 +48,7 @@ pub struct TrackSelection {
pub state: WidgetState,
}
// FIXME: should be with browse state (maybe?)
pub enum Delta {
Line,
Page,
@ -268,6 +269,7 @@ impl ArtistSelection {
result
}
// FIXME: search logic should be with the search state
fn incremental_search(
&mut self,
artists: &[Artist],

View File

@ -677,8 +677,8 @@ impl IUi for Ui {
fn render<APP: IAppAccess>(app: &mut APP, frame: &mut Frame) {
let app = app.get();
let collection = app.collection;
let selection = app.selection;
let collection = app.inner.collection;
let selection = app.inner.selection;
let state = app.state;
Self::render_main_frame(collection, selection, &state, frame);
@ -695,7 +695,10 @@ impl IUi for Ui {
#[cfg(test)]
mod tests {
use crate::tui::{
app::{app::AppPublic, selection::Delta},
app::{
app::{AppPublic, AppPublicInner},
selection::Delta,
},
testmod::COLLECTION,
tests::terminal,
};
@ -706,9 +709,18 @@ mod tests {
impl IAppAccess for AppPublic<'_> {
fn get(&mut self) -> AppPublic {
AppPublic {
collection: self.collection,
selection: self.selection,
state: self.state,
inner: AppPublicInner {
collection: self.inner.collection,
selection: self.inner.selection,
},
state: match self.state {
AppState::Browse(()) => AppState::Browse(()),
AppState::Info(()) => AppState::Info(()),
AppState::Reload(()) => AppState::Reload(()),
AppState::Search(s) => AppState::Search(s),
AppState::Error(s) => AppState::Error(s),
AppState::Critical(s) => AppState::Critical(s),
},
}
}
}
@ -717,8 +729,10 @@ mod tests {
let mut terminal = terminal();
let mut app = AppPublic {
collection,
selection,
inner: AppPublicInner {
collection,
selection,
},
state: AppState::Browse(()),
};
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();