cached/
macros.rs

1/*!
2Declarative macros for defining functions that wrap a static-ref cache object.
3
4### `cached!` and `cached_key!` Usage & Options:
5
6There are several options depending on how explicit you want to be. See below for a full syntax breakdown.
7
8
91.) Using the shorthand will use an unbounded cache.
10
11
12```rust,no_run
13#[macro_use] extern crate cached;
14
15/// Defines a function named `fib` that uses a cache named `FIB`
16cached!{
17    FIB;
18    fn fib(n: u64) -> u64 = {
19        if n == 0 || n == 1 { return n }
20        fib(n-1) + fib(n-2)
21    }
22}
23# pub fn main() { }
24```
25
26
272.) Using the full syntax requires specifying the full cache type and providing
28    an instance of the cache to use. Note that the cache's key-type is a tuple
29    of the function argument types. If you would like fine grained control over
30    the key, you can use the `cached_key!` macro.
31    The following example uses a `SizedCache` (LRU):
32
33```rust,no_run
34#[macro_use] extern crate cached;
35
36use std::thread::sleep;
37use std::time::Duration;
38use cached::SizedCache;
39
40/// Defines a function `compute` that uses an LRU cache named `COMPUTE` which has a
41/// size limit of 50 items. The `cached!` macro will implicitly combine
42/// the function arguments into a tuple to be used as the cache key.
43cached!{
44    COMPUTE: SizedCache<(u64, u64), u64> = SizedCache::with_size(50);
45    fn compute(a: u64, b: u64) -> u64 = {
46        sleep(Duration::new(2, 0));
47        return a * b;
48    }
49}
50# pub fn main() { }
51```
52
53
543.) The `cached_key` macro functions identically, but allows you to define the
55    cache key as an expression.
56
57```rust,no_run
58#[macro_use] extern crate cached;
59
60use std::thread::sleep;
61use std::time::Duration;
62use cached::SizedCache;
63
64/// Defines a function named `length` that uses an LRU cache named `LENGTH`.
65/// The `Key = ` expression is used to explicitly define the value that
66/// should be used as the cache key. Here the borrowed arguments are converted
67/// to an owned string that can be stored in the global function cache.
68cached_key!{
69    LENGTH: SizedCache<String, usize> = SizedCache::with_size(50);
70    Key = { format!("{}{}", a, b) };
71    fn length(a: &str, b: &str) -> usize = {
72        let size = a.len() + b.len();
73        sleep(Duration::new(size as u64, 0));
74        size
75    }
76}
77# pub fn main() { }
78```
79
804.) The `cached_result` and `cached_key_result` macros function similarly to `cached`
81    and `cached_key` respectively but the cached function needs to return `Result`
82    (or some type alias like `io::Result`). If the function returns `Ok(val)` then `val`
83    is cached, but errors are not. Note that only the success type needs to implement
84    `Clone`, _not_ the error type. When using `cached_result` and `cached_key_result`,
85    the cache type cannot be derived and must always be explicitly specified.
86
87```rust,no_run
88#[macro_use] extern crate cached;
89
90use cached::UnboundCache;
91
92/// Cache the successes of a function.
93/// To use `cached_key_result` add a key function as in `cached_key`.
94cached_result!{
95   MULT: UnboundCache<(u64, u64), u64> = UnboundCache::new(); // Type must always be specified
96   fn mult(a: u64, b: u64) -> Result<u64, ()> = {
97        if a == 0 || b == 0 {
98            return Err(());
99        } else {
100            return Ok(a * b);
101        }
102   }
103}
104# pub fn main() { }
105```
106
107----
108
109```rust,ignore
110#[macro_use] extern crate cached;
111use std::thread::sleep;
112use std::time::Duration;
113use cached::RedisCache;
114
115cached! {
116    UNBOUND_REDIS: RedisCache<u32, u32> = RedisCache::new();
117    fn cached_redis(n: u32) -> u32 = {
118        sleep(Duration::new(3, 0));
119        n
120    }
121}
122
123cached! {
124    TIMED_REDIS: RedisCache<u32, u32> = RedisCache::with_lifespan(Duration::from_secs(2));
125    fn cached_timed_redis(n: u32) -> u32 = {
126        sleep(Duration::new(3, 0));
127        n
128    }
129}
130# pub fn main() { }
131```
132
133
134----
135
136## Syntax
137
138The common macro syntax is:
139
140
141```rust,ignore
142cached_key!{
143    CACHE_NAME: CacheType = CacheInstance;
144    Key = KeyExpression;
145    fn func_name(arg1: arg_type, arg2: arg_type) -> return_ty = {
146        // do stuff like normal
147        return_type
148    }
149}
150```
151
152Where:
153
154- `CACHE_NAME` is the unique name used to hold a `static ref` to the cache
155- `CacheType` is the full type of the cache
156- `CacheInstance` is any expression that yields an instance of `CacheType` to be used
157  as the cache-store, followed by `;`
158- When using the `cached_key!` macro, the "Key" line must be specified. This line must start with
159  the literal tokens `Key = `, followed by an expression that evaluates to the key, followed by `;`
160- `fn func_name(arg1: arg_type) -> return_type` is the same form as a regular function signature, with the exception
161  that functions with no return value must be explicitly stated (e.g. `fn func_name(arg: arg_type) -> ()`)
162- The expression following `=` is the function body assigned to `func_name`. Note, the function
163  body can make recursive calls to its cached-self (`func_name`).
164
165
166# Fine grained control using `cached_control!`
167
168The `cached_control!` macro allows you to provide expressions that get plugged into key areas
169of the memoized function. While the `cached` and `cached_result` variants are adequate for most
170scenarios, it can be useful to have the ability to customize the macro's functionality.
171
172```rust,no_run
173#[macro_use] extern crate cached;
174
175use cached::UnboundCache;
176
177/// The following usage plugs in expressions to make the macro behave like
178/// the `cached_result!` macro.
179cached_control!{
180    CACHE: UnboundCache<String, String> = UnboundCache::new();
181
182    // Use an owned copy of the argument `input` as the cache key
183    Key = { input.to_owned() };
184
185    // If a cached value exists, it will bind to `cached_val` and
186    // a `Result` will be returned containing a copy of the cached
187    // evaluated body. This will return before the function body
188    // is executed.
189    PostGet(cached_val) = { return Ok(cached_val.clone()) };
190
191    // The result of executing the function body will be bound to
192    // `body_result`. In this case, the function body returns a `Result`.
193    // We match on the `Result`, returning an early `Err` if the function errored.
194    // Otherwise, we pass on the function's result to be cached.
195    PostExec(body_result) = {
196        match body_result {
197            Ok(v) => v,
198            Err(e) => return Err(e),
199        }
200    };
201
202    // When inserting the value into the cache we bind
203    // the to-be-set-value to `set_value` and give back a copy
204    // of it to be inserted into the cache
205    Set(set_value) = { set_value.clone() };
206
207    // Before returning, print the value that will be returned
208    Return(return_value) = {
209        println!("{}", return_value);
210        Ok(return_value)
211    };
212
213    fn can_fail(input: &str) -> Result<String, String> = {
214        let len = input.len();
215        if len < 3 { Ok(format!("{}-{}", input, len)) }
216        else { Err("too big".to_string()) }
217    }
218}
219# pub fn main() {}
220```
221
222
223 */
224
225#[macro_export]
226macro_rules! cached {
227    // Use default cached::Cache
228    ($cachename:ident;
229     fn $name:ident ($($arg:ident : $argtype:ty),*) -> $ret:ty = $body:expr) => {
230        cached!(
231            $cachename : $crate::UnboundCache<($($argtype),*), $ret> = $crate::UnboundCache::new();
232            fn $name($($arg : $argtype),*) -> $ret = $body
233        );
234    };
235
236    // Use a specified cache-type and an explicitly created cache-instance
237    ($cachename:ident : $cachetype:ty = $cacheinstance:expr ;
238     fn $name:ident ($($arg:ident : $argtype:ty),*) -> $ret:ty = $body:expr) => {
239        static $cachename: $crate::once_cell::sync::Lazy<::std::sync::Mutex<$cachetype>>
240            = $crate::once_cell::sync::Lazy::new(|| ::std::sync::Mutex::new($cacheinstance));
241
242        #[allow(unused_parens)]
243        pub fn $name($($arg: $argtype),*) -> $ret {
244            let key = ($($arg.clone()),*);
245            {
246                let mut cache = $cachename.lock().unwrap();
247                let res = $crate::Cached::cache_get(&mut *cache, &key);
248                if let Some(res) = res { return res.clone(); }
249            }
250            let val = (||$body)();
251            let mut cache = $cachename.lock().unwrap();
252            $crate::Cached::cache_set(&mut *cache, key, val.clone());
253            val
254        }
255    };
256
257    ($cachename:ident : $cachetype:ty = $cacheinstance:expr ;
258     async fn $name:ident ($($arg:ident : $argtype:ty),*) -> $ret:ty = $body:block) => {
259        static $cachename: $crate::once_cell::sync::Lazy<::std::sync::Mutex<$cachetype>>
260            = $crate::once_cell::sync::Lazy::new(|| ::std::sync::Mutex::new($cacheinstance));
261
262        #[allow(unused_parens)]
263        pub async fn $name($($arg: $argtype),*) -> $ret {
264            let key = ($($arg.clone()),*);
265            {
266                let mut cache = $cachename.lock().unwrap();
267                let res = $crate::Cached::cache_get(&mut *cache, &key);
268                if let Some(res) = res { return res.clone(); }
269            }
270            // run the function and cache the result
271            async fn inner($($arg: $argtype),*) -> $ret $body
272            let val = inner($($arg),*).await;
273
274            let mut cache = $cachename.lock().unwrap();
275            $crate::Cached::cache_set(&mut *cache, key, val.clone());
276            val
277        }
278    };
279}
280
281#[macro_export]
282macro_rules! cached_key {
283    // Use a specified cache-type and an explicitly created cache-instance
284    ($cachename:ident : $cachetype:ty = $cacheinstance:expr ;
285     Key = $key:expr;
286     fn $name:ident ($($arg:ident : $argtype:ty),*) -> $ret:ty = $body:expr) => {
287        static $cachename: $crate::once_cell::sync::Lazy<::std::sync::Mutex<$cachetype>>
288            = $crate::once_cell::sync::Lazy::new(|| ::std::sync::Mutex::new($cacheinstance));
289
290        #[allow(unused_parens)]
291        pub fn $name($($arg: $argtype),*) -> $ret {
292            let key = $key;
293            {
294                let mut cache = $cachename.lock().unwrap();
295                let res = $crate::Cached::cache_get(&mut *cache, &key);
296                if let Some(res) = res { return res.clone(); }
297            }
298            let val = (||$body)();
299            let mut cache = $cachename.lock().unwrap();
300            $crate::Cached::cache_set(&mut *cache, key, val.clone());
301            val
302        }
303    };
304
305    ($cachename:ident : $cachetype:ty = $cacheinstance:expr ;
306     Key = $key:expr;
307     async fn $name:ident ($($arg:ident : $argtype:ty),*) -> $ret:ty = $body:expr) => {
308        static $cachename: $crate::once_cell::sync::Lazy<::std::sync::Mutex<$cachetype>>
309            = $crate::once_cell::sync::Lazy::new(|| ::std::sync::Mutex::new($cacheinstance));
310
311        #[allow(unused_parens)]
312        pub async fn $name($($arg: $argtype),*) -> $ret {
313            let key = $key;
314            {
315                let mut cache = $cachename.lock().unwrap();
316                let res = $crate::Cached::cache_get(&mut *cache, &key);
317                if let Some(res) = res { return res.clone(); }
318            }
319            // run the function and cache the result
320            async fn inner($($arg: $argtype),*) -> $ret $body
321            let val = inner($($arg),*).await;
322            let mut cache = $cachename.lock().unwrap();
323            $crate::Cached::cache_set(&mut *cache, key, val.clone());
324            val
325        }
326    };
327}
328
329#[macro_export]
330macro_rules! cached_result {
331    // Unfortunately it's impossible to infer the cache type because it's not the function return type
332    ($cachename:ident : $cachetype:ty = $cacheinstance:expr ;
333     fn $name:ident ($($arg:ident : $argtype:ty),*) -> $ret:ty = $body:expr) => {
334        static $cachename: $crate::once_cell::sync::Lazy<::std::sync::Mutex<$cachetype>>
335            = $crate::once_cell::sync::Lazy::new(|| ::std::sync::Mutex::new($cacheinstance));
336
337        #[allow(unused_parens)]
338        pub fn $name($($arg: $argtype),*) -> $ret {
339            let key = ($($arg.clone()),*);
340            {
341                let mut cache = $cachename.lock().unwrap();
342                let res = $crate::Cached::cache_get(&mut *cache, &key);
343                if let Some(res) = res { return Ok(res.clone()); }
344            }
345
346            // Store return in temporary typed variable in case type cannot be inferred
347            let ret : $ret = (||$body)();
348            let val = ret?;
349
350            let mut cache = $cachename.lock().unwrap();
351            $crate::Cached::cache_set(&mut *cache, key, val.clone());
352            Ok(val)
353        }
354    };
355
356    ($cachename:ident : $cachetype:ty = $cacheinstance:expr ;
357     async fn $name:ident ($($arg:ident : $argtype:ty),*) -> $ret:ty = $body:expr) => {
358        static $cachename: $crate::once_cell::sync::Lazy<::std::sync::Mutex<$cachetype>>
359            = $crate::once_cell::sync::Lazy::new(|| ::std::sync::Mutex::new($cacheinstance));
360
361        #[allow(unused_parens)]
362        pub async fn $name($($arg: $argtype),*) -> $ret {
363            let key = ($($arg.clone()),*);
364            {
365                let mut cache = $cachename.lock().unwrap();
366                let res = $crate::Cached::cache_get(&mut *cache, &key);
367                if let Some(res) = res { return Ok(res.clone()); }
368            }
369
370            // run the function and cache the result
371            async fn inner($($arg: $argtype),*) -> $ret $body
372            let val = inner($($arg),*).await?;
373
374            let mut cache = $cachename.lock().unwrap();
375            $crate::Cached::cache_set(&mut *cache, key, val.clone());
376            Ok(val)
377        }
378    };
379}
380
381#[macro_export]
382macro_rules! cached_key_result {
383    // Use a specified cache-type and an explicitly created cache-instance
384    ($cachename:ident : $cachetype:ty = $cacheinstance:expr ;
385     Key = $key:expr;
386     fn $name:ident ($($arg:ident : $argtype:ty),*) -> $ret:ty = $body:expr) => {
387        static $cachename: $crate::once_cell::sync::Lazy<::std::sync::Mutex<$cachetype>>
388            = $crate::once_cell::sync::Lazy::new(|| ::std::sync::Mutex::new($cacheinstance));
389
390        #[allow(unused_parens)]
391        pub fn $name($($arg: $argtype),*) -> $ret {
392            let key = $key;
393            {
394                let mut cache = $cachename.lock().unwrap();
395                let res = $crate::Cached::cache_get(&mut *cache, &key);
396                if let Some(res) = res { return Ok(res.clone()); }
397            }
398
399            // Store return in temporary typed variable in case type cannot be inferred
400            let ret : $ret = (||$body)();
401            let val = ret?;
402
403            let mut cache = $cachename.lock().unwrap();
404            $crate::Cached::cache_set(&mut *cache, key, val.clone());
405            Ok(val)
406        }
407    };
408
409    ($cachename:ident : $cachetype:ty = $cacheinstance:expr ;
410     Key = $key:expr;
411     async fn $name:ident ($($arg:ident : $argtype:ty),*) -> $ret:ty = $body:expr) => {
412        static $cachename: $crate::once_cell::sync::Lazy<::std::sync::Mutex<$cachetype>>
413            = $crate::once_cell::sync::Lazy::new(|| ::std::sync::Mutex::new($cacheinstance));
414
415        #[allow(unused_parens)]
416        pub async fn $name($($arg: $argtype),*) -> $ret {
417            let key = $key;
418            {
419                let mut cache = $cachename.lock().unwrap();
420                let res = $crate::Cached::cache_get(&mut *cache, &key);
421                if let Some(res) = res { return Ok(res.clone()); }
422            }
423
424            // run the function and cache the result
425            async fn inner($($arg: $argtype),*) -> $ret $body
426            let val = inner($($arg),*).await?;
427
428            let mut cache = $cachename.lock().unwrap();
429            $crate::Cached::cache_set(&mut *cache, key, val.clone());
430            Ok(val)
431        }
432    };
433}
434
435#[macro_export]
436macro_rules! cached_control {
437    // Use a specified cache-type and an explicitly created cache-instance
438    ($cachename:ident : $cachetype:ty = $cacheinstance:expr ;
439     Key = $key:expr;
440     PostGet($cached_value:ident) = $post_get:expr;
441     PostExec($body_value:ident) = $post_exec:expr;
442     Set($set_value:ident) = $pre_set:expr;
443     Return($ret_value:ident) = $return:expr;
444     fn $name:ident ($($arg:ident : $argtype:ty),*) -> $ret:ty = $body:expr) => {
445        static $cachename: $crate::once_cell::sync::Lazy<::std::sync::Mutex<$cachetype>>
446            = $crate::once_cell::sync::Lazy::new(|| ::std::sync::Mutex::new($cacheinstance));
447
448        #[allow(unused_parens)]
449        pub fn $name($($arg: $argtype),*) -> $ret {
450            let key = $key;
451            {
452                let mut cache = $cachename.lock().unwrap();
453                let res = $crate::Cached::cache_get(&mut *cache, &key);
454                if let Some($cached_value) = res {
455                    $post_get
456                }
457            }
458            let $body_value = (||$body)();
459            let $set_value = $post_exec;
460            let mut cache = $cachename.lock().unwrap();
461            $crate::Cached::cache_set(&mut *cache, key, $pre_set);
462            let $ret_value = $set_value;
463            $return
464        }
465    };
466
467    ($cachename:ident : $cachetype:ty = $cacheinstance:expr ;
468     Key = $key:expr;
469     PostGet($cached_value:ident) = $post_get:expr;
470     PostExec($body_value:ident) = $post_exec:expr;
471     Set($set_value:ident) = $pre_set:expr;
472     Return($ret_value:ident) = $return:expr;
473     async fn $name:ident ($($arg:ident : $argtype:ty),*) -> $ret:ty = $body:expr) => {
474        static $cachename: $crate::once_cell::sync::Lazy<::std::sync::Mutex<$cachetype>>
475            = $crate::once_cell::sync::Lazy::new(|| ::std::sync::Mutex::new($cacheinstance));
476
477        #[allow(unused_parens)]
478        pub async fn $name($($arg: $argtype),*) -> $ret {
479            let key = $key;
480            {
481                let mut cache = $cachename.lock().unwrap();
482                let res = $crate::Cached::cache_get(&mut *cache, &key);
483                if let Some($cached_value) = res {
484                    $post_get
485                }
486            }
487             // run the function and cache the result
488            async fn inner($($arg: $argtype),*) -> $ret $body
489            let $body_value = inner($($arg),*).await?;
490            let $set_value = $post_exec;
491            let mut cache = $cachename.lock().unwrap();
492            $crate::Cached::cache_set(&mut *cache, key, $pre_set);
493            let $ret_value = $set_value;
494            $return
495        }
496    };
497}