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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//! cacache is a Rust library for managing local key and content address
//! caches. It's really fast, really good at concurrency, and it will never
//! give you corrupted data, even if cache files get corrupted or manipulated.
//!
//! ## API Layout
//!
//! The cacache API is organized roughly similar to `std::fs`; most of the
//! toplevel functionality is available as free functions directly in the
//! `cacache` module, with some additional functionality available through
//! returned objects, as well as `WriteOpts`, which is analogous to
//! `OpenOpts`, but is only able to write.
//!
//! One major difference is that the default APIs are all async functions, as
//! opposed to `std::fs`, where they're all synchronous. Synchronous APIs in
//! cacache are accessible through the `_sync` suffix.
//!
//! ### Suffixes
//!
//! You may notice various suffixes associated with otherwise familiar
//! functions:
//!
//! * `_sync` - Most cacache APIs are asynchronous by default. Anything using
//!   the `_sync` suffix behaves just like its unprefixed counterpart, except
//!   the operation is synchronous.
//! * `_hash` - Since cacache is a content-addressable cache, the `_hash`
//!   suffix means you're interacting directly with content data, skipping the
//!   index and its metadata. These functions use an `Integrity` to look up
//!   data, instead of a string key.
//!
//! ## Examples
//!
//! Un-suffixed APIs are all async, using
//! [`async-std`](https://crates.io/crates/async-std). They let you put data
//! in and get it back out -- asynchronously!
//!
//! ```no_run
//! use async_attributes;
//!
//! #[async_attributes::main]
//! async fn main() -> cacache::Result<()> {
//!   // Data goes in...
//!   cacache::write("./my-cache", "key", b"hello").await?;
//!
//!   // ...data comes out!
//!   let data = cacache::read("./my-cache", "key").await?;
//!   assert_eq!(data, b"hello");
//!
//!   Ok(())
//! }
//! ```
//!
//! ### Lookup by hash
//!
//! What makes `cacache` content addressable, though, is its ability to fetch
//! data by its "content address", which in our case is a ["subresource
//! integrity" hash](https://crates.io/crates/ssri), which `cacache::put`
//! conveniently returns for us. Fetching data by hash is significantly faster
//! than doing key lookups:
//!
//! ```no_run
//! use async_attributes;
//!
//! #[async_attributes::main]
//! async fn main() -> cacache::Result<()> {
//!   // Data goes in...
//!   let sri = cacache::write("./my-cache", "key", b"hello").await?;
//!
//!   // ...data gets looked up by `sri` ("Subresource Integrity").
//!   let data = cacache::read_hash("./my-cache", &sri).await?;
//!   assert_eq!(data, b"hello");
//!
//!   Ok(())
//! }
//! ```
//!
//! ### Large file support
//!
//! `cacache` supports large file reads, in both async and sync mode, through
//! an API reminiscent of `std::fs::OpenOptions`:
//!
//! ```no_run
//! use async_attributes;
//! use async_std::prelude::*;
//!
//! #[async_attributes::main]
//! async fn main() -> cacache::Result<()> {
//!   let mut fd = cacache::Writer::create("./my-cache", "key").await?;
//!   for _ in 0..10 {
//!     fd.write_all(b"very large data").await.expect("Failed to write to cache");
//!   }
//!   // Data is only committed to the cache after you do `fd.commit()`!
//!   let sri = fd.commit().await?;
//!   println!("integrity: {}", &sri);
//!
//!   let mut fd = cacache::Reader::open("./my-cache", "key").await?;
//!   let mut buf = String::new();
//!   fd.read_to_string(&mut buf).await.expect("Failed to read to string");
//!
//!   // Make sure to call `.check()` when you're done! It makes sure that what
//!   // you just read is actually valid. `cacache` always verifies the data
//!   // you get out is what it's supposed to be. The check is very cheap!
//!   fd.check()?;
//!
//!   Ok(())
//! }
//! ```
//!
//! ### Sync API
//!
//! There are also sync APIs available if you don't want to use async/await.
//! The synchronous APIs are generally faster for linear operations -- that
//! is, doing one thing after another, as opposed to doing many things at
//! once. If you're only reading and writing one thing at a time across your
//! application, you probably want to use these instead.
//!
//! If you wish to _only_ use sync APIs and not pull in an async runtime, you
//! can disable default features:
//!
//! ```toml
//! # Cargo.toml
//! [dependencies]
//! cacache = { version = "X.Y.Z", default-features = false, features = ["mmap"] }
//! ```
//!
//! ```no_run
//! fn main() -> cacache::Result<()> {
//!   cacache::write_sync("./my-cache", "key", b"my-data").unwrap();
//!   let data = cacache::read_sync("./my-cache", "key").unwrap();
//!   assert_eq!(data, b"my-data");
//!   Ok(())
//! }
//! ```
//!
//! ### Linking to existing files
//!
//! The `link_to` feature enables an additional set of APIs for adding
//! existing files into the cache via symlinks, without having to duplicate
//! their data. Once the cache links to them, these files can be accessed by
//! key just like other cached data, with the same integrity checking.
//!
//! The `link_to` methods are available in both async and sync variants, using
//! the same suffixes as the other APIs.
//!
//! ```no_run
//! #[async_attributes::main]
//! async fn main() -> cacache::Result<()> {
//!   #[cfg(feature = "link_to")]
//!   cacache::link_to("./my-cache", "key", "/path/to/my-other-file.txt").await?;
//!   let data = cacache::read("./my-cache", "key").await?;
//!   assert_eq!(data, b"my-data");
//!   Ok(())
//! }
//! ```
#![warn(missing_docs)]

#[cfg(all(feature = "async-std", feature = "tokio-runtime"))]
compile_error!("Only either feature \"async-std\" or \"tokio-runtime\" must be enabled for this crate, not both.");

pub use serde_json::Value;
pub use ssri::{Algorithm, Integrity};

#[cfg(any(feature = "async-std", feature = "tokio"))]
mod async_lib;

mod content;
mod errors;
pub mod index;

mod get;
#[cfg(feature = "link_to")]
mod linkto;
mod ls;
mod put;
mod rm;

pub use errors::{Error, Result};
pub use index::{Metadata, RemoveOpts};

pub use get::*;
#[cfg(feature = "link_to")]
pub use linkto::*;
pub use ls::*;
pub use put::*;
pub use rm::*;