ddup_bak/chunks/
storage.rs

1use super::ChunkHash;
2use std::{io::Write, path::PathBuf};
3
4pub trait ChunkStorage: Sync + Send {
5    #[inline]
6    fn path_from_chunk(&self, chunk: &ChunkHash) -> PathBuf {
7        let mut path = PathBuf::new();
8        for byte in chunk.iter().take(2) {
9            path.push(format!("{byte:02x}"));
10        }
11
12        let mut file_name = String::with_capacity(32 * 2 - 2 * 2 + 6);
13        for byte in chunk.iter().skip(2) {
14            file_name.push_str(&format!("{byte:02x}"));
15        }
16        file_name.push_str(".chunk");
17
18        path.push(file_name);
19
20        path
21    }
22
23    fn read_chunk_content(
24        &self,
25        chunk: &ChunkHash,
26    ) -> std::io::Result<Box<dyn std::io::Read + Send>>;
27    fn write_chunk_content(
28        &self,
29        chunk: &ChunkHash,
30        content: Box<dyn std::io::Read + Send>,
31    ) -> std::io::Result<()>;
32    fn delete_chunk_content(&self, chunk: &ChunkHash) -> std::io::Result<()>;
33}
34
35pub struct ChunkStorageLocal(pub PathBuf);
36impl ChunkStorage for ChunkStorageLocal {
37    #[inline]
38    fn read_chunk_content(
39        &self,
40        chunk: &ChunkHash,
41    ) -> std::io::Result<Box<dyn std::io::Read + Send>> {
42        let path = self.0.join(self.path_from_chunk(chunk));
43        let file = std::fs::File::open(path)?;
44
45        Ok(Box::new(file))
46    }
47
48    #[inline]
49    fn write_chunk_content(
50        &self,
51        chunk: &ChunkHash,
52        mut content: Box<dyn std::io::Read + Send>,
53    ) -> std::io::Result<()> {
54        let path = self.0.join(self.path_from_chunk(chunk));
55        std::fs::create_dir_all(path.parent().unwrap())?;
56
57        let mut file = std::fs::File::create(path)?;
58
59        let mut buffer = [0; 4096];
60        loop {
61            let bytes_read = content.read(&mut buffer)?;
62            if bytes_read == 0 {
63                break;
64            }
65            file.write_all(&buffer[..bytes_read])?;
66        }
67
68        Ok(())
69    }
70
71    #[inline]
72    fn delete_chunk_content(&self, chunk: &ChunkHash) -> std::io::Result<()> {
73        let mut path = self.0.join(self.path_from_chunk(chunk));
74        std::fs::remove_file(&path)?;
75
76        while let Some(parent) = path.parent() {
77            if parent == self.0 {
78                break;
79            }
80
81            if std::fs::read_dir(parent)?.count() == 0 {
82                std::fs::remove_dir(parent)?;
83            } else {
84                break;
85            }
86
87            path = parent.to_path_buf();
88        }
89
90        Ok(())
91    }
92}