Skip to main content

selene_core/database/
validator.rs

1use std::collections::HashMap;
2
3use thiserror::Error;
4
5use crate::{
6    database::{DatabaseEntry, DatabaseError},
7    library::{
8        album::{Album, AlbumId},
9        artist::{Artist, ArtistId},
10        track::{Track, TrackId},
11    },
12};
13
14#[derive(Debug, Error)]
15pub enum DatabaseReferenceError {
16    #[error("Track points to an album, but the album does not point back")]
17    TrackDetatchedAlbumRef { track: TrackId, album: AlbumId },
18
19    #[error("Track points to an album, but the album does not exist")]
20    TrackDanglingAlbumRef { track: TrackId, album: AlbumId },
21
22    #[error("Track points to an artist, but the artist does not point back")]
23    TrackDetatchedArtistRef { track: TrackId, artist: ArtistId },
24
25    #[error("Track points to an artist, but the artist does not exist")]
26    TrackDanglingArtistRef { track: TrackId, artist: ArtistId },
27
28    #[error("Album points to a track, but the track does not point back")]
29    AlbumDetatchedTrackRef { album: AlbumId, track: TrackId },
30
31    #[error("Album points to a track, but the track does not exist")]
32    AlbumDanglingTrackRef { album: AlbumId, track: TrackId },
33
34    #[error("Album points to an artist, but the artist does not point back")]
35    AlbumDetatchedArtistRef { album: AlbumId, artist: ArtistId },
36
37    #[error("Album points to an artist, but the artist does not exist")]
38    AlbumDanglingArtistRef { album: AlbumId, artist: ArtistId },
39
40    #[error("Artist points to a track, but the track does not exist")]
41    ArtistDanglingTrackRef { artist: ArtistId, track: TrackId },
42
43    #[error("Artist points to a track, but the track does not point back")]
44    ArtistDetachedTrackRef { artist: ArtistId, track: TrackId },
45
46    #[error("Artist points to an album, but the album does not exist")]
47    ArtistDanglingAlbumRef { artist: ArtistId, album: AlbumId },
48
49    #[error("Artist points to an album, but the album does not point back")]
50    ArtistDetachedAlbumRef { artist: ArtistId, album: AlbumId },
51
52    #[error("Tried to add track to an artist, but the track is part of the artist's albums")]
53    ArtistInvalidTrackRef {
54        artist: ArtistId,
55        track: TrackId,
56        album: AlbumId,
57    },
58}
59
60pub fn validate() -> Result<Vec<DatabaseReferenceError>, DatabaseError> {
61    let mut errors = Vec::new();
62
63    let tracks: HashMap<TrackId, Track> = Track::db_get_all()?
64        .into_iter()
65        .map(|t| (t.id(), t))
66        .collect();
67
68    let albums: HashMap<AlbumId, Album> = Album::db_get_all()?
69        .into_iter()
70        .map(|a| (a.id(), a))
71        .collect();
72
73    let artists: HashMap<ArtistId, Artist> = Artist::db_get_all()?
74        .into_iter()
75        .map(|a| (a.id(), a))
76        .collect();
77
78    for track in tracks.values() {
79        if let Some(album_id) = track.metadata.album {
80            if let Some(album) = albums.get(&album_id) {
81                if !album.track_refs().iter().any(|t| t.id == track.id()) {
82                    errors.push(DatabaseReferenceError::TrackDetatchedAlbumRef {
83                        track: track.id(),
84                        album: album.id(),
85                    });
86                }
87            } else {
88                errors.push(DatabaseReferenceError::TrackDanglingAlbumRef {
89                    track: track.id(),
90                    album: album_id,
91                });
92            }
93        }
94
95        for artist_id in track.metadata.artists.artist_ids() {
96            if let Some(artist) = artists.get(artist_id) {
97                if !artist.tracks.iter().any(|t| *t == track.id()) {
98                    if let Some(album_id) = track.metadata.album
99                        && artist.albums.contains(&album_id)
100                    {
101                        continue;
102                    } else {
103                        errors.push(DatabaseReferenceError::TrackDetatchedArtistRef {
104                            track: track.id(),
105                            artist: artist.id(),
106                        });
107                    }
108                }
109            } else {
110                errors.push(DatabaseReferenceError::TrackDanglingArtistRef {
111                    track: track.id(),
112                    artist: *artist_id,
113                });
114            }
115        }
116    }
117
118    for album in albums.values() {
119        for track in &album.tracks {
120            if let Some(track) = tracks.get(&track.id) {
121                if !(track.metadata.album == Some(album.id())) {
122                    errors.push(DatabaseReferenceError::AlbumDetatchedTrackRef {
123                        album: album.id(),
124                        track: track.id(),
125                    });
126                }
127            } else {
128                errors.push(DatabaseReferenceError::AlbumDanglingTrackRef {
129                    album: album.id(),
130                    track: track.id,
131                });
132            }
133        }
134
135        for artist_id in album.artist_group.artist_ids() {
136            if let Some(artist) = artists.get(artist_id) {
137                if !artist.albums.iter().any(|t| *t == album.id()) {
138                    errors.push(DatabaseReferenceError::AlbumDetatchedArtistRef {
139                        album: album.id(),
140                        artist: artist.id(),
141                    });
142                }
143            } else {
144                errors.push(DatabaseReferenceError::AlbumDanglingArtistRef {
145                    album: album.id(),
146                    artist: *artist_id,
147                });
148            }
149        }
150    }
151
152    for artist in artists.values() {
153        for track in &artist.tracks {
154            if let Some(track) = tracks.get(track) {
155                if !track
156                    .metadata
157                    .artists
158                    .artist_ids()
159                    .iter()
160                    .any(|a| *a == artist.id())
161                {
162                    errors.push(DatabaseReferenceError::ArtistDetachedTrackRef {
163                        artist: artist.id(),
164                        track: track.id(),
165                    });
166                }
167            } else {
168                errors.push(DatabaseReferenceError::ArtistDanglingTrackRef {
169                    artist: artist.id(),
170                    track: *track,
171                });
172            }
173        }
174
175        for album in &artist.albums {
176            if let Some(album) = albums.get(album) {
177                if !album
178                    .artists()
179                    .artist_ids()
180                    .iter()
181                    .any(|a| *a == artist.id())
182                {
183                    errors.push(DatabaseReferenceError::ArtistDetachedAlbumRef {
184                        artist: artist.id(),
185                        album: album.id(),
186                    });
187                }
188            } else {
189                errors.push(DatabaseReferenceError::ArtistDanglingAlbumRef {
190                    artist: artist.id(),
191                    album: *album,
192                });
193            }
194        }
195    }
196
197    Ok(errors)
198}