cranpose-services 0.22.4

Multiplatform service contracts and platform integrations for Cranpose apps
Documentation
use std::{
    io,
    path::Path,
    sync::{
        Arc,
        atomic::{AtomicUsize, Ordering},
    },
    thread,
};

use cranpose_services::{
    MediaSourceHandle, MediaSourceOpener, clear_platform_media_source_opener, file_folder,
    folder_files, set_platform_media_source_opener, uri_for_path,
};

struct Opener {
    ui: thread::ThreadId,
    retained: AtomicUsize,
}
impl MediaSourceOpener for Opener {
    fn open(&self, _: &str) -> io::Result<MediaSourceHandle> {
        panic!("collecting playback references must not open audio")
    }
    fn reference(&self, path: &Path) -> io::Result<String> {
        assert_ne!(
            thread::current().id(),
            self.ui,
            "platform grants must not block the UI"
        );
        self.retained.fetch_add(1, Ordering::SeqCst);
        Ok(uri_for_path(path))
    }
}
struct Registration;
impl Drop for Registration {
    fn drop(&mut self) {
        clear_platform_media_source_opener();
    }
}

#[test]
fn a_folder_retains_one_grant_off_the_ui_and_reuses_it_for_nested_tracks() {
    let _serial = super::media_support::one_media_player_at_a_time();
    let root = tempfile::tempdir().unwrap();
    std::fs::create_dir(root.path().join("Album")).unwrap();
    let paths = [
        root.path().join("one.flac"),
        root.path().join("Album/02 # ΕΎ.opus"),
    ];
    for path in &paths {
        std::fs::write(path, b"original").unwrap();
    }
    let opener = Arc::new(Opener {
        ui: thread::current().id(),
        retained: AtomicUsize::new(0),
    });
    let registered: Arc<dyn MediaSourceOpener> = Arc::clone(&opener) as Arc<dyn MediaSourceOpener>;
    set_platform_media_source_opener(registered);
    let _registration = Registration;
    let stream = folder_files(file_folder(root.path()));
    let first = pollster::block_on(stream.next()).unwrap().unwrap();
    let first_uri = pollster::block_on(first.media_uri_async()).unwrap();
    assert_eq!(first_uri, uri_for_path(&paths[0]));
    assert_eq!(
        stream.produced(),
        Some(1),
        "first playback reference precedes discovery of the tree"
    );
    let second = pollster::block_on(stream.next()).unwrap().unwrap();
    assert_eq!(
        pollster::block_on(second.media_uri_async()).unwrap(),
        uri_for_path(&paths[1])
    );
    assert_eq!(
        pollster::block_on(first.media_uri_async()).unwrap(),
        first_uri
    );
    assert_eq!(opener.retained.load(Ordering::SeqCst), 1);
    for path in &paths {
        assert_eq!(std::fs::read(path).unwrap(), b"original");
    }
}