Sort albums by month if two releases of the same artist happen in the same year #155
@ -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()));
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user