Skip to main content

chilen_backend/music_lib/sorting/
mod.rs

1//! Sorting helper functions for library items.
2
3use std::sync::{Arc, RwLock};
4
5use icu::collator::CollatorBorrowed;
6
7use crate::music_lib::{AsAlbum, AsArtist, AsGenre, AsPlaylist, AsTrack};
8
9pub(crate) static COLLATOR: RwLock<Option<Arc<CollatorBorrowed<'_>>>> = RwLock::new(None);
10
11/// Sorts tracks alphabetically by titles.
12pub fn sort_tracks<T: AsTrack>(tracks: &mut [T]) {
13    let guard = COLLATOR.read().unwrap();
14    let collator = guard.as_ref();
15
16    tracks.sort_by(|t1, t2| {
17        let t1 = t1.as_track().title.as_deref().unwrap_or_default();
18        let t2 = t2.as_track().title.as_deref().unwrap_or_default();
19        match collator {
20            Some(collator) => collator.compare(t1, t2),
21            None => t1.cmp(t2),
22        }
23    });
24}
25
26/// Sorts tracks by dates, falls back to alphabetic title sorting.
27pub fn sort_tracks_chronologically<T: AsTrack>(tracks: &mut [T]) {
28    let guard = COLLATOR.read().unwrap();
29    let collator = guard.as_ref();
30
31    tracks.sort_by(|a, b| match (a.as_track().date, b.as_track().date) {
32        (Some(ad), Some(bd)) => bd.cmp(&ad),
33        (Some(_), None) => std::cmp::Ordering::Less,
34        (None, Some(_)) => std::cmp::Ordering::Greater,
35        (None, None) => {
36            let t1 = a.as_track().title.as_deref().unwrap_or_default();
37            let t2 = b.as_track().title.as_deref().unwrap_or_default();
38
39            match collator {
40                Some(collator) => collator.compare(t1, t2),
41                None => t1.cmp(t2),
42            }
43        }
44    });
45}
46
47/// Sorts tracks by disc numbers, then track numbers, falls back to sorting by titles.
48pub fn sort_tracks_in_album<T: AsTrack>(tracks: &mut [T]) {
49    let guard = COLLATOR.read().unwrap();
50    let collator = guard.as_ref();
51
52    tracks.sort_by(|a, b| {
53        let a = a.as_track();
54        let b = b.as_track();
55
56        let disc_cmp = match (a.disc, b.disc) {
57            (Some(ad), Some(bd)) => ad.cmp(&bd),
58            (Some(_), None) => std::cmp::Ordering::Less,
59            (None, Some(_)) => std::cmp::Ordering::Greater,
60            (None, None) => std::cmp::Ordering::Equal,
61        };
62
63        let track_cmp = match (a.track, b.track) {
64            (Some(at), Some(bt)) => at.cmp(&bt),
65            (Some(_), None) => std::cmp::Ordering::Less,
66            (None, Some(_)) => std::cmp::Ordering::Greater,
67            (None, None) => std::cmp::Ordering::Equal,
68        };
69
70        disc_cmp.then(track_cmp).then_with(|| {
71            let s1 = a.title.as_deref().unwrap_or_default();
72            let s2 = b.title.as_deref().unwrap_or_default();
73            match collator {
74                Some(collator) => collator.compare(s1, s2),
75                None => s1.cmp(s2),
76            }
77        })
78    });
79}
80
81/// Sorts tracks by artist names alphabetically.
82pub fn sort_tracks_by_artist<T: AsTrack>(tracks: &mut [T]) {
83    let guard = COLLATOR.read().unwrap();
84    let collator = guard.as_ref();
85
86    tracks.sort_by(|t1, t2| {
87        match (
88            t1.as_track().artists.as_ref(),
89            t2.as_track().artists.as_ref(),
90        ) {
91            (Some(l1), Some(l2)) => {
92                for (s1, s2) in l1.iter().zip(l2.iter()) {
93                    let res = match collator {
94                        Some(collator) => collator.compare(s1, s2),
95                        None => s1.cmp(s2),
96                    };
97                    if res != std::cmp::Ordering::Equal {
98                        return res;
99                    }
100                }
101                l1.len().cmp(&l2.len())
102            }
103            (Some(_), None) => std::cmp::Ordering::Less,
104            (None, Some(_)) => std::cmp::Ordering::Greater,
105            (None, None) => std::cmp::Ordering::Equal,
106        }
107    });
108}
109
110/// Sorts tracks by album titles alphabetically.
111pub fn sort_tracks_by_album<T: AsTrack>(tracks: &mut [T]) {
112    let guard = COLLATOR.read().unwrap();
113    let collator = guard.as_ref();
114
115    tracks.sort_by(|t1, t2| {
116        let s1 = t1.as_track().album.as_deref().unwrap_or_default();
117        let s2 = t2.as_track().album.as_deref().unwrap_or_default();
118
119        match collator {
120            Some(collator) => collator.compare(s1, s2),
121            None => s1.cmp(s2),
122        }
123    });
124}
125
126/// Sorts albums by their titles alphabetically.
127pub fn sort_albums<T: AsAlbum>(albums: &mut [T]) {
128    let guard = COLLATOR.read().unwrap();
129    let collator = guard.as_ref();
130
131    match collator {
132        Some(collator) => {
133            albums.sort_by(|a1, a2| collator.compare(&a1.as_album().title, &a2.as_album().title));
134        }
135        None => {
136            albums.sort_by(|a1, a2| a1.as_album().title.cmp(&a2.as_album().title));
137        }
138    }
139}
140
141/// Sorts albums by dates, falls back to sorting by titles alphabetically.
142pub fn sort_albums_chronologically<T: AsAlbum>(albums: &mut [T]) {
143    let guard = COLLATOR.read().unwrap();
144    let collator = guard.as_ref();
145
146    albums.sort_by(|a, b| match (a.as_album().date, b.as_album().date) {
147        (Some(ad), Some(bd)) => bd.cmp(&ad),
148        (Some(_), None) => std::cmp::Ordering::Less,
149        (None, Some(_)) => std::cmp::Ordering::Greater,
150        (None, None) => match collator {
151            Some(collator) => collator.compare(&a.as_album().title, &b.as_album().title),
152            None => a.as_album().title.cmp(&b.as_album().title),
153        },
154    });
155}
156
157/// Sorts albums by artist names alphabetically.
158pub fn sort_albums_by_artist<T: AsAlbum>(albums: &mut [T]) {
159    let guard = COLLATOR.read().unwrap();
160    let collator = guard.as_ref();
161
162    albums.sort_by(|a1, a2| {
163        let a1 = a1.as_album();
164        let a2 = a2.as_album();
165        for (s1, s2) in a1.artists.iter().zip(a2.artists.iter()) {
166            let res = match collator {
167                Some(collator) => collator.compare(s1, s2),
168                None => s1.cmp(s2),
169            };
170            if res != std::cmp::Ordering::Equal {
171                return res;
172            }
173        }
174        a1.artists.len().cmp(&a2.artists.len())
175    });
176}
177
178/// Sorts artists by names alphabetically.
179pub fn sort_artists<T: AsArtist>(artists: &mut [T]) {
180    let guard = COLLATOR.read().unwrap();
181    let collator = guard.as_ref();
182
183    match collator {
184        Some(collator) => {
185            artists.sort_by(|a1, a2| collator.compare(&a1.as_artist().name, &a2.as_artist().name));
186        }
187        None => {
188            artists.sort_by(|a1, a2| a1.as_artist().name.cmp(&a2.as_artist().name));
189        }
190    }
191}
192
193/// Sorts playlists by titles alphabetically.
194pub fn sort_playlists<T: AsPlaylist>(playlists: &mut [T]) {
195    let guard = COLLATOR.read().unwrap();
196    let collator = guard.as_ref();
197
198    match collator {
199        Some(collator) => {
200            playlists
201                .sort_by(|p1, p2| collator.compare(&p1.as_playlist().name, &p2.as_playlist().name));
202        }
203        None => {
204            playlists.sort_by(|p1, p2| p1.as_playlist().name.cmp(&p2.as_playlist().name));
205        }
206    }
207}
208
209/// Sorts genres by names alphabetically.
210pub fn sort_genres<T: AsGenre>(genres: &mut [T]) {
211    let guard = COLLATOR.read().unwrap();
212    let collator = guard.as_ref();
213
214    match collator {
215        Some(collator) => {
216            genres.sort_by(|g1, g2| collator.compare(&g1.as_genre().name, &g2.as_genre().name));
217        }
218        None => {
219            genres.sort_by(|g1, g2| g1.as_genre().name.cmp(&g2.as_genre().name));
220        }
221    }
222}