selene_core/database/
validator.rs1use 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}