Sort albums by month if two releases of the same artist happen in the same year #155

Merged
6 changed files with 56 additions and 51 deletions
Showing only changes of commit bb060770b4 - Show all commits

View File

@ -41,8 +41,8 @@ impl Artist {
} }
} }
pub fn get_sort_key(&self) -> &ArtistId { pub fn get_sort_key(&self) -> (&ArtistId,) {
self.sort.as_ref().unwrap_or(&self.id) (self.sort.as_ref().unwrap_or(&self.id),)
} }
pub fn set_sort_key<SORT: Into<ArtistId>>(&mut self, sort: SORT) { pub fn set_sort_key<SORT: Into<ArtistId>>(&mut self, sort: SORT) {
@ -114,7 +114,7 @@ impl PartialOrd for Artist {
impl Ord for Artist { impl Ord for Artist {
fn cmp(&self, other: &Self) -> std::cmp::Ordering { fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.get_sort_key().cmp(other.get_sort_key()) self.get_sort_key().cmp(&other.get_sort_key())
} }
} }
@ -260,7 +260,7 @@ mod tests {
assert_eq!(artist.id, artist_id); assert_eq!(artist.id, artist_id);
assert_eq!(artist.sort, None); assert_eq!(artist.sort, None);
assert_eq!(artist.get_sort_key(), &artist_id); assert_eq!(artist.get_sort_key(), (&artist_id,));
assert!(artist < Artist::new(sort_id_1.clone())); assert!(artist < Artist::new(sort_id_1.clone()));
assert!(artist < Artist::new(sort_id_2.clone())); assert!(artist < Artist::new(sort_id_2.clone()));
@ -268,7 +268,7 @@ mod tests {
assert_eq!(artist.id, artist_id); assert_eq!(artist.id, artist_id);
assert_eq!(artist.sort.as_ref(), Some(&sort_id_1)); assert_eq!(artist.sort.as_ref(), Some(&sort_id_1));
assert_eq!(artist.get_sort_key(), &sort_id_1); assert_eq!(artist.get_sort_key(), (&sort_id_1,));
assert!(artist > Artist::new(artist_id.clone())); assert!(artist > Artist::new(artist_id.clone()));
assert!(artist < Artist::new(sort_id_2.clone())); assert!(artist < Artist::new(sort_id_2.clone()));
@ -276,7 +276,7 @@ mod tests {
assert_eq!(artist.id, artist_id); assert_eq!(artist.id, artist_id);
assert_eq!(artist.sort.as_ref(), Some(&sort_id_2)); assert_eq!(artist.sort.as_ref(), Some(&sort_id_2));
assert_eq!(artist.get_sort_key(), &sort_id_2); assert_eq!(artist.get_sort_key(), (&sort_id_2,));
assert!(artist > Artist::new(artist_id.clone())); assert!(artist > Artist::new(artist_id.clone()));
assert!(artist > Artist::new(sort_id_1.clone())); assert!(artist > Artist::new(sort_id_1.clone()));
@ -284,7 +284,7 @@ mod tests {
assert_eq!(artist.id, artist_id); assert_eq!(artist.id, artist_id);
assert_eq!(artist.sort, None); assert_eq!(artist.sort, None);
assert_eq!(artist.get_sort_key(), &artist_id); assert_eq!(artist.get_sort_key(), (&artist_id,));
assert!(artist < Artist::new(sort_id_1.clone())); assert!(artist < Artist::new(sort_id_1.clone()));
assert!(artist < Artist::new(sort_id_2.clone())); assert!(artist < Artist::new(sort_id_2.clone()));
} }

View File

@ -1,7 +1,7 @@
use crate::tui::{ use crate::tui::{
app::{ app::{
machine::{App, AppInner, AppMachine}, machine::{App, AppInner, AppMachine},
selection::IdSelection, selection::KeySelection,
AppPublic, AppState, IAppInteractReload, AppPublic, AppState, IAppInteractReload,
}, },
lib::IMusicHoard, lib::IMusicHoard,
@ -36,7 +36,7 @@ impl<MH: IMusicHoard> IAppInteractReload for AppMachine<MH, AppReload> {
type APP = App<MH>; type APP = App<MH>;
fn reload_library(mut self) -> Self::APP { fn reload_library(mut self) -> Self::APP {
let previous = IdSelection::get( let previous = KeySelection::get(
self.inner.music_hoard.get_collection(), self.inner.music_hoard.get_collection(),
&self.inner.selection, &self.inner.selection,
); );
@ -45,7 +45,7 @@ impl<MH: IMusicHoard> IAppInteractReload for AppMachine<MH, AppReload> {
} }
fn reload_database(mut self) -> Self::APP { fn reload_database(mut self) -> Self::APP {
let previous = IdSelection::get( let previous = KeySelection::get(
self.inner.music_hoard.get_collection(), self.inner.music_hoard.get_collection(),
&self.inner.selection, &self.inner.selection,
); );
@ -63,11 +63,11 @@ impl<MH: IMusicHoard> IAppInteractReload for AppMachine<MH, AppReload> {
} }
trait IAppInteractReloadPrivate<MH: IMusicHoard> { trait IAppInteractReloadPrivate<MH: IMusicHoard> {
fn refresh(self, previous: IdSelection, result: Result<(), musichoard::Error>) -> App<MH>; fn refresh(self, previous: KeySelection, result: Result<(), musichoard::Error>) -> App<MH>;
} }
impl<MH: IMusicHoard> IAppInteractReloadPrivate<MH> for AppMachine<MH, AppReload> { impl<MH: IMusicHoard> IAppInteractReloadPrivate<MH> for AppMachine<MH, AppReload> {
fn refresh(mut self, previous: IdSelection, result: Result<(), musichoard::Error>) -> App<MH> { fn refresh(mut self, previous: KeySelection, result: Result<(), musichoard::Error>) -> App<MH> {
match result { match result {
Ok(()) => { Ok(()) => {
self.inner self.inner

View File

@ -6,7 +6,7 @@ use musichoard::collection::{
}; };
use crate::tui::app::selection::{ use crate::tui::app::selection::{
track::{IdSelectTrack, TrackSelection}, track::{KeySelectTrack, TrackSelection},
Delta, SelectionState, WidgetState, Delta, SelectionState, WidgetState,
}; };
@ -26,7 +26,7 @@ impl AlbumSelection {
selection selection
} }
pub fn reinitialise(&mut self, albums: &[Album], album: Option<IdSelectAlbum>) { pub fn reinitialise(&mut self, albums: &[Album], album: Option<KeySelectAlbum>) {
if let Some(album) = album { if let Some(album) = album {
let result = albums.binary_search_by(|a| a.get_sort_key().cmp(&album.get_sort_key())); let result = albums.binary_search_by(|a| a.get_sort_key().cmp(&album.get_sort_key()));
match result { match result {
@ -42,7 +42,7 @@ impl AlbumSelection {
&mut self, &mut self,
albums: &[Album], albums: &[Album],
index: usize, index: usize,
active_track: Option<IdSelectTrack>, active_track: Option<KeySelectTrack>,
) { ) {
if albums.is_empty() { if albums.is_empty() {
self.state.list.select(None); self.state.list.select(None);
@ -160,19 +160,19 @@ impl AlbumSelection {
} }
} }
pub struct IdSelectAlbum { pub struct KeySelectAlbum {
key: (AlbumDate, AlbumSeq, AlbumId), key: (AlbumDate, AlbumSeq, AlbumId),
track: Option<IdSelectTrack>, track: Option<KeySelectTrack>,
} }
impl IdSelectAlbum { impl KeySelectAlbum {
pub fn get(albums: &[Album], selection: &AlbumSelection) -> Option<Self> { pub fn get(albums: &[Album], selection: &AlbumSelection) -> Option<Self> {
selection.state.list.selected().map(|index| { selection.state.list.selected().map(|index| {
let album = &albums[index]; let album = &albums[index];
let key = album.get_sort_key(); let key = album.get_sort_key();
IdSelectAlbum { KeySelectAlbum {
key: (key.0.to_owned(), key.1.to_owned(), key.2.to_owned()), key: (key.0.to_owned(), key.1.to_owned(), key.2.to_owned()),
track: IdSelectTrack::get(&album.tracks, &selection.track), track: KeySelectTrack::get(&album.tracks, &selection.track),
} }
}) })
} }
@ -335,20 +335,20 @@ mod tests {
// Re-initialise. // Re-initialise.
let expected = sel.clone(); let expected = sel.clone();
let active_album = IdSelectAlbum::get(albums, &sel); let active_album = KeySelectAlbum::get(albums, &sel);
sel.reinitialise(albums, active_album); sel.reinitialise(albums, active_album);
assert_eq!(sel, expected); assert_eq!(sel, expected);
// Re-initialise out-of-bounds. // Re-initialise out-of-bounds.
let mut expected = sel.clone(); let mut expected = sel.clone();
expected.decrement(albums, Delta::Line); expected.decrement(albums, Delta::Line);
let active_album = IdSelectAlbum::get(albums, &sel); let active_album = KeySelectAlbum::get(albums, &sel);
sel.reinitialise(&albums[..(albums.len() - 1)], active_album); sel.reinitialise(&albums[..(albums.len() - 1)], active_album);
assert_eq!(sel, expected); assert_eq!(sel, expected);
// Re-initialise empty. // Re-initialise empty.
let expected = AlbumSelection::initialise(&[]); let expected = AlbumSelection::initialise(&[]);
let active_album = IdSelectAlbum::get(albums, &sel); let active_album = KeySelectAlbum::get(albums, &sel);
sel.reinitialise(&[], active_album); sel.reinitialise(&[], active_album);
assert_eq!(sel, expected); assert_eq!(sel, expected);
} }

View File

@ -7,7 +7,7 @@ use musichoard::collection::{
}; };
use crate::tui::app::selection::{ use crate::tui::app::selection::{
album::{AlbumSelection, IdSelectAlbum}, album::{AlbumSelection, KeySelectAlbum},
Delta, SelectionState, WidgetState, Delta, SelectionState, WidgetState,
}; };
@ -27,9 +27,9 @@ impl ArtistSelection {
selection selection
} }
pub fn reinitialise(&mut self, artists: &[Artist], active: Option<IdSelectArtist>) { pub fn reinitialise(&mut self, artists: &[Artist], active: Option<KeySelectArtist>) {
if let Some(active) = active { if let Some(active) = active {
let result = artists.binary_search_by(|a| a.get_sort_key().cmp(&active.artist_id)); let result = artists.binary_search_by(|a| a.get_sort_key().cmp(&active.get_sort_key()));
match result { match result {
Ok(index) => self.reinitialise_with_index(artists, index, active.album), Ok(index) => self.reinitialise_with_index(artists, index, active.album),
Err(index) => self.reinitialise_with_index(artists, index, None), Err(index) => self.reinitialise_with_index(artists, index, None),
@ -43,7 +43,7 @@ impl ArtistSelection {
&mut self, &mut self,
artists: &[Artist], artists: &[Artist],
index: usize, index: usize,
active_album: Option<IdSelectAlbum>, active_album: Option<KeySelectAlbum>,
) { ) {
if artists.is_empty() { if artists.is_empty() {
self.state.list.select(None); self.state.list.select(None);
@ -193,21 +193,26 @@ impl ArtistSelection {
} }
} }
pub struct IdSelectArtist { pub struct KeySelectArtist {
artist_id: ArtistId, key: (ArtistId,),
album: Option<IdSelectAlbum>, album: Option<KeySelectAlbum>,
} }
impl IdSelectArtist { impl KeySelectArtist {
pub fn get(artists: &[Artist], selection: &ArtistSelection) -> Option<Self> { pub fn get(artists: &[Artist], selection: &ArtistSelection) -> Option<Self> {
selection.state.list.selected().map(|index| { selection.state.list.selected().map(|index| {
let artist = &artists[index]; let artist = &artists[index];
IdSelectArtist { let key = artist.get_sort_key();
artist_id: artist.get_sort_key().clone(), KeySelectArtist {
album: IdSelectAlbum::get(&artist.albums, &selection.album), key: (key.0.to_owned(),),
album: KeySelectAlbum::get(&artist.albums, &selection.album),
} }
}) })
} }
pub fn get_sort_key(&self) -> (&ArtistId,) {
(&self.key.0,)
}
} }
#[cfg(test)] #[cfg(test)]
@ -385,20 +390,20 @@ mod tests {
// Re-initialise. // Re-initialise.
let expected = sel.clone(); let expected = sel.clone();
let active_artist = IdSelectArtist::get(artists, &sel); let active_artist = KeySelectArtist::get(artists, &sel);
sel.reinitialise(artists, active_artist); sel.reinitialise(artists, active_artist);
assert_eq!(sel, expected); assert_eq!(sel, expected);
// Re-initialise out-of-bounds. // Re-initialise out-of-bounds.
let mut expected = sel.clone(); let mut expected = sel.clone();
expected.decrement(artists, Delta::Line); expected.decrement(artists, Delta::Line);
let active_artist = IdSelectArtist::get(artists, &sel); let active_artist = KeySelectArtist::get(artists, &sel);
sel.reinitialise(&artists[..(artists.len() - 1)], active_artist); sel.reinitialise(&artists[..(artists.len() - 1)], active_artist);
assert_eq!(sel, expected); assert_eq!(sel, expected);
// Re-initialise empty. // Re-initialise empty.
let expected = ArtistSelection::initialise(&[]); let expected = ArtistSelection::initialise(&[]);
let active_artist = IdSelectArtist::get(artists, &sel); let active_artist = KeySelectArtist::get(artists, &sel);
sel.reinitialise(&[], active_artist); sel.reinitialise(&[], active_artist);
assert_eq!(sel, expected); assert_eq!(sel, expected);
} }

View File

@ -5,7 +5,7 @@ mod track;
use musichoard::collection::{album::Album, artist::Artist, track::Track, Collection}; use musichoard::collection::{album::Album, artist::Artist, track::Track, Collection};
use ratatui::widgets::ListState; use ratatui::widgets::ListState;
use artist::{ArtistSelection, IdSelectArtist}; use artist::{ArtistSelection, KeySelectArtist};
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Category { pub enum Category {
@ -64,7 +64,7 @@ impl Selection {
self.artist.album.track.state.list = selected.track; self.artist.album.track.state.list = selected.track;
} }
pub fn select_by_id(&mut self, artists: &[Artist], selected: IdSelection) { pub fn select_by_id(&mut self, artists: &[Artist], selected: KeySelection) {
self.artist.reinitialise(artists, selected.artist); self.artist.reinitialise(artists, selected.artist);
} }
@ -229,14 +229,14 @@ impl ListSelection {
} }
} }
pub struct IdSelection { pub struct KeySelection {
artist: Option<IdSelectArtist>, artist: Option<KeySelectArtist>,
} }
impl IdSelection { impl KeySelection {
pub fn get(collection: &Collection, selection: &Selection) -> Self { pub fn get(collection: &Collection, selection: &Selection) -> Self {
IdSelection { KeySelection {
artist: IdSelectArtist::get(collection, &selection.artist), artist: KeySelectArtist::get(collection, &selection.artist),
} }
} }
} }

View File

@ -18,7 +18,7 @@ impl TrackSelection {
selection selection
} }
pub fn reinitialise(&mut self, tracks: &[Track], track: Option<IdSelectTrack>) { pub fn reinitialise(&mut self, tracks: &[Track], track: Option<KeySelectTrack>) {
if let Some(track) = track { if let Some(track) = track {
let result = tracks.binary_search_by(|t| t.get_sort_key().cmp(&track.get_sort_key())); let result = tracks.binary_search_by(|t| t.get_sort_key().cmp(&track.get_sort_key()));
match result { match result {
@ -100,16 +100,16 @@ impl TrackSelection {
} }
} }
pub struct IdSelectTrack { pub struct KeySelectTrack {
key: (TrackNum, TrackId), key: (TrackNum, TrackId),
} }
impl IdSelectTrack { impl KeySelectTrack {
pub fn get(tracks: &[Track], selection: &TrackSelection) -> Option<Self> { pub fn get(tracks: &[Track], selection: &TrackSelection) -> Option<Self> {
selection.state.list.selected().map(|index| { selection.state.list.selected().map(|index| {
let track = &tracks[index]; let track = &tracks[index];
let key = track.get_sort_key(); let key = track.get_sort_key();
IdSelectTrack { KeySelectTrack {
key: (key.0.to_owned(), key.1.to_owned()), key: (key.0.to_owned(), key.1.to_owned()),
} }
}) })
@ -215,20 +215,20 @@ mod tests {
// Re-initialise. // Re-initialise.
let expected = sel.clone(); let expected = sel.clone();
let active_track = IdSelectTrack::get(tracks, &sel); let active_track = KeySelectTrack::get(tracks, &sel);
sel.reinitialise(tracks, active_track); sel.reinitialise(tracks, active_track);
assert_eq!(sel, expected); assert_eq!(sel, expected);
// Re-initialise out-of-bounds. // Re-initialise out-of-bounds.
let mut expected = sel.clone(); let mut expected = sel.clone();
expected.decrement(tracks, Delta::Line); expected.decrement(tracks, Delta::Line);
let active_track = IdSelectTrack::get(tracks, &sel); let active_track = KeySelectTrack::get(tracks, &sel);
sel.reinitialise(&tracks[..(tracks.len() - 1)], active_track); sel.reinitialise(&tracks[..(tracks.len() - 1)], active_track);
assert_eq!(sel, expected); assert_eq!(sel, expected);
// Re-initialise empty. // Re-initialise empty.
let expected = TrackSelection::initialise(&[]); let expected = TrackSelection::initialise(&[]);
let active_track = IdSelectTrack::get(tracks, &sel); let active_track = KeySelectTrack::get(tracks, &sel);
sel.reinitialise(&[], active_track); sel.reinitialise(&[], active_track);
assert_eq!(sel, expected); assert_eq!(sel, expected);
} }