use std::future::Future;
use std::sync::Arc;
use wasm_pkg_common::{
digest::ContentDigest,
package::{PackageRef, Version},
Error,
};
use crate::{Client, ContentStream, Release, VersionInfo};
mod file;
pub use file::FileCache;
pub trait Cache {
fn put_data(
&self,
digest: ContentDigest,
data: ContentStream,
) -> impl Future<Output = Result<(), Error>> + Send;
fn get_data(
&self,
digest: &ContentDigest,
) -> impl Future<Output = Result<Option<ContentStream>, Error>> + Send;
fn put_release(
&self,
package: &PackageRef,
release: &Release,
) -> impl Future<Output = Result<(), Error>> + Send;
fn get_release(
&self,
package: &PackageRef,
version: &Version,
) -> impl Future<Output = Result<Option<Release>, Error>> + Send;
}
pub struct CachingClient<T> {
client: Option<Client>,
cache: Arc<T>,
}
impl<T: Cache> Clone for CachingClient<T> {
fn clone(&self) -> Self {
Self {
client: self.client.clone(),
cache: self.cache.clone(),
}
}
}
impl<T: Cache> CachingClient<T> {
pub fn new(client: Option<Client>, cache: T) -> Self {
Self {
client,
cache: Arc::new(cache),
}
}
pub fn is_readonly(&self) -> bool {
self.client.is_none()
}
pub async fn list_all_versions(&self, package: &PackageRef) -> Result<Vec<VersionInfo>, Error> {
let client = self.client()?;
client.list_all_versions(package).await
}
pub async fn get_release(
&self,
package: &PackageRef,
version: &Version,
) -> Result<Release, Error> {
if let Some(data) = self.cache.get_release(package, version).await? {
return Ok(data);
}
let client = self.client()?;
let release = client.get_release(package, version).await?;
self.cache.put_release(package, &release).await?;
Ok(release)
}
pub async fn get_content(
&self,
package: &PackageRef,
release: &Release,
) -> Result<ContentStream, Error> {
if let Some(data) = self.cache.get_data(&release.content_digest).await? {
return Ok(data);
}
let client = self.client()?;
let stream = client.stream_content(package, release).await?;
self.cache
.put_data(release.content_digest.clone(), stream)
.await?;
self.cache
.get_data(&release.content_digest)
.await?
.ok_or_else(|| {
Error::CacheError(anyhow::anyhow!(
"Cached data was deleted after putting the data in cache"
))
})
}
pub fn client(&self) -> Result<&Client, Error> {
self.client
.as_ref()
.ok_or_else(|| Error::CacheError(anyhow::anyhow!("Client is in read only mode")))
}
pub fn into_client(self) -> Result<Client, Error> {
self.client
.ok_or_else(|| Error::CacheError(anyhow::anyhow!("Client is in read only mode")))
}
}