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