selene-core 0.9.0-alpha.2

selene-core is the backend for Selene, a local-first music player
Documentation
use lunar_lib::{
    database::{Db, DbIdExt, TransactionError},
    id::Id,
    log::{error, trace},
};
use thiserror::Error;

use crate::{
    database::Library,
    library::{Entry, album::Album, artist::Artist, track::Track},
};

#[derive(Debug, Error, Hash, PartialEq, Eq)]
pub enum DatabaseReferenceError {
    #[error("Track points to an album, but the album does not point back")]
    TrackDetatchedAlbumRef { track: Id<Track>, album: Id<Album> },

    #[error("Track points to an album, but the album does not exist")]
    TrackDanglingAlbumRef { track: Id<Track>, album: Id<Album> },

    #[error("Track points to an artist, but the artist does not point back")]
    TrackDetatchedArtistRef {
        track: Id<Track>,
        artist: Id<Artist>,
    },

    #[error("Track points to an artist, but the artist does not exist")]
    TrackDanglingArtistRef {
        track: Id<Track>,
        artist: Id<Artist>,
    },

    #[error("Album points to a track, but the track does not point back")]
    AlbumDetatchedTrackRef { album: Id<Album>, track: Id<Track> },

    #[error("Album points to a track, but the track does not exist")]
    AlbumDanglingTrackRef { album: Id<Album>, track: Id<Track> },

    #[error("Album points to an artist, but the artist does not point back")]
    AlbumDetatchedArtistRef {
        album: Id<Album>,
        artist: Id<Artist>,
    },

    #[error("Album points to an artist, but the artist does not exist")]
    AlbumDanglingArtistRef {
        album: Id<Album>,
        artist: Id<Artist>,
    },

    #[error("Artist points to a track, but the track does not exist")]
    ArtistDanglingTrackRef {
        artist: Id<Artist>,
        track: Id<Track>,
    },

    #[error("Artist points to a track, but the track does not point back")]
    ArtistDetachedTrackRef {
        artist: Id<Artist>,
        track: Id<Track>,
    },

    #[error("Artist points to an album, but the album does not exist")]
    ArtistDanglingAlbumRef {
        artist: Id<Artist>,
        album: Id<Album>,
    },

    #[error("Artist points to an album, but the album does not point back")]
    ArtistDetachedAlbumRef {
        artist: Id<Artist>,
        album: Id<Album>,
    },

    #[error("Tried to add track to an artist, but the track is part of the artist's albums")]
    ArtistInvalidTrackRef {
        artist: Id<Artist>,
        track: Id<Track>,
        album: Id<Album>,
    },
}

pub fn validate(db: &Db<Library>) -> Result<(), TransactionError> {
    let mut was_error = false;

    for track in db.iter_entries::<Track>() {
        let track = Entry::from(track?);

        if let Some(album_id) = track.metadata.album {
            if let Some(album) = album_id.db_get(db)? {
                if album.track_refs().iter().any(|t| *t.id == *track.id()) {
                    trace!(
                        "Track '{tr}' is correctly linked to album '{al}'",
                        tr = track.metadata.safe_title(),
                        al = album.name()
                    );
                } else {
                    error!(
                        "Track '{tr}' points to album '{al}', but {al} does not point back",
                        tr = track.metadata.safe_title(),
                        al = album.name()
                    );
                    was_error = true;
                }
            } else {
                error!(
                    "Track '{tr}' points to album '{al}' which does not exist",
                    tr = track.metadata.safe_title(),
                    al = album_id
                );
                was_error = true;
            }
        }

        for artist_id in &track.metadata.artists {
            if let Some(artist) = artist_id.db_get(db)? {
                if artist.tracks.iter().any(|t| **t == *track.id()) {
                    trace!(
                        "Track '{tr}' is correctly linked to artist '{ar}'",
                        tr = track.metadata.safe_title(),
                        ar = artist.name()
                    );
                } else {
                    error!(
                        "Track '{tr}' points to artist '{ar}', but {ar} does not point back",
                        tr = track.metadata.safe_title(),
                        ar = artist.name()
                    );
                    was_error = true;
                }
            } else {
                error!(
                    "Track '{tr}' points to artist '{ar}' which does not exist",
                    tr = track.metadata.safe_title(),
                    ar = artist_id
                );
                was_error = true;
            }
        }
    }

    for album in db.iter_entries::<Album>() {
        let album = album?;

        for track_id in album.tracks.iter().map(|t| t.id) {
            if let Some(track) = track_id.db_get(db)? {
                if track.metadata.album.as_deref() == Some(&*album.id()) {
                    trace!(
                        "Album '{al}' is correctly linked to track '{tr}'",
                        al = album.name(),
                        tr = track.metadata.safe_title()
                    );
                } else {
                    error!(
                        "Album '{al}' points to track '{tr}', but {tr} does not point back",
                        al = album.name(),
                        tr = track.metadata.safe_title()
                    );
                    was_error = true;
                }
            } else {
                error!(
                    "Album '{al}' points to track '{tr}' which does not exist",
                    al = album.name(),
                    tr = track_id
                );
                was_error = true;
            }
        }

        for artist_id in &album.artists {
            if let Some(artist) = artist_id.db_get(db)? {
                if artist.albums.iter().any(|t| **t == *album.id()) {
                    trace!(
                        "Album '{al}' is correctly linked to artist '{ar}'",
                        al = album.name(),
                        ar = artist.name()
                    );
                } else {
                    error!(
                        "Album '{al}' points to artist '{ar}', but {ar} does not point back",
                        al = album.name(),
                        ar = artist.name()
                    );
                    was_error = true;
                }
            } else {
                error!(
                    "Album '{al}' points to artist '{ar}' which does not exist",
                    al = album.name(),
                    ar = artist_id
                );
                was_error = true;
            }
        }
    }

    for artist in db.iter_entries::<Artist>() {
        let artist = artist?;

        for track_id in &artist.tracks {
            if let Some(track) = track_id.db_get(db)? {
                if track.metadata.artists.iter().any(|a| **a == *artist.id()) {
                    trace!(
                        "Artist '{ar}' is correctly linked to track '{tr}'",
                        ar = artist.name(),
                        tr = track.metadata.safe_title()
                    );
                } else {
                    error!(
                        "Artist '{ar}' points to track '{tr}', but {tr} does not point back",
                        ar = artist.name(),
                        tr = track.metadata.safe_title()
                    );
                    was_error = true;
                }
            } else {
                error!(
                    "Artist '{ar}' points to track '{tr}' which does not exist",
                    ar = artist.name(),
                    tr = track_id
                );
                was_error = true;
            }
        }

        for album_id in &artist.albums {
            if let Some(album) = album_id.db_get(db)? {
                if album.artists().iter().any(|a| **a == *artist.id()) {
                    trace!(
                        "Artist '{ar}' is correctly linked to album '{al}'",
                        ar = artist.name(),
                        al = album.name()
                    );
                } else {
                    error!(
                        "Artist '{ar}' points to album '{al}', but {al} does not point back",
                        ar = artist.name(),
                        al = album.name()
                    );
                    was_error = true;
                }
            } else {
                error!(
                    "Artist '{ar}' points to album '{al}' which does not exist",
                    ar = artist.name(),
                    al = album_id
                );
                was_error = true;
            }
        }
    }

    assert!(!was_error);
    Ok(())
}