use std::collections::HashMap;
use lunar_lib::database::{DatabaseEntry, DatabaseError};
use thiserror::Error;
use crate::library::{
album::{Album, AlbumId},
artist::{Artist, ArtistId},
track::{Track, TrackId},
};
#[derive(Debug, Error)]
pub enum DatabaseReferenceError {
#[error("Track points to an album, but the album does not point back")]
TrackDetatchedAlbumRef { track: TrackId, album: AlbumId },
#[error("Track points to an album, but the album does not exist")]
TrackDanglingAlbumRef { track: TrackId, album: AlbumId },
#[error("Track points to an artist, but the artist does not point back")]
TrackDetatchedArtistRef { track: TrackId, artist: ArtistId },
#[error("Track points to an artist, but the artist does not exist")]
TrackDanglingArtistRef { track: TrackId, artist: ArtistId },
#[error("Album points to a track, but the track does not point back")]
AlbumDetatchedTrackRef { album: AlbumId, track: TrackId },
#[error("Album points to a track, but the track does not exist")]
AlbumDanglingTrackRef { album: AlbumId, track: TrackId },
#[error("Album points to an artist, but the artist does not point back")]
AlbumDetatchedArtistRef { album: AlbumId, artist: ArtistId },
#[error("Album points to an artist, but the artist does not exist")]
AlbumDanglingArtistRef { album: AlbumId, artist: ArtistId },
#[error("Artist points to a track, but the track does not exist")]
ArtistDanglingTrackRef { artist: ArtistId, track: TrackId },
#[error("Artist points to a track, but the track does not point back")]
ArtistDetachedTrackRef { artist: ArtistId, track: TrackId },
#[error("Artist points to an album, but the album does not exist")]
ArtistDanglingAlbumRef { artist: ArtistId, album: AlbumId },
#[error("Artist points to an album, but the album does not point back")]
ArtistDetachedAlbumRef { artist: ArtistId, album: AlbumId },
#[error("Tried to add track to an artist, but the track is part of the artist's albums")]
ArtistInvalidTrackRef {
artist: ArtistId,
track: TrackId,
album: AlbumId,
},
}
pub fn validate() -> Result<Vec<DatabaseReferenceError>, DatabaseError> {
let mut errors = Vec::new();
let tracks: HashMap<TrackId, Track> = Track::db_get_all()?
.into_iter()
.map(|t| (t.id(), t))
.collect();
let albums: HashMap<AlbumId, Album> = Album::db_get_all()?
.into_iter()
.map(|a| (a.id(), a))
.collect();
let artists: HashMap<ArtistId, Artist> = Artist::db_get_all()?
.into_iter()
.map(|a| (a.id(), a))
.collect();
for track in tracks.values() {
if let Some(album_id) = track.metadata.album {
if let Some(album) = albums.get(&album_id) {
if !album.track_refs().iter().any(|t| t.id == track.id()) {
errors.push(DatabaseReferenceError::TrackDetatchedAlbumRef {
track: track.id(),
album: album.id(),
});
}
} else {
errors.push(DatabaseReferenceError::TrackDanglingAlbumRef {
track: track.id(),
album: album_id,
});
}
}
for artist_id in track.metadata.artists.artist_ids() {
if let Some(artist) = artists.get(artist_id) {
if !artist.tracks.iter().any(|t| *t == track.id()) {
if let Some(album_id) = track.metadata.album
&& artist.albums.contains(&album_id)
{
continue;
}
errors.push(DatabaseReferenceError::TrackDetatchedArtistRef {
track: track.id(),
artist: artist.id(),
});
}
} else {
errors.push(DatabaseReferenceError::TrackDanglingArtistRef {
track: track.id(),
artist: *artist_id,
});
}
}
}
for album in albums.values() {
for track in &album.tracks {
if let Some(track) = tracks.get(&track.id) {
if !(track.metadata.album == Some(album.id())) {
errors.push(DatabaseReferenceError::AlbumDetatchedTrackRef {
album: album.id(),
track: track.id(),
});
}
} else {
errors.push(DatabaseReferenceError::AlbumDanglingTrackRef {
album: album.id(),
track: track.id,
});
}
}
for artist_id in album.artist_group.artist_ids() {
if let Some(artist) = artists.get(artist_id) {
if !artist.albums.iter().any(|t| *t == album.id()) {
errors.push(DatabaseReferenceError::AlbumDetatchedArtistRef {
album: album.id(),
artist: artist.id(),
});
}
} else {
errors.push(DatabaseReferenceError::AlbumDanglingArtistRef {
album: album.id(),
artist: *artist_id,
});
}
}
}
for artist in artists.values() {
for track in &artist.tracks {
if let Some(track) = tracks.get(track) {
if !track
.metadata
.artists
.artist_ids()
.iter()
.any(|a| *a == artist.id())
{
errors.push(DatabaseReferenceError::ArtistDetachedTrackRef {
artist: artist.id(),
track: track.id(),
});
}
} else {
errors.push(DatabaseReferenceError::ArtistDanglingTrackRef {
artist: artist.id(),
track: *track,
});
}
}
for album in &artist.albums {
if let Some(album) = albums.get(album) {
if !album
.artists()
.artist_ids()
.iter()
.any(|a| *a == artist.id())
{
errors.push(DatabaseReferenceError::ArtistDetachedAlbumRef {
artist: artist.id(),
album: album.id(),
});
}
} else {
errors.push(DatabaseReferenceError::ArtistDanglingAlbumRef {
artist: artist.id(),
album: *album,
});
}
}
}
Ok(errors)
}