[go: up one dir, main page]

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}