Provide a keyboard shortcut to sync all existing albums with MusicBrainz #167
@ -20,7 +20,7 @@ pub trait IMusicBrainz {
|
|||||||
) -> Result<Vec<Match<Album>>, Error>;
|
) -> Result<Vec<Match<Album>>, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Match<T> {
|
pub struct Match<T> {
|
||||||
pub score: u8,
|
pub score: u8,
|
||||||
pub item: T,
|
pub item: T,
|
||||||
|
5
src/external/musicbrainz/api/mod.rs
vendored
5
src/external/musicbrainz/api/mod.rs
vendored
@ -391,6 +391,11 @@ mod tests {
|
|||||||
let mut client = MockIMusicBrainzApiClient::new();
|
let mut client = MockIMusicBrainzApiClient::new();
|
||||||
|
|
||||||
let error = ClientError::Client(String::from("get rekt"));
|
let error = ClientError::Client(String::from("get rekt"));
|
||||||
|
assert!(!error.to_string().is_empty());
|
||||||
|
assert!(!format!("{error:?}").is_empty());
|
||||||
|
|
||||||
|
let error = ClientError::Status(404);
|
||||||
|
assert!(!error.to_string().is_empty());
|
||||||
assert!(!format!("{error:?}").is_empty());
|
assert!(!format!("{error:?}").is_empty());
|
||||||
|
|
||||||
client
|
client
|
||||||
|
@ -10,6 +10,7 @@ use crate::tui::{
|
|||||||
lib::IMusicHoard,
|
lib::IMusicHoard,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct AppMatchesInfo {
|
pub struct AppMatchesInfo {
|
||||||
pub matching: Album,
|
pub matching: Album,
|
||||||
pub matches: Vec<Match<Album>>,
|
pub matches: Vec<Match<Album>>,
|
||||||
@ -124,10 +125,162 @@ impl<MH: IMusicHoard> IAppInteractMatches for AppMachine<MH, AppMatches> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tui::app::machine::tests::{inner, music_hoard};
|
use musichoard::collection::album::{AlbumDate, AlbumId, AlbumPrimaryType, AlbumSecondaryType};
|
||||||
|
|
||||||
|
use crate::tui::app::{machine::tests::{inner, music_hoard}, IAppAccess};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
fn matches_info_vec() -> Vec<AppMatchesInfo> {
|
||||||
|
let album_1 = Album::new(
|
||||||
|
AlbumId::new("Album 1"),
|
||||||
|
AlbumDate::new(Some(1990), Some(5), None),
|
||||||
|
Some(AlbumPrimaryType::Album),
|
||||||
|
vec![AlbumSecondaryType::Live, AlbumSecondaryType::Compilation],
|
||||||
|
);
|
||||||
|
|
||||||
|
let album_1_1 = album_1.clone();
|
||||||
|
let album_match_1_1 = Match {
|
||||||
|
score: 100,
|
||||||
|
item: album_1_1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut album_1_2 = album_1.clone();
|
||||||
|
album_1_2.id.title.push_str(" extra title part");
|
||||||
|
album_1_2.secondary_types.pop();
|
||||||
|
let album_match_1_2 = Match {
|
||||||
|
score: 100,
|
||||||
|
item: album_1_2,
|
||||||
|
};
|
||||||
|
|
||||||
|
let matches_info_1 = AppMatchesInfo {
|
||||||
|
matching: album_1.clone(),
|
||||||
|
matches: vec![album_match_1_1.clone(), album_match_1_2.clone()],
|
||||||
|
};
|
||||||
|
|
||||||
|
let album_2 = Album::new(
|
||||||
|
AlbumId::new("Album 2"),
|
||||||
|
AlbumDate::new(Some(2001), None, None),
|
||||||
|
Some(AlbumPrimaryType::Album),
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
|
||||||
|
let album_2_1 = album_1.clone();
|
||||||
|
let album_match_2_1 = Match {
|
||||||
|
score: 100,
|
||||||
|
item: album_2_1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let matches_info_2 = AppMatchesInfo {
|
||||||
|
matching: album_2.clone(),
|
||||||
|
matches: vec![album_match_2_1.clone()],
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![matches_info_1, matches_info_2]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_empty() {
|
||||||
|
let matches = AppMachine::matches(inner(music_hoard(vec![])), vec![]);
|
||||||
|
|
||||||
|
let widget_state = WidgetState::default();
|
||||||
|
|
||||||
|
assert_eq!(matches.state.matches_info_vec, vec![]);
|
||||||
|
assert_eq!(matches.state.index, None);
|
||||||
|
assert_eq!(matches.state.state, widget_state);
|
||||||
|
|
||||||
|
let mut app = matches.no_op();
|
||||||
|
let public = app.get();
|
||||||
|
let public_matches = public.state.unwrap_matches();
|
||||||
|
|
||||||
|
assert_eq!(public_matches.matching, None);
|
||||||
|
assert_eq!(public_matches.matches, None);
|
||||||
|
assert_eq!(public_matches.state, &widget_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_nonempty() {
|
||||||
|
let matches_info_vec = matches_info_vec();
|
||||||
|
let matches = AppMachine::matches(inner(music_hoard(vec![])), matches_info_vec.clone());
|
||||||
|
|
||||||
|
let mut widget_state = WidgetState::default();
|
||||||
|
widget_state.list.select(Some(0));
|
||||||
|
|
||||||
|
assert_eq!(matches.state.matches_info_vec, matches_info_vec);
|
||||||
|
assert_eq!(matches.state.index, Some(0));
|
||||||
|
assert_eq!(matches.state.state, widget_state);
|
||||||
|
|
||||||
|
let mut app = matches.no_op();
|
||||||
|
let public = app.get();
|
||||||
|
let public_matches = public.state.unwrap_matches();
|
||||||
|
|
||||||
|
assert_eq!(public_matches.matching, Some(&matches_info_vec[0].matching));
|
||||||
|
assert_eq!(public_matches.matches, Some(matches_info_vec[0].matches.as_slice()));
|
||||||
|
assert_eq!(public_matches.state, &widget_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matches_flow() {
|
||||||
|
let matches_info_vec = matches_info_vec();
|
||||||
|
let matches = AppMachine::matches(inner(music_hoard(vec![])), matches_info_vec.clone());
|
||||||
|
|
||||||
|
let mut widget_state = WidgetState::default();
|
||||||
|
widget_state.list.select(Some(0));
|
||||||
|
|
||||||
|
assert_eq!(matches.state.matches_info_vec, matches_info_vec);
|
||||||
|
assert_eq!(matches.state.index, Some(0));
|
||||||
|
assert_eq!(matches.state.state, widget_state);
|
||||||
|
|
||||||
|
let matches = matches.prev_match().unwrap_matches();
|
||||||
|
|
||||||
|
assert_eq!(matches.state.index, Some(0));
|
||||||
|
assert_eq!(matches.state.state.list.selected(), Some(0));
|
||||||
|
|
||||||
|
let matches = matches.next_match().unwrap_matches();
|
||||||
|
|
||||||
|
assert_eq!(matches.state.index, Some(0));
|
||||||
|
assert_eq!(matches.state.state.list.selected(), Some(1));
|
||||||
|
|
||||||
|
let matches = matches.next_match().unwrap_matches();
|
||||||
|
|
||||||
|
assert_eq!(matches.state.index, Some(0));
|
||||||
|
assert_eq!(matches.state.state.list.selected(), Some(1));
|
||||||
|
|
||||||
|
let matches = matches.select().unwrap_matches();
|
||||||
|
|
||||||
|
assert_eq!(matches.state.index, Some(1));
|
||||||
|
assert_eq!(matches.state.state.list.selected(), Some(0));
|
||||||
|
|
||||||
|
// And it's done
|
||||||
|
matches.select().unwrap_browse();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matches_abort() {
|
||||||
|
let matches_info_vec = matches_info_vec();
|
||||||
|
let matches = AppMachine::matches(inner(music_hoard(vec![])), matches_info_vec.clone());
|
||||||
|
|
||||||
|
let mut widget_state = WidgetState::default();
|
||||||
|
widget_state.list.select(Some(0));
|
||||||
|
|
||||||
|
assert_eq!(matches.state.matches_info_vec, matches_info_vec);
|
||||||
|
assert_eq!(matches.state.index, Some(0));
|
||||||
|
assert_eq!(matches.state.state, widget_state);
|
||||||
|
|
||||||
|
matches.abort().unwrap_browse();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matches_select_empty() {
|
||||||
|
let matches = AppMachine::matches(inner(music_hoard(vec![])), vec![]);
|
||||||
|
|
||||||
|
assert_eq!(matches.state.matches_info_vec, vec![]);
|
||||||
|
assert_eq!(matches.state.index, None);
|
||||||
|
|
||||||
|
matches.select().unwrap_browse();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_op() {
|
fn no_op() {
|
||||||
let matches = AppMachine::matches(inner(music_hoard(vec![])), vec![]);
|
let matches = AppMachine::matches(inner(music_hoard(vec![])), vec![]);
|
||||||
|
@ -229,11 +229,11 @@ mod tests {
|
|||||||
assert!(app.is_running());
|
assert!(app.is_running());
|
||||||
|
|
||||||
let state = app.state();
|
let state = app.state();
|
||||||
matches!(state, AppState::Browse(_));
|
assert!(matches!(state, AppState::Browse(_)));
|
||||||
app = state;
|
app = state;
|
||||||
|
|
||||||
let public = app.get();
|
let public = app.get();
|
||||||
matches!(public.state, AppState::Browse(_));
|
assert!(matches!(public.state, AppState::Browse(_)));
|
||||||
|
|
||||||
let app = app.force_quit();
|
let app = app.force_quit();
|
||||||
assert!(!app.is_running());
|
assert!(!app.is_running());
|
||||||
@ -247,11 +247,11 @@ mod tests {
|
|||||||
app = app.unwrap_browse().show_info_overlay();
|
app = app.unwrap_browse().show_info_overlay();
|
||||||
|
|
||||||
let state = app.state();
|
let state = app.state();
|
||||||
matches!(state, AppState::Info(_));
|
assert!(matches!(state, AppState::Info(_)));
|
||||||
app = state;
|
app = state;
|
||||||
|
|
||||||
let public = app.get();
|
let public = app.get();
|
||||||
matches!(public.state, AppState::Info(_));
|
assert!(matches!(public.state, AppState::Info(_)));
|
||||||
|
|
||||||
let app = app.force_quit();
|
let app = app.force_quit();
|
||||||
assert!(!app.is_running());
|
assert!(!app.is_running());
|
||||||
@ -265,11 +265,11 @@ mod tests {
|
|||||||
app = app.unwrap_browse().show_reload_menu();
|
app = app.unwrap_browse().show_reload_menu();
|
||||||
|
|
||||||
let state = app.state();
|
let state = app.state();
|
||||||
matches!(state, AppState::Reload(_));
|
assert!(matches!(state, AppState::Reload(_)));
|
||||||
app = state;
|
app = state;
|
||||||
|
|
||||||
let public = app.get();
|
let public = app.get();
|
||||||
matches!(public.state, AppState::Reload(_));
|
assert!(matches!(public.state, AppState::Reload(_)));
|
||||||
|
|
||||||
let app = app.force_quit();
|
let app = app.force_quit();
|
||||||
assert!(!app.is_running());
|
assert!(!app.is_running());
|
||||||
@ -283,11 +283,29 @@ mod tests {
|
|||||||
app = app.unwrap_browse().begin_search();
|
app = app.unwrap_browse().begin_search();
|
||||||
|
|
||||||
let state = app.state();
|
let state = app.state();
|
||||||
matches!(state, AppState::Search(_));
|
assert!(matches!(state, AppState::Search(_)));
|
||||||
app = state;
|
app = state;
|
||||||
|
|
||||||
let public = app.get();
|
let public = app.get();
|
||||||
matches!(public.state, AppState::Search(""));
|
assert!(matches!(public.state, AppState::Search("")));
|
||||||
|
|
||||||
|
let app = app.force_quit();
|
||||||
|
assert!(!app.is_running());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn state_matches() {
|
||||||
|
let mut app = App::new(music_hoard_init(vec![]));
|
||||||
|
assert!(app.is_running());
|
||||||
|
|
||||||
|
app = AppMachine::matches(app.unwrap_browse().inner, vec![]).into();
|
||||||
|
|
||||||
|
let state = app.state();
|
||||||
|
assert!(matches!(state, AppState::Matches(_)));
|
||||||
|
app = state;
|
||||||
|
|
||||||
|
let public = app.get();
|
||||||
|
assert!(matches!(public.state, AppState::Matches(_)));
|
||||||
|
|
||||||
let app = app.force_quit();
|
let app = app.force_quit();
|
||||||
assert!(!app.is_running());
|
assert!(!app.is_running());
|
||||||
@ -301,11 +319,11 @@ mod tests {
|
|||||||
app = AppMachine::error(app.unwrap_browse().inner, "get rekt").into();
|
app = AppMachine::error(app.unwrap_browse().inner, "get rekt").into();
|
||||||
|
|
||||||
let state = app.state();
|
let state = app.state();
|
||||||
matches!(state, AppState::Error(_));
|
assert!(matches!(state, AppState::Error(_)));
|
||||||
app = state;
|
app = state;
|
||||||
|
|
||||||
let public = app.get();
|
let public = app.get();
|
||||||
matches!(public.state, AppState::Error("get rekt"));
|
assert!(matches!(public.state, AppState::Error("get rekt")));
|
||||||
|
|
||||||
app = app.force_quit();
|
app = app.force_quit();
|
||||||
assert!(!app.is_running());
|
assert!(!app.is_running());
|
||||||
@ -319,11 +337,11 @@ mod tests {
|
|||||||
app = AppMachine::critical(app.unwrap_browse().inner, "get rekt").into();
|
app = AppMachine::critical(app.unwrap_browse().inner, "get rekt").into();
|
||||||
|
|
||||||
let state = app.state();
|
let state = app.state();
|
||||||
matches!(state, AppState::Critical(_));
|
assert!(matches!(state, AppState::Critical(_)));
|
||||||
app = state;
|
app = state;
|
||||||
|
|
||||||
let public = app.get();
|
let public = app.get();
|
||||||
matches!(public.state, AppState::Critical("get rekt"));
|
assert!(matches!(public.state, AppState::Critical("get rekt")));
|
||||||
|
|
||||||
app = app.force_quit();
|
app = app.force_quit();
|
||||||
assert!(!app.is_running());
|
assert!(!app.is_running());
|
||||||
|
@ -909,7 +909,7 @@ impl IUi for Ui {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use musichoard::collection::artist::ArtistId;
|
use musichoard::collection::{album::AlbumId, artist::ArtistId};
|
||||||
|
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
app::{AppPublic, AppPublicInner, AppPublicMatches, Delta},
|
app::{AppPublic, AppPublicInner, AppPublicMatches, Delta},
|
||||||
@ -947,6 +947,18 @@ mod tests {
|
|||||||
fn draw_test_suite(collection: &Collection, selection: &mut Selection) {
|
fn draw_test_suite(collection: &Collection, selection: &mut Selection) {
|
||||||
let mut terminal = terminal();
|
let mut terminal = terminal();
|
||||||
|
|
||||||
|
let album = Album::new(
|
||||||
|
AlbumId::new("An Album"),
|
||||||
|
AlbumDate::new(Some(1990), Some(5), None),
|
||||||
|
Some(AlbumPrimaryType::Album),
|
||||||
|
vec![AlbumSecondaryType::Live, AlbumSecondaryType::Compilation],
|
||||||
|
);
|
||||||
|
|
||||||
|
let album_match = Match {
|
||||||
|
score: 80,
|
||||||
|
item: album.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
let mut app = AppPublic {
|
let mut app = AppPublic {
|
||||||
inner: AppPublicInner {
|
inner: AppPublicInner {
|
||||||
collection,
|
collection,
|
||||||
@ -965,6 +977,26 @@ mod tests {
|
|||||||
app.state = AppState::Search("");
|
app.state = AppState::Search("");
|
||||||
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
||||||
|
|
||||||
|
let album_matches = [album_match.clone(), album_match.clone()];
|
||||||
|
let mut widget_state = WidgetState::default();
|
||||||
|
widget_state.list.select(Some(0));
|
||||||
|
|
||||||
|
app.state = AppState::Matches(AppPublicMatches {
|
||||||
|
matching: Some(&album),
|
||||||
|
matches: Some(&album_matches),
|
||||||
|
state: &mut widget_state,
|
||||||
|
});
|
||||||
|
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
||||||
|
|
||||||
|
let mut widget_state = WidgetState::default();
|
||||||
|
|
||||||
|
app.state = AppState::Matches(AppPublicMatches {
|
||||||
|
matching: None,
|
||||||
|
matches: None,
|
||||||
|
state: &mut widget_state,
|
||||||
|
});
|
||||||
|
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
||||||
|
|
||||||
app.state = AppState::Error("get rekt scrub");
|
app.state = AppState::Error("get rekt scrub");
|
||||||
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user