moosicbox_library 0.1.0

MoosicBox Library Music API package
Documentation
use enum_as_inner::EnumAsInner;
use futures::Future;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::error::Error;
use std::sync::{Arc, LazyLock, RwLock};
use std::time::{Duration, SystemTime, UNIX_EPOCH};

use crate::models::{LibraryAlbum, LibraryArtist, LibraryTrack};

#[derive(Debug, Serialize, Deserialize, Clone)]
struct CacheItem {
    expiration: u128,
    data: CacheItemType,
}

#[derive(Debug, Serialize, Deserialize, Clone, EnumAsInner)]
#[serde(untagged)]
pub enum CacheItemType {
    Albums(Arc<Vec<LibraryAlbum>>),
    AlbumTracks(Arc<Vec<LibraryTrack>>),
    ArtistAlbums(Arc<Vec<LibraryAlbum>>),
    Artist(Arc<LibraryArtist>),
    Album(Arc<LibraryAlbum>),
}

pub fn current_time_nanos() -> u128 {
    let start = SystemTime::now();
    let since_the_epoch = start
        .duration_since(UNIX_EPOCH)
        .expect("Time went backwards");
    since_the_epoch.as_nanos()
}

pub struct CacheRequest<'a> {
    pub key: &'a str,
    pub expiration: Duration,
}

static CACHE_MAP: LazyLock<RwLock<HashMap<String, CacheItem>>> =
    LazyLock::new(|| RwLock::new(HashMap::new()));

pub fn clear_cache() {
    CACHE_MAP.write().unwrap().clear();
}

pub async fn get_or_set_to_cache<Fut, Err>(
    request: CacheRequest<'_>,
    compute: impl Fn() -> Fut,
) -> Result<CacheItemType, Err>
where
    Err: Error,
    Fut: Future<Output = Result<CacheItemType, Err>>,
{
    if let Some(entry) = CACHE_MAP.read().unwrap().get(request.key) {
        if entry.expiration > current_time_nanos() {
            return Ok(entry.data.clone());
        }
    }

    let value = match compute().await {
        Ok(x) => x,
        Err(error) => return Err(error),
    };

    CACHE_MAP.write().unwrap().insert(
        request.key.to_string(),
        CacheItem {
            expiration: current_time_nanos() + request.expiration.as_nanos(),
            data: value.clone(),
        },
    );

    Ok(value)
}