cacache_sync/
get.rs

1//! Functions for reading from cache.
2use std::path::Path;
3
4use ssri::{Algorithm, Integrity};
5
6use crate::content::read;
7use crate::errors::{Error, Result};
8use crate::index::{self, Metadata};
9
10// ---------------
11// Synchronous API
12// ---------------
13
14/// File handle for reading data synchronously.
15///
16/// Make sure to call `get.check()` when done reading
17/// to verify that the extracted data passes integrity
18/// verification.
19pub struct Reader {
20    reader: read::Reader,
21}
22
23impl std::io::Read for Reader {
24    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
25        self.reader.read(buf)
26    }
27}
28
29impl Reader {
30    /// Checks that data read from disk passes integrity checks. Returns the
31    /// algorithm that was used verified the data. Should be called only after
32    /// all data has been read from disk.
33    ///
34    /// ## Example
35    /// ```no_run
36    /// use std::io::Read;
37    ///
38    /// fn main() -> cacache_sync::Result<()> {
39    ///     let mut fd = cacache_sync::Reader::open("./my-cache", "my-key")?;
40    ///     let mut str = String::new();
41    ///     fd.read_to_string(&mut str).expect("Failed to read to string");
42    ///     // Remember to check that the data you got was correct!
43    ///     fd.check()?;
44    ///     Ok(())
45    /// }
46    /// ```
47    pub fn check(self) -> Result<Algorithm> {
48        self.reader.check()
49    }
50
51    /// Opens a new synchronous file handle into the cache, looking it up in the
52    /// index using `key`.
53    ///
54    /// ## Example
55    /// ```no_run
56    /// use std::io::Read;
57    ///
58    /// fn main() -> cacache_sync::Result<()> {
59    ///     let mut fd = cacache_sync::Reader::open("./my-cache", "my-key")?;
60    ///     let mut str = String::new();
61    ///     fd.read_to_string(&mut str).expect("Failed to parse string");
62    ///     // Remember to check that the data you got was correct!
63    ///     fd.check()?;
64    ///     Ok(())
65    /// }
66    /// ```
67    pub fn open<P, K>(cache: P, key: K) -> Result<Reader>
68    where
69        P: AsRef<Path>,
70        K: AsRef<str>,
71    {
72        if let Some(entry) = index::find(cache.as_ref(), key.as_ref())? {
73            Reader::open_hash(cache, entry.integrity)
74        } else {
75            return Err(Error::EntryNotFound(
76                cache.as_ref().to_path_buf(),
77                key.as_ref().into(),
78            ));
79        }
80    }
81
82    /// Opens a new synchronous file handle into the cache, based on its integrity address.
83    ///
84    /// ## Example
85    /// ```no_run
86    /// use std::io::Read;
87    ///
88    /// fn main() -> cacache_sync::Result<()> {
89    ///     let sri = cacache_sync::write("./my-cache", "key", b"hello world")?;
90    ///     let mut fd = cacache_sync::Reader::open_hash("./my-cache", sri)?;
91    ///     let mut str = String::new();
92    ///     fd.read_to_string(&mut str).expect("Failed to read to string");
93    ///     // Remember to check that the data you got was correct!
94    ///     fd.check()?;
95    ///     Ok(())
96    /// }
97    /// ```
98    pub fn open_hash<P>(cache: P, sri: Integrity) -> Result<Reader>
99    where
100        P: AsRef<Path>,
101    {
102        Ok(Reader {
103            reader: read::open(cache.as_ref(), sri)?,
104        })
105    }
106}
107
108/// Reads the entire contents of a cache file synchronously into a bytes
109/// vector, looking the data up by key.
110///
111/// ## Example
112/// ```no_run
113/// use std::io::Read;
114///
115/// fn main() -> cacache_sync::Result<()> {
116///     let data = cacache_sync::read("./my-cache", "my-key")?;
117///     Ok(())
118/// }
119/// ```
120pub fn read<P, K>(cache: P, key: K) -> Result<Vec<u8>>
121where
122    P: AsRef<Path>,
123    K: AsRef<str>,
124{
125    if let Some(entry) = index::find(cache.as_ref(), key.as_ref())? {
126        read_hash(cache, &entry.integrity)
127    } else {
128        return Err(Error::EntryNotFound(
129            cache.as_ref().to_path_buf(),
130            key.as_ref().into(),
131        ));
132    }
133}
134
135/// Reads the entire contents of a cache file synchronously into a bytes
136/// vector, looking the data up by its content address.
137///
138/// ## Example
139/// ```no_run
140/// use std::io::Read;
141///
142/// fn main() -> cacache_sync::Result<()> {
143///     let sri = cacache_sync::write("./my-cache", "my-key", b"hello")?;
144///     let data = cacache_sync::read_hash("./my-cache", &sri)?;
145///     Ok(())
146/// }
147/// ```
148pub fn read_hash<P>(cache: P, sri: &Integrity) -> Result<Vec<u8>>
149where
150    P: AsRef<Path>,
151{
152    read::read(cache.as_ref(), sri)
153}
154
155/// Copies a cache entry by key to a specified location. Returns the number of
156/// bytes copied.
157///
158/// ## Example
159/// ```no_run
160/// use std::io::Read;
161///
162/// fn main() -> cacache_sync::Result<()> {
163///     cacache_sync::copy("./my-cache", "my-key", "./my-hello.txt")?;
164///     Ok(())
165/// }
166/// ```
167pub fn copy<P, K, Q>(cache: P, key: K, to: Q) -> Result<u64>
168where
169    P: AsRef<Path>,
170    K: AsRef<str>,
171    Q: AsRef<Path>,
172{
173    if let Some(entry) = index::find(cache.as_ref(), key.as_ref())? {
174        copy_hash(cache, &entry.integrity, to)
175    } else {
176        return Err(Error::EntryNotFound(
177            cache.as_ref().to_path_buf(),
178            key.as_ref().into(),
179        ));
180    }
181}
182
183/// Copies a cache entry by integrity address to a specified location. Returns
184/// the number of bytes copied.
185///
186/// ## Example
187/// ```no_run
188/// use std::io::Read;
189///
190/// fn main() -> cacache_sync::Result<()> {
191///     let sri = cacache_sync::write("./my-cache", "my-key", b"hello")?;
192///     cacache_sync::copy_hash("./my-cache", &sri, "./my-hello.txt")?;
193///     Ok(())
194/// }
195/// ```
196pub fn copy_hash<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<u64>
197where
198    P: AsRef<Path>,
199    Q: AsRef<Path>,
200{
201    read::copy(cache.as_ref(), sri, to.as_ref())
202}
203
204/// Gets metadata for a certain key.
205///
206/// Note that the existence of a metadata entry is not a guarantee that the
207/// underlying data exists, since they are stored and managed independently.
208/// To verify that the underlying associated data exists, use `exists()`.
209pub fn metadata<P, K>(cache: P, key: K) -> Result<Option<Metadata>>
210where
211    P: AsRef<Path>,
212    K: AsRef<str>,
213{
214    index::find(cache.as_ref(), key.as_ref())
215}
216
217/// Returns true if the given hash exists in the cache.
218pub fn exists<P: AsRef<Path>>(cache: P, sri: &Integrity) -> bool {
219    read::has_content(cache.as_ref(), sri).is_some()
220}
221
222#[cfg(test)]
223mod tests {
224    use std::fs;
225
226    #[test]
227    fn test_open() {
228        use std::io::prelude::*;
229        let tmp = tempfile::tempdir().unwrap();
230        let dir = tmp.path().to_owned();
231        crate::write(&dir, "my-key", b"hello world").unwrap();
232
233        let mut handle = crate::Reader::open(&dir, "my-key").unwrap();
234        let mut str = String::new();
235        handle.read_to_string(&mut str).unwrap();
236        handle.check().unwrap();
237        assert_eq!(str, String::from("hello world"));
238    }
239
240    #[test]
241    fn test_open_hash() {
242        use std::io::prelude::*;
243        let tmp = tempfile::tempdir().unwrap();
244        let dir = tmp.path().to_owned();
245        let sri = crate::write(&dir, "my-key", b"hello world").unwrap();
246
247        let mut handle = crate::Reader::open_hash(&dir, sri).unwrap();
248        let mut str = String::new();
249        handle.read_to_string(&mut str).unwrap();
250        handle.check().unwrap();
251        assert_eq!(str, String::from("hello world"));
252    }
253
254    #[test]
255    fn test_read() {
256        let tmp = tempfile::tempdir().unwrap();
257        let dir = tmp.path().to_owned();
258        crate::write(&dir, "my-key", b"hello world").unwrap();
259
260        let data = crate::read(&dir, "my-key").unwrap();
261        assert_eq!(data, b"hello world");
262    }
263
264    #[test]
265    fn test_read_hash() {
266        let tmp = tempfile::tempdir().unwrap();
267        let dir = tmp.path().to_owned();
268        let sri = crate::write(&dir, "my-key", b"hello world").unwrap();
269
270        let data = crate::read_hash(&dir, &sri).unwrap();
271        assert_eq!(data, b"hello world");
272    }
273
274    #[test]
275    fn test_copy() {
276        let tmp = tempfile::tempdir().unwrap();
277        let dir = tmp.path();
278        let dest = dir.join("data");
279        crate::write(dir, "my-key", b"hello world").unwrap();
280
281        crate::copy(dir, "my-key", &dest).unwrap();
282        let data = fs::read(&dest).unwrap();
283        assert_eq!(data, b"hello world");
284    }
285
286    #[test]
287    fn test_copy_hash() {
288        let tmp = tempfile::tempdir().unwrap();
289        let dir = tmp.path();
290        let dest = dir.join("data");
291        let sri = crate::write(dir, "my-key", b"hello world").unwrap();
292
293        crate::copy_hash(dir, &sri, &dest).unwrap();
294        let data = fs::read(&dest).unwrap();
295        assert_eq!(data, b"hello world");
296    }
297}