use crate::domain::action::identity::{ActionId, CommitSha, Version};
use crate::domain::action::resolved::Commit;
use crate::domain::resolution::{Error as ResolutionError, ShaDescription, VersionRegistry};
use elsa::FrozenMap;
pub struct Caching<R: VersionRegistry> {
inner: R,
commits: FrozenMap<(ActionId, Version), Box<Commit>>,
repo_tags: FrozenMap<ActionId, Vec<Version>>,
descriptions: FrozenMap<(ActionId, CommitSha), Box<ShaDescription>>,
}
impl<R: VersionRegistry> Caching<R> {
#[must_use]
pub fn new(inner: R) -> Self {
Self {
inner,
commits: FrozenMap::new(),
repo_tags: FrozenMap::new(),
descriptions: FrozenMap::new(),
}
}
}
impl<R: VersionRegistry> VersionRegistry for Caching<R> {
fn lookup_sha(&self, id: &ActionId, version: &Version) -> Result<Commit, ResolutionError> {
let key = (id.clone(), version.clone());
if let Some(hit) = self.commits.get(&key) {
return Ok(hit.clone());
}
let commit = self.inner.lookup_sha(id, version)?;
Ok(self.commits.insert(key, Box::new(commit)).clone())
}
fn all_tags(&self, id: &ActionId) -> Result<Vec<Version>, ResolutionError> {
if let Some(hit) = self.repo_tags.get(id) {
return Ok(hit.to_vec());
}
let tags = self.inner.all_tags(id)?;
Ok(self.repo_tags.insert(id.clone(), tags).to_vec())
}
fn describe_sha(
&self,
id: &ActionId,
sha: &CommitSha,
) -> Result<ShaDescription, ResolutionError> {
let key = (id.clone(), sha.clone());
if let Some(hit) = self.descriptions.get(&key) {
return Ok(hit.clone());
}
let description = self.inner.describe_sha(id, sha)?;
Ok(self.descriptions.insert(key, Box::new(description)).clone())
}
}
#[cfg(test)]
#[expect(
clippy::unwrap_used,
clippy::assertions_on_result_states,
clippy::arithmetic_side_effects,
reason = "tests use unwrap, indexing, and other patterns freely"
)]
#[path = "caching_tests.rs"]
mod tests;