feat(errors): remove anyhow and use custom error types (#24)

Co-authored-by: Florian Albertz <git@albertz.io>

BREAKING CHANGE: This changes the exported error type(s) for cacache, making it way easier to do error handling when something goes kaput.
This commit is contained in:
Florian Albertz 2020-04-30 04:18:21 +02:00 committed by Kat Marchán
parent 88a76189fc
commit bb815f5f22
11 changed files with 167 additions and 361 deletions

View File

@ -29,7 +29,6 @@ serde_derive = "1.0.102"
walkdir = "2.2.9"
either = "1.5.3"
async-std = { version = "1.0.1", features = ["unstable"] }
anyhow = "1.0.19"
thiserror = "1.0.5"
futures = "0.3.1"

View File

@ -3,12 +3,12 @@ use std::path::Path;
use std::pin::Pin;
use std::task::{Context, Poll};
use anyhow::Result;
use async_std;
use futures::prelude::*;
use ssri::{Algorithm, Integrity, IntegrityChecker};
use crate::content::path;
use crate::errors::{Internal, Result};
pub struct Reader {
fd: File,
@ -55,7 +55,7 @@ impl AsyncReader {
pub fn open(cache: &Path, sri: Integrity) -> Result<Reader> {
let cpath = path::content_path(&cache, &sri);
Ok(Reader {
fd: File::open(cpath)?,
fd: File::open(cpath).to_internal()?,
checker: IntegrityChecker::new(sri),
})
}
@ -63,37 +63,37 @@ pub fn open(cache: &Path, sri: Integrity) -> Result<Reader> {
pub async fn open_async(cache: &Path, sri: Integrity) -> Result<AsyncReader> {
let cpath = path::content_path(&cache, &sri);
Ok(AsyncReader {
fd: async_std::fs::File::open(cpath).await?,
fd: async_std::fs::File::open(cpath).await.to_internal()?,
checker: IntegrityChecker::new(sri),
})
}
pub fn read(cache: &Path, sri: &Integrity) -> Result<Vec<u8>> {
let cpath = path::content_path(&cache, &sri);
let ret = fs::read(&cpath)?;
let ret = fs::read(&cpath).to_internal()?;
sri.check(&ret)?;
Ok(ret)
}
pub async fn read_async<'a>(cache: &'a Path, sri: &'a Integrity) -> Result<Vec<u8>> {
let cpath = path::content_path(&cache, &sri);
let ret = async_std::fs::read(&cpath).await?;
let ret = async_std::fs::read(&cpath).await.to_internal()?;
sri.check(&ret)?;
Ok(ret)
}
pub fn copy(cache: &Path, sri: &Integrity, to: &Path) -> Result<u64> {
let cpath = path::content_path(&cache, &sri);
let ret = fs::copy(&cpath, to)?;
let data = fs::read(cpath)?;
let ret = fs::copy(&cpath, to).to_internal()?;
let data = fs::read(cpath).to_internal()?;
sri.check(data)?;
Ok(ret)
}
pub async fn copy_async<'a>(cache: &'a Path, sri: &'a Integrity, to: &'a Path) -> Result<u64> {
let cpath = path::content_path(&cache, &sri);
let ret = async_std::fs::copy(&cpath, to).await?;
let data = async_std::fs::read(cpath).await?;
let ret = async_std::fs::copy(&cpath, to).await.to_internal()?;
let data = async_std::fs::read(cpath).await.to_internal()?;
sri.check(data)?;
Ok(ret)
}

View File

@ -1,18 +1,20 @@
use std::fs;
use std::path::Path;
use anyhow::Result;
use async_std::fs as afs;
use ssri::Integrity;
use crate::content::path;
use crate::errors::{Internal, Result};
pub fn rm(cache: &Path, sri: &Integrity) -> Result<()> {
fs::remove_file(path::content_path(&cache, &sri))?;
fs::remove_file(path::content_path(&cache, &sri)).to_internal()?;
Ok(())
}
pub async fn rm_async(cache: &Path, sri: &Integrity) -> Result<()> {
afs::remove_file(path::content_path(&cache, &sri)).await?;
afs::remove_file(path::content_path(&cache, &sri))
.await
.to_internal()?;
Ok(())
}

View File

@ -4,7 +4,6 @@ use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::sync::Mutex;
use anyhow::Result;
use async_std::fs as afs;
use async_std::future::Future;
use async_std::task::{self, Context, JoinHandle, Poll};
@ -14,6 +13,7 @@ use ssri::{Algorithm, Integrity, IntegrityOpts};
use tempfile::NamedTempFile;
use crate::content::path;
use crate::errors::{Internal, Result};
pub struct Writer {
cache: PathBuf,
@ -26,11 +26,14 @@ impl Writer {
let cache_path = cache.to_path_buf();
let mut tmp_path = cache_path.clone();
tmp_path.push("tmp");
DirBuilder::new().recursive(true).create(&tmp_path)?;
DirBuilder::new()
.recursive(true)
.create(&tmp_path)
.to_internal()?;
Ok(Writer {
cache: cache_path,
builder: IntegrityOpts::new().algorithm(algo),
tmpfile: NamedTempFile::new_in(tmp_path)?,
tmpfile: NamedTempFile::new_in(tmp_path).to_internal()?,
})
}
@ -40,8 +43,9 @@ impl Writer {
DirBuilder::new()
.recursive(true)
// Safe unwrap. cpath always has multiple segments
.create(cpath.parent().unwrap())?;
self.tmpfile.persist(cpath)?;
.create(cpath.parent().unwrap())
.to_internal()?;
self.tmpfile.persist(cpath).to_internal()?;
Ok(sri)
}
}
@ -87,11 +91,14 @@ impl AsyncWriter {
afs::DirBuilder::new()
.recursive(true)
.create(&tmp_path)
.await?;
.await
.to_internal()?;
Ok(AsyncWriter(Mutex::new(State::Idle(Some(Inner {
cache: cache_path,
builder: IntegrityOpts::new().algorithm(algo),
tmpfile: task::spawn_blocking(|| NamedTempFile::new_in(tmp_path)).await?,
tmpfile: task::spawn_blocking(|| NamedTempFile::new_in(tmp_path))
.await
.to_internal()?,
buf: vec![],
last_op: None,
})))))
@ -101,7 +108,7 @@ impl AsyncWriter {
// NOTE: How do I even get access to `inner` safely???
// let inner = ???;
// Blocking, but should be a very fast op.
futures::future::poll_fn(|cx| {
Ok(futures::future::poll_fn(|cx| {
let state = &mut *self.0.lock().unwrap();
loop {
@ -121,12 +128,18 @@ impl AsyncWriter {
// Safe unwrap. cpath always has multiple segments
.create(cpath.parent().unwrap())
.await
.map_err(anyhow::Error::new);
.with_context(|| {
format!(
"building directory {} failed",
cpath.parent().unwrap().display()
)
});
if res.is_err() {
let _ = s.send(res.map(|_| sri));
} else {
let res = tmpfile.persist(cpath);
let res = res.map_err(anyhow::Error::new);
let res = tmpfile.persist(cpath).with_context(|| {
String::from("persisting tempfile failed")
});
let _ = s.send(res.map(|_| sri));
}
State::Idle(None)
@ -141,8 +154,10 @@ impl AsyncWriter {
}
})
.map(|opt| opt.ok_or_else(|| io_error("file closed")))
.await?
.await?
.await
.to_internal()?
.await
.to_internal()??)
}
}

View File

@ -1,8 +1,35 @@
use std::path::PathBuf;
use ssri::Integrity;
use thiserror::Error;
#[derive(Error, Debug)]
#[error("{source}\n\n {}", context.join("\n "))]
pub struct InternalError {
source: Box<dyn std::error::Error + Send + Sync>,
context: Vec<String>,
}
pub trait Internal<T> {
fn to_internal(self) -> InternalResult<T>;
fn with_context<F: FnOnce() -> String>(self, f: F) -> InternalResult<T>;
}
impl<T, E: 'static + std::error::Error + Send + Sync> Internal<T> for std::result::Result<T, E> {
fn to_internal(self) -> InternalResult<T> {
self.map_err(|e| InternalError {
source: Box::new(e),
context: Vec::new(),
})
}
fn with_context<F: FnOnce() -> String>(self, f: F) -> InternalResult<T> {
self.map_err(|e| InternalError {
source: Box::new(e),
context: vec![f()],
})
}
}
/// Error type returned by all API calls.
#[derive(Error, Debug)]
pub enum Error {
@ -10,10 +37,29 @@ pub enum Error {
/// lookup.
#[error("Entry not found for key {1:?} in cache {0:?}")]
EntryNotFound(PathBuf, String),
/// Returned when an integrity check has failed.
#[error("Integrity check failed.\n\tWanted: {0}\n\tActual: {1}")]
IntegrityError(Integrity, Integrity),
/// Returned when a size check has failed.
#[error("Size check failed.\n\tWanted: {0}\n\tActual: {1}")]
SizeError(usize, usize),
/// Returned when an integrity check has failed.
#[error(transparent)]
IntegrityError {
#[from]
/// The underlying error
source: ssri::Error,
},
/// Returned if an internal (e.g. io) operation has failed.
#[error(transparent)]
InternalError {
#[from]
/// The underlying error
source: InternalError,
},
}
/// The result type returned by calls to this library
pub type Result<T> = std::result::Result<T, Error>;
pub type InternalResult<T> = std::result::Result<T, InternalError>;

View File

@ -5,11 +5,10 @@ use std::task::{Context as TaskContext, Poll};
use futures::prelude::*;
use anyhow::{Context, Result};
use ssri::{Algorithm, Integrity};
use crate::content::read;
use crate::errors::Error;
use crate::errors::{Error, Result};
use crate::index::{self, Metadata};
// ---------
@ -47,22 +46,19 @@ impl Reader {
/// ```no_run
/// use async_std::prelude::*;
/// use async_attributes;
/// use anyhow::Result;
///
/// #[async_attributes::main]
/// async fn main() -> Result<()> {
/// async fn main() -> cacache::Result<()> {
/// let mut fd = cacache::Reader::open("./my-cache", "my-key").await?;
/// let mut str = String::new();
/// fd.read_to_string(&mut str).await?;
/// fd.read_to_string(&mut str).await.expect("Failed to read to string");
/// // Remember to check that the data you got was correct!
/// fd.check()?;
/// Ok(())
/// }
/// ```
pub fn check(self) -> Result<Algorithm> {
self.reader
.check()
.context("Cache read data verification check failed.")
self.reader.check()
}
/// Opens a new file handle into the cache, looking it up in the index using
/// `key`.
@ -71,13 +67,12 @@ impl Reader {
/// ```no_run
/// use async_std::prelude::*;
/// use async_attributes;
/// use anyhow::Result;
///
/// #[async_attributes::main]
/// async fn main() -> Result<()> {
/// async fn main() -> cacache::Result<()> {
/// let mut fd = cacache::Reader::open("./my-cache", "my-key").await?;
/// let mut str = String::new();
/// fd.read_to_string(&mut str).await?;
/// fd.read_to_string(&mut str).await.expect("Failed to read to string");
/// // Remember to check that the data you got was correct!
/// fd.check()?;
/// Ok(())
@ -104,14 +99,13 @@ impl Reader {
/// ```no_run
/// use async_std::prelude::*;
/// use async_attributes;
/// use anyhow::Result;
///
/// #[async_attributes::main]
/// async fn main() -> Result<()> {
/// async fn main() -> cacache::Result<()> {
/// let sri = cacache::write("./my-cache", "key", b"hello world").await?;
/// let mut fd = cacache::Reader::open_hash("./my-cache", sri).await?;
/// let mut str = String::new();
/// fd.read_to_string(&mut str).await?;
/// fd.read_to_string(&mut str).await.expect("Failed to read to string");
/// // Remember to check that the data you got was correct!
/// fd.check()?;
/// Ok(())
@ -134,10 +128,9 @@ impl Reader {
/// ```no_run
/// use async_std::prelude::*;
/// use async_attributes;
/// use anyhow::Result;
///
/// #[async_attributes::main]
/// async fn main() -> Result<()> {
/// async fn main() -> cacache::Result<()> {
/// let data: Vec<u8> = cacache::read("./my-cache", "my-key").await?;
/// Ok(())
/// }
@ -157,36 +150,6 @@ where
}
}
/// Reads the entire contents of a cache file into a string, looking the
/// data up by key.
///
/// ## Example
/// ```no_run
/// use async_std::prelude::*;
/// use async_attributes;
/// use anyhow::Result;
///
/// #[async_attributes::main]
/// async fn main() -> Result<()> {
/// let str: String = cacache::read_to_string("./my-cache", "my-key").await?;
/// Ok(())
/// }
/// ```
pub async fn read_to_string<P, K>(cache: P, key: K) -> Result<String>
where
P: AsRef<Path>,
K: AsRef<str>,
{
if let Some(entry) = index::find_async(cache.as_ref(), key.as_ref()).await? {
read_hash_to_string(cache, &entry.integrity).await
} else {
Err(Error::EntryNotFound(
cache.as_ref().to_path_buf(),
key.as_ref().into(),
))?
}
}
/// Reads the entire contents of a cache file into a bytes vector, looking the
/// data up by its content address.
///
@ -194,10 +157,9 @@ where
/// ```no_run
/// use async_std::prelude::*;
/// use async_attributes;
/// use anyhow::Result;
///
/// #[async_attributes::main]
/// async fn main() -> Result<()> {
/// async fn main() -> cacache::Result<()> {
/// let sri = cacache::write("./my-cache", "my-key", b"hello").await?;
/// let data: Vec<u8> = cacache::read_hash("./my-cache", &sri).await?;
/// Ok(())
@ -210,31 +172,6 @@ where
Ok(read::read_async(cache.as_ref(), sri).await?)
}
/// Reads the entire contents of a cache file into a string, looking the
/// data up by its content address.
///
/// ## Example
/// ```no_run
/// use async_std::prelude::*;
/// use async_attributes;
/// use anyhow::Result;
///
/// #[async_attributes::main]
/// async fn main() -> Result<()> {
/// let sri = cacache::write("./my-cache", "my-key", b"hello").await?;
/// let str: String = cacache::read_hash_to_string("./my-cache", &sri).await?;
/// Ok(())
/// }
/// ```
pub async fn read_hash_to_string<P>(cache: P, sri: &Integrity) -> Result<String>
where
P: AsRef<Path>,
{
Ok(String::from_utf8(
read::read_async(cache.as_ref(), sri).await?,
)?)
}
/// Copies cache data to a specified location. Returns the number of bytes
/// copied.
///
@ -242,10 +179,9 @@ where
/// ```no_run
/// use async_std::prelude::*;
/// use async_attributes;
/// use anyhow::Result;
///
/// #[async_attributes::main]
/// async fn main() -> Result<()> {
/// async fn main() -> cacache::Result<()> {
/// cacache::copy("./my-cache", "my-key", "./data.txt").await?;
/// Ok(())
/// }
@ -273,10 +209,9 @@ where
/// ```no_run
/// use async_std::prelude::*;
/// use async_attributes;
/// use anyhow::Result;
///
/// #[async_attributes::main]
/// async fn main() -> Result<()> {
/// async fn main() -> cacache::Result<()> {
/// let sri = cacache::write("./my-cache", "my-key", b"hello world").await?;
/// cacache::copy_hash("./my-cache", &sri, "./data.txt").await?;
/// Ok(())
@ -336,22 +271,19 @@ impl SyncReader {
///
/// ## Example
/// ```no_run
/// use anyhow::Result;
/// use std::io::Read;
///
/// fn main() -> Result<()> {
/// fn main() -> cacache::Result<()> {
/// let mut fd = cacache::SyncReader::open("./my-cache", "my-key")?;
/// let mut str = String::new();
/// fd.read_to_string(&mut str)?;
/// fd.read_to_string(&mut str).expect("Failed to read to string");
/// // Remember to check that the data you got was correct!
/// fd.check()?;
/// Ok(())
/// }
/// ```
pub fn check(self) -> Result<Algorithm> {
self.reader
.check()
.context("Cache read data verification check failed.")
self.reader.check()
}
/// Opens a new synchronous file handle into the cache, looking it up in the
@ -359,13 +291,12 @@ impl SyncReader {
///
/// ## Example
/// ```no_run
/// use anyhow::Result;
/// use std::io::Read;
///
/// fn main() -> Result<()> {
/// fn main() -> cacache::Result<()> {
/// let mut fd = cacache::SyncReader::open("./my-cache", "my-key")?;
/// let mut str = String::new();
/// fd.read_to_string(&mut str)?;
/// fd.read_to_string(&mut str).expect("Failed to parse string");
/// // Remember to check that the data you got was correct!
/// fd.check()?;
/// Ok(())
@ -390,14 +321,13 @@ impl SyncReader {
///
/// ## Example
/// ```no_run
/// use anyhow::Result;
/// use std::io::Read;
///
/// fn main() -> Result<()> {
/// fn main() -> cacache::Result<()> {
/// let sri = cacache::write_sync("./my-cache", "key", b"hello world")?;
/// let mut fd = cacache::SyncReader::open_hash("./my-cache", sri)?;
/// let mut str = String::new();
/// fd.read_to_string(&mut str)?;
/// fd.read_to_string(&mut str).expect("Failed to read to string");
/// // Remember to check that the data you got was correct!
/// fd.check()?;
/// Ok(())
@ -418,10 +348,9 @@ impl SyncReader {
///
/// ## Example
/// ```no_run
/// use anyhow::Result;
/// use std::io::Read;
///
/// fn main() -> Result<()> {
/// fn main() -> cacache::Result<()> {
/// let data = cacache::read_sync("./my-cache", "my-key")?;
/// Ok(())
/// }
@ -441,42 +370,14 @@ where
}
}
/// Reads the entire contents of a cache file synchronously into a string,
/// looking the data up by key.
///
/// ## Example
/// ```no_run
/// use anyhow::Result;
/// use std::io::Read;
///
/// fn main() -> Result<()> {
/// let str: String = cacache::read_to_string_sync("./my-cache", "my-key")?;
/// Ok(())
/// }
/// ```
pub fn read_to_string_sync<P, K>(cache: P, key: K) -> Result<String>
where
P: AsRef<Path>,
K: AsRef<str>,
{
if let Some(entry) = index::find(cache.as_ref(), key.as_ref())? {
read_hash_to_string_sync(cache, &entry.integrity)
} else {
Err(Error::EntryNotFound(
cache.as_ref().to_path_buf(),
key.as_ref().into(),
))?
}
}
/// Reads the entire contents of a cache file synchronously into a bytes
/// vector, looking the data up by its content address.
///
/// ## Example
/// ```no_run
/// use anyhow::Result;
/// use std::io::Read;
///
/// fn main() -> Result<()> {
/// fn main() -> cacache::Result<()> {
/// let sri = cacache::write_sync("./my-cache", "my-key", b"hello")?;
/// let data = cacache::read_hash_sync("./my-cache", &sri)?;
/// Ok(())
@ -489,36 +390,14 @@ where
Ok(read::read(cache.as_ref(), sri)?)
}
/// Reads the entire contents of a cache file synchronously into a string,
/// looking the data up by its content address.
///
/// ## Example
/// ```no_run
/// use anyhow::Result;
/// use std::io::Read;
///
/// fn main() -> Result<()> {
/// let sri = cacache::write_sync("./my-cache", "my-key", b"hello")?;
/// let data = cacache::read_hash_sync("./my-cache", &sri)?;
/// Ok(())
/// }
/// ```
pub fn read_hash_to_string_sync<P>(cache: P, sri: &Integrity) -> Result<String>
where
P: AsRef<Path>,
{
Ok(String::from_utf8(read::read(cache.as_ref(), sri)?)?)
}
/// Copies a cache entry by key to a specified location. Returns the number of
/// bytes copied.
///
/// ## Example
/// ```no_run
/// use anyhow::Result;
/// use std::io::Read;
///
/// fn main() -> Result<()> {
/// fn main() -> cacache::Result<()> {
/// cacache::copy_sync("./my-cache", "my-key", "./my-hello.txt")?;
/// Ok(())
/// }
@ -544,10 +423,9 @@ where
///
/// ## Example
/// ```no_run
/// use anyhow::Result;
/// use std::io::Read;
///
/// fn main() -> Result<()> {
/// fn main() -> cacache::Result<()> {
/// let sri = cacache::write_sync("./my-cache", "my-key", b"hello")?;
/// cacache::copy_hash_sync("./my-cache", &sri, "./my-hello.txt")?;
/// Ok(())
@ -651,16 +529,6 @@ mod tests {
assert_eq!(data, b"hello world");
}
#[async_attributes::test]
async fn test_read_to_string() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path().to_owned();
crate::write(&dir, "my-key", "hello world").await.unwrap();
let data = crate::read_to_string(&dir, "my-key").await.unwrap();
assert_eq!(data, "hello world");
}
#[async_attributes::test]
async fn test_read_hash() {
let tmp = tempfile::tempdir().unwrap();
@ -671,16 +539,6 @@ mod tests {
assert_eq!(data, b"hello world");
}
#[async_attributes::test]
async fn test_read_hash_to_string() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path().to_owned();
let sri = crate::write(&dir, "my-key", "hello world").await.unwrap();
let data = crate::read_hash_to_string(&dir, &sri).await.unwrap();
assert_eq!(data, "hello world");
}
#[test]
fn test_read_sync() {
let tmp = tempfile::tempdir().unwrap();
@ -691,16 +549,6 @@ mod tests {
assert_eq!(data, b"hello world");
}
#[test]
fn test_read_to_string_sync() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path().to_owned();
crate::write_sync(&dir, "my-key", "hello world").unwrap();
let data = crate::read_to_string_sync(&dir, "my-key").unwrap();
assert_eq!(data, "hello world");
}
#[test]
fn test_read_hash_sync() {
let tmp = tempfile::tempdir().unwrap();
@ -711,16 +559,6 @@ mod tests {
assert_eq!(data, b"hello world");
}
#[test]
fn test_read_hash_to_string_sync() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path().to_owned();
let sri = crate::write_sync(&dir, "my-key", "hello world").unwrap();
let data = crate::read_hash_to_string_sync(&dir, &sri).unwrap();
assert_eq!(data, "hello world");
}
#[async_attributes::test]
async fn test_copy() {
let tmp = tempfile::tempdir().unwrap();

View File

@ -5,7 +5,6 @@ use std::io::{ErrorKind, Write};
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
use anyhow::{Context, Result};
use async_std::fs as afs;
use async_std::io::BufReader;
use digest::Digest;
@ -20,6 +19,7 @@ use sha2::Sha256;
use ssri::Integrity;
use walkdir::WalkDir;
use crate::errors::{Internal, InternalResult, Result};
use crate::put::WriteOpts;
const INDEX_VERSION: &str = "5";
@ -88,7 +88,8 @@ pub fn insert(cache: &Path, key: &str, opts: WriteOpts) -> Result<Integrity> {
let out = format!("\n{}\t{}", hash_entry(&stringified), stringified);
buck.write_all(out.as_bytes())
.with_context(|| format!("Failed to write to index bucket at {:?}", bucket))?;
buck.flush()?;
buck.flush()
.with_context(|| format!("Failed to flush bucket at {:?}", bucket))?;
Ok(opts
.sri
.or_else(|| "sha1-deadbeef".parse::<Integrity>().ok())
@ -125,7 +126,9 @@ pub async fn insert_async<'a>(cache: &'a Path, key: &'a str, opts: WriteOpts) ->
buck.write_all(out.as_bytes())
.await
.with_context(|| format!("Failed to write to index bucket at {:?}", bucket))?;
buck.flush().await?;
buck.flush()
.await
.with_context(|| format!("Failed to flush bucket at {:?}", bucket))?;
Ok(opts
.sri
.or_else(|| "sha1-deadbeef".parse::<Integrity>().ok())
@ -223,7 +226,8 @@ pub fn ls(cache: &Path) -> impl Iterator<Item = Result<Metadata>> {
WalkDir::new(cache.join(format!("index-v{}", INDEX_VERSION)))
.into_iter()
.map(|bucket| {
let bucket = bucket?;
let bucket = bucket.to_internal()?;
if bucket.file_type().is_dir() {
return Ok(Vec::new());
}
@ -281,13 +285,13 @@ fn now() -> u128 {
.as_millis()
}
fn bucket_entries(bucket: &Path) -> Result<Vec<SerializableMetadata>> {
fn bucket_entries(bucket: &Path) -> InternalResult<Vec<SerializableMetadata>> {
use std::io::{BufRead, BufReader};
fs::File::open(bucket)
.map(|file| {
BufReader::new(file)
.lines()
.filter_map(Result::ok)
.filter_map(std::result::Result::ok)
.filter_map(|entry| {
let entry_str = match entry.split('\t').collect::<Vec<&str>>()[..] {
[hash, entry_str] if hash_entry(entry_str) == hash => entry_str,
@ -302,19 +306,19 @@ fn bucket_entries(bucket: &Path) -> Result<Vec<SerializableMetadata>> {
if err.kind() == ErrorKind::NotFound {
Ok(Vec::new())
} else {
Err(err)?
Err(err).to_internal()?
}
})
}
async fn bucket_entries_async(bucket: &Path) -> Result<Vec<SerializableMetadata>> {
async fn bucket_entries_async(bucket: &Path) -> InternalResult<Vec<SerializableMetadata>> {
let file_result = afs::File::open(bucket).await;
let file;
if let Err(err) = file_result {
if err.kind() == ErrorKind::NotFound {
return Ok(Vec::new());
}
return Err(err.into());
return Err(err).to_internal()?;
} else {
file = file_result.unwrap();
}

View File

@ -35,10 +35,9 @@
//!
//! ```no_run
//! use async_attributes;
//! use anyhow::Result;
//!
//! #[async_attributes::main]
//! async fn main() -> Result<()> {
//! async fn main() -> cacache::Result<()> {
//! // Data goes in...
//! cacache::write("./my-cache", "key", b"hello").await?;
//!
@ -60,10 +59,9 @@
//!
//! ```no_run
//! use async_attributes;
//! use anyhow::Result;
//!
//! #[async_attributes::main]
//! async fn main() -> Result<()> {
//! async fn main() -> cacache::Result<()> {
//! // Data goes in...
//! let sri = cacache::write("./my-cache", "key", b"hello").await?;
//!
@ -81,15 +79,14 @@
//! an API reminiscent of `std::fs::OpenOptions`:
//!
//! ```no_run
//! use anyhow::Result;
//! use async_attributes;
//! use async_std::prelude::*;
//!
//! #[async_attributes::main]
//! async fn main() -> Result<()> {
//! 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?;
//! 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?;
@ -97,7 +94,7 @@
//!
//! let mut fd = cacache::Reader::open("./my-cache", "key").await?;
//! let mut buf = String::new();
//! fd.read_to_string(&mut buf).await?;
//! 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
@ -117,8 +114,7 @@
//! application, you probably want to use these instead.
//!
//! ```no_run
//! use anyhow::Result;
//! fn main() -> Result<()> {
//! 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");
@ -139,7 +135,7 @@ mod ls;
mod put;
mod rm;
pub use errors::Error;
pub use errors::{Error, Result};
pub use index::Metadata;
pub use get::*;

View File

@ -1,7 +1,7 @@
//! Functions for iterating over the cache.
use anyhow::Result;
use std::path::Path;
use crate::errors::Result;
use crate::index;
/// Returns a synchronous iterator that lists all cache index entries.

View File

@ -5,12 +5,11 @@ use std::pin::Pin;
use futures::prelude::*;
use anyhow::{Context, Result};
use serde_json::Value;
use ssri::{Algorithm, Integrity};
use crate::content::write;
use crate::errors::Error;
use crate::errors::{Error, Internal, Result};
use crate::index;
use std::task::{Context as TaskContext, Poll};
@ -20,10 +19,9 @@ use std::task::{Context as TaskContext, Poll};
/// ## Example
/// ```no_run
/// use async_attributes;
/// use anyhow::Result;
///
/// #[async_attributes::main]
/// async fn main() -> Result<()> {
/// async fn main() -> cacache::Result<()> {
/// cacache::write("./my-cache", "my-key", b"hello").await?;
/// Ok(())
/// }
@ -42,13 +40,7 @@ where
cache.as_ref()
)
})?;
writer.commit().await.with_context(|| {
format!(
"Failed to write to commit data for key {} for cache at {:?}",
key.as_ref(),
cache.as_ref()
)
})
writer.commit().await
}
/// A reference to an open file writing to the cache.
@ -85,12 +77,11 @@ impl Writer {
/// ```no_run
/// use async_attributes;
/// use async_std::prelude::*;
/// use anyhow::Result;
///
/// #[async_attributes::main]
/// async fn main() -> Result<()> {
/// async fn main() -> cacache::Result<()> {
/// let mut fd = cacache::Writer::create("./my-cache", "my-key").await?;
/// fd.write_all(b"hello world").await?;
/// fd.write_all(b"hello world").await.expect("Failed to write to cache");
/// // Data is not saved into the cache until you commit it.
/// fd.commit().await?;
/// Ok(())
@ -105,13 +96,6 @@ impl Writer {
.algorithm(Algorithm::Sha256)
.open(cache.as_ref(), key.as_ref())
.await
.with_context(|| {
format!(
"Failed to open a write handle for key {} for cache at {:?}",
key.as_ref(),
cache.as_ref()
)
})
}
/// Closes the Writer handle and writes content and index entries. Also
@ -121,39 +105,20 @@ impl Writer {
pub async fn commit(mut self) -> Result<Integrity> {
let key = self.key;
let cache = self.cache;
let writer_sri = self.writer.close().await.with_context(|| {
format!(
"Failed to properly close save file data for key {} in cache at {:?}",
key, cache
)
})?;
let writer_sri = self.writer.close().await?;
if let Some(sri) = &self.opts.sri {
if sri.matches(&writer_sri).is_none() {
return Err(Error::IntegrityError(sri.clone(), writer_sri)).with_context(|| {
format!(
"Failed to verify data integrity while inserting {} into cache at {:?}",
key, cache
)
})?;
return Err(ssri::Error::IntegrityCheckError(sri.clone(), writer_sri))?;
}
} else {
self.opts.sri = Some(writer_sri);
}
if let Some(size) = self.opts.size {
if size != self.written {
return Err(Error::SizeError(size, self.written)).with_context(|| {
format!("A size was passed in but the value inserted into {} could not be verified for cache at {:?}", key, cache)
})?;
return Err(Error::SizeError(size, self.written));
}
}
index::insert_async(&cache, &key, self.opts)
.await
.with_context(|| {
format!(
"Failed to write index entry for {} in cache at {:?}",
key, cache
)
})
index::insert_async(&cache, &key, self.opts).await
}
}
@ -161,10 +126,9 @@ impl Writer {
///
/// ## Example
/// ```no_run
/// use anyhow::Result;
/// use std::io::Read;
///
/// fn main() -> Result<()> {
/// fn main() -> cacache::Result<()> {
/// let data = cacache::write_sync("./my-cache", "my-key", b"hello")?;
/// Ok(())
/// }
@ -183,13 +147,7 @@ where
cache.as_ref()
)
})?;
writer.commit().with_context(|| {
format!(
"Failed to write to commit data for key {} for cache at {:?}",
key.as_ref(),
cache.as_ref()
)
})
writer.commit()
}
/// Builder for options and flags for opening a new cache file to write data into.
@ -304,12 +262,11 @@ impl SyncWriter {
///
/// ## Example
/// ```no_run
/// use anyhow::Result;
/// use std::io::prelude::*;
///
/// fn main() -> Result<()> {
/// fn main() -> cacache::Result<()> {
/// let mut fd = cacache::SyncWriter::create("./my-cache", "my-key")?;
/// fd.write_all(b"hello world")?;
/// fd.write_all(b"hello world").expect("Failed to write to cache");
/// // Data is not saved into the cache until you commit it.
/// fd.commit()?;
/// Ok(())
@ -323,13 +280,6 @@ impl SyncWriter {
WriteOpts::new()
.algorithm(Algorithm::Sha256)
.open_sync(cache.as_ref(), key.as_ref())
.with_context(|| {
format!(
"Failed to open a write handle for key {} for cache at {:?}",
key.as_ref(),
cache.as_ref()
)
})
}
/// Closes the Writer handle and writes content and index entries. Also
@ -348,29 +298,17 @@ impl SyncWriter {
if let Some(sri) = &self.opts.sri {
// TODO - ssri should have a .matches method
if sri.matches(&writer_sri).is_none() {
return Err(Error::IntegrityError(sri.clone(), writer_sri)).with_context(|| {
format!(
"Failed to verify data integrity while inserting {} into cache at {:?}",
key, cache
)
})?;
return Err(ssri::Error::IntegrityCheckError(sri.clone(), writer_sri))?;
}
} else {
self.opts.sri = Some(writer_sri);
}
if let Some(size) = self.opts.size {
if size != self.written {
return Err(Error::SizeError(size, self.written)).with_context(|| {
format!("A size was passed in but the value inserted into {} could not be verified for cache at {:?}", key, cache)
})?;
return Err(Error::SizeError(size, self.written))?;
}
}
index::insert(&cache, &key, self.opts).with_context(|| {
format!(
"Failed to write index entry for {} in cache at {:?}",
key, cache
)
})
index::insert(&cache, &key, self.opts)
}
}

View File

@ -4,10 +4,10 @@ use std::path::Path;
use async_std::fs as afs;
use anyhow::{Context, Result};
use ssri::Integrity;
use crate::content::rm;
use crate::errors::{Internal, Result};
use crate::index;
/// Removes an individual index metadata entry. The associated content will be
@ -17,10 +17,9 @@ use crate::index;
/// ```no_run
/// use async_std::prelude::*;
/// use async_attributes;
/// use anyhow::Result;
///
/// #[async_attributes::main]
/// async fn main() -> Result<()> {
/// async fn main() -> cacache::Result<()> {
/// let sri = cacache::write("./my-cache", "my-key", b"hello").await?;
///
/// cacache::remove("./my-cache", "my-key").await?;
@ -39,15 +38,7 @@ where
P: AsRef<Path>,
K: AsRef<str>,
{
index::delete_async(cache.as_ref(), key.as_ref())
.await
.with_context(|| {
format!(
"Failed to delete cache entry for {} in cache at {:?}",
key.as_ref(),
cache.as_ref()
)
})
index::delete_async(cache.as_ref(), key.as_ref()).await
}
/// Removes an individual content entry. Any index entries pointing to this
@ -57,10 +48,9 @@ where
/// ```no_run
/// use async_std::prelude::*;
/// use async_attributes;
/// use anyhow::Result;
///
/// #[async_attributes::main]
/// async fn main() -> Result<()> {
/// async fn main() -> cacache::Result<()> {
/// let sri = cacache::write("./my-cache", "my-key", b"hello").await?;
///
/// cacache::remove_hash("./my-cache", &sri).await?;
@ -76,13 +66,7 @@ where
/// }
/// ```
pub async fn remove_hash<P: AsRef<Path>>(cache: P, sri: &Integrity) -> Result<()> {
rm::rm_async(cache.as_ref(), &sri).await.with_context(|| {
format!(
"Failed to remove content under {} in cache at {:?}",
sri.to_string(),
cache.as_ref()
)
})
Ok(rm::rm_async(cache.as_ref(), &sri).await?)
}
/// Removes entire contents of the cache, including temporary files, the entry
@ -92,10 +76,9 @@ pub async fn remove_hash<P: AsRef<Path>>(cache: P, sri: &Integrity) -> Result<()
/// ```no_run
/// use async_std::prelude::*;
/// use async_attributes;
/// use anyhow::Result;
///
/// #[async_attributes::main]
/// async fn main() -> Result<()> {
/// async fn main() -> cacache::Result<()> {
/// let sri = cacache::write("./my-cache", "my-key", b"hello").await?;
///
/// cacache::clear("./my-cache").await?;
@ -109,9 +92,9 @@ pub async fn remove_hash<P: AsRef<Path>>(cache: P, sri: &Integrity) -> Result<()
/// }
/// ```
pub async fn clear<P: AsRef<Path>>(cache: P) -> Result<()> {
for entry in cache.as_ref().read_dir()? {
for entry in cache.as_ref().read_dir().to_internal()? {
if let Ok(entry) = entry {
afs::remove_dir_all(entry.path()).await?;
afs::remove_dir_all(entry.path()).await.to_internal()?;
}
}
Ok(())
@ -122,10 +105,9 @@ pub async fn clear<P: AsRef<Path>>(cache: P) -> Result<()> {
///
/// ## Example
/// ```no_run
/// use anyhow::Result;
/// use std::io::Read;
///
/// fn main() -> Result<()> {
/// fn main() -> cacache::Result<()> {
/// let sri = cacache::write_sync("./my-cache", "my-key", b"hello")?;
///
/// cacache::remove_sync("./my-cache", "my-key")?;
@ -144,13 +126,7 @@ where
P: AsRef<Path>,
K: AsRef<str>,
{
index::delete(cache.as_ref(), key.as_ref()).with_context(|| {
format!(
"Failed to delete cache entry for {} in cache at {:?}",
key.as_ref(),
cache.as_ref()
)
})
index::delete(cache.as_ref(), key.as_ref())
}
/// Removes an individual content entry synchronously. Any index entries
@ -158,10 +134,9 @@ where
///
/// ## Example
/// ```no_run
/// use anyhow::Result;
/// use std::io::Read;
///
/// fn main() -> Result<()> {
/// fn main() -> cacache::Result<()> {
/// let sri = cacache::write_sync("./my-cache", "my-key", b"hello")?;
///
/// cacache::remove_hash_sync("./my-cache", &sri)?;
@ -177,13 +152,7 @@ where
/// }
/// ```
pub fn remove_hash_sync<P: AsRef<Path>>(cache: P, sri: &Integrity) -> Result<()> {
rm::rm(cache.as_ref(), &sri).with_context(|| {
format!(
"Failed to remove content under {} in cache at {:?}",
sri.to_string(),
cache.as_ref()
)
})
Ok(rm::rm(cache.as_ref(), &sri)?)
}
/// Removes entire contents of the cache synchronously, including temporary
@ -191,10 +160,9 @@ pub fn remove_hash_sync<P: AsRef<Path>>(cache: P, sri: &Integrity) -> Result<()>
///
/// ## Example
/// ```no_run
/// use anyhow::Result;
/// use std::io::Read;
///
/// fn main() -> Result<()> {
/// fn main() -> cacache::Result<()> {
/// let sri = cacache::write_sync("./my-cache", "my-key", b"hello")?;
///
/// cacache::clear_sync("./my-cache")?;
@ -208,9 +176,9 @@ pub fn remove_hash_sync<P: AsRef<Path>>(cache: P, sri: &Integrity) -> Result<()>
/// }
/// ```
pub fn clear_sync<P: AsRef<Path>>(cache: P) -> Result<()> {
for entry in cache.as_ref().read_dir()? {
for entry in cache.as_ref().read_dir().to_internal()? {
if let Ok(entry) = entry {
fs::remove_dir_all(entry.path())?;
fs::remove_dir_all(entry.path()).to_internal()?;
}
}
Ok(())