kash/
lib.rs

1/*!
2[![crates.io version](https://img.shields.io/crates/v/kash.svg?style=flat-square)](https://crates.io/crates/kash)
3![build status](https://img.shields.io/github/actions/workflow/status/omid/kash/build.yml?style=flat-square)
4[![downloads](https://img.shields.io/crates/d/kash.svg?style=flat-square)](https://crates.io/crates/kash)
5[![docs.rs docs](https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square)](https://docs.rs/kash)
6![MIT licensed](https://img.shields.io/crates/l/kash.svg?style=flat-square)
7[![dependency status](https://deps.rs/crate/kash/latest/status.svg?style=flat-square)](https://deps.rs/crate/kash)
8
9Function and method cache and memoization library for Rust, using [`#[kash]`](kash) macro.
10
11```rust
12use kash::kash;
13
14/// Defines a function named `fib` that uses a cache implicitly named `FIB`.
15/// By default, the cache will be the function's name in all caps.
16#[kash]
17fn fib(n: u64) -> u64 {
18    if n == 0 || n == 1 { return n }
19    fib(n-1) + fib(n-2)
20}
21```
22
23Or if you want to limit the size and time-to-live:
24
25```rust
26use kash::kash;
27
28const TTL: u64 = 1000;
29#[kash(size = "100", ttl = "TTL")]
30fn fib(n: u64) -> u64 {
31    if n == 0 || n == 1 { return n }
32    fib(n-1) + fib(n-2)
33}
34```
35
36## Features
37
38- `default`: Includes `ahash` feature.
39- `ahash`: Enable `ahash` hasher as default hashing algorithm.
40- `async`: Include support for async functions.
41- `redis_store`: Include Redis cache store.
42- `redis_tokio`: Include async Redis support using `tokio` and `tokio` tls support, implies `redis_store` and `async`.
43- `redis_connection_manager`: Enable the optional `connection-manager` feature of `redis`. Any async redis caches created
44  will use a connection manager instead of a `MultiplexedConnection`.
45- `redis_ahash`: Enable the optional `ahash` feature of `redis`.
46- `disk_store`: Include disk cache store.
47
48----
49
50```rust
51use std::thread::sleep;
52use std::time::Duration;
53use kash::kash;
54
55/// Use an explicit cache-type with a custom creation block and custom cache-key generating block
56#[kash(
57    size = "100",
58    key(ty = "String", expr = r#"{ format!("{}{}", a, b) }"#)
59)]
60fn keyed(a: &str, b: &str) -> usize {
61    let size = a.len() + b.len();
62    sleep(Duration::new(size as u64, 0));
63    size
64}
65# pub fn main() { }
66```
67
68----
69
70```rust
71use kash::{kash, RedisCacheError};
72use kash::AsyncRedisCache;
73use thiserror::Error;
74
75#[derive(Error, Debug, PartialEq, Clone)]
76enum ExampleError {
77    #[error("error with redis cache `{0}`")]
78    RedisError(String),
79}
80
81impl From<RedisCacheError> for ExampleError {
82    fn from(e: RedisCacheError) -> Self {
83        ExampleError::RedisError(format!("{:?}", e))
84    }
85}
86
87/// Cache the results of an async function in redis. Cache
88/// keys will be prefixed with `cache_redis_prefix`.
89#[kash(redis)]
90async fn async_kash_sleep_secs(secs: u64) -> Result<String, ExampleError> {
91    std::thread::sleep(std::time::Duration::from_secs(secs));
92    Ok(secs.to_string())
93}
94```
95
96----
97
98```rust
99use kash::{kash, DiskCacheError};
100use kash::DiskCache;
101use thiserror::Error;
102
103#[derive(Error, Debug, PartialEq, Clone)]
104enum ExampleError {
105    #[error("error with disk cache `{0}`")]
106    DiskError(String),
107}
108
109impl From<DiskCacheError> for ExampleError {
110    fn from(e: DiskCacheError) -> Self {
111        ExampleError::DiskError(format!("{:?}", e))
112    }
113}
114
115/// Cache the results of a function on disk.
116/// Cache files will be stored under the system cache dir
117/// unless otherwise specified with `dir` or the `create` argument.
118#[kash(disk)]
119fn kash_sleep_secs(secs: u64) -> Result<String, ExampleError> {
120    std::thread::sleep(std::time::Duration::from_secs(secs));
121    Ok(secs.to_string())
122}
123```
124
125Functions defined via macros will have their result, cached using the
126function's arguments as a key by default.
127
128When a macro-defined function is called, the function's cache is first checked for an already
129computed (and still valid) value before evaluating the function body.
130
131See [`examples`](https://github.com/omid/kash/tree/master/examples) directory for more examples.
132*/
133
134#![cfg_attr(docsrs, feature(doc_cfg))]
135
136#[doc(hidden)]
137pub use moka;
138#[doc(hidden)]
139pub use once_cell;
140
141#[cfg(feature = "async")]
142use async_trait::async_trait;
143
144#[doc(inline)]
145pub use kash_macros::kash;
146
147#[cfg(feature = "redis_tokio")]
148#[cfg_attr(docsrs, doc(cfg(feature = "redis_tokio")))]
149pub use stores::AsyncRedisCache;
150#[cfg(feature = "disk_store")]
151#[cfg_attr(docsrs, doc(cfg(feature = "disk_store")))]
152pub use stores::{DiskCache, DiskCacheError};
153#[cfg(feature = "redis_store")]
154#[cfg_attr(docsrs, doc(cfg(feature = "redis_store")))]
155pub use stores::{RedisCache, RedisCacheError};
156
157pub mod stores;
158#[doc(hidden)]
159pub use instant;
160
161#[cfg(feature = "tokio")]
162#[doc(hidden)]
163pub mod async_sync {
164    pub use tokio::sync::Mutex;
165    pub use tokio::sync::OnceCell;
166    pub use tokio::sync::RwLock;
167}
168
169/// Cache operations on an io-connected store
170pub trait IOKash<K, V> {
171    type Error;
172
173    /// Attempt to retrieve a cached value
174    ///
175    /// # Errors
176    ///
177    /// Should return `Self::Error` if the operation fails
178    fn get(&self, k: &K) -> Result<Option<V>, Self::Error>;
179
180    /// Insert a key, value pair and return the previous value
181    ///
182    /// # Errors
183    ///
184    /// Should return `Self::Error` if the operation fails
185    fn set(&self, k: K, v: V) -> Result<Option<V>, Self::Error>;
186
187    /// Remove a cached value
188    ///
189    /// # Errors
190    ///
191    /// Should return `Self::Error` if the operation fails
192    fn remove(&self, k: &K) -> Result<Option<V>, Self::Error>;
193
194    /// Remove all cached values
195    ///
196    /// # Errors
197    ///
198    /// Should return `Self::Error` if the operation fails
199    fn clear(&self) -> Result<(), Self::Error>;
200
201    /// Return the ttl of cached values (time to eviction)
202    fn ttl(&self) -> Option<u64> {
203        None
204    }
205
206    /// Set the ttl of cached values, returns the old value.
207    fn set_ttl(&mut self, _seconds: u64) -> Option<u64> {
208        None
209    }
210
211    /// Remove the ttl for cached values, returns the old value.
212    ///
213    /// For cache implementations that don't support retaining values indefinitely, this method is
214    /// a no-op.
215    fn unset_ttl(&mut self) -> Option<u64> {
216        None
217    }
218}
219
220#[cfg(feature = "async")]
221#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
222#[async_trait]
223pub trait IOKashAsync<K, V> {
224    type Error;
225    async fn get(&self, k: &K) -> Result<Option<V>, Self::Error>;
226
227    async fn set(&self, k: K, v: V) -> Result<Option<V>, Self::Error>;
228
229    /// Remove a cached value
230    async fn remove(&self, k: &K) -> Result<Option<V>, Self::Error>;
231
232    /// Remove all cached values
233    async fn clear(&self) -> Result<(), Self::Error>;
234
235    /// Return the ttl of cached values (time to eviction)
236    fn ttl(&self) -> Option<u64> {
237        None
238    }
239
240    /// Set the ttl of cached values, returns the old value
241    fn set_ttl(&mut self, _seconds: u64) -> Option<u64> {
242        None
243    }
244
245    /// Remove the ttl for cached values, returns the old value.
246    ///
247    /// For cache implementations that don't support retaining values indefinitely, this method is
248    /// a no-op.
249    fn unset_ttl(&mut self) -> Option<u64> {
250        None
251    }
252}