1use sha2::{Digest, Sha256};
7use std::fs;
8use std::io::{self, Write};
9use std::path::{Path, PathBuf};
10
11pub struct Store {
12 root: PathBuf,
13}
14
15impl Store {
16 pub fn new(root: PathBuf) -> Self {
17 Self { root }
18 }
19
20 pub fn ensure(&self) -> io::Result<()> {
21 fs::create_dir_all(&self.root)
22 }
23
24 fn hash_hex(data: &[u8]) -> String {
25 let digest = Sha256::digest(data);
26 let mut s = String::with_capacity(64);
27 for b in digest {
28 s.push_str(&format!("{b:02x}"));
29 }
30 s
31 }
32
33 pub fn put_bytes(&self, data: &[u8]) -> io::Result<String> {
37 self.ensure()?;
38 let hash = Self::hash_hex(data);
39 let path = self.root.join(&hash);
40 match fs::OpenOptions::new()
41 .write(true)
42 .create_new(true)
43 .open(&path)
44 {
45 Ok(mut f) => {
46 f.write_all(data)?;
47 f.sync_all()?;
48 }
49 Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {}
50 Err(e) => return Err(e),
51 }
52 Ok(hash)
53 }
54
55 pub fn put_file(&self, path: &Path) -> io::Result<String> {
57 let data = fs::read(path)?;
58 self.put_bytes(&data)
59 }
60
61 pub fn get(&self, hash: &str) -> io::Result<Vec<u8>> {
63 fs::read(self.root.join(hash))
64 }
65}