188---add-option-for-manual-input-during-fetch (#214)
PR 1 for #188 Reviewed-on: #214
This commit is contained in:
parent
9d1caffd9c
commit
f2996903b2
@ -4,25 +4,25 @@ use crate::tui::app::{
|
||||
AppPublic, AppState, IAppInteractBrowse,
|
||||
};
|
||||
|
||||
pub struct AppBrowse;
|
||||
pub struct BrowseState;
|
||||
|
||||
impl AppMachine<AppBrowse> {
|
||||
pub fn browse(inner: AppInner) -> Self {
|
||||
impl AppMachine<BrowseState> {
|
||||
pub fn browse_state(inner: AppInner) -> Self {
|
||||
AppMachine {
|
||||
inner,
|
||||
state: AppBrowse,
|
||||
state: BrowseState,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AppMachine<AppBrowse>> for App {
|
||||
fn from(machine: AppMachine<AppBrowse>) -> Self {
|
||||
impl From<AppMachine<BrowseState>> for App {
|
||||
fn from(machine: AppMachine<BrowseState>) -> Self {
|
||||
AppState::Browse(machine)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut AppMachine<AppBrowse>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<AppBrowse>) -> Self {
|
||||
impl<'a> From<&'a mut AppMachine<BrowseState>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<BrowseState>) -> Self {
|
||||
AppPublic {
|
||||
inner: (&mut machine.inner).into(),
|
||||
state: AppState::Browse(()),
|
||||
@ -30,7 +30,7 @@ impl<'a> From<&'a mut AppMachine<AppBrowse>> for AppPublic<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl IAppInteractBrowse for AppMachine<AppBrowse> {
|
||||
impl IAppInteractBrowse for AppMachine<BrowseState> {
|
||||
type APP = App;
|
||||
|
||||
fn quit(mut self) -> Self::APP {
|
||||
@ -63,11 +63,11 @@ impl IAppInteractBrowse for AppMachine<AppBrowse> {
|
||||
}
|
||||
|
||||
fn show_info_overlay(self) -> Self::APP {
|
||||
AppMachine::info(self.inner).into()
|
||||
AppMachine::info_state(self.inner).into()
|
||||
}
|
||||
|
||||
fn show_reload_menu(self) -> Self::APP {
|
||||
AppMachine::reload(self.inner).into()
|
||||
AppMachine::reload_state(self.inner).into()
|
||||
}
|
||||
|
||||
fn begin_search(mut self) -> Self::APP {
|
||||
@ -75,7 +75,7 @@ impl IAppInteractBrowse for AppMachine<AppBrowse> {
|
||||
self.inner
|
||||
.selection
|
||||
.reset(self.inner.music_hoard.get_collection());
|
||||
AppMachine::search(self.inner, orig).into()
|
||||
AppMachine::search_state(self.inner, orig).into()
|
||||
}
|
||||
|
||||
fn fetch_musicbrainz(self) -> Self::APP {
|
||||
@ -88,7 +88,7 @@ mod tests {
|
||||
use crate::tui::{
|
||||
app::{
|
||||
machine::tests::{inner, inner_with_mb, music_hoard},
|
||||
Category, IAppAccess, IAppInteract,
|
||||
Category, IApp, IAppAccess,
|
||||
},
|
||||
lib::interface::musicbrainz::MockIMusicBrainz,
|
||||
testmod::COLLECTION,
|
||||
@ -100,7 +100,7 @@ mod tests {
|
||||
fn quit() {
|
||||
let music_hoard = music_hoard(vec![]);
|
||||
|
||||
let browse = AppMachine::browse(inner(music_hoard));
|
||||
let browse = AppMachine::browse_state(inner(music_hoard));
|
||||
|
||||
let app = browse.quit();
|
||||
assert!(!app.is_running());
|
||||
@ -109,7 +109,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn increment_decrement() {
|
||||
let mut browse = AppMachine::browse(inner(music_hoard(COLLECTION.to_owned())));
|
||||
let mut browse = AppMachine::browse_state(inner(music_hoard(COLLECTION.to_owned())));
|
||||
let sel = &browse.inner.selection;
|
||||
assert_eq!(sel.category(), Category::Artist);
|
||||
assert_eq!(sel.selected(), Some(0));
|
||||
@ -147,21 +147,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn show_info_overlay() {
|
||||
let browse = AppMachine::browse(inner(music_hoard(vec![])));
|
||||
let browse = AppMachine::browse_state(inner(music_hoard(vec![])));
|
||||
let app = browse.show_info_overlay();
|
||||
app.unwrap_info();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_reload_menu() {
|
||||
let browse = AppMachine::browse(inner(music_hoard(vec![])));
|
||||
let browse = AppMachine::browse_state(inner(music_hoard(vec![])));
|
||||
let app = browse.show_reload_menu();
|
||||
app.unwrap_reload();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn begin_search() {
|
||||
let browse = AppMachine::browse(inner(music_hoard(vec![])));
|
||||
let browse = AppMachine::browse_state(inner(music_hoard(vec![])));
|
||||
let app = browse.begin_search();
|
||||
app.unwrap_search();
|
||||
}
|
||||
@ -169,7 +169,8 @@ mod tests {
|
||||
#[test]
|
||||
fn fetch_musicbrainz() {
|
||||
let mb_api = MockIMusicBrainz::new();
|
||||
let browse = AppMachine::browse(inner_with_mb(music_hoard(COLLECTION.to_owned()), mb_api));
|
||||
let browse =
|
||||
AppMachine::browse_state(inner_with_mb(music_hoard(COLLECTION.to_owned()), mb_api));
|
||||
|
||||
// Use the second artist for this test.
|
||||
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
|
||||
@ -179,7 +180,7 @@ mod tests {
|
||||
|
||||
// Because of fetch's threaded behaviour, this unit test cannot expect one or the other.
|
||||
assert!(
|
||||
matches!(public.state, AppState::Matches(_))
|
||||
matches!(public.state, AppState::Match(_))
|
||||
|| matches!(public.state, AppState::Fetch(_))
|
||||
);
|
||||
}
|
@ -3,29 +3,29 @@ use crate::tui::app::{
|
||||
AppPublic, AppState,
|
||||
};
|
||||
|
||||
pub struct AppCritical {
|
||||
pub struct CriticalState {
|
||||
string: String,
|
||||
}
|
||||
|
||||
impl AppMachine<AppCritical> {
|
||||
pub fn critical<S: Into<String>>(inner: AppInner, string: S) -> Self {
|
||||
impl AppMachine<CriticalState> {
|
||||
pub fn critical_state<S: Into<String>>(inner: AppInner, string: S) -> Self {
|
||||
AppMachine {
|
||||
inner,
|
||||
state: AppCritical {
|
||||
state: CriticalState {
|
||||
string: string.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AppMachine<AppCritical>> for App {
|
||||
fn from(machine: AppMachine<AppCritical>) -> Self {
|
||||
impl From<AppMachine<CriticalState>> for App {
|
||||
fn from(machine: AppMachine<CriticalState>) -> Self {
|
||||
AppState::Critical(machine)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut AppMachine<AppCritical>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<AppCritical>) -> Self {
|
||||
impl<'a> From<&'a mut AppMachine<CriticalState>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<CriticalState>) -> Self {
|
||||
AppPublic {
|
||||
inner: (&mut machine.inner).into(),
|
||||
state: AppState::Critical(&machine.state.string),
|
@ -3,29 +3,29 @@ use crate::tui::app::{
|
||||
AppPublic, AppState, IAppInteractError,
|
||||
};
|
||||
|
||||
pub struct AppError {
|
||||
pub struct ErrorState {
|
||||
string: String,
|
||||
}
|
||||
|
||||
impl AppMachine<AppError> {
|
||||
pub fn error<S: Into<String>>(inner: AppInner, string: S) -> Self {
|
||||
impl AppMachine<ErrorState> {
|
||||
pub fn error_state<S: Into<String>>(inner: AppInner, string: S) -> Self {
|
||||
AppMachine {
|
||||
inner,
|
||||
state: AppError {
|
||||
state: ErrorState {
|
||||
string: string.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AppMachine<AppError>> for App {
|
||||
fn from(machine: AppMachine<AppError>) -> Self {
|
||||
impl From<AppMachine<ErrorState>> for App {
|
||||
fn from(machine: AppMachine<ErrorState>) -> Self {
|
||||
AppState::Error(machine)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut AppMachine<AppError>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<AppError>) -> Self {
|
||||
impl<'a> From<&'a mut AppMachine<ErrorState>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<ErrorState>) -> Self {
|
||||
AppPublic {
|
||||
inner: (&mut machine.inner).into(),
|
||||
state: AppState::Error(&machine.state.string),
|
||||
@ -33,11 +33,11 @@ impl<'a> From<&'a mut AppMachine<AppError>> for AppPublic<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl IAppInteractError for AppMachine<AppError> {
|
||||
impl IAppInteractError for AppMachine<ErrorState> {
|
||||
type APP = App;
|
||||
|
||||
fn dismiss_error(self) -> Self::APP {
|
||||
AppMachine::browse(self.inner).into()
|
||||
AppMachine::browse_state(self.inner).into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn dismiss_error() {
|
||||
let error = AppMachine::error(inner(music_hoard(vec![])), "get rekt");
|
||||
let error = AppMachine::error_state(inner(music_hoard(vec![])), "get rekt");
|
||||
let app = error.dismiss_error();
|
||||
app.unwrap_browse();
|
||||
}
|
@ -15,31 +15,31 @@ use musichoard::collection::{
|
||||
use crate::tui::{
|
||||
app::{
|
||||
machine::{App, AppInner, AppMachine},
|
||||
AppMatchesInfo, AppPublic, AppState, IAppEventFetch, IAppInteractFetch,
|
||||
AppPublic, AppState, IAppEventFetch, IAppInteractFetch, MatchStateInfo,
|
||||
},
|
||||
event::{Event, EventSender},
|
||||
lib::interface::musicbrainz::{self, Error as MbError, IMusicBrainz},
|
||||
};
|
||||
|
||||
use super::matches::AppMatches;
|
||||
use super::match_state::MatchState;
|
||||
|
||||
pub struct AppFetch {
|
||||
pub struct FetchState {
|
||||
fetch_rx: FetchReceiver,
|
||||
}
|
||||
|
||||
impl AppFetch {
|
||||
impl FetchState {
|
||||
pub fn new(fetch_rx: FetchReceiver) -> Self {
|
||||
AppFetch { fetch_rx }
|
||||
FetchState { fetch_rx }
|
||||
}
|
||||
}
|
||||
|
||||
pub type FetchError = MbError;
|
||||
pub type FetchResult = Result<AppMatchesInfo, FetchError>;
|
||||
pub type FetchResult = Result<MatchStateInfo, FetchError>;
|
||||
pub type FetchSender = mpsc::Sender<FetchResult>;
|
||||
pub type FetchReceiver = mpsc::Receiver<FetchResult>;
|
||||
|
||||
impl AppMachine<AppFetch> {
|
||||
fn fetch(inner: AppInner, state: AppFetch) -> Self {
|
||||
impl AppMachine<FetchState> {
|
||||
fn fetch_state(inner: AppInner, state: FetchState) -> Self {
|
||||
AppMachine { inner, state }
|
||||
}
|
||||
|
||||
@ -47,38 +47,40 @@ impl AppMachine<AppFetch> {
|
||||
let coll = inner.music_hoard.get_collection();
|
||||
let artist = match inner.selection.state_artist(coll) {
|
||||
Some(artist_state) => &coll[artist_state.index],
|
||||
None => return AppMachine::error(inner, "cannot fetch: no artist selected").into(),
|
||||
None => {
|
||||
return AppMachine::error_state(inner, "cannot fetch: no artist selected").into()
|
||||
}
|
||||
};
|
||||
|
||||
let (fetch_tx, fetch_rx) = mpsc::channel::<FetchResult>();
|
||||
Self::spawn_fetch_thread(&inner, artist, fetch_tx);
|
||||
|
||||
let fetch = AppFetch::new(fetch_rx);
|
||||
let fetch = FetchState::new(fetch_rx);
|
||||
AppMachine::app_fetch(inner, fetch, true)
|
||||
}
|
||||
|
||||
pub fn app_fetch_next(inner: AppInner, fetch: AppFetch) -> App {
|
||||
pub fn app_fetch_next(inner: AppInner, fetch: FetchState) -> App {
|
||||
Self::app_fetch(inner, fetch, false)
|
||||
}
|
||||
|
||||
fn app_fetch(inner: AppInner, fetch: AppFetch, first: bool) -> App {
|
||||
fn app_fetch(inner: AppInner, fetch: FetchState, first: bool) -> App {
|
||||
match fetch.fetch_rx.try_recv() {
|
||||
Ok(fetch_result) => match fetch_result {
|
||||
Ok(next_match) => {
|
||||
let current = Some(next_match);
|
||||
AppMachine::matches(inner, AppMatches::new(current, fetch)).into()
|
||||
AppMachine::match_state(inner, MatchState::new(current, fetch)).into()
|
||||
}
|
||||
Err(fetch_err) => {
|
||||
AppMachine::error(inner, format!("fetch failed: {fetch_err}")).into()
|
||||
AppMachine::error_state(inner, format!("fetch failed: {fetch_err}")).into()
|
||||
}
|
||||
},
|
||||
Err(recv_err) => match recv_err {
|
||||
TryRecvError::Empty => AppMachine::fetch(inner, fetch).into(),
|
||||
TryRecvError::Empty => AppMachine::fetch_state(inner, fetch).into(),
|
||||
TryRecvError::Disconnected => {
|
||||
if first {
|
||||
AppMachine::matches(inner, AppMatches::new(None, fetch)).into()
|
||||
AppMachine::match_state(inner, MatchState::new(None, fetch)).into()
|
||||
} else {
|
||||
AppMachine::browse(inner).into()
|
||||
AppMachine::browse_state(inner).into()
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -114,7 +116,7 @@ impl AppMachine<AppFetch> {
|
||||
artist: ArtistMeta,
|
||||
) {
|
||||
let result = musicbrainz.lock().unwrap().search_artist(&artist);
|
||||
let result = result.map(|list| AppMatchesInfo::artist(artist, list));
|
||||
let result = result.map(|list| MatchStateInfo::artist(artist, list));
|
||||
Self::send_fetch_result(&fetch_tx, &events, result).ok();
|
||||
}
|
||||
|
||||
@ -133,7 +135,7 @@ impl AppMachine<AppFetch> {
|
||||
}
|
||||
|
||||
let result = musicbrainz.search_release_group(&arid, &album);
|
||||
let result = result.map(|list| AppMatchesInfo::album(album, list));
|
||||
let result = result.map(|list| MatchStateInfo::album(album, list));
|
||||
if Self::send_fetch_result(&fetch_tx, &events, result).is_err() {
|
||||
return;
|
||||
};
|
||||
@ -147,7 +149,7 @@ impl AppMachine<AppFetch> {
|
||||
fn send_fetch_result(
|
||||
fetch_tx: &FetchSender,
|
||||
events: &EventSender,
|
||||
result: Result<AppMatchesInfo, musicbrainz::Error>,
|
||||
result: Result<MatchStateInfo, musicbrainz::Error>,
|
||||
) -> Result<(), ()> {
|
||||
// If receiver disconnects just drop the rest.
|
||||
fetch_tx.send(result).map_err(|_| ())?;
|
||||
@ -160,14 +162,14 @@ impl AppMachine<AppFetch> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AppMachine<AppFetch>> for App {
|
||||
fn from(machine: AppMachine<AppFetch>) -> Self {
|
||||
impl From<AppMachine<FetchState>> for App {
|
||||
fn from(machine: AppMachine<FetchState>) -> Self {
|
||||
AppState::Fetch(machine)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut AppMachine<AppFetch>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<AppFetch>) -> Self {
|
||||
impl<'a> From<&'a mut AppMachine<FetchState>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<FetchState>) -> Self {
|
||||
AppPublic {
|
||||
inner: (&mut machine.inner).into(),
|
||||
state: AppState::Fetch(()),
|
||||
@ -175,15 +177,15 @@ impl<'a> From<&'a mut AppMachine<AppFetch>> for AppPublic<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl IAppInteractFetch for AppMachine<AppFetch> {
|
||||
impl IAppInteractFetch for AppMachine<FetchState> {
|
||||
type APP = App;
|
||||
|
||||
fn abort(self) -> Self::APP {
|
||||
AppMachine::browse(self.inner).into()
|
||||
AppMachine::browse_state(self.inner).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl IAppEventFetch for AppMachine<AppFetch> {
|
||||
impl IAppEventFetch for AppMachine<FetchState> {
|
||||
type APP = App;
|
||||
|
||||
fn fetch_result_ready(self) -> Self::APP {
|
||||
@ -199,7 +201,7 @@ mod tests {
|
||||
use crate::tui::{
|
||||
app::{
|
||||
machine::tests::{inner, music_hoard},
|
||||
AppAlbumMatches, AppArtistMatches, IAppInteract,
|
||||
AlbumMatches, ArtistMatches, IApp,
|
||||
},
|
||||
event::EventReceiver,
|
||||
lib::interface::musicbrainz::{self, Match, MockIMusicBrainz},
|
||||
@ -285,7 +287,7 @@ mod tests {
|
||||
|
||||
assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady);
|
||||
let result = fetch_rx.try_recv().unwrap();
|
||||
let expected = Ok(AppMatchesInfo::Album(AppAlbumMatches {
|
||||
let expected = Ok(MatchStateInfo::Album(AlbumMatches {
|
||||
matching: album_1.clone(),
|
||||
list: matches_1.iter().cloned().map(Into::into).collect(),
|
||||
}));
|
||||
@ -293,7 +295,7 @@ mod tests {
|
||||
|
||||
assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady);
|
||||
let result = fetch_rx.try_recv().unwrap();
|
||||
let expected = Ok(AppMatchesInfo::Album(AppAlbumMatches {
|
||||
let expected = Ok(MatchStateInfo::Album(AlbumMatches {
|
||||
matching: album_4.clone(),
|
||||
list: matches_4.iter().cloned().map(Into::into).collect(),
|
||||
}));
|
||||
@ -343,7 +345,7 @@ mod tests {
|
||||
|
||||
assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady);
|
||||
let result = fetch_rx.try_recv().unwrap();
|
||||
let expected = Ok(AppMatchesInfo::Artist(AppArtistMatches {
|
||||
let expected = Ok(MatchStateInfo::Artist(ArtistMatches {
|
||||
matching: artist.clone(),
|
||||
list: matches.iter().cloned().map(Into::into).collect(),
|
||||
}));
|
||||
@ -391,7 +393,7 @@ mod tests {
|
||||
handle.join().unwrap();
|
||||
|
||||
let result = fetch_rx.try_recv().unwrap();
|
||||
let expected = Ok(AppMatchesInfo::Album(AppAlbumMatches {
|
||||
let expected = Ok(MatchStateInfo::Album(AlbumMatches {
|
||||
matching: album_1.clone(),
|
||||
list: matches_1.iter().cloned().map(Into::into).collect(),
|
||||
}));
|
||||
@ -405,13 +407,13 @@ mod tests {
|
||||
let (tx, rx) = mpsc::channel::<FetchResult>();
|
||||
|
||||
let artist = COLLECTION[3].meta.clone();
|
||||
let fetch_result = Ok(AppMatchesInfo::artist::<Match<ArtistMeta>>(artist, vec![]));
|
||||
let fetch_result = Ok(MatchStateInfo::artist::<Match<ArtistMeta>>(artist, vec![]));
|
||||
tx.send(fetch_result).unwrap();
|
||||
|
||||
let inner = inner(music_hoard(COLLECTION.clone()));
|
||||
let fetch = AppFetch::new(rx);
|
||||
let fetch = FetchState::new(rx);
|
||||
let app = AppMachine::app_fetch(inner, fetch, true);
|
||||
assert!(matches!(app, AppState::Matches(_)));
|
||||
assert!(matches!(app, AppState::Match(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -422,7 +424,7 @@ mod tests {
|
||||
tx.send(fetch_result).unwrap();
|
||||
|
||||
let inner = inner(music_hoard(COLLECTION.clone()));
|
||||
let fetch = AppFetch::new(rx);
|
||||
let fetch = FetchState::new(rx);
|
||||
let app = AppMachine::app_fetch(inner, fetch, true);
|
||||
assert!(matches!(app, AppState::Error(_)));
|
||||
}
|
||||
@ -432,7 +434,7 @@ mod tests {
|
||||
let (_tx, rx) = mpsc::channel::<FetchResult>();
|
||||
|
||||
let inner = inner(music_hoard(COLLECTION.clone()));
|
||||
let fetch = AppFetch::new(rx);
|
||||
let fetch = FetchState::new(rx);
|
||||
let app = AppMachine::app_fetch(inner, fetch, true);
|
||||
assert!(matches!(app, AppState::Fetch(_)));
|
||||
}
|
||||
@ -442,16 +444,16 @@ mod tests {
|
||||
let (_, rx) = mpsc::channel::<FetchResult>();
|
||||
|
||||
let inner = inner(music_hoard(COLLECTION.clone()));
|
||||
let fetch = AppFetch::new(rx);
|
||||
let fetch = FetchState::new(rx);
|
||||
let app = AppMachine::app_fetch(inner, fetch, true);
|
||||
assert!(matches!(app, AppState::Matches(_)));
|
||||
assert!(matches!(app, AppState::Match(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recv_err_disconnected_next() {
|
||||
let (_, rx) = mpsc::channel::<FetchResult>();
|
||||
|
||||
let fetch = AppFetch::new(rx);
|
||||
let fetch = FetchState::new(rx);
|
||||
let app = AppMachine::app_fetch_next(inner(music_hoard(COLLECTION.clone())), fetch);
|
||||
assert!(matches!(app, AppState::Browse(_)));
|
||||
}
|
||||
@ -461,24 +463,24 @@ mod tests {
|
||||
let (tx, rx) = mpsc::channel::<FetchResult>();
|
||||
|
||||
let inner = inner(music_hoard(COLLECTION.clone()));
|
||||
let fetch = AppFetch::new(rx);
|
||||
let fetch = FetchState::new(rx);
|
||||
let app = AppMachine::app_fetch(inner, fetch, true);
|
||||
assert!(matches!(app, AppState::Fetch(_)));
|
||||
|
||||
let artist = COLLECTION[3].meta.clone();
|
||||
let fetch_result = Ok(AppMatchesInfo::artist::<Match<ArtistMeta>>(artist, vec![]));
|
||||
let fetch_result = Ok(MatchStateInfo::artist::<Match<ArtistMeta>>(artist, vec![]));
|
||||
tx.send(fetch_result).unwrap();
|
||||
|
||||
let app = app.unwrap_fetch().fetch_result_ready();
|
||||
assert!(matches!(app, AppState::Matches(_)));
|
||||
assert!(matches!(app, AppState::Match(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn abort() {
|
||||
let (_, rx) = mpsc::channel::<FetchResult>();
|
||||
|
||||
let fetch = AppFetch::new(rx);
|
||||
let app = AppMachine::fetch(inner(music_hoard(COLLECTION.clone())), fetch);
|
||||
let fetch = FetchState::new(rx);
|
||||
let app = AppMachine::fetch_state(inner(music_hoard(COLLECTION.clone())), fetch);
|
||||
|
||||
let app = app.abort();
|
||||
assert!(matches!(app, AppState::Browse(_)));
|
@ -3,25 +3,25 @@ use crate::tui::app::{
|
||||
AppPublic, AppState, IAppInteractInfo,
|
||||
};
|
||||
|
||||
pub struct AppInfo;
|
||||
pub struct InfoState;
|
||||
|
||||
impl AppMachine<AppInfo> {
|
||||
pub fn info(inner: AppInner) -> Self {
|
||||
impl AppMachine<InfoState> {
|
||||
pub fn info_state(inner: AppInner) -> Self {
|
||||
AppMachine {
|
||||
inner,
|
||||
state: AppInfo,
|
||||
state: InfoState,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AppMachine<AppInfo>> for App {
|
||||
fn from(machine: AppMachine<AppInfo>) -> Self {
|
||||
impl From<AppMachine<InfoState>> for App {
|
||||
fn from(machine: AppMachine<InfoState>) -> Self {
|
||||
AppState::Info(machine)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut AppMachine<AppInfo>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<AppInfo>) -> Self {
|
||||
impl<'a> From<&'a mut AppMachine<InfoState>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<InfoState>) -> Self {
|
||||
AppPublic {
|
||||
inner: (&mut machine.inner).into(),
|
||||
state: AppState::Info(()),
|
||||
@ -29,11 +29,11 @@ impl<'a> From<&'a mut AppMachine<AppInfo>> for AppPublic<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl IAppInteractInfo for AppMachine<AppInfo> {
|
||||
impl IAppInteractInfo for AppMachine<InfoState> {
|
||||
type APP = App;
|
||||
|
||||
fn hide_info_overlay(self) -> Self::APP {
|
||||
AppMachine::browse(self.inner).into()
|
||||
AppMachine::browse_state(self.inner).into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn hide_info_overlay() {
|
||||
let info = AppMachine::info(inner(music_hoard(vec![])));
|
||||
let info = AppMachine::info_state(inner(music_hoard(vec![])));
|
||||
let app = info.hide_info_overlay();
|
||||
app.unwrap_browse();
|
||||
}
|
@ -2,13 +2,13 @@ use std::cmp;
|
||||
|
||||
use crate::tui::app::{
|
||||
machine::{App, AppInner, AppMachine},
|
||||
AppAlbumMatches, AppArtistMatches, AppMatchesInfo, AppPublic, AppPublicMatches, AppState,
|
||||
IAppInteractMatches, MatchOption, WidgetState,
|
||||
AlbumMatches, AppPublic, AppState, ArtistMatches, IAppInteractMatch, MatchOption,
|
||||
MatchStateInfo, MatchStatePublic, WidgetState,
|
||||
};
|
||||
|
||||
use super::fetch::AppFetch;
|
||||
use super::fetch_state::FetchState;
|
||||
|
||||
impl AppArtistMatches {
|
||||
impl ArtistMatches {
|
||||
fn len(&self) -> usize {
|
||||
self.list.len()
|
||||
}
|
||||
@ -18,7 +18,7 @@ impl AppArtistMatches {
|
||||
}
|
||||
}
|
||||
|
||||
impl AppAlbumMatches {
|
||||
impl AlbumMatches {
|
||||
fn len(&self) -> usize {
|
||||
self.list.len()
|
||||
}
|
||||
@ -28,7 +28,7 @@ impl AppAlbumMatches {
|
||||
}
|
||||
}
|
||||
|
||||
impl AppMatchesInfo {
|
||||
impl MatchStateInfo {
|
||||
fn len(&self) -> usize {
|
||||
match self {
|
||||
Self::Artist(a) => a.len(),
|
||||
@ -44,20 +44,20 @@ impl AppMatchesInfo {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AppMatches {
|
||||
current: Option<AppMatchesInfo>,
|
||||
pub struct MatchState {
|
||||
current: Option<MatchStateInfo>,
|
||||
state: WidgetState,
|
||||
fetch: AppFetch,
|
||||
fetch: FetchState,
|
||||
}
|
||||
|
||||
impl AppMatches {
|
||||
pub fn new(mut current: Option<AppMatchesInfo>, fetch: AppFetch) -> Self {
|
||||
impl MatchState {
|
||||
pub fn new(mut current: Option<MatchStateInfo>, fetch: FetchState) -> Self {
|
||||
let mut state = WidgetState::default();
|
||||
if let Some(ref mut current) = current {
|
||||
state.list.select(Some(0));
|
||||
current.push_cannot_have_mbid();
|
||||
}
|
||||
AppMatches {
|
||||
MatchState {
|
||||
current,
|
||||
state,
|
||||
fetch,
|
||||
@ -65,31 +65,31 @@ impl AppMatches {
|
||||
}
|
||||
}
|
||||
|
||||
impl AppMachine<AppMatches> {
|
||||
pub fn matches(inner: AppInner, state: AppMatches) -> Self {
|
||||
impl AppMachine<MatchState> {
|
||||
pub fn match_state(inner: AppInner, state: MatchState) -> Self {
|
||||
AppMachine { inner, state }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AppMachine<AppMatches>> for App {
|
||||
fn from(machine: AppMachine<AppMatches>) -> Self {
|
||||
AppState::Matches(machine)
|
||||
impl From<AppMachine<MatchState>> for App {
|
||||
fn from(machine: AppMachine<MatchState>) -> Self {
|
||||
AppState::Match(machine)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut AppMachine<AppMatches>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<AppMatches>) -> Self {
|
||||
impl<'a> From<&'a mut AppMachine<MatchState>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<MatchState>) -> Self {
|
||||
AppPublic {
|
||||
inner: (&mut machine.inner).into(),
|
||||
state: AppState::Matches(AppPublicMatches {
|
||||
matches: machine.state.current.as_ref().map(Into::into),
|
||||
state: AppState::Match(MatchStatePublic {
|
||||
info: machine.state.current.as_ref().map(Into::into),
|
||||
state: &mut machine.state.state,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IAppInteractMatches for AppMachine<AppMatches> {
|
||||
impl IAppInteractMatch for AppMachine<MatchState> {
|
||||
type APP = App;
|
||||
|
||||
fn prev_match(mut self) -> Self::APP {
|
||||
@ -120,7 +120,7 @@ impl IAppInteractMatches for AppMachine<AppMatches> {
|
||||
}
|
||||
|
||||
fn abort(self) -> Self::APP {
|
||||
AppMachine::browse(self.inner).into()
|
||||
AppMachine::browse_state(self.inner).into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,7 +153,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn artist_match() -> AppMatchesInfo {
|
||||
fn artist_match() -> MatchStateInfo {
|
||||
let artist = ArtistMeta::new(ArtistId::new("Artist"));
|
||||
|
||||
let artist_1 = artist.clone();
|
||||
@ -164,10 +164,10 @@ mod tests {
|
||||
artist_match_2.disambiguation = Some(String::from("some disambiguation"));
|
||||
|
||||
let list = vec![artist_match_1.clone(), artist_match_2.clone()];
|
||||
AppMatchesInfo::artist(artist, list)
|
||||
MatchStateInfo::artist(artist, list)
|
||||
}
|
||||
|
||||
fn album_match() -> AppMatchesInfo {
|
||||
fn album_match() -> MatchStateInfo {
|
||||
let album = AlbumMeta::new(
|
||||
AlbumId::new("Album"),
|
||||
AlbumDate::new(Some(1990), Some(5), None),
|
||||
@ -184,21 +184,21 @@ mod tests {
|
||||
let album_match_2 = Match::new(100, album_2);
|
||||
|
||||
let list = vec![album_match_1.clone(), album_match_2.clone()];
|
||||
AppMatchesInfo::album(album, list)
|
||||
MatchStateInfo::album(album, list)
|
||||
}
|
||||
|
||||
fn fetch() -> AppFetch {
|
||||
fn fetch_state() -> FetchState {
|
||||
let (_, rx) = mpsc::channel();
|
||||
AppFetch::new(rx)
|
||||
FetchState::new(rx)
|
||||
}
|
||||
|
||||
fn matches(matches_info: Option<AppMatchesInfo>) -> AppMatches {
|
||||
AppMatches::new(matches_info, fetch())
|
||||
fn match_state(matches_info: Option<MatchStateInfo>) -> MatchState {
|
||||
MatchState::new(matches_info, fetch_state())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_empty() {
|
||||
let matches = AppMachine::matches(inner(music_hoard(vec![])), matches(None));
|
||||
let matches = AppMachine::match_state(inner(music_hoard(vec![])), match_state(None));
|
||||
|
||||
let widget_state = WidgetState::default();
|
||||
|
||||
@ -207,18 +207,18 @@ mod tests {
|
||||
|
||||
let mut app: App = matches.into();
|
||||
let public = app.get();
|
||||
let public_matches = public.state.unwrap_matches();
|
||||
let public_matches = public.state.unwrap_match();
|
||||
|
||||
assert_eq!(public_matches.matches, None);
|
||||
assert_eq!(public_matches.info, None);
|
||||
assert_eq!(public_matches.state, &widget_state);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_nonempty() {
|
||||
let mut album_match = album_match();
|
||||
let matches = AppMachine::matches(
|
||||
let matches = AppMachine::match_state(
|
||||
inner(music_hoard(vec![])),
|
||||
matches(Some(album_match.clone())),
|
||||
match_state(Some(album_match.clone())),
|
||||
);
|
||||
album_match.push_cannot_have_mbid();
|
||||
|
||||
@ -230,19 +230,19 @@ mod tests {
|
||||
|
||||
let mut app: App = matches.into();
|
||||
let public = app.get();
|
||||
let public_matches = public.state.unwrap_matches();
|
||||
let public_matches = public.state.unwrap_match();
|
||||
|
||||
assert_eq!(public_matches.matches, Some(&album_match));
|
||||
assert_eq!(public_matches.info, Some(&album_match));
|
||||
assert_eq!(public_matches.state, &widget_state);
|
||||
}
|
||||
|
||||
fn matches_flow(mut matches_info: AppMatchesInfo) {
|
||||
fn match_state_flow(mut matches_info: MatchStateInfo) {
|
||||
// tx must exist for rx to return Empty rather than Disconnected.
|
||||
#[allow(unused_variables)]
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let app_matches = AppMatches::new(Some(matches_info.clone()), AppFetch::new(rx));
|
||||
let app_matches = MatchState::new(Some(matches_info.clone()), FetchState::new(rx));
|
||||
|
||||
let matches = AppMachine::matches(inner(music_hoard(vec![])), app_matches);
|
||||
let matches = AppMachine::match_state(inner(music_hoard(vec![])), app_matches);
|
||||
matches_info.push_cannot_have_mbid();
|
||||
|
||||
let mut widget_state = WidgetState::default();
|
||||
@ -251,23 +251,23 @@ mod tests {
|
||||
assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
|
||||
assert_eq!(matches.state.state, widget_state);
|
||||
|
||||
let matches = matches.prev_match().unwrap_matches();
|
||||
let matches = matches.prev_match().unwrap_match();
|
||||
|
||||
assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
|
||||
assert_eq!(matches.state.state.list.selected(), Some(0));
|
||||
|
||||
let matches = matches.next_match().unwrap_matches();
|
||||
let matches = matches.next_match().unwrap_match();
|
||||
|
||||
assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
|
||||
assert_eq!(matches.state.state.list.selected(), Some(1));
|
||||
|
||||
// Next is CannotHaveMBID
|
||||
let matches = matches.next_match().unwrap_matches();
|
||||
let matches = matches.next_match().unwrap_match();
|
||||
|
||||
assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
|
||||
assert_eq!(matches.state.state.list.selected(), Some(2));
|
||||
|
||||
let matches = matches.next_match().unwrap_matches();
|
||||
let matches = matches.next_match().unwrap_match();
|
||||
|
||||
assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
|
||||
assert_eq!(matches.state.state.list.selected(), Some(2));
|
||||
@ -278,20 +278,20 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn artist_matches_flow() {
|
||||
matches_flow(artist_match());
|
||||
match_state_flow(artist_match());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn album_matches_flow() {
|
||||
matches_flow(album_match());
|
||||
match_state_flow(album_match());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matches_abort() {
|
||||
fn abort() {
|
||||
let mut album_match = album_match();
|
||||
let matches = AppMachine::matches(
|
||||
let matches = AppMachine::match_state(
|
||||
inner(music_hoard(vec![])),
|
||||
matches(Some(album_match.clone())),
|
||||
match_state(Some(album_match.clone())),
|
||||
);
|
||||
album_match.push_cannot_have_mbid();
|
||||
|
||||
@ -305,10 +305,10 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matches_select_empty() {
|
||||
fn select_empty() {
|
||||
// Note that what really matters in this test is actually that the transmit channel has
|
||||
// disconnected and so the receive within AppFetch concludes there are no more matches.
|
||||
let matches = AppMachine::matches(inner(music_hoard(vec![])), matches(None));
|
||||
// disconnected and so the receive within FetchState concludes there are no more matches.
|
||||
let matches = AppMachine::match_state(inner(music_hoard(vec![])), match_state(None));
|
||||
matches.select().unwrap_browse();
|
||||
}
|
||||
}
|
@ -1,40 +1,40 @@
|
||||
mod browse;
|
||||
mod critical;
|
||||
mod error;
|
||||
mod fetch;
|
||||
mod info;
|
||||
mod matches;
|
||||
mod reload;
|
||||
mod search;
|
||||
mod browse_state;
|
||||
mod critical_state;
|
||||
mod error_state;
|
||||
mod fetch_state;
|
||||
mod info_state;
|
||||
mod match_state;
|
||||
mod reload_state;
|
||||
mod search_state;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::tui::{
|
||||
app::{selection::Selection, AppPublic, AppPublicInner, AppState, IAppAccess, IAppInteract},
|
||||
app::{selection::Selection, AppPublic, AppPublicInner, AppState, IApp, IAppAccess},
|
||||
event::EventSender,
|
||||
lib::{interface::musicbrainz::IMusicBrainz, IMusicHoard},
|
||||
};
|
||||
|
||||
use browse::AppBrowse;
|
||||
use critical::AppCritical;
|
||||
use error::AppError;
|
||||
use fetch::AppFetch;
|
||||
use info::AppInfo;
|
||||
use matches::AppMatches;
|
||||
use reload::AppReload;
|
||||
use search::AppSearch;
|
||||
use browse_state::BrowseState;
|
||||
use critical_state::CriticalState;
|
||||
use error_state::ErrorState;
|
||||
use fetch_state::FetchState;
|
||||
use info_state::InfoState;
|
||||
use match_state::MatchState;
|
||||
use reload_state::ReloadState;
|
||||
use search_state::SearchState;
|
||||
|
||||
use super::IAppBase;
|
||||
|
||||
pub type App = AppState<
|
||||
AppMachine<AppBrowse>,
|
||||
AppMachine<AppInfo>,
|
||||
AppMachine<AppReload>,
|
||||
AppMachine<AppSearch>,
|
||||
AppMachine<AppFetch>,
|
||||
AppMachine<AppMatches>,
|
||||
AppMachine<AppError>,
|
||||
AppMachine<AppCritical>,
|
||||
AppMachine<BrowseState>,
|
||||
AppMachine<InfoState>,
|
||||
AppMachine<ReloadState>,
|
||||
AppMachine<SearchState>,
|
||||
AppMachine<FetchState>,
|
||||
AppMachine<MatchState>,
|
||||
AppMachine<ErrorState>,
|
||||
AppMachine<CriticalState>,
|
||||
>;
|
||||
|
||||
pub struct AppMachine<STATE> {
|
||||
@ -59,8 +59,8 @@ impl App {
|
||||
let init_result = Self::init(&mut music_hoard);
|
||||
let inner = AppInner::new(music_hoard, musicbrainz, events);
|
||||
match init_result {
|
||||
Ok(()) => AppMachine::browse(inner).into(),
|
||||
Err(err) => AppMachine::critical(inner, err.to_string()).into(),
|
||||
Ok(()) => AppMachine::browse_state(inner).into(),
|
||||
Err(err) => AppMachine::critical_state(inner, err.to_string()).into(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,40 +71,40 @@ impl App {
|
||||
|
||||
fn inner_ref(&self) -> &AppInner {
|
||||
match self {
|
||||
AppState::Browse(browse) => &browse.inner,
|
||||
AppState::Info(info) => &info.inner,
|
||||
AppState::Reload(reload) => &reload.inner,
|
||||
AppState::Search(search) => &search.inner,
|
||||
AppState::Fetch(fetch) => &fetch.inner,
|
||||
AppState::Matches(matches) => &matches.inner,
|
||||
AppState::Error(error) => &error.inner,
|
||||
AppState::Critical(critical) => &critical.inner,
|
||||
AppState::Browse(browse_state) => &browse_state.inner,
|
||||
AppState::Info(info_state) => &info_state.inner,
|
||||
AppState::Reload(reload_state) => &reload_state.inner,
|
||||
AppState::Search(search_state) => &search_state.inner,
|
||||
AppState::Fetch(fetch_state) => &fetch_state.inner,
|
||||
AppState::Match(match_state) => &match_state.inner,
|
||||
AppState::Error(error_state) => &error_state.inner,
|
||||
AppState::Critical(critical_state) => &critical_state.inner,
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_mut(&mut self) -> &mut AppInner {
|
||||
match self {
|
||||
AppState::Browse(browse) => &mut browse.inner,
|
||||
AppState::Info(info) => &mut info.inner,
|
||||
AppState::Reload(reload) => &mut reload.inner,
|
||||
AppState::Search(search) => &mut search.inner,
|
||||
AppState::Fetch(fetch) => &mut fetch.inner,
|
||||
AppState::Matches(matches) => &mut matches.inner,
|
||||
AppState::Error(error) => &mut error.inner,
|
||||
AppState::Critical(critical) => &mut critical.inner,
|
||||
AppState::Browse(browse_state) => &mut browse_state.inner,
|
||||
AppState::Info(info_state) => &mut info_state.inner,
|
||||
AppState::Reload(reload_state) => &mut reload_state.inner,
|
||||
AppState::Search(search_state) => &mut search_state.inner,
|
||||
AppState::Fetch(fetch_state) => &mut fetch_state.inner,
|
||||
AppState::Match(match_state) => &mut match_state.inner,
|
||||
AppState::Error(error_state) => &mut error_state.inner,
|
||||
AppState::Critical(critical_state) => &mut critical_state.inner,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IAppInteract for App {
|
||||
type BS = AppMachine<AppBrowse>;
|
||||
type IS = AppMachine<AppInfo>;
|
||||
type RS = AppMachine<AppReload>;
|
||||
type SS = AppMachine<AppSearch>;
|
||||
type FS = AppMachine<AppFetch>;
|
||||
type MS = AppMachine<AppMatches>;
|
||||
type ES = AppMachine<AppError>;
|
||||
type CS = AppMachine<AppCritical>;
|
||||
impl IApp for App {
|
||||
type BrowseState = AppMachine<BrowseState>;
|
||||
type InfoState = AppMachine<InfoState>;
|
||||
type ReloadState = AppMachine<ReloadState>;
|
||||
type SearchState = AppMachine<SearchState>;
|
||||
type FetchState = AppMachine<FetchState>;
|
||||
type MatchState = AppMachine<MatchState>;
|
||||
type ErrorState = AppMachine<ErrorState>;
|
||||
type CriticalState = AppMachine<CriticalState>;
|
||||
|
||||
fn is_running(&self) -> bool {
|
||||
self.inner_ref().running
|
||||
@ -117,8 +117,16 @@ impl IAppInteract for App {
|
||||
|
||||
fn state(
|
||||
self,
|
||||
) -> AppState<Self::BS, Self::IS, Self::RS, Self::SS, Self::FS, Self::MS, Self::ES, Self::CS>
|
||||
{
|
||||
) -> AppState<
|
||||
Self::BrowseState,
|
||||
Self::InfoState,
|
||||
Self::ReloadState,
|
||||
Self::SearchState,
|
||||
Self::FetchState,
|
||||
Self::MatchState,
|
||||
Self::ErrorState,
|
||||
Self::CriticalState,
|
||||
> {
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -139,7 +147,7 @@ impl IAppAccess for App {
|
||||
AppState::Reload(reload) => reload.into(),
|
||||
AppState::Search(search) => search.into(),
|
||||
AppState::Fetch(fetch) => fetch.into(),
|
||||
AppState::Matches(matches) => matches.into(),
|
||||
AppState::Match(matches) => matches.into(),
|
||||
AppState::Error(error) => error.into(),
|
||||
AppState::Critical(critical) => critical.into(),
|
||||
}
|
||||
@ -179,7 +187,7 @@ mod tests {
|
||||
use musichoard::collection::Collection;
|
||||
|
||||
use crate::tui::{
|
||||
app::{AppState, IAppInteract, IAppInteractBrowse},
|
||||
app::{AppState, IApp, IAppInteractBrowse},
|
||||
lib::{interface::musicbrainz::MockIMusicBrainz, MockIMusicHoard},
|
||||
EventChannel,
|
||||
};
|
||||
@ -222,9 +230,9 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_matches(self) -> MS {
|
||||
pub fn unwrap_match(self) -> MS {
|
||||
match self {
|
||||
AppState::Matches(matches) => matches,
|
||||
AppState::Match(matches) => matches,
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
@ -375,7 +383,7 @@ mod tests {
|
||||
|
||||
let (_, rx) = mpsc::channel();
|
||||
let inner = app.unwrap_browse().inner;
|
||||
let state = AppFetch::new(rx);
|
||||
let state = FetchState::new(rx);
|
||||
app = AppMachine { inner, state }.into();
|
||||
|
||||
let state = app.state();
|
||||
@ -400,20 +408,21 @@ mod tests {
|
||||
assert!(app.is_running());
|
||||
|
||||
let (_, rx) = mpsc::channel();
|
||||
let fetch = AppFetch::new(rx);
|
||||
app = AppMachine::matches(app.unwrap_browse().inner, AppMatches::new(None, fetch)).into();
|
||||
let fetch = FetchState::new(rx);
|
||||
app =
|
||||
AppMachine::match_state(app.unwrap_browse().inner, MatchState::new(None, fetch)).into();
|
||||
|
||||
let state = app.state();
|
||||
assert!(matches!(state, AppState::Matches(_)));
|
||||
assert!(matches!(state, AppState::Match(_)));
|
||||
app = state;
|
||||
|
||||
app = app.no_op();
|
||||
let state = app.state();
|
||||
assert!(matches!(state, AppState::Matches(_)));
|
||||
assert!(matches!(state, AppState::Match(_)));
|
||||
app = state;
|
||||
|
||||
let public = app.get();
|
||||
assert!(matches!(public.state, AppState::Matches(_)));
|
||||
assert!(matches!(public.state, AppState::Match(_)));
|
||||
|
||||
let app = app.force_quit();
|
||||
assert!(!app.is_running());
|
||||
@ -424,7 +433,7 @@ mod tests {
|
||||
let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
|
||||
assert!(app.is_running());
|
||||
|
||||
app = AppMachine::error(app.unwrap_browse().inner, "get rekt").into();
|
||||
app = AppMachine::error_state(app.unwrap_browse().inner, "get rekt").into();
|
||||
|
||||
let state = app.state();
|
||||
assert!(matches!(state, AppState::Error(_)));
|
||||
@ -447,7 +456,7 @@ mod tests {
|
||||
let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
|
||||
assert!(app.is_running());
|
||||
|
||||
app = AppMachine::critical(app.unwrap_browse().inner, "get rekt").into();
|
||||
app = AppMachine::critical_state(app.unwrap_browse().inner, "get rekt").into();
|
||||
|
||||
let state = app.state();
|
||||
assert!(matches!(state, AppState::Critical(_)));
|
||||
|
@ -4,24 +4,24 @@ use crate::tui::app::{
|
||||
AppPublic, AppState, IAppInteractReload,
|
||||
};
|
||||
|
||||
pub struct AppReload;
|
||||
pub struct ReloadState;
|
||||
|
||||
impl AppMachine<AppReload> {
|
||||
pub fn reload(inner: AppInner) -> Self {
|
||||
impl AppMachine<ReloadState> {
|
||||
pub fn reload_state(inner: AppInner) -> Self {
|
||||
AppMachine {
|
||||
inner,
|
||||
state: AppReload,
|
||||
state: ReloadState,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AppMachine<AppReload>> for App {
|
||||
fn from(machine: AppMachine<AppReload>) -> Self {
|
||||
impl From<AppMachine<ReloadState>> for App {
|
||||
fn from(machine: AppMachine<ReloadState>) -> Self {
|
||||
AppState::Reload(machine)
|
||||
}
|
||||
}
|
||||
impl<'a> From<&'a mut AppMachine<AppReload>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<AppReload>) -> Self {
|
||||
impl<'a> From<&'a mut AppMachine<ReloadState>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<ReloadState>) -> Self {
|
||||
AppPublic {
|
||||
inner: (&mut machine.inner).into(),
|
||||
state: AppState::Reload(()),
|
||||
@ -29,7 +29,7 @@ impl<'a> From<&'a mut AppMachine<AppReload>> for AppPublic<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl IAppInteractReload for AppMachine<AppReload> {
|
||||
impl IAppInteractReload for AppMachine<ReloadState> {
|
||||
type APP = App;
|
||||
|
||||
fn reload_library(mut self) -> Self::APP {
|
||||
@ -51,7 +51,7 @@ impl IAppInteractReload for AppMachine<AppReload> {
|
||||
}
|
||||
|
||||
fn hide_reload_menu(self) -> Self::APP {
|
||||
AppMachine::browse(self.inner).into()
|
||||
AppMachine::browse_state(self.inner).into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,16 +59,16 @@ trait IAppInteractReloadPrivate {
|
||||
fn refresh(self, previous: KeySelection, result: Result<(), musichoard::Error>) -> App;
|
||||
}
|
||||
|
||||
impl IAppInteractReloadPrivate for AppMachine<AppReload> {
|
||||
impl IAppInteractReloadPrivate for AppMachine<ReloadState> {
|
||||
fn refresh(mut self, previous: KeySelection, result: Result<(), musichoard::Error>) -> App {
|
||||
match result {
|
||||
Ok(()) => {
|
||||
self.inner
|
||||
.selection
|
||||
.select_by_id(self.inner.music_hoard.get_collection(), previous);
|
||||
AppMachine::browse(self.inner).into()
|
||||
AppMachine::browse_state(self.inner).into()
|
||||
}
|
||||
Err(err) => AppMachine::error(self.inner, err.to_string()).into(),
|
||||
Err(err) => AppMachine::error_state(self.inner, err.to_string()).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -81,7 +81,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn hide_reload_menu() {
|
||||
let reload = AppMachine::reload(inner(music_hoard(vec![])));
|
||||
let reload = AppMachine::reload_state(inner(music_hoard(vec![])));
|
||||
let app = reload.hide_reload_menu();
|
||||
app.unwrap_browse();
|
||||
}
|
||||
@ -95,7 +95,7 @@ mod tests {
|
||||
.times(1)
|
||||
.return_once(|| Ok(()));
|
||||
|
||||
let reload = AppMachine::reload(inner(music_hoard));
|
||||
let reload = AppMachine::reload_state(inner(music_hoard));
|
||||
let app = reload.reload_database();
|
||||
app.unwrap_browse();
|
||||
}
|
||||
@ -109,7 +109,7 @@ mod tests {
|
||||
.times(1)
|
||||
.return_once(|| Ok(()));
|
||||
|
||||
let reload = AppMachine::reload(inner(music_hoard));
|
||||
let reload = AppMachine::reload_state(inner(music_hoard));
|
||||
let app = reload.reload_library();
|
||||
app.unwrap_browse();
|
||||
}
|
||||
@ -123,7 +123,7 @@ mod tests {
|
||||
.times(1)
|
||||
.return_once(|| Err(musichoard::Error::DatabaseError(String::from("get rekt"))));
|
||||
|
||||
let reload = AppMachine::reload(inner(music_hoard));
|
||||
let reload = AppMachine::reload_state(inner(music_hoard));
|
||||
let app = reload.reload_database();
|
||||
app.unwrap_error();
|
||||
}
|
@ -20,22 +20,22 @@ const REPLACE: [&str; 11] = ["-", "-", "-", "-", "-", "'", "'", "\"", "\"", "...
|
||||
static AC: Lazy<AhoCorasick> =
|
||||
Lazy::new(|| AhoCorasick::new(SPECIAL.map(|ch| ch.to_string())).unwrap());
|
||||
|
||||
pub struct AppSearch {
|
||||
pub struct SearchState {
|
||||
string: String,
|
||||
orig: ListSelection,
|
||||
memo: Vec<AppSearchMemo>,
|
||||
memo: Vec<SearchStateMemo>,
|
||||
}
|
||||
|
||||
struct AppSearchMemo {
|
||||
struct SearchStateMemo {
|
||||
index: Option<usize>,
|
||||
char: bool,
|
||||
}
|
||||
|
||||
impl AppMachine<AppSearch> {
|
||||
pub fn search(inner: AppInner, orig: ListSelection) -> Self {
|
||||
impl AppMachine<SearchState> {
|
||||
pub fn search_state(inner: AppInner, orig: ListSelection) -> Self {
|
||||
AppMachine {
|
||||
inner,
|
||||
state: AppSearch {
|
||||
state: SearchState {
|
||||
string: String::new(),
|
||||
orig,
|
||||
memo: vec![],
|
||||
@ -44,14 +44,14 @@ impl AppMachine<AppSearch> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AppMachine<AppSearch>> for App {
|
||||
fn from(machine: AppMachine<AppSearch>) -> Self {
|
||||
impl From<AppMachine<SearchState>> for App {
|
||||
fn from(machine: AppMachine<SearchState>) -> Self {
|
||||
AppState::Search(machine)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut AppMachine<AppSearch>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<AppSearch>) -> Self {
|
||||
impl<'a> From<&'a mut AppMachine<SearchState>> for AppPublic<'a> {
|
||||
fn from(machine: &'a mut AppMachine<SearchState>) -> Self {
|
||||
AppPublic {
|
||||
inner: (&mut machine.inner).into(),
|
||||
state: AppState::Search(&machine.state.string),
|
||||
@ -59,13 +59,13 @@ impl<'a> From<&'a mut AppMachine<AppSearch>> for AppPublic<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl IAppInteractSearch for AppMachine<AppSearch> {
|
||||
impl IAppInteractSearch for AppMachine<SearchState> {
|
||||
type APP = App;
|
||||
|
||||
fn append_character(mut self, ch: char) -> Self::APP {
|
||||
self.state.string.push(ch);
|
||||
let index = self.inner.selection.selected();
|
||||
self.state.memo.push(AppSearchMemo { index, char: true });
|
||||
self.state.memo.push(SearchStateMemo { index, char: true });
|
||||
self.incremental_search(false);
|
||||
self.into()
|
||||
}
|
||||
@ -73,7 +73,7 @@ impl IAppInteractSearch for AppMachine<AppSearch> {
|
||||
fn search_next(mut self) -> Self::APP {
|
||||
if !self.state.string.is_empty() {
|
||||
let index = self.inner.selection.selected();
|
||||
self.state.memo.push(AppSearchMemo { index, char: false });
|
||||
self.state.memo.push(SearchStateMemo { index, char: false });
|
||||
self.incremental_search(true);
|
||||
}
|
||||
self.into()
|
||||
@ -91,12 +91,12 @@ impl IAppInteractSearch for AppMachine<AppSearch> {
|
||||
}
|
||||
|
||||
fn finish_search(self) -> Self::APP {
|
||||
AppMachine::browse(self.inner).into()
|
||||
AppMachine::browse_state(self.inner).into()
|
||||
}
|
||||
|
||||
fn cancel_search(mut self) -> Self::APP {
|
||||
self.inner.selection.select_by_list(self.state.orig);
|
||||
AppMachine::browse(self.inner).into()
|
||||
AppMachine::browse_state(self.inner).into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +120,7 @@ trait IAppInteractSearchPrivate {
|
||||
fn normalize_search(search: &str, lowercase: bool, asciify: bool) -> String;
|
||||
}
|
||||
|
||||
impl IAppInteractSearchPrivate for AppMachine<AppSearch> {
|
||||
impl IAppInteractSearchPrivate for AppMachine<SearchState> {
|
||||
fn incremental_search(&mut self, next: bool) {
|
||||
let collection = self.inner.music_hoard.get_collection();
|
||||
let search = &self.state.string;
|
||||
@ -254,7 +254,7 @@ mod tests {
|
||||
#[test]
|
||||
fn artist_incremental_search() {
|
||||
// Empty collection.
|
||||
let mut search = AppMachine::search(inner(music_hoard(vec![])), orig(None));
|
||||
let mut search = AppMachine::search_state(inner(music_hoard(vec![])), orig(None));
|
||||
assert_eq!(search.inner.selection.selected(), None);
|
||||
|
||||
search.state.string = String::from("album_artist 'a'");
|
||||
@ -263,7 +263,7 @@ mod tests {
|
||||
|
||||
// Basic test, first element.
|
||||
let mut search =
|
||||
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
assert_eq!(search.inner.selection.selected(), Some(0));
|
||||
|
||||
search.state.string = String::from("");
|
||||
@ -280,7 +280,7 @@ mod tests {
|
||||
|
||||
// Basic test, non-first element.
|
||||
let mut search =
|
||||
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
assert_eq!(search.inner.selection.selected(), Some(0));
|
||||
|
||||
search.state.string = String::from("album_artist ");
|
||||
@ -293,7 +293,7 @@ mod tests {
|
||||
|
||||
// Non-lowercase.
|
||||
let mut search =
|
||||
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
assert_eq!(search.inner.selection.selected(), Some(0));
|
||||
|
||||
search.state.string = String::from("Album_Artist ");
|
||||
@ -306,7 +306,7 @@ mod tests {
|
||||
|
||||
// Non-ascii.
|
||||
let mut search =
|
||||
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
assert_eq!(search.inner.selection.selected(), Some(0));
|
||||
|
||||
search.state.string = String::from("album_artist ");
|
||||
@ -319,7 +319,7 @@ mod tests {
|
||||
|
||||
// Non-lowercase, non-ascii.
|
||||
let mut search =
|
||||
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
assert_eq!(search.inner.selection.selected(), Some(0));
|
||||
|
||||
search.state.string = String::from("Album_Artist ");
|
||||
@ -332,7 +332,7 @@ mod tests {
|
||||
|
||||
// Stop at name, not sort name.
|
||||
let mut search =
|
||||
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
assert_eq!(search.inner.selection.selected(), Some(0));
|
||||
|
||||
search.state.string = String::from("the ");
|
||||
@ -345,7 +345,7 @@ mod tests {
|
||||
|
||||
// Search next with common prefix.
|
||||
let mut search =
|
||||
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
assert_eq!(search.inner.selection.selected(), Some(0));
|
||||
|
||||
search.state.string = String::from("album_artist");
|
||||
@ -368,7 +368,7 @@ mod tests {
|
||||
#[test]
|
||||
fn album_incremental_search() {
|
||||
let mut search =
|
||||
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
search.inner.selection.increment_category();
|
||||
assert_eq!(search.inner.selection.category(), Category::Album);
|
||||
|
||||
@ -392,7 +392,7 @@ mod tests {
|
||||
#[test]
|
||||
fn track_incremental_search() {
|
||||
let mut search =
|
||||
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
|
||||
search.inner.selection.increment_category();
|
||||
search.inner.selection.increment_category();
|
||||
assert_eq!(search.inner.selection.category(), Category::Track);
|
||||
@ -418,7 +418,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn search() {
|
||||
let search = AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
|
||||
let search =
|
||||
AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
|
||||
assert_eq!(search.inner.selection.selected(), Some(0));
|
||||
|
||||
let search = search.append_character('a').unwrap_search();
|
||||
@ -458,7 +459,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn search_next_step_back() {
|
||||
let search = AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
|
||||
let search =
|
||||
AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
|
||||
assert_eq!(search.inner.selection.selected(), Some(0));
|
||||
|
||||
let search = search.step_back().unwrap_search();
|
||||
@ -495,7 +497,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn cancel_search() {
|
||||
let search = AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
|
||||
let search =
|
||||
AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
|
||||
assert_eq!(search.inner.selection.selected(), Some(0));
|
||||
|
||||
let search = search.append_character('a').unwrap_search();
|
||||
@ -522,7 +525,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn empty_search() {
|
||||
let search = AppMachine::search(inner(music_hoard(vec![])), orig(None));
|
||||
let search = AppMachine::search_state(inner(music_hoard(vec![])), orig(None));
|
||||
assert_eq!(search.inner.selection.selected(), None);
|
||||
|
||||
let search = search.append_character('a').unwrap_search();
|
||||
@ -552,7 +555,7 @@ mod benches {
|
||||
|
||||
use super::*;
|
||||
|
||||
type Search = AppMachine<MockIMusicHoard, AppSearch>;
|
||||
type Search = AppMachine<MockIMusicHoard, SearchState>;
|
||||
|
||||
#[bench]
|
||||
fn is_char_sensitive(b: &mut Bencher) {
|
@ -8,26 +8,37 @@ use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta, Collection};
|
||||
|
||||
use crate::tui::lib::interface::musicbrainz::Match;
|
||||
|
||||
pub enum AppState<BS, IS, RS, SS, FS, MS, ES, CS> {
|
||||
Browse(BS),
|
||||
Info(IS),
|
||||
Reload(RS),
|
||||
Search(SS),
|
||||
Fetch(FS),
|
||||
Matches(MS),
|
||||
Error(ES),
|
||||
Critical(CS),
|
||||
pub enum AppState<
|
||||
BrowseState,
|
||||
InfoState,
|
||||
ReloadState,
|
||||
SearchState,
|
||||
FetchState,
|
||||
MatchState,
|
||||
ErrorState,
|
||||
CriticalState,
|
||||
> {
|
||||
Browse(BrowseState),
|
||||
Info(InfoState),
|
||||
Reload(ReloadState),
|
||||
Search(SearchState),
|
||||
Fetch(FetchState),
|
||||
Match(MatchState),
|
||||
Error(ErrorState),
|
||||
Critical(CriticalState),
|
||||
}
|
||||
|
||||
pub trait IAppInteract {
|
||||
type BS: IAppBase<APP = Self> + IAppInteractBrowse<APP = Self>;
|
||||
type IS: IAppBase<APP = Self> + IAppInteractInfo<APP = Self>;
|
||||
type RS: IAppBase<APP = Self> + IAppInteractReload<APP = Self>;
|
||||
type SS: IAppBase<APP = Self> + IAppInteractSearch<APP = Self>;
|
||||
type FS: IAppBase<APP = Self> + IAppInteractFetch<APP = Self> + IAppEventFetch<APP = Self>;
|
||||
type MS: IAppBase<APP = Self> + IAppInteractMatches<APP = Self>;
|
||||
type ES: IAppBase<APP = Self> + IAppInteractError<APP = Self>;
|
||||
type CS: IAppBase<APP = Self>;
|
||||
pub trait IApp {
|
||||
type BrowseState: IAppBase<APP = Self> + IAppInteractBrowse<APP = Self>;
|
||||
type InfoState: IAppBase<APP = Self> + IAppInteractInfo<APP = Self>;
|
||||
type ReloadState: IAppBase<APP = Self> + IAppInteractReload<APP = Self>;
|
||||
type SearchState: IAppBase<APP = Self> + IAppInteractSearch<APP = Self>;
|
||||
type FetchState: IAppBase<APP = Self>
|
||||
+ IAppInteractFetch<APP = Self>
|
||||
+ IAppEventFetch<APP = Self>;
|
||||
type MatchState: IAppBase<APP = Self> + IAppInteractMatch<APP = Self>;
|
||||
type ErrorState: IAppBase<APP = Self> + IAppInteractError<APP = Self>;
|
||||
type CriticalState: IAppBase<APP = Self>;
|
||||
|
||||
fn is_running(&self) -> bool;
|
||||
fn force_quit(self) -> Self;
|
||||
@ -35,17 +46,26 @@ pub trait IAppInteract {
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn state(
|
||||
self,
|
||||
) -> AppState<Self::BS, Self::IS, Self::RS, Self::SS, Self::FS, Self::MS, Self::ES, Self::CS>;
|
||||
) -> AppState<
|
||||
Self::BrowseState,
|
||||
Self::InfoState,
|
||||
Self::ReloadState,
|
||||
Self::SearchState,
|
||||
Self::FetchState,
|
||||
Self::MatchState,
|
||||
Self::ErrorState,
|
||||
Self::CriticalState,
|
||||
>;
|
||||
}
|
||||
|
||||
pub trait IAppBase {
|
||||
type APP: IAppInteract;
|
||||
type APP: IApp;
|
||||
|
||||
fn no_op(self) -> Self::APP;
|
||||
}
|
||||
|
||||
pub trait IAppInteractBrowse {
|
||||
type APP: IAppInteract;
|
||||
type APP: IApp;
|
||||
|
||||
fn quit(self) -> Self::APP;
|
||||
|
||||
@ -64,13 +84,13 @@ pub trait IAppInteractBrowse {
|
||||
}
|
||||
|
||||
pub trait IAppInteractInfo {
|
||||
type APP: IAppInteract;
|
||||
type APP: IApp;
|
||||
|
||||
fn hide_info_overlay(self) -> Self::APP;
|
||||
}
|
||||
|
||||
pub trait IAppInteractReload {
|
||||
type APP: IAppInteract;
|
||||
type APP: IApp;
|
||||
|
||||
fn reload_library(self) -> Self::APP;
|
||||
fn reload_database(self) -> Self::APP;
|
||||
@ -78,7 +98,7 @@ pub trait IAppInteractReload {
|
||||
}
|
||||
|
||||
pub trait IAppInteractSearch {
|
||||
type APP: IAppInteract;
|
||||
type APP: IApp;
|
||||
|
||||
fn append_character(self, ch: char) -> Self::APP;
|
||||
fn search_next(self) -> Self::APP;
|
||||
@ -88,19 +108,19 @@ pub trait IAppInteractSearch {
|
||||
}
|
||||
|
||||
pub trait IAppInteractFetch {
|
||||
type APP: IAppInteract;
|
||||
type APP: IApp;
|
||||
|
||||
fn abort(self) -> Self::APP;
|
||||
}
|
||||
|
||||
pub trait IAppEventFetch {
|
||||
type APP: IAppInteract;
|
||||
type APP: IApp;
|
||||
|
||||
fn fetch_result_ready(self) -> Self::APP;
|
||||
}
|
||||
|
||||
pub trait IAppInteractMatches {
|
||||
type APP: IAppInteract;
|
||||
pub trait IAppInteractMatch {
|
||||
type APP: IApp;
|
||||
|
||||
fn prev_match(self) -> Self::APP;
|
||||
fn next_match(self) -> Self::APP;
|
||||
@ -110,7 +130,7 @@ pub trait IAppInteractMatches {
|
||||
}
|
||||
|
||||
pub trait IAppInteractError {
|
||||
type APP: IAppInteract;
|
||||
type APP: IApp;
|
||||
|
||||
fn dismiss_error(self) -> Self::APP;
|
||||
}
|
||||
@ -146,42 +166,42 @@ impl<T> From<Match<T>> for MatchOption<T> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct AppArtistMatches {
|
||||
pub struct ArtistMatches {
|
||||
pub matching: ArtistMeta,
|
||||
pub list: Vec<MatchOption<ArtistMeta>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct AppAlbumMatches {
|
||||
pub struct AlbumMatches {
|
||||
pub matching: AlbumMeta,
|
||||
pub list: Vec<MatchOption<AlbumMeta>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum AppMatchesInfo {
|
||||
Artist(AppArtistMatches),
|
||||
Album(AppAlbumMatches),
|
||||
pub enum MatchStateInfo {
|
||||
Artist(ArtistMatches),
|
||||
Album(AlbumMatches),
|
||||
}
|
||||
|
||||
impl AppMatchesInfo {
|
||||
impl MatchStateInfo {
|
||||
pub fn artist<M: Into<MatchOption<ArtistMeta>>>(matching: ArtistMeta, list: Vec<M>) -> Self {
|
||||
let list: Vec<MatchOption<ArtistMeta>> = list.into_iter().map(Into::into).collect();
|
||||
AppMatchesInfo::Artist(AppArtistMatches { matching, list })
|
||||
MatchStateInfo::Artist(ArtistMatches { matching, list })
|
||||
}
|
||||
|
||||
pub fn album<M: Into<MatchOption<AlbumMeta>>>(matching: AlbumMeta, list: Vec<M>) -> Self {
|
||||
let list: Vec<MatchOption<AlbumMeta>> = list.into_iter().map(Into::into).collect();
|
||||
AppMatchesInfo::Album(AppAlbumMatches { matching, list })
|
||||
MatchStateInfo::Album(AlbumMatches { matching, list })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AppPublicMatches<'app> {
|
||||
pub matches: Option<&'app AppMatchesInfo>,
|
||||
pub struct MatchStatePublic<'app> {
|
||||
pub info: Option<&'app MatchStateInfo>,
|
||||
pub state: &'app mut WidgetState,
|
||||
}
|
||||
|
||||
pub type AppPublicState<'app> =
|
||||
AppState<(), (), (), &'app str, (), AppPublicMatches<'app>, &'app str, &'app str>;
|
||||
AppState<(), (), (), &'app str, (), MatchStatePublic<'app>, &'app str, &'app str>;
|
||||
|
||||
impl<BS, IS, RS, SS, FS, MS, ES, CS> AppState<BS, IS, RS, SS, FS, MS, ES, CS> {
|
||||
pub fn is_search(&self) -> bool {
|
||||
|
@ -5,8 +5,8 @@ use mockall::automock;
|
||||
|
||||
use crate::tui::{
|
||||
app::{
|
||||
AppState, Delta, IAppInteract, IAppInteractBrowse, IAppInteractError, IAppInteractFetch,
|
||||
IAppInteractInfo, IAppInteractMatches, IAppInteractReload, IAppInteractSearch,
|
||||
AppState, Delta, IApp, IAppInteractBrowse, IAppInteractError, IAppInteractFetch,
|
||||
IAppInteractInfo, IAppInteractMatch, IAppInteractReload, IAppInteractSearch,
|
||||
},
|
||||
event::{Event, EventError, EventReceiver},
|
||||
};
|
||||
@ -14,20 +14,20 @@ use crate::tui::{
|
||||
use super::app::{IAppBase, IAppEventFetch};
|
||||
|
||||
#[cfg_attr(test, automock)]
|
||||
pub trait IEventHandler<APP: IAppInteract> {
|
||||
pub trait IEventHandler<APP: IApp> {
|
||||
fn handle_next_event(&self, app: APP) -> Result<APP, EventError>;
|
||||
}
|
||||
|
||||
trait IEventHandlerPrivate<APP: IAppInteract> {
|
||||
trait IEventHandlerPrivate<APP: IApp> {
|
||||
fn handle_key_event(app: APP, key_event: KeyEvent) -> APP;
|
||||
fn handle_browse_key_event(app: <APP as IAppInteract>::BS, key_event: KeyEvent) -> APP;
|
||||
fn handle_info_key_event(app: <APP as IAppInteract>::IS, key_event: KeyEvent) -> APP;
|
||||
fn handle_reload_key_event(app: <APP as IAppInteract>::RS, key_event: KeyEvent) -> APP;
|
||||
fn handle_search_key_event(app: <APP as IAppInteract>::SS, key_event: KeyEvent) -> APP;
|
||||
fn handle_fetch_key_event(app: <APP as IAppInteract>::FS, key_event: KeyEvent) -> APP;
|
||||
fn handle_matches_key_event(app: <APP as IAppInteract>::MS, key_event: KeyEvent) -> APP;
|
||||
fn handle_error_key_event(app: <APP as IAppInteract>::ES, key_event: KeyEvent) -> APP;
|
||||
fn handle_critical_key_event(app: <APP as IAppInteract>::CS, key_event: KeyEvent) -> APP;
|
||||
fn handle_browse_key_event(app: <APP as IApp>::BrowseState, key_event: KeyEvent) -> APP;
|
||||
fn handle_info_key_event(app: <APP as IApp>::InfoState, key_event: KeyEvent) -> APP;
|
||||
fn handle_reload_key_event(app: <APP as IApp>::ReloadState, key_event: KeyEvent) -> APP;
|
||||
fn handle_search_key_event(app: <APP as IApp>::SearchState, key_event: KeyEvent) -> APP;
|
||||
fn handle_fetch_key_event(app: <APP as IApp>::FetchState, key_event: KeyEvent) -> APP;
|
||||
fn handle_match_key_event(app: <APP as IApp>::MatchState, key_event: KeyEvent) -> APP;
|
||||
fn handle_error_key_event(app: <APP as IApp>::ErrorState, key_event: KeyEvent) -> APP;
|
||||
fn handle_critical_key_event(app: <APP as IApp>::CriticalState, key_event: KeyEvent) -> APP;
|
||||
|
||||
fn handle_fetch_result_ready_event(app: APP) -> APP;
|
||||
}
|
||||
@ -43,7 +43,7 @@ impl EventHandler {
|
||||
}
|
||||
}
|
||||
|
||||
impl<APP: IAppInteract> IEventHandler<APP> for EventHandler {
|
||||
impl<APP: IApp> IEventHandler<APP> for EventHandler {
|
||||
fn handle_next_event(&self, app: APP) -> Result<APP, EventError> {
|
||||
Ok(match self.events.recv()? {
|
||||
Event::Key(key_event) => Self::handle_key_event(app, key_event),
|
||||
@ -52,7 +52,7 @@ impl<APP: IAppInteract> IEventHandler<APP> for EventHandler {
|
||||
}
|
||||
}
|
||||
|
||||
impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
||||
impl<APP: IApp> IEventHandlerPrivate<APP> for EventHandler {
|
||||
fn handle_key_event(app: APP, key_event: KeyEvent) -> APP {
|
||||
if key_event.modifiers == KeyModifiers::CONTROL {
|
||||
match key_event.code {
|
||||
@ -63,47 +63,39 @@ impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
||||
}
|
||||
|
||||
match app.state() {
|
||||
AppState::Browse(browse) => {
|
||||
<Self as IEventHandlerPrivate<APP>>::handle_browse_key_event(browse, key_event)
|
||||
AppState::Browse(browse_state) => {
|
||||
Self::handle_browse_key_event(browse_state, key_event)
|
||||
}
|
||||
AppState::Info(info) => {
|
||||
<Self as IEventHandlerPrivate<APP>>::handle_info_key_event(info, key_event)
|
||||
AppState::Info(info_state) => Self::handle_info_key_event(info_state, key_event),
|
||||
AppState::Reload(reload_state) => {
|
||||
Self::handle_reload_key_event(reload_state, key_event)
|
||||
}
|
||||
AppState::Reload(reload) => {
|
||||
<Self as IEventHandlerPrivate<APP>>::handle_reload_key_event(reload, key_event)
|
||||
AppState::Search(search_state) => {
|
||||
Self::handle_search_key_event(search_state, key_event)
|
||||
}
|
||||
AppState::Search(search) => {
|
||||
<Self as IEventHandlerPrivate<APP>>::handle_search_key_event(search, key_event)
|
||||
}
|
||||
AppState::Fetch(fetch) => {
|
||||
<Self as IEventHandlerPrivate<APP>>::handle_fetch_key_event(fetch, key_event)
|
||||
}
|
||||
AppState::Matches(matches) => {
|
||||
<Self as IEventHandlerPrivate<APP>>::handle_matches_key_event(matches, key_event)
|
||||
}
|
||||
AppState::Error(error) => {
|
||||
<Self as IEventHandlerPrivate<APP>>::handle_error_key_event(error, key_event)
|
||||
}
|
||||
AppState::Critical(critical) => {
|
||||
<Self as IEventHandlerPrivate<APP>>::handle_critical_key_event(critical, key_event)
|
||||
AppState::Fetch(fetch_state) => Self::handle_fetch_key_event(fetch_state, key_event),
|
||||
AppState::Match(match_state) => Self::handle_match_key_event(match_state, key_event),
|
||||
AppState::Error(error_state) => Self::handle_error_key_event(error_state, key_event),
|
||||
AppState::Critical(critical_state) => {
|
||||
Self::handle_critical_key_event(critical_state, key_event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_fetch_result_ready_event(app: APP) -> APP {
|
||||
match app.state() {
|
||||
AppState::Browse(browse) => browse.no_op(),
|
||||
AppState::Info(info) => info.no_op(),
|
||||
AppState::Reload(reload) => reload.no_op(),
|
||||
AppState::Search(search) => search.no_op(),
|
||||
AppState::Fetch(fetch) => fetch.fetch_result_ready(),
|
||||
AppState::Matches(matches) => matches.no_op(),
|
||||
AppState::Error(error) => error.no_op(),
|
||||
AppState::Critical(critical) => critical.no_op(),
|
||||
AppState::Browse(browse_state) => browse_state.no_op(),
|
||||
AppState::Info(info_state) => info_state.no_op(),
|
||||
AppState::Reload(reload_state) => reload_state.no_op(),
|
||||
AppState::Search(search_state) => search_state.no_op(),
|
||||
AppState::Fetch(fetch_state) => fetch_state.fetch_result_ready(),
|
||||
AppState::Match(match_state) => match_state.no_op(),
|
||||
AppState::Error(error_state) => error_state.no_op(),
|
||||
AppState::Critical(critical_state) => critical_state.no_op(),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_browse_key_event(app: <APP as IAppInteract>::BS, key_event: KeyEvent) -> APP {
|
||||
fn handle_browse_key_event(app: <APP as IApp>::BrowseState, key_event: KeyEvent) -> APP {
|
||||
match key_event.code {
|
||||
// Exit application on `ESC` or `q`.
|
||||
KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => app.quit(),
|
||||
@ -133,7 +125,7 @@ impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_info_key_event(app: <APP as IAppInteract>::IS, key_event: KeyEvent) -> APP {
|
||||
fn handle_info_key_event(app: <APP as IApp>::InfoState, key_event: KeyEvent) -> APP {
|
||||
match key_event.code {
|
||||
// Toggle overlay.
|
||||
KeyCode::Esc
|
||||
@ -146,7 +138,7 @@ impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_reload_key_event(app: <APP as IAppInteract>::RS, key_event: KeyEvent) -> APP {
|
||||
fn handle_reload_key_event(app: <APP as IApp>::ReloadState, key_event: KeyEvent) -> APP {
|
||||
match key_event.code {
|
||||
// Reload keys.
|
||||
KeyCode::Char('l') | KeyCode::Char('L') => app.reload_library(),
|
||||
@ -162,7 +154,7 @@ impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_search_key_event(app: <APP as IAppInteract>::SS, key_event: KeyEvent) -> APP {
|
||||
fn handle_search_key_event(app: <APP as IApp>::SearchState, key_event: KeyEvent) -> APP {
|
||||
if key_event.modifiers == KeyModifiers::CONTROL {
|
||||
return match key_event.code {
|
||||
KeyCode::Char('s') | KeyCode::Char('S') => app.search_next(),
|
||||
@ -182,7 +174,7 @@ impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_fetch_key_event(app: <APP as IAppInteract>::FS, key_event: KeyEvent) -> APP {
|
||||
fn handle_fetch_key_event(app: <APP as IApp>::FetchState, key_event: KeyEvent) -> APP {
|
||||
match key_event.code {
|
||||
// Abort.
|
||||
KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => app.abort(),
|
||||
@ -191,7 +183,7 @@ impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_matches_key_event(app: <APP as IAppInteract>::MS, key_event: KeyEvent) -> APP {
|
||||
fn handle_match_key_event(app: <APP as IApp>::MatchState, key_event: KeyEvent) -> APP {
|
||||
match key_event.code {
|
||||
// Abort.
|
||||
KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => app.abort(),
|
||||
@ -204,12 +196,12 @@ impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_error_key_event(app: <APP as IAppInteract>::ES, _key_event: KeyEvent) -> APP {
|
||||
fn handle_error_key_event(app: <APP as IApp>::ErrorState, _key_event: KeyEvent) -> APP {
|
||||
// Any key dismisses the error.
|
||||
app.dismiss_error()
|
||||
}
|
||||
|
||||
fn handle_critical_key_event(app: <APP as IAppInteract>::CS, _key_event: KeyEvent) -> APP {
|
||||
fn handle_critical_key_event(app: <APP as IApp>::CriticalState, _key_event: KeyEvent) -> APP {
|
||||
// No action is allowed.
|
||||
app.no_op()
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use ratatui::{backend::Backend, Terminal};
|
||||
use std::{io, marker::PhantomData};
|
||||
|
||||
use crate::tui::{
|
||||
app::{IAppAccess, IAppInteract},
|
||||
app::{IApp, IAppAccess},
|
||||
event::EventError,
|
||||
handler::IEventHandler,
|
||||
listener::IEventListener,
|
||||
@ -46,12 +46,12 @@ impl From<EventError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Tui<B: Backend, UI: IUi, APP: IAppInteract + IAppAccess> {
|
||||
pub struct Tui<B: Backend, UI: IUi, APP: IApp + IAppAccess> {
|
||||
terminal: Terminal<B>,
|
||||
_phantom: PhantomData<(UI, APP)>,
|
||||
}
|
||||
|
||||
impl<B: Backend, UI: IUi, APP: IAppInteract + IAppAccess> Tui<B, UI, APP> {
|
||||
impl<B: Backend, UI: IUi, APP: IApp + IAppAccess> Tui<B, UI, APP> {
|
||||
fn init(&mut self) -> Result<(), Error> {
|
||||
self.terminal.hide_cursor()?;
|
||||
self.terminal.clear()?;
|
||||
|
@ -4,7 +4,7 @@ use musichoard::collection::{
|
||||
track::{TrackFormat, TrackQuality},
|
||||
};
|
||||
|
||||
use crate::tui::app::{AppMatchesInfo, MatchOption};
|
||||
use crate::tui::app::{MatchOption, MatchStateInfo};
|
||||
|
||||
pub struct UiDisplay;
|
||||
|
||||
@ -114,11 +114,11 @@ impl UiDisplay {
|
||||
"Matching nothing"
|
||||
}
|
||||
|
||||
pub fn display_matching_info(matches: Option<&AppMatchesInfo>) -> String {
|
||||
match matches.as_ref() {
|
||||
pub fn display_matching_info(info: Option<&MatchStateInfo>) -> String {
|
||||
match info.as_ref() {
|
||||
Some(kind) => match kind {
|
||||
AppMatchesInfo::Artist(m) => UiDisplay::display_artist_matching(&m.matching),
|
||||
AppMatchesInfo::Album(m) => UiDisplay::display_album_matching(&m.matching),
|
||||
MatchStateInfo::Artist(m) => UiDisplay::display_artist_matching(&m.matching),
|
||||
MatchStateInfo::Album(m) => UiDisplay::display_album_matching(&m.matching),
|
||||
},
|
||||
None => UiDisplay::display_nothing_matching().to_string(),
|
||||
}
|
||||
|
@ -2,29 +2,29 @@ use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta};
|
||||
use ratatui::widgets::{List, ListItem};
|
||||
|
||||
use crate::tui::{
|
||||
app::{AppMatchesInfo, MatchOption, WidgetState},
|
||||
app::{MatchOption, MatchStateInfo, WidgetState},
|
||||
ui::display::UiDisplay,
|
||||
};
|
||||
|
||||
pub struct MatchesState<'a, 'b> {
|
||||
pub struct MatchOverlay<'a, 'b> {
|
||||
pub matching: String,
|
||||
pub list: List<'a>,
|
||||
pub state: &'b mut WidgetState,
|
||||
}
|
||||
|
||||
impl<'a, 'b> MatchesState<'a, 'b> {
|
||||
pub fn new(matches: Option<&AppMatchesInfo>, state: &'b mut WidgetState) -> Self {
|
||||
match matches {
|
||||
impl<'a, 'b> MatchOverlay<'a, 'b> {
|
||||
pub fn new(info: Option<&MatchStateInfo>, state: &'b mut WidgetState) -> Self {
|
||||
match info {
|
||||
Some(info) => match info {
|
||||
AppMatchesInfo::Artist(m) => Self::artists(&m.matching, &m.list, state),
|
||||
AppMatchesInfo::Album(m) => Self::albums(&m.matching, &m.list, state),
|
||||
MatchStateInfo::Artist(m) => Self::artists(&m.matching, &m.list, state),
|
||||
MatchStateInfo::Album(m) => Self::albums(&m.matching, &m.list, state),
|
||||
},
|
||||
None => Self::empty(state),
|
||||
}
|
||||
}
|
||||
|
||||
fn empty(state: &'b mut WidgetState) -> Self {
|
||||
MatchesState {
|
||||
MatchOverlay {
|
||||
matching: UiDisplay::display_nothing_matching().to_string(),
|
||||
list: List::default(),
|
||||
state,
|
||||
@ -46,7 +46,7 @@ impl<'a, 'b> MatchesState<'a, 'b> {
|
||||
.collect::<Vec<ListItem>>(),
|
||||
);
|
||||
|
||||
MatchesState {
|
||||
MatchOverlay {
|
||||
matching,
|
||||
list,
|
||||
state,
|
||||
@ -68,7 +68,7 @@ impl<'a, 'b> MatchesState<'a, 'b> {
|
||||
.collect::<Vec<ListItem>>(),
|
||||
);
|
||||
|
||||
MatchesState {
|
||||
MatchOverlay {
|
||||
matching,
|
||||
list,
|
||||
state,
|
@ -60,9 +60,9 @@ impl Minibuffer<'_> {
|
||||
paragraphs: vec![Paragraph::new("fetching..."), Paragraph::new("q: abort")],
|
||||
columns: 2,
|
||||
},
|
||||
AppState::Matches(public) => Minibuffer {
|
||||
AppState::Match(public) => Minibuffer {
|
||||
paragraphs: vec![
|
||||
Paragraph::new(UiDisplay::display_matching_info(public.matches)),
|
||||
Paragraph::new(UiDisplay::display_matching_info(public.info)),
|
||||
Paragraph::new("q: abort"),
|
||||
],
|
||||
columns: 2,
|
||||
|
@ -1,33 +1,33 @@
|
||||
mod browse;
|
||||
mod browse_state;
|
||||
mod display;
|
||||
mod error;
|
||||
mod fetch;
|
||||
mod info;
|
||||
mod matches;
|
||||
mod error_state;
|
||||
mod fetch_state;
|
||||
mod info_state;
|
||||
mod match_state;
|
||||
mod minibuffer;
|
||||
mod overlay;
|
||||
mod reload;
|
||||
mod reload_state;
|
||||
mod style;
|
||||
mod widgets;
|
||||
|
||||
use fetch::FetchOverlay;
|
||||
use ratatui::{layout::Rect, widgets::Paragraph, Frame};
|
||||
|
||||
use musichoard::collection::{album::Album, Collection};
|
||||
|
||||
use crate::tui::{
|
||||
app::{AppMatchesInfo, AppPublicState, AppState, Category, IAppAccess, Selection, WidgetState},
|
||||
app::{AppPublicState, AppState, Category, IAppAccess, MatchStateInfo, Selection, WidgetState},
|
||||
ui::{
|
||||
browse::{
|
||||
browse_state::{
|
||||
AlbumArea, AlbumState, ArtistArea, ArtistState, FrameArea, TrackArea, TrackState,
|
||||
},
|
||||
display::UiDisplay,
|
||||
error::ErrorOverlay,
|
||||
info::{AlbumOverlay, ArtistOverlay},
|
||||
matches::MatchesState,
|
||||
error_state::ErrorOverlay,
|
||||
fetch_state::FetchOverlay,
|
||||
info_state::{AlbumOverlay, ArtistOverlay},
|
||||
match_state::MatchOverlay,
|
||||
minibuffer::Minibuffer,
|
||||
overlay::{OverlayBuilder, OverlaySize},
|
||||
reload::ReloadOverlay,
|
||||
reload_state::ReloadOverlay,
|
||||
widgets::UiWidget,
|
||||
},
|
||||
};
|
||||
@ -137,13 +137,13 @@ impl Ui {
|
||||
UiWidget::render_overlay_widget("Fetching", fetch_text, area, false, frame)
|
||||
}
|
||||
|
||||
fn render_matches_overlay(
|
||||
matches: Option<&AppMatchesInfo>,
|
||||
fn render_match_overlay(
|
||||
info: Option<&MatchStateInfo>,
|
||||
state: &mut WidgetState,
|
||||
frame: &mut Frame,
|
||||
) {
|
||||
let area = OverlayBuilder::default().build(frame.size());
|
||||
let st = MatchesState::new(matches, state);
|
||||
let st = MatchOverlay::new(info, state);
|
||||
UiWidget::render_overlay_list_widget(&st.matching, st.list, st.state, true, area, frame)
|
||||
}
|
||||
|
||||
@ -169,9 +169,7 @@ impl IUi for Ui {
|
||||
AppState::Info(()) => Self::render_info_overlay(collection, selection, frame),
|
||||
AppState::Reload(()) => Self::render_reload_overlay(frame),
|
||||
AppState::Fetch(()) => Self::render_fetch_overlay(frame),
|
||||
AppState::Matches(public) => {
|
||||
Self::render_matches_overlay(public.matches, public.state, frame)
|
||||
}
|
||||
AppState::Match(public) => Self::render_match_overlay(public.info, public.state, frame),
|
||||
AppState::Error(msg) => Self::render_error_overlay("Error", msg, frame),
|
||||
AppState::Critical(msg) => Self::render_error_overlay("Critical Error", msg, frame),
|
||||
_ => {}
|
||||
@ -187,7 +185,7 @@ mod tests {
|
||||
};
|
||||
|
||||
use crate::tui::{
|
||||
app::{AppPublic, AppPublicInner, AppPublicMatches, Delta, MatchOption},
|
||||
app::{AppPublic, AppPublicInner, Delta, MatchOption, MatchStatePublic},
|
||||
lib::interface::musicbrainz::Match,
|
||||
testmod::COLLECTION,
|
||||
tests::terminal,
|
||||
@ -209,8 +207,8 @@ mod tests {
|
||||
AppState::Reload(()) => AppState::Reload(()),
|
||||
AppState::Search(s) => AppState::Search(s),
|
||||
AppState::Fetch(()) => AppState::Fetch(()),
|
||||
AppState::Matches(ref mut m) => AppState::Matches(AppPublicMatches {
|
||||
matches: m.matches,
|
||||
AppState::Match(ref mut m) => AppState::Match(MatchStatePublic {
|
||||
info: m.info,
|
||||
state: m.state,
|
||||
}),
|
||||
AppState::Error(s) => AppState::Error(s),
|
||||
@ -230,16 +228,16 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn artist_matches(matching: ArtistMeta, list: Vec<Match<ArtistMeta>>) -> AppMatchesInfo {
|
||||
fn artist_matches(matching: ArtistMeta, list: Vec<Match<ArtistMeta>>) -> MatchStateInfo {
|
||||
let mut list: Vec<MatchOption<ArtistMeta>> = list.into_iter().map(Into::into).collect();
|
||||
list.push(MatchOption::CannotHaveMbid);
|
||||
AppMatchesInfo::artist(matching, list)
|
||||
MatchStateInfo::artist(matching, list)
|
||||
}
|
||||
|
||||
fn album_matches(matching: AlbumMeta, list: Vec<Match<AlbumMeta>>) -> AppMatchesInfo {
|
||||
fn album_matches(matching: AlbumMeta, list: Vec<Match<AlbumMeta>>) -> MatchStateInfo {
|
||||
let mut list: Vec<MatchOption<AlbumMeta>> = list.into_iter().map(Into::into).collect();
|
||||
list.push(MatchOption::CannotHaveMbid);
|
||||
AppMatchesInfo::album(matching, list)
|
||||
MatchStateInfo::album(matching, list)
|
||||
}
|
||||
|
||||
fn draw_test_suite(collection: &Collection, selection: &mut Selection) {
|
||||
@ -322,8 +320,8 @@ mod tests {
|
||||
|
||||
let mut app = AppPublic {
|
||||
inner: public_inner(collection, &mut selection),
|
||||
state: AppState::Matches(AppPublicMatches {
|
||||
matches: None,
|
||||
state: AppState::Match(MatchStatePublic {
|
||||
info: None,
|
||||
state: &mut widget_state,
|
||||
}),
|
||||
};
|
||||
@ -351,8 +349,8 @@ mod tests {
|
||||
|
||||
let mut app = AppPublic {
|
||||
inner: public_inner(collection, &mut selection),
|
||||
state: AppState::Matches(AppPublicMatches {
|
||||
matches: Some(&artist_matches),
|
||||
state: AppState::Match(MatchStatePublic {
|
||||
info: Some(&artist_matches),
|
||||
state: &mut widget_state,
|
||||
}),
|
||||
};
|
||||
@ -385,8 +383,8 @@ mod tests {
|
||||
|
||||
let mut app = AppPublic {
|
||||
inner: public_inner(collection, &mut selection),
|
||||
state: AppState::Matches(AppPublicMatches {
|
||||
matches: Some(&album_matches),
|
||||
state: AppState::Match(MatchStatePublic {
|
||||
info: Some(&album_matches),
|
||||
state: &mut widget_state,
|
||||
}),
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user