1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use super::Objects;
use checksum::Checksum;
use core::errors::*;
use core::{Object, PathObject};
use hex_slice::HexSlice;
use std::fs::{self, File};
use std::io::{self, Read};
use std::path::PathBuf;
use std::time::{self, Duration};
pub struct CachedObjects<T> {
objects_cache: PathBuf,
missing_cache_time: Duration,
inner: T,
}
impl<T: Objects> CachedObjects<T> {
pub fn new(objects_cache: PathBuf, missing_cache_time: Duration, inner: T) -> CachedObjects<T> {
CachedObjects {
objects_cache: objects_cache,
missing_cache_time: missing_cache_time,
inner: inner,
}
}
fn cache_path(&self, checksum: &Checksum) -> Result<PathBuf> {
let path = self.objects_cache
.join(format!("{}", HexSlice::new(&checksum[0..1])));
let path = path.join(format!("{}", HexSlice::new(&checksum[1..2])));
Ok(path.join(format!("{}", HexSlice::new(&checksum))))
}
fn missing_path(&self, checksum: &Checksum) -> Result<PathBuf> {
Ok(self.objects_cache
.join("missing")
.join(format!("{}", HexSlice::new(checksum))))
}
fn check_missing(&self, checksum: &Checksum) -> Result<(bool, PathBuf)> {
let path = self.missing_path(checksum)?;
match fs::metadata(&path) {
Err(e) => {
if e.kind() != io::ErrorKind::NotFound {
return Err(e.into());
}
}
Ok(m) => {
let now = time::SystemTime::now();
let age = now.duration_since(m.modified()?)?;
let expires = self.missing_cache_time
.checked_sub(age)
.unwrap_or_else(|| Duration::new(0, 0));
debug!(
"cache: missing file exists: {} (age: {}s, expires: {}s)",
path.display(),
age.as_secs(),
expires.as_secs()
);
if age < self.missing_cache_time {
return Ok((true, path));
}
debug!("cache: removing missing entry: {}", path.display());
fs::remove_file(&path)?;
}
}
Ok((false, path))
}
}
impl<T: Objects> Objects for CachedObjects<T> {
fn put_object(&mut self, checksum: &Checksum, source: &mut Read, force: bool) -> Result<()> {
self.inner.put_object(checksum, source, force)
}
fn get_object(&mut self, checksum: &Checksum) -> Result<Option<Box<Object>>> {
let cache_path = self.cache_path(checksum)?;
if cache_path.is_file() {
return Ok(Some(Box::new(PathObject::new(None, cache_path))));
}
let (missing, missing_path) = self.check_missing(checksum)?;
if missing {
return Ok(None);
}
let out = self.inner.get_object(checksum)?;
if let Some(object) = out {
if let Some(parent) = cache_path.parent() {
if !parent.is_dir() {
fs::create_dir_all(parent)?;
}
}
io::copy(&mut object.read()?, &mut File::create(cache_path)?)?;
return Ok(Some(object));
} else {
debug!(
"cache: creating missing cache entry: {}",
missing_path.display()
);
if let Some(parent) = missing_path.parent() {
if !parent.is_dir() {
fs::create_dir_all(parent)?;
}
}
File::create(missing_path)?;
}
return Ok(None);
}
}