1use super::*;
2use crate::change::{Change, ChangeFile};
3use crate::pristine::{Base32, ChangeId, Hash, Merkle, Vertex};
4use parking_lot::Mutex;
5use std::path::{Path, PathBuf};
6
7pub struct FileSystem {
9 change_cache: Mutex<lru_cache::LruCache<ChangeId, ChangeFile>>,
10 changes_dir: PathBuf,
11}
12
13impl Clone for FileSystem {
14 fn clone(&self) -> Self {
15 let len = self.change_cache.lock().capacity();
16 FileSystem {
17 changes_dir: self.changes_dir.clone(),
18 change_cache: Mutex::new(lru_cache::LruCache::new(len)),
19 }
20 }
21}
22
23#[derive(Debug, Error)]
24pub enum Error {
25 #[error(transparent)]
26 Io(#[from] std::io::Error),
27 #[error(transparent)]
28 Utf8(#[from] std::str::Utf8Error),
29 #[error(transparent)]
30 ChangeFile(#[from] crate::change::ChangeError),
31 #[error(transparent)]
32 Persist(#[from] tempfile::PersistError),
33}
34
35pub fn push_filename(changes_dir: &mut PathBuf, hash: &Hash) {
36 let h32 = hash.to_base32();
37 let (a, b) = h32.split_at(2);
38 changes_dir.push(a);
39 changes_dir.push(b);
40 changes_dir.set_extension("change");
41}
42
43pub fn push_tag_filename(changes_dir: &mut PathBuf, hash: &Merkle) {
44 let h32 = hash.to_base32();
45 let (a, b) = h32.split_at(2);
46 changes_dir.push(a);
47 changes_dir.push(b);
48 changes_dir.set_extension("tag");
49}
50
51pub fn pop_filename(changes_dir: &mut PathBuf) {
52 changes_dir.pop();
53 changes_dir.pop();
54}
55
56impl FileSystem {
57 pub fn filename(&self, hash: &Hash) -> PathBuf {
58 let mut path = self.changes_dir.clone();
59 push_filename(&mut path, hash);
60 path
61 }
62
63 pub fn tag_filename(&self, hash: &Merkle) -> PathBuf {
64 let mut path = self.changes_dir.clone();
65 push_tag_filename(&mut path, hash);
66 path
67 }
68
69 pub fn has_change(&self, hash: &Hash) -> bool {
70 std::fs::metadata(self.filename(hash)).is_ok()
71 }
72
73 pub fn from_root<P: AsRef<Path>>(root: P, cap: usize) -> Self {
76 let dot_pijul = root.as_ref().join(crate::DOT_DIR);
77 let changes_dir = dot_pijul.join("changes");
78 Self::from_changes(changes_dir, cap)
79 }
80
81 pub fn from_changes(changes_dir: PathBuf, cap: usize) -> Self {
84 std::fs::create_dir_all(&changes_dir).unwrap();
85 FileSystem {
86 changes_dir,
87 change_cache: Mutex::new(lru_cache::LruCache::new(cap)),
88 }
89 }
90
91 fn load<'a, F: Fn(ChangeId) -> Option<Hash>>(
92 &'a self,
93 hash: F,
94 change: ChangeId,
95 ) -> Result<
96 parking_lot::MutexGuard<'a, lru_cache::LruCache<ChangeId, ChangeFile>>,
97 crate::change::ChangeError,
98 > {
99 let mut change_cache = self.change_cache.lock();
100 if !change_cache.contains_key(&change) {
101 let h = hash(change).unwrap();
102 let path = self.filename(&h);
103 debug!("changefile: {:?}", path);
104 let p = crate::change::ChangeFile::open(h, path.to_str().unwrap())?;
105 debug!("patch done");
106 change_cache.insert(change, p);
107 }
108 Ok(change_cache)
109 }
110
111 pub fn save_from_buf(
112 &self,
113 buf: &[u8],
114 hash: &Hash,
115 change_id: Option<ChangeId>,
116 ) -> Result<(), crate::change::ChangeError> {
117 Change::check_from_buffer(buf, hash)?;
118 self.save_from_buf_unchecked(buf, hash, change_id)?;
119 Ok(())
120 }
121
122 pub fn save_from_buf_unchecked(
123 &self,
124 buf: &[u8],
125 hash: &Hash,
126 change_id: Option<ChangeId>,
127 ) -> Result<(), std::io::Error> {
128 let mut f = tempfile::NamedTempFile::new_in(&self.changes_dir)?;
129 let file_name = self.filename(hash);
130 use std::io::Write;
131 f.write_all(buf)?;
132 debug!("file_name = {:?}", file_name);
133 std::fs::create_dir_all(file_name.parent().unwrap())?;
134 f.persist(file_name)?;
135 if let Some(ref change_id) = change_id {
136 self.change_cache.lock().remove(change_id);
137 }
138 Ok(())
139 }
140}
141
142impl ChangeStore for FileSystem {
143 type Error = Error;
144 fn has_contents(&self, hash: Hash, change_id: Option<ChangeId>) -> bool {
145 if let Some(ref change_id) = change_id
146 && let Some(l) = self.change_cache.lock().get_mut(change_id)
147 {
148 return l.has_contents();
149 }
150 let path = self.filename(&hash);
151 match crate::change::ChangeFile::open(hash, path.to_str().unwrap()) {
152 Ok(p) => p.has_contents(),
153 _ => false,
154 }
155 }
156
157 fn get_header(&self, h: &Hash) -> Result<ChangeHeader, Self::Error> {
158 let path = self.filename(h);
159 let p = crate::change::ChangeFile::open(*h, path.to_str().unwrap())?;
160 Ok(p.hashed().header.clone())
161 }
162
163 fn get_contents<F: Fn(ChangeId) -> Option<Hash>>(
164 &self,
165 hash: F,
166 key: Vertex<ChangeId>,
167 buf: &mut [u8],
168 ) -> Result<usize, Self::Error> {
169 debug!("get_contents {:?}", key);
170 if key.end <= key.start || key.is_root() {
171 debug!("return 0");
172 return Ok(0);
173 }
174 assert_eq!(key.end - key.start, buf.len());
175 let mut cache = self.load(hash, key.change)?;
176 let p = cache.get_mut(&key.change).unwrap();
177 let n = p.read_contents(key.start.into(), buf)?;
178 debug!("get_contents {:?}", n);
179 Ok(n)
180 }
181 fn get_contents_ext(
182 &self,
183 key: Vertex<Option<Hash>>,
184 buf: &mut [u8],
185 ) -> Result<usize, Self::Error> {
186 if let Some(change) = key.change {
187 assert_eq!(key.end.us() - key.start.us(), buf.len());
188 if key.end <= key.start {
189 return Ok(0);
190 }
191 let path = self.filename(&change);
192 let mut p = crate::change::ChangeFile::open(change, path.to_str().unwrap())?;
193 let n = p.read_contents(key.start.into(), buf)?;
194 Ok(n)
195 } else {
196 Ok(0)
197 }
198 }
199 fn change_deletes_position<F: Fn(ChangeId) -> Option<Hash>>(
200 &self,
201 hash: F,
202 change: ChangeId,
203 pos: Position<Option<Hash>>,
204 ) -> Result<Vec<Hash>, Self::Error> {
205 let mut cache = self.load(hash, change)?;
206 let p = cache.get_mut(&change).unwrap();
207 let mut v = Vec::new();
208 for c in p.hashed().changes.iter() {
209 for c in c.iter() {
210 v.extend(c.deletes_pos(pos))
211 }
212 }
213 Ok(v)
214 }
215 fn save_change<
216 E: From<Self::Error> + From<ChangeError>,
217 F: FnOnce(&mut Change, &Hash) -> Result<(), E>,
218 >(
219 &self,
220 p: &mut Change,
221 ff: F,
222 ) -> Result<Hash, E> {
223 let mut f = match tempfile::NamedTempFile::new_in(&self.changes_dir) {
224 Ok(f) => f,
225 Err(e) => return Err(E::from(Error::from(e))),
226 };
227 let hash = {
228 let w = std::io::BufWriter::new(&mut f);
229 p.serialize(w, ff)?
230 };
231 let file_name = self.filename(&hash);
232 if let Err(e) = std::fs::create_dir_all(file_name.parent().unwrap()) {
233 return Err(E::from(Error::from(e)));
234 }
235 debug!("file_name = {:?}", file_name);
236 if let Err(e) = f.persist(file_name) {
237 return Err(E::from(Error::from(e)));
238 }
239 Ok(hash)
240 }
241 fn del_change(&self, hash: &Hash) -> Result<bool, Self::Error> {
242 let file_name = self.filename(hash);
243 debug!("file_name = {:?}", file_name);
244 let result = std::fs::remove_file(&file_name).is_ok();
245 std::fs::remove_dir(file_name.parent().unwrap()).unwrap_or(()); Ok(result)
247 }
248 fn get_change(&self, h: &Hash) -> Result<Change, Self::Error> {
249 let file_name = self.filename(h);
250 let file_name = file_name.to_str().unwrap();
251 debug!("file_name = {:?}", file_name);
252 Ok(Change::deserialize(file_name, Some(h))?)
253 }
254}