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
//! `forceps` is a crate that provides a simple and easy-to-use on-disk cache/database.
//!
//! **This crate is intended to be used with the [`tokio`](tokio) runtime.**
//!
//! `forceps` is made to be an easy-to-use, thread-safe, performant, and asynchronous disk cache
//! that has easy reading and manipulation of data. It levereges tokio's async `fs` APIs
//! and fast task schedulers to perform IO operations, and `sled` as a fast metadata database.
//!
//! It was originally designed to be used in [`scalpel`](https://github.com/blockba5her/scalpel),
//! the MD@Home implementation for the Rust language.
//!
//! ## Features
//!
//! - Asynchronous APIs
//! - Fast and reliable reading/writing
//! - Optional memory-cache layer
//! - Tuned for large-file databases
//! - Included cache eviction (LRU/FIFO)
//! - Easily accessible value metadata
//! - Optimized for cache `HIT`s
//! - Easy error handling
//! - `bytes` crate support (non-optional)
//!
//! ## Database and Meta-database
//!
//! This database solution easily separates data into two databases: the LFS (large-file-storage)
//! database, and the metadata database. The LFS database is powered using Tokio's async filesystem
//! operations, whereas the metadata database is powered using [`sled`](sled).
//!
//! The advantage of splitting these two up is simple: Accessing metadata (for things like database
//! eviction) is realatively cheap and efficient, with the only downside being that `async` is not
//! present.
//!
//! # Examples
//!
//! ```rust,no_run
//! use std::error::Error;
//! use forceps::Cache;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn Error>> {
//!     let cache = Cache::new("./cache")
//!         .build()
//!         .await?;
//!
//!     cache.write(b"MY_KEY", b"Hello World").await?;
//!     let data = cache.read(b"MY_KEY").await?;
//!     assert_eq!(data.as_ref(), b"Hello World");
//!
//!     Ok(())
//! }
//! ```

#![warn(missing_docs)]
#![warn(broken_intra_doc_links)]

use std::error;
use std::io;

/// Global error type for the `forceps` crate, which is used in the `Result` types of all calls to
/// forcep APIs.
#[derive(Debug)]
pub enum ForcepError {
    /// An I/O operation error. This can occur on reads, writes, or builds.
    Io(io::Error),
    /// Error deserialization metadata information (most likely corrupted)
    MetaDe(bson::de::Error),
    /// Error serializing metadata information
    MetaSer(bson::ser::Error),
    /// Error with metadata sled database operation
    MetaDb(sled::Error),
    /// The entry was found successfully, but the metadata was strangely not present
    MetaNotFound,
    /// The entry for the specified key is not found
    NotFound,
}
/// Re-export of [`ForcepError`]
pub type Error = ForcepError;
/// Result that is returned by all error-bound operations of `forceps`.
pub type Result<T> = std::result::Result<T, ForcepError>;

impl std::fmt::Display for ForcepError {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Io(e) => write!(fmt, "an I/O error occurred: {}", e),
            Self::MetaDe(e) => write!(fmt, "there was a problem deserializing metadata: {}", e),
            Self::MetaSer(e) => write!(fmt, "there was a problem serializing metadata: {}", e),
            Self::MetaDb(e) => write!(fmt, "an error with the metadata database occurred: {}", e),
            Self::MetaNotFound => write!(fmt, "the entry for the key provided was found, but the metadata was strangely not present"),
            Self::NotFound => write!(fmt, "the entry for the key provided was not found"),
        }
    }
}
impl error::Error for ForcepError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match self {
            Self::Io(e) => Some(e),
            Self::MetaDe(e) => Some(e),
            Self::MetaSer(e) => Some(e),
            Self::MetaDb(e) => Some(e),
            Self::MetaNotFound => None,
            Self::NotFound => None,
        }
    }
}

mod mem_cache;
mod tmp;

mod builder;
pub use builder::CacheBuilder;

mod cache;
pub use cache::Cache;

mod metadata;
pub(crate) use metadata::MetaDb;
pub use metadata::{Md5Bytes, Metadata};

/// A collection of [`Cache`] eviction algorithms and generics
///
/// This module contains the [`Evictor`] trait, which is used to signify a structure or enum that
/// is used to evict items out of a cache, as well as some implementations of that trait.
///
/// [`Evictor`]: crate::evictors::Evictor
pub mod evictors;