use std::fmt::Write;
use camino::Utf8PathBuf;
use sha2::{Digest, Sha256};
use crate::error::{Error, Result};
use crate::git;
use crate::paths::template_cache_dir;
pub struct TemplateCache {
pub root: Utf8PathBuf,
}
impl TemplateCache {
pub fn ensure() -> Result<Self> {
let root = template_cache_dir()?;
std::fs::create_dir_all(root.as_std_path())
.map_err(|e| Error::io_at(root.as_std_path(), e))?;
Ok(Self { root })
}
pub fn slot(&self, source: &str) -> Utf8PathBuf {
let mut h = Sha256::new();
h.update(source.as_bytes());
let bytes = h.finalize();
let mut hex = String::with_capacity(16);
for b in bytes.iter().take(8) {
let _ = write!(hex, "{b:02x}");
}
self.root.join(hex)
}
pub async fn fetch_or_clone(
&self,
source: &str,
rev_spec: Option<&str>,
) -> Result<(Utf8PathBuf, String)> {
let slot = self.slot(source);
let cached = slot.join(".git").is_dir();
if !cached {
if slot.exists() {
std::fs::remove_dir_all(slot.as_std_path())
.map_err(|e| Error::io_at(slot.as_std_path(), e))?;
}
if let Some(parent) = slot.parent() {
std::fs::create_dir_all(parent.as_std_path())
.map_err(|e| Error::io_at(parent.as_std_path(), e))?;
}
git::clone_at(source, slot.as_path()).await?;
}
if let Some(rev) = rev_spec {
git::checkout(slot.as_path(), rev).await?;
}
let head = git::current_head(slot.as_path()).await?;
Ok((slot, head))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn slot_is_stable_for_same_source() {
let cache = TemplateCache {
root: Utf8PathBuf::from("/tmp/kata-cache"),
};
let a = cache.slot("github.com/yukimemi/pj-base");
let b = cache.slot("github.com/yukimemi/pj-base");
assert_eq!(a, b);
}
#[test]
fn slot_is_invariant_to_rev() {
let cache = TemplateCache {
root: Utf8PathBuf::from("/tmp/kata-cache"),
};
let s = cache.slot("github.com/x/y");
let s_again = cache.slot("github.com/x/y");
assert_eq!(s, s_again);
}
#[test]
fn slot_differs_for_different_sources() {
let cache = TemplateCache {
root: Utf8PathBuf::from("/tmp/kata-cache"),
};
let a = cache.slot("github.com/x/a");
let b = cache.slot("github.com/x/b");
assert_ne!(a, b);
}
}