1 use data_encoding::BASE64;
2 use digest::{Digest, Update};
3 use serde::{Deserialize, Serialize};
5 use sha2::{Sha256, Sha512};
7 fmt::Write as FmtWrite,
14 #[allow(clippy::struct_field_names)]
15 #[derive(Serialize, Deserialize)]
16 pub(super) struct Key {
17 pub(super) key: String,
18 pub(super) integrity: String,
20 pub(super) size: usize,
21 pub(super) metadata: Metadata,
24 #[derive(Serialize, Deserialize)]
25 pub(super) struct Metadata {
27 pub(super) options: Options,
30 #[derive(Serialize, Deserialize)]
31 pub(super) struct Options {
32 pub(super) compress: bool,
35 pub struct Cache(PathBuf);
37 fn push_hash_segments(path: &mut PathBuf, hash: &str) {
38 path.push(&hash[0..2]);
39 path.push(&hash[2..4]);
40 path.push(&hash[4..]);
44 pub fn new(path: PathBuf) -> Cache {
48 pub fn init(&self) -> anyhow::Result<()> {
49 fs::create_dir_all(self.0.join("content-v2"))?;
50 fs::create_dir_all(self.0.join("index-v5"))?;
60 integrity: Option<String>,
61 ) -> anyhow::Result<()> {
62 let (algo, hash, integrity) = if let Some(integrity) = integrity {
63 let (algo, hash) = integrity
65 .expect("hash should be SRI format");
67 (algo.to_string(), BASE64.decode(hash.as_bytes())?, integrity)
69 let hash = Sha512::new().chain(data).finalize();
72 String::from("sha512"),
74 format!("sha512-{}", BASE64.encode(&hash)),
79 let mut p = self.0.join("content-v2");
85 &hash.into_iter().fold(String::new(), |mut out, n| {
86 let _ = write!(out, "{n:02x}");
94 fs::create_dir_all(content_path.parent().unwrap())?;
96 fs::write(content_path, data)?;
99 let mut p = self.0.join("index-v5");
103 &format!("{:x}", Sha256::new().chain(&key).finalize()),
109 fs::create_dir_all(index_path.parent().unwrap())?;
111 let data = serde_json::to_string(&Key {
118 options: Options { compress: true },
122 let mut file = File::options().append(true).create(true).open(index_path)?;
124 write!(file, "{:x}\t{data}", Sha1::new().chain(&data).finalize())?;