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}