cacache/get.rs
1//! Functions for reading from cache.
2use std::path::Path;
3#[cfg(any(feature = "async-std", feature = "tokio"))]
4use std::pin::Pin;
5#[cfg(any(feature = "async-std", feature = "tokio"))]
6use std::task::{Context as TaskContext, Poll};
7
8use ssri::{Algorithm, Integrity};
9
10#[cfg(any(feature = "async-std", feature = "tokio"))]
11use crate::async_lib::AsyncRead;
12use crate::content::read;
13use crate::errors::{Error, Result};
14use crate::index::{self, Metadata};
15
16// ---------
17// Async API
18// ---------
19
20/// File handle for reading data asynchronously.
21///
22/// Make sure to call `.check()` when done reading to verify that the
23/// extracted data passes integrity verification.
24#[cfg(any(feature = "async-std", feature = "tokio"))]
25pub struct Reader {
26 reader: read::AsyncReader,
27}
28
29#[cfg(any(feature = "async-std", feature = "tokio"))]
30impl AsyncRead for Reader {
31 #[cfg(feature = "async-std")]
32 fn poll_read(
33 mut self: Pin<&mut Self>,
34 cx: &mut TaskContext<'_>,
35 buf: &mut [u8],
36 ) -> Poll<std::io::Result<usize>> {
37 Pin::new(&mut self.reader).poll_read(cx, buf)
38 }
39
40 #[cfg(feature = "tokio")]
41 fn poll_read(
42 mut self: Pin<&mut Self>,
43 cx: &mut TaskContext<'_>,
44 buf: &mut tokio::io::ReadBuf<'_>,
45 ) -> Poll<tokio::io::Result<()>> {
46 Pin::new(&mut self.reader).poll_read(cx, buf)
47 }
48}
49
50#[cfg(any(feature = "async-std", feature = "tokio"))]
51impl Reader {
52 /// Checks that data read from disk passes integrity checks. Returns the
53 /// algorithm that was used verified the data. Should be called only after
54 /// all data has been read from disk.
55 ///
56 /// This check is very cheap, since most of the verification is done on
57 /// the fly. This simply finalizes verification, and is always
58 /// synchronous.
59 ///
60 /// ## Example
61 /// ```no_run
62 /// use async_std::prelude::*;
63 /// use async_attributes;
64 ///
65 /// #[async_attributes::main]
66 /// async fn main() -> cacache::Result<()> {
67 /// let mut fd = cacache::Reader::open("./my-cache", "my-key").await?;
68 /// let mut str = String::new();
69 /// fd.read_to_string(&mut str).await.expect("Failed to read to string");
70 /// // Remember to check that the data you got was correct!
71 /// fd.check()?;
72 /// Ok(())
73 /// }
74 /// ```
75 pub fn check(self) -> Result<Algorithm> {
76 self.reader.check()
77 }
78
79 /// Opens a new file handle into the cache, looking it up in the index using
80 /// `key`.
81 ///
82 /// ## Example
83 /// ```no_run
84 /// use async_std::prelude::*;
85 /// use async_attributes;
86 ///
87 /// #[async_attributes::main]
88 /// async fn main() -> cacache::Result<()> {
89 /// let mut fd = cacache::Reader::open("./my-cache", "my-key").await?;
90 /// let mut str = String::new();
91 /// fd.read_to_string(&mut str).await.expect("Failed to read to string");
92 /// // Remember to check that the data you got was correct!
93 /// fd.check()?;
94 /// Ok(())
95 /// }
96 /// ```
97 pub async fn open<P, K>(cache: P, key: K) -> Result<Reader>
98 where
99 P: AsRef<Path>,
100 K: AsRef<str>,
101 {
102 async fn inner(cache: &Path, key: &str) -> Result<Reader> {
103 if let Some(entry) = index::find_async(cache, key).await? {
104 Reader::open_hash(cache, entry.integrity).await
105 } else {
106 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
107 }
108 }
109 inner(cache.as_ref(), key.as_ref()).await
110 }
111
112 /// Opens a new file handle into the cache, based on its integrity address.
113 ///
114 /// ## Example
115 /// ```no_run
116 /// use async_std::prelude::*;
117 /// use async_attributes;
118 ///
119 /// #[async_attributes::main]
120 /// async fn main() -> cacache::Result<()> {
121 /// let sri = cacache::write("./my-cache", "key", b"hello world").await?;
122 /// let mut fd = cacache::Reader::open_hash("./my-cache", sri).await?;
123 /// let mut str = String::new();
124 /// fd.read_to_string(&mut str).await.expect("Failed to read to string");
125 /// // Remember to check that the data you got was correct!
126 /// fd.check()?;
127 /// Ok(())
128 /// }
129 /// ```
130 pub async fn open_hash<P>(cache: P, sri: Integrity) -> Result<Reader>
131 where
132 P: AsRef<Path>,
133 {
134 Ok(Reader {
135 reader: read::open_async(cache.as_ref(), sri).await?,
136 })
137 }
138}
139
140/// Reads the entire contents of a cache file into a bytes vector, looking the
141/// data up by key.
142///
143/// ## Example
144/// ```no_run
145/// use async_std::prelude::*;
146/// use async_attributes;
147///
148/// #[async_attributes::main]
149/// async fn main() -> cacache::Result<()> {
150/// let data: Vec<u8> = cacache::read("./my-cache", "my-key").await?;
151/// Ok(())
152/// }
153/// ```
154#[cfg(any(feature = "async-std", feature = "tokio"))]
155pub async fn read<P, K>(cache: P, key: K) -> Result<Vec<u8>>
156where
157 P: AsRef<Path>,
158 K: AsRef<str>,
159{
160 async fn inner(cache: &Path, key: &str) -> Result<Vec<u8>> {
161 if let Some(entry) = index::find_async(cache, key).await? {
162 read_hash(cache, &entry.integrity).await
163 } else {
164 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
165 }
166 }
167 inner(cache.as_ref(), key.as_ref()).await
168}
169
170/// Reads the entire contents of a cache file into a bytes vector, looking the
171/// data up by its content address.
172///
173/// ## Example
174/// ```no_run
175/// use async_std::prelude::*;
176/// use async_attributes;
177///
178/// #[async_attributes::main]
179/// async fn main() -> cacache::Result<()> {
180/// let sri = cacache::write("./my-cache", "my-key", b"hello").await?;
181/// let data: Vec<u8> = cacache::read_hash("./my-cache", &sri).await?;
182/// Ok(())
183/// }
184/// ```
185#[cfg(any(feature = "async-std", feature = "tokio"))]
186pub async fn read_hash<P>(cache: P, sri: &Integrity) -> Result<Vec<u8>>
187where
188 P: AsRef<Path>,
189{
190 read::read_async(cache.as_ref(), sri).await
191}
192
193/// Copies cache data to a specified location. Returns the number of bytes
194/// copied.
195///
196/// ## Example
197/// ```no_run
198/// use async_std::prelude::*;
199/// use async_attributes;
200///
201/// #[async_attributes::main]
202/// async fn main() -> cacache::Result<()> {
203/// cacache::copy("./my-cache", "my-key", "./data.txt").await?;
204/// Ok(())
205/// }
206/// ```
207#[cfg(any(feature = "async-std", feature = "tokio"))]
208pub async fn copy<P, K, Q>(cache: P, key: K, to: Q) -> Result<u64>
209where
210 P: AsRef<Path>,
211 K: AsRef<str>,
212 Q: AsRef<Path>,
213{
214 async fn inner(cache: &Path, key: &str, to: &Path) -> Result<u64> {
215 if let Some(entry) = index::find_async(cache, key).await? {
216 copy_hash(cache, &entry.integrity, to).await
217 } else {
218 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
219 }
220 }
221 inner(cache.as_ref(), key.as_ref(), to.as_ref()).await
222}
223
224/// Copies cache data to a specified location. Cache data will not be checked
225/// during copy.
226///
227/// ## Example
228/// ```no_run
229/// use async_std::prelude::*;
230/// use async_attributes;
231///
232/// #[async_attributes::main]
233/// async fn main() -> cacache::Result<()> {
234/// cacache::copy_unchecked("./my-cache", "my-key", "./data.txt").await?;
235/// Ok(())
236/// }
237/// ```
238#[cfg(any(feature = "async-std", feature = "tokio"))]
239pub async fn copy_unchecked<P, K, Q>(cache: P, key: K, to: Q) -> Result<u64>
240where
241 P: AsRef<Path>,
242 K: AsRef<str>,
243 Q: AsRef<Path>,
244{
245 async fn inner(cache: &Path, key: &str, to: &Path) -> Result<u64> {
246 if let Some(entry) = index::find_async(cache, key).await? {
247 copy_hash_unchecked(cache, &entry.integrity, to).await
248 } else {
249 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
250 }
251 }
252 inner(cache.as_ref(), key.as_ref(), to.as_ref()).await
253}
254
255/// Copies a cache data by hash to a specified location. Returns the number of
256/// bytes copied.
257///
258/// ## Example
259/// ```no_run
260/// use async_std::prelude::*;
261/// use async_attributes;
262///
263/// #[async_attributes::main]
264/// async fn main() -> cacache::Result<()> {
265/// let sri = cacache::write("./my-cache", "my-key", b"hello world").await?;
266/// cacache::copy_hash("./my-cache", &sri, "./data.txt").await?;
267/// Ok(())
268/// }
269/// ```
270#[cfg(any(feature = "async-std", feature = "tokio"))]
271pub async fn copy_hash<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<u64>
272where
273 P: AsRef<Path>,
274 Q: AsRef<Path>,
275{
276 read::copy_async(cache.as_ref(), sri, to.as_ref()).await
277}
278
279/// Copies a cache data by hash to a specified location. Copied data will not
280/// be checked against the given hash.
281///
282/// ## Example
283/// ```no_run
284/// use async_std::prelude::*;
285/// use async_attributes;
286///
287/// #[async_attributes::main]
288/// async fn main() -> cacache::Result<()> {
289/// let sri = cacache::write("./my-cache", "my-key", b"hello world").await?;
290/// cacache::copy_hash_unchecked("./my-cache", &sri, "./data.txt").await?;
291/// Ok(())
292/// }
293/// ```
294#[cfg(any(feature = "async-std", feature = "tokio"))]
295pub async fn copy_hash_unchecked<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<u64>
296where
297 P: AsRef<Path>,
298 Q: AsRef<Path>,
299{
300 read::copy_unchecked_async(cache.as_ref(), sri, to.as_ref()).await
301}
302
303/// Creates a reflink/clonefile from a cache entry to a destination path.
304///
305/// Fails if the destination is on a different filesystem or if the filesystem
306/// does not support reflinks.
307///
308/// Currently, reflinks are known to work on APFS (macOS), XFS, btrfs, and
309/// ReFS (Windows DevDrive)
310///
311/// ## Example
312/// ```no_run
313/// use async_std::prelude::*;
314/// use async_attributes;
315///
316/// #[async_attributes::main]
317/// async fn main() -> cacache::Result<()> {
318/// cacache::reflink("./my-cache", "my-key", "./data.txt").await?;
319/// Ok(())
320/// }
321/// ```
322#[cfg(any(feature = "async-std", feature = "tokio"))]
323pub async fn reflink<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
324where
325 P: AsRef<Path>,
326 K: AsRef<str>,
327 Q: AsRef<Path>,
328{
329 async fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
330 if let Some(entry) = index::find_async(cache, key).await? {
331 reflink_hash(cache, &entry.integrity, to).await
332 } else {
333 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
334 }
335 }
336 inner(cache.as_ref(), key.as_ref(), to.as_ref()).await
337}
338
339/// Reflinks/clonefiles cache data to a specified location. Cache data will
340/// not be checked during linking.
341///
342/// Fails if the destination is on a different filesystem or if the filesystem
343/// does not support reflinks.
344///
345/// Currently, reflinks are known to work on APFS (macOS), XFS, btrfs, and
346/// ReFS (Windows DevDrive)
347///
348/// ## Example
349/// ```no_run
350/// use async_std::prelude::*;
351/// use async_attributes;
352///
353/// #[async_attributes::main]
354/// async fn main() -> cacache::Result<()> {
355/// cacache::reflink_unchecked("./my-cache", "my-key", "./data.txt").await?;
356/// Ok(())
357/// }
358/// ```
359#[cfg(any(feature = "async-std", feature = "tokio"))]
360pub async fn reflink_unchecked<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
361where
362 P: AsRef<Path>,
363 K: AsRef<str>,
364 Q: AsRef<Path>,
365{
366 async fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
367 if let Some(entry) = index::find_async(cache, key).await? {
368 reflink_hash_unchecked_sync(cache, &entry.integrity, to)
369 } else {
370 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
371 }
372 }
373 inner(cache.as_ref(), key.as_ref(), to.as_ref()).await
374}
375
376/// Reflinks/clonefiles cache data by hash to a specified location.
377///
378/// Fails if the destination is on a different filesystem or if the filesystem
379/// does not support reflinks.
380///
381/// Currently, reflinks are known to work on APFS (macOS), XFS, btrfs, and
382/// ReFS (Windows DevDrive)
383///
384/// ## Example
385/// ```no_run
386/// use async_std::prelude::*;
387/// use async_attributes;
388///
389/// #[async_attributes::main]
390/// async fn main() -> cacache::Result<()> {
391/// let sri = cacache::write("./my-cache", "my-key", b"hello world").await?;
392/// cacache::reflink_hash("./my-cache", &sri, "./data.txt").await?;
393/// Ok(())
394/// }
395/// ```
396#[cfg(any(feature = "async-std", feature = "tokio"))]
397pub async fn reflink_hash<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<()>
398where
399 P: AsRef<Path>,
400 Q: AsRef<Path>,
401{
402 read::reflink_async(cache.as_ref(), sri, to.as_ref()).await
403}
404
405/// Hard links a cache entry by hash to a specified location.
406#[cfg(any(feature = "async-std", feature = "tokio"))]
407pub async fn hard_link_hash<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<()>
408where
409 P: AsRef<Path>,
410 Q: AsRef<Path>,
411{
412 read::hard_link_async(cache.as_ref(), sri, to.as_ref()).await
413}
414
415/// Hard links a cache entry by key to a specified location.
416#[cfg(any(feature = "async-std", feature = "tokio"))]
417pub async fn hard_link<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
418where
419 P: AsRef<Path>,
420 K: AsRef<str>,
421 Q: AsRef<Path>,
422{
423 async fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
424 if let Some(entry) = index::find(cache, key)? {
425 hard_link_hash(cache, &entry.integrity, to).await
426 } else {
427 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
428 }
429 }
430 inner(cache.as_ref(), key.as_ref(), to.as_ref()).await
431}
432
433/// Gets the metadata entry for a certain key.
434///
435/// Note that the existence of a metadata entry is not a guarantee that the
436/// underlying data exists, since they are stored and managed independently.
437/// To verify that the underlying associated data exists, use `exists()`.
438#[cfg(any(feature = "async-std", feature = "tokio"))]
439pub async fn metadata<P, K>(cache: P, key: K) -> Result<Option<Metadata>>
440where
441 P: AsRef<Path>,
442 K: AsRef<str>,
443{
444 index::find_async(cache.as_ref(), key.as_ref()).await
445}
446
447/// Returns true if the given hash exists in the cache.
448#[cfg(any(feature = "async-std", feature = "tokio"))]
449pub async fn exists<P: AsRef<Path>>(cache: P, sri: &Integrity) -> bool {
450 read::has_content_async(cache.as_ref(), sri).await.is_some()
451}
452
453// ---------------
454// Synchronous API
455// ---------------
456
457/// File handle for reading data synchronously.
458///
459/// Make sure to call `get.check()` when done reading
460/// to verify that the extracted data passes integrity
461/// verification.
462pub struct SyncReader {
463 reader: read::Reader,
464}
465
466impl std::io::Read for SyncReader {
467 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
468 self.reader.read(buf)
469 }
470}
471
472impl SyncReader {
473 /// Checks that data read from disk passes integrity checks. Returns the
474 /// algorithm that was used verified the data. Should be called only after
475 /// all data has been read from disk.
476 ///
477 /// ## Example
478 /// ```no_run
479 /// use std::io::Read;
480 ///
481 /// fn main() -> cacache::Result<()> {
482 /// let mut fd = cacache::SyncReader::open("./my-cache", "my-key")?;
483 /// let mut str = String::new();
484 /// fd.read_to_string(&mut str).expect("Failed to read to string");
485 /// // Remember to check that the data you got was correct!
486 /// fd.check()?;
487 /// Ok(())
488 /// }
489 /// ```
490 pub fn check(self) -> Result<Algorithm> {
491 self.reader.check()
492 }
493
494 /// Opens a new synchronous file handle into the cache, looking it up in the
495 /// index using `key`.
496 ///
497 /// ## Example
498 /// ```no_run
499 /// use std::io::Read;
500 ///
501 /// fn main() -> cacache::Result<()> {
502 /// let mut fd = cacache::SyncReader::open("./my-cache", "my-key")?;
503 /// let mut str = String::new();
504 /// fd.read_to_string(&mut str).expect("Failed to parse string");
505 /// // Remember to check that the data you got was correct!
506 /// fd.check()?;
507 /// Ok(())
508 /// }
509 /// ```
510 pub fn open<P, K>(cache: P, key: K) -> Result<SyncReader>
511 where
512 P: AsRef<Path>,
513 K: AsRef<str>,
514 {
515 fn inner(cache: &Path, key: &str) -> Result<SyncReader> {
516 if let Some(entry) = index::find(cache, key)? {
517 SyncReader::open_hash(cache, entry.integrity)
518 } else {
519 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
520 }
521 }
522 inner(cache.as_ref(), key.as_ref())
523 }
524
525 /// Opens a new synchronous file handle into the cache, based on its integrity address.
526 ///
527 /// ## Example
528 /// ```no_run
529 /// use std::io::Read;
530 ///
531 /// fn main() -> cacache::Result<()> {
532 /// let sri = cacache::write_sync("./my-cache", "key", b"hello world")?;
533 /// let mut fd = cacache::SyncReader::open_hash("./my-cache", sri)?;
534 /// let mut str = String::new();
535 /// fd.read_to_string(&mut str).expect("Failed to read to string");
536 /// // Remember to check that the data you got was correct!
537 /// fd.check()?;
538 /// Ok(())
539 /// }
540 /// ```
541 pub fn open_hash<P>(cache: P, sri: Integrity) -> Result<SyncReader>
542 where
543 P: AsRef<Path>,
544 {
545 Ok(SyncReader {
546 reader: read::open(cache.as_ref(), sri)?,
547 })
548 }
549}
550
551/// Reads the entire contents of a cache file synchronously into a bytes
552/// vector, looking the data up by key.
553///
554/// ## Example
555/// ```no_run
556/// use std::io::Read;
557///
558/// fn main() -> cacache::Result<()> {
559/// let data = cacache::read_sync("./my-cache", "my-key")?;
560/// Ok(())
561/// }
562/// ```
563pub fn read_sync<P, K>(cache: P, key: K) -> Result<Vec<u8>>
564where
565 P: AsRef<Path>,
566 K: AsRef<str>,
567{
568 fn inner(cache: &Path, key: &str) -> Result<Vec<u8>> {
569 if let Some(entry) = index::find(cache, key)? {
570 read_hash_sync(cache, &entry.integrity)
571 } else {
572 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
573 }
574 }
575 inner(cache.as_ref(), key.as_ref())
576}
577
578/// Reads the entire contents of a cache file synchronously into a bytes
579/// vector, looking the data up by its content address.
580///
581/// ## Example
582/// ```no_run
583/// use std::io::Read;
584///
585/// fn main() -> cacache::Result<()> {
586/// let sri = cacache::write_sync("./my-cache", "my-key", b"hello")?;
587/// let data = cacache::read_hash_sync("./my-cache", &sri)?;
588/// Ok(())
589/// }
590/// ```
591pub fn read_hash_sync<P>(cache: P, sri: &Integrity) -> Result<Vec<u8>>
592where
593 P: AsRef<Path>,
594{
595 read::read(cache.as_ref(), sri)
596}
597
598/// Copies a cache entry by key to a specified location. Returns the number of
599/// bytes copied.
600///
601/// On platforms that support it, this will create a copy-on-write "reflink"
602/// with a full-copy fallback.
603///
604/// ## Example
605/// ```no_run
606/// use std::io::Read;
607///
608/// fn main() -> cacache::Result<()> {
609/// cacache::copy_sync("./my-cache", "my-key", "./my-hello.txt")?;
610/// Ok(())
611/// }
612/// ```
613pub fn copy_sync<P, K, Q>(cache: P, key: K, to: Q) -> Result<u64>
614where
615 P: AsRef<Path>,
616 K: AsRef<str>,
617 Q: AsRef<Path>,
618{
619 fn inner(cache: &Path, key: &str, to: &Path) -> Result<u64> {
620 if let Some(entry) = index::find(cache, key)? {
621 copy_hash_sync(cache, &entry.integrity, to)
622 } else {
623 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
624 }
625 }
626 inner(cache.as_ref(), key.as_ref(), to.as_ref())
627}
628
629/// Copies a cache entry by key to a specified location. Does not verify cache
630/// contents while copying.
631///
632/// On platforms that support it, this will create a copy-on-write "reflink"
633/// with a full-copy fallback.
634///
635/// ## Example
636/// ```no_run
637/// use std::io::Read;
638///
639/// fn main() -> cacache::Result<()> {
640/// cacache::copy_unchecked_sync("./my-cache", "my-key", "./my-hello.txt")?;
641/// Ok(())
642/// }
643/// ```
644pub fn copy_unchecked_sync<P, K, Q>(cache: P, key: K, to: Q) -> Result<u64>
645where
646 P: AsRef<Path>,
647 K: AsRef<str>,
648 Q: AsRef<Path>,
649{
650 fn inner(cache: &Path, key: &str, to: &Path) -> Result<u64> {
651 if let Some(entry) = index::find(cache, key)? {
652 copy_hash_unchecked_sync(cache, &entry.integrity, to)
653 } else {
654 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
655 }
656 }
657 inner(cache.as_ref(), key.as_ref(), to.as_ref())
658}
659
660/// Copies a cache entry by integrity address to a specified location. Returns
661/// the number of bytes copied.
662///
663/// On platforms that support it, this will create a copy-on-write "reflink"
664/// with a full-copy fallback.
665///
666/// ## Example
667/// ```no_run
668/// use std::io::Read;
669///
670/// fn main() -> cacache::Result<()> {
671/// let sri = cacache::write_sync("./my-cache", "my-key", b"hello")?;
672/// cacache::copy_hash_sync("./my-cache", &sri, "./my-hello.txt")?;
673/// Ok(())
674/// }
675/// ```
676pub fn copy_hash_sync<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<u64>
677where
678 P: AsRef<Path>,
679 Q: AsRef<Path>,
680{
681 read::copy(cache.as_ref(), sri, to.as_ref())
682}
683
684/// Copies a cache entry by integrity address to a specified location. Does
685/// not verify cache contents while copying.
686///
687/// On platforms that support it, this will create a copy-on-write "reflink"
688/// with a full-copy fallback.
689///
690/// ## Example
691/// ```no_run
692/// use std::io::Read;
693///
694/// fn main() -> cacache::Result<()> {
695/// let sri = cacache::write_sync("./my-cache", "my-key", b"hello")?;
696/// cacache::copy_hash_unchecked_sync("./my-cache", &sri, "./my-hello.txt")?;
697/// Ok(())
698/// }
699/// ```
700pub fn copy_hash_unchecked_sync<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<u64>
701where
702 P: AsRef<Path>,
703 Q: AsRef<Path>,
704{
705 read::copy_unchecked(cache.as_ref(), sri, to.as_ref())
706}
707
708/// Creates a reflink/clonefile from a cache entry to a destination path.
709///
710/// Fails if the destination is on a different filesystem or if the filesystem
711/// does not support reflinks.
712///
713/// Currently, reflinks are known to work on APFS (macOS), XFS, btrfs, and
714/// ReFS (Windows DevDrive)
715///
716/// ## Example
717/// ```no_run
718/// use async_std::prelude::*;
719/// use async_attributes;
720///
721/// #[async_attributes::main]
722/// async fn main() -> cacache::Result<()> {
723/// cacache::reflink_sync("./my-cache", "my-key", "./data.txt")?;
724/// Ok(())
725/// }
726/// ```
727pub fn reflink_sync<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
728where
729 P: AsRef<Path>,
730 K: AsRef<str>,
731 Q: AsRef<Path>,
732{
733 fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
734 if let Some(entry) = index::find(cache, key)? {
735 reflink_hash_sync(cache, &entry.integrity, to)
736 } else {
737 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
738 }
739 }
740 inner(cache.as_ref(), key.as_ref(), to.as_ref())
741}
742
743/// Reflinks/clonefiles cache data by hash to a specified location.
744///
745/// Fails if the destination is on a different filesystem or if the filesystem
746/// does not support reflinks.
747///
748/// Currently, reflinks are known to work on APFS (macOS), XFS, btrfs, and
749/// ReFS (Windows DevDrive)
750///
751/// ## Example
752/// ```no_run
753/// use async_std::prelude::*;
754/// use async_attributes;
755///
756/// #[async_attributes::main]
757/// async fn main() -> cacache::Result<()> {
758/// let sri = cacache::write_sync("./my-cache", "my-key", b"hello world")?;
759/// cacache::reflink_hash_sync("./my-cache", &sri, "./data.txt")?;
760/// Ok(())
761/// }
762/// ```
763pub fn reflink_hash_sync<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<()>
764where
765 P: AsRef<Path>,
766 Q: AsRef<Path>,
767{
768 read::reflink(cache.as_ref(), sri, to.as_ref())
769}
770
771/// Reflinks/clonefiles cache data by hash to a specified location. Cache data
772/// will not be checked during linking.
773///
774/// Fails if the destination is on a different filesystem or if the filesystem
775/// does not support reflinks.
776///
777/// Currently, reflinks are known to work on APFS (macOS), XFS, btrfs, and
778/// ReFS (Windows DevDrive)
779///
780/// ## Example
781/// ```no_run
782/// use async_std::prelude::*;
783/// use async_attributes;
784///
785/// #[async_attributes::main]
786/// async fn main() -> cacache::Result<()> {
787/// let sri = cacache::write_sync("./my-cache", "my-key", b"hello world")?;
788/// cacache::reflink_hash_unchecked_sync("./my-cache", &sri, "./data.txt")?;
789/// Ok(())
790/// }
791/// ```
792pub fn reflink_hash_unchecked_sync<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<()>
793where
794 P: AsRef<Path>,
795 Q: AsRef<Path>,
796{
797 read::reflink_unchecked(cache.as_ref(), sri, to.as_ref())
798}
799
800/// Reflinks/clonefiles cache data to a specified location. Cache data will
801/// not be checked during linking.
802///
803/// Fails if the destination is on a different filesystem or if the filesystem
804/// does not support reflinks.
805///
806/// Currently, reflinks are known to work on APFS (macOS), XFS, btrfs, and
807/// ReFS (Windows DevDrive)
808///
809/// ## Example
810/// ```no_run
811/// use async_std::prelude::*;
812/// use async_attributes;
813///
814/// #[async_attributes::main]
815/// async fn main() -> cacache::Result<()> {
816/// cacache::reflink_unchecked_sync("./my-cache", "my-key", "./data.txt")?;
817/// Ok(())
818/// }
819/// ```
820pub fn reflink_unchecked_sync<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
821where
822 P: AsRef<Path>,
823 K: AsRef<str>,
824 Q: AsRef<Path>,
825{
826 fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
827 if let Some(entry) = index::find(cache, key)? {
828 reflink_hash_unchecked_sync(cache, &entry.integrity, to)
829 } else {
830 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
831 }
832 }
833 inner(cache.as_ref(), key.as_ref(), to.as_ref())
834}
835
836/// Hard links a cache entry by key to a specified location. The cache entry
837/// contents will not be checked, and all the usual caveats of hard links
838/// apply: The potentially-shared cache might be corrupted if the hard link is
839/// modified.
840pub fn hard_link_unchecked_sync<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
841where
842 P: AsRef<Path>,
843 K: AsRef<str>,
844 Q: AsRef<Path>,
845{
846 fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
847 if let Some(entry) = index::find(cache, key)? {
848 hard_link_hash_unchecked_sync(cache, &entry.integrity, to)
849 } else {
850 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
851 }
852 }
853 inner(cache.as_ref(), key.as_ref(), to.as_ref())
854}
855
856/// Hard links a cache entry by key to a specified location.
857pub fn hard_link_sync<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
858where
859 P: AsRef<Path>,
860 K: AsRef<str>,
861 Q: AsRef<Path>,
862{
863 fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
864 if let Some(entry) = index::find(cache, key)? {
865 read::hard_link(cache, &entry.integrity, to)
866 } else {
867 Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
868 }
869 }
870 inner(cache.as_ref(), key.as_ref(), to.as_ref())
871}
872
873/// Hard links a cache entry by integrity address to a specified location,
874/// verifying contents as hard links are created.
875pub fn hard_link_hash_sync<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<()>
876where
877 P: AsRef<Path>,
878 Q: AsRef<Path>,
879{
880 read::hard_link(cache.as_ref(), sri, to.as_ref())
881}
882
883/// Hard links a cache entry by integrity address to a specified location. The
884/// cache entry contents will not be checked, and all the usual caveats of
885/// hard links apply: The potentially-shared cache might be corrupted if the
886/// hard link is modified.
887pub fn hard_link_hash_unchecked_sync<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<()>
888where
889 P: AsRef<Path>,
890 Q: AsRef<Path>,
891{
892 read::hard_link_unchecked(cache.as_ref(), sri, to.as_ref())
893}
894
895/// Gets metadata for a certain key.
896///
897/// Note that the existence of a metadata entry is not a guarantee that the
898/// underlying data exists, since they are stored and managed independently.
899/// To verify that the underlying associated data exists, use `exists_sync()`.
900pub fn metadata_sync<P, K>(cache: P, key: K) -> Result<Option<Metadata>>
901where
902 P: AsRef<Path>,
903 K: AsRef<str>,
904{
905 index::find(cache.as_ref(), key.as_ref())
906}
907
908/// Returns true if the given hash exists in the cache.
909pub fn exists_sync<P: AsRef<Path>>(cache: P, sri: &Integrity) -> bool {
910 read::has_content(cache.as_ref(), sri).is_some()
911}
912
913#[cfg(test)]
914mod tests {
915 #[cfg(any(feature = "async-std", feature = "tokio"))]
916 use crate::async_lib::AsyncReadExt;
917 use std::fs;
918
919 #[cfg(feature = "async-std")]
920 use async_attributes::test as async_test;
921 #[cfg(feature = "tokio")]
922 use tokio::test as async_test;
923
924 #[cfg(any(feature = "async-std", feature = "tokio"))]
925 #[async_test]
926 async fn test_open() {
927 let tmp = tempfile::tempdir().unwrap();
928 let dir = tmp.path().to_owned();
929 crate::write(&dir, "my-key", b"hello world").await.unwrap();
930
931 let mut handle = crate::Reader::open(&dir, "my-key").await.unwrap();
932 let mut str = String::new();
933 handle.read_to_string(&mut str).await.unwrap();
934 handle.check().unwrap();
935 assert_eq!(str, String::from("hello world"));
936 }
937
938 #[cfg(any(feature = "async-std", feature = "tokio"))]
939 #[async_test]
940 async fn test_open_hash() {
941 let tmp = tempfile::tempdir().unwrap();
942 let dir = tmp.path().to_owned();
943 let sri = crate::write(&dir, "my-key", b"hello world").await.unwrap();
944
945 let mut handle = crate::Reader::open_hash(&dir, sri).await.unwrap();
946 let mut str = String::new();
947 handle.read_to_string(&mut str).await.unwrap();
948 handle.check().unwrap();
949 assert_eq!(str, String::from("hello world"));
950 }
951
952 #[test]
953 fn test_open_sync() {
954 use std::io::prelude::*;
955 let tmp = tempfile::tempdir().unwrap();
956 let dir = tmp.path().to_owned();
957 crate::write_sync(&dir, "my-key", b"hello world").unwrap();
958
959 let mut handle = crate::SyncReader::open(&dir, "my-key").unwrap();
960 let mut str = String::new();
961 handle.read_to_string(&mut str).unwrap();
962 handle.check().unwrap();
963 assert_eq!(str, String::from("hello world"));
964 }
965
966 #[test]
967 fn test_open_hash_sync() {
968 use std::io::prelude::*;
969 let tmp = tempfile::tempdir().unwrap();
970 let dir = tmp.path().to_owned();
971 let sri = crate::write_sync(&dir, "my-key", b"hello world").unwrap();
972
973 let mut handle = crate::SyncReader::open_hash(&dir, sri).unwrap();
974 let mut str = String::new();
975 handle.read_to_string(&mut str).unwrap();
976 handle.check().unwrap();
977 assert_eq!(str, String::from("hello world"));
978 }
979
980 #[cfg(any(feature = "async-std", feature = "tokio"))]
981 #[async_test]
982 async fn test_read() {
983 let tmp = tempfile::tempdir().unwrap();
984 let dir = tmp.path().to_owned();
985 crate::write(&dir, "my-key", b"hello world").await.unwrap();
986
987 let data = crate::read(&dir, "my-key").await.unwrap();
988 assert_eq!(data, b"hello world");
989 }
990
991 #[cfg(any(feature = "async-std", feature = "tokio"))]
992 #[async_test]
993 async fn test_read_hash() {
994 let tmp = tempfile::tempdir().unwrap();
995 let dir = tmp.path().to_owned();
996 let sri = crate::write(&dir, "my-key", b"hello world").await.unwrap();
997
998 let data = crate::read_hash(&dir, &sri).await.unwrap();
999 assert_eq!(data, b"hello world");
1000 }
1001
1002 #[test]
1003 fn test_read_sync() {
1004 let tmp = tempfile::tempdir().unwrap();
1005 let dir = tmp.path().to_owned();
1006 crate::write_sync(&dir, "my-key", b"hello world").unwrap();
1007
1008 let data = crate::read_sync(&dir, "my-key").unwrap();
1009 assert_eq!(data, b"hello world");
1010 }
1011
1012 #[test]
1013 fn test_read_hash_sync() {
1014 let tmp = tempfile::tempdir().unwrap();
1015 let dir = tmp.path().to_owned();
1016 let sri = crate::write_sync(&dir, "my-key", b"hello world").unwrap();
1017
1018 let data = crate::read_hash_sync(&dir, &sri).unwrap();
1019 assert_eq!(data, b"hello world");
1020 }
1021
1022 #[cfg(any(feature = "async-std", feature = "tokio"))]
1023 #[async_test]
1024 async fn test_copy() {
1025 let tmp = tempfile::tempdir().unwrap();
1026 let dir = tmp.path();
1027 let dest = dir.join("data");
1028 crate::write(&dir, "my-key", b"hello world").await.unwrap();
1029
1030 crate::copy(&dir, "my-key", &dest).await.unwrap();
1031 let data = crate::async_lib::read(&dest).await.unwrap();
1032 assert_eq!(data, b"hello world");
1033 }
1034
1035 #[cfg(any(feature = "async-std", feature = "tokio"))]
1036 #[async_test]
1037 async fn test_copy_hash() {
1038 let tmp = tempfile::tempdir().unwrap();
1039 let dir = tmp.path();
1040 let dest = dir.join("data");
1041 let sri = crate::write(&dir, "my-key", b"hello world").await.unwrap();
1042
1043 crate::copy_hash(&dir, &sri, &dest).await.unwrap();
1044 let data = crate::async_lib::read(&dest).await.unwrap();
1045 assert_eq!(data, b"hello world");
1046 }
1047
1048 #[test]
1049 fn test_copy_sync() {
1050 let tmp = tempfile::tempdir().unwrap();
1051 let dir = tmp.path();
1052 let dest = dir.join("data");
1053 crate::write_sync(dir, "my-key", b"hello world").unwrap();
1054
1055 crate::copy_sync(dir, "my-key", &dest).unwrap();
1056 let data = fs::read(&dest).unwrap();
1057 assert_eq!(data, b"hello world");
1058 }
1059
1060 #[test]
1061 fn test_copy_hash_sync() {
1062 let tmp = tempfile::tempdir().unwrap();
1063 let dir = tmp.path();
1064 let dest = dir.join("data");
1065 let sri = crate::write_sync(dir, "my-key", b"hello world").unwrap();
1066
1067 crate::copy_hash_sync(dir, &sri, &dest).unwrap();
1068 let data = fs::read(&dest).unwrap();
1069 assert_eq!(data, b"hello world");
1070 }
1071}