about summary refs log tree commit diff
path: root/pkgs/build-support/node/fetch-npm-deps/src/cacache.rs
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/build-support/node/fetch-npm-deps/src/cacache.rs')
-rw-r--r--pkgs/build-support/node/fetch-npm-deps/src/cacache.rs116
1 files changed, 116 insertions, 0 deletions
diff --git a/pkgs/build-support/node/fetch-npm-deps/src/cacache.rs b/pkgs/build-support/node/fetch-npm-deps/src/cacache.rs
new file mode 100644
index 0000000000000..865a320954b58
--- /dev/null
+++ b/pkgs/build-support/node/fetch-npm-deps/src/cacache.rs
@@ -0,0 +1,116 @@
+use digest::{Digest, Update};
+use serde::Serialize;
+use sha1::Sha1;
+use sha2::{Sha256, Sha512};
+use std::{
+    fs::{self, File},
+    io::Write,
+    path::PathBuf,
+};
+use url::Url;
+
+#[derive(Serialize)]
+struct Key {
+    key: String,
+    integrity: String,
+    time: u8,
+    size: usize,
+    metadata: Metadata,
+}
+
+#[derive(Serialize)]
+struct Metadata {
+    url: Url,
+    options: Options,
+}
+
+#[derive(Serialize)]
+struct Options {
+    compress: bool,
+}
+
+pub struct Cache(PathBuf);
+
+fn push_hash_segments(path: &mut PathBuf, hash: &str) {
+    path.push(&hash[0..2]);
+    path.push(&hash[2..4]);
+    path.push(&hash[4..]);
+}
+
+impl Cache {
+    pub fn new(path: PathBuf) -> Cache {
+        Cache(path)
+    }
+
+    pub fn put(
+        &self,
+        key: String,
+        url: Url,
+        data: &[u8],
+        integrity: Option<String>,
+    ) -> anyhow::Result<()> {
+        let (algo, hash, integrity) = if let Some(integrity) = integrity {
+            let (algo, hash) = integrity.split_once('-').unwrap();
+
+            (algo.to_string(), base64::decode(hash)?, integrity)
+        } else {
+            let hash = Sha512::new().chain(data).finalize();
+
+            (
+                String::from("sha512"),
+                hash.to_vec(),
+                format!("sha512-{}", base64::encode(hash)),
+            )
+        };
+
+        let content_path = {
+            let mut p = self.0.join("content-v2");
+
+            p.push(algo);
+
+            push_hash_segments(
+                &mut p,
+                &hash
+                    .into_iter()
+                    .map(|x| format!("{:02x}", x))
+                    .collect::<String>(),
+            );
+
+            p
+        };
+
+        fs::create_dir_all(content_path.parent().unwrap())?;
+
+        fs::write(content_path, data)?;
+
+        let index_path = {
+            let mut p = self.0.join("index-v5");
+
+            push_hash_segments(
+                &mut p,
+                &format!("{:x}", Sha256::new().chain(&key).finalize()),
+            );
+
+            p
+        };
+
+        fs::create_dir_all(index_path.parent().unwrap())?;
+
+        let data = serde_json::to_string(&Key {
+            key,
+            integrity,
+            time: 0,
+            size: data.len(),
+            metadata: Metadata {
+                url,
+                options: Options { compress: true },
+            },
+        })?;
+
+        let mut file = File::options().append(true).create(true).open(index_path)?;
+
+        write!(file, "\n{:x}\t{data}", Sha1::new().chain(&data).finalize())?;
+
+        Ok(())
+    }
+}