selene-core 0.5.1

selene-core is the backend for Selene, a local-first music player
Documentation
use std::{collections::HashMap, fs, io, path::PathBuf, sync::Arc};

use barber::{ProgressBar, ProgressRenderer};
use lunar_lib::database::{Database, DatabaseEntry, DatabaseError};

use crate::{
    database::LibraryDb,
    errors::OrphanRelinkError,
    library::track::{Track, TrackId},
};

pub fn relink_orphans(
    check_tracks: &mut [Track],
    progress_renderer: Arc<dyn ProgressRenderer>,
    sources: &HashMap<TrackId, PathBuf>,
) -> Result<(), OrphanRelinkError> {
    let orphaned_tracks: Vec<&mut Track> = check_tracks
        .iter_mut()
        .filter(|t| !t.container().path().exists())
        .collect();

    if orphaned_tracks.is_empty() {
        return Ok(());
    }

    let progress_bar = ProgressBar::new(0, orphaned_tracks.len(), progress_renderer);
    progress_bar.set_label("Relinking orphaned sources...");

    for track in orphaned_tracks {
        if let Some(source) = sources.get(&track.id()) {
            track.set_source_file(source.to_owned());
            track.db_upsert()?;

            progress_bar.set_label(&format!(
                "Succesfully relinked {}",
                track.metadata.safe_title()
            ));
        } else {
            if let Err(err) = fs::remove_file(track.container().path())
                && !matches!(err.kind(), io::ErrorKind::NotFound)
            {
                return Err(err.into());
            }

            let db = LibraryDb::open()?;
            db.remove(*track.id()).map_err(DatabaseError::Sled)?;

            progress_bar.set_label(&format!(
                "Failed to relink {}: No source found. Removing from library",
                track.metadata.safe_title()
            ));
        }

        progress_bar.increment();
    }
    progress_bar.flush();
    Ok(())
}