Skip to content

Commit

Permalink
add attributes for cache type and create block
Browse files Browse the repository at this point in the history
  • Loading branch information
csos95 committed May 30, 2020
1 parent b1c7590 commit 6a0de22
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 11 deletions.
43 changes: 32 additions & 11 deletions cached_proc_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ struct MacroArgs {
result: bool,
#[darling(default)]
option: bool,
#[darling(default, rename = "type")]
cache_type: Option<String>,
#[darling(default, rename = "create")]
cache_create: Option<String>,
}

#[proc_macro_attribute]
Expand Down Expand Up @@ -77,45 +81,62 @@ pub fn cached(args: TokenStream, input: TokenStream) -> TokenStream {
};

// make the cache key type and block that converts the inputs into the key type
let (cache_key_ty, key_convert_block) = match (&args.key, &args.convert) {
(Some(key_str), Some(convert_str)) => {
let (cache_key_ty, key_convert_block) = match (&args.key, &args.convert, &args.cache_type) {
(Some(key_str), Some(convert_str), _) => {
let cache_key_ty = parse_str::<Type>(key_str).expect("unable to parse cache key type");

let key_convert_block =
parse_str::<Block>(convert_str).expect("unable to parse key convert block");

(quote! {#cache_key_ty}, quote! {#key_convert_block})
}
(None, None) => (
(None, Some(convert_str), Some(_)) => {
let key_convert_block =
parse_str::<Block>(convert_str).expect("unable to parse key convert block");

(quote! {}, quote! {#key_convert_block})
}
(None, None, _) => (
quote! {(#(#input_tys),*)},
quote! {(#(#input_names.clone()),*)},
),
(_, _) => panic!("key and convert arguments must be used together or not at all"),
(Some(_), None, _) => panic!("key requires convert to be set"),
(None, Some(_), None) => panic!("convert requires key or type to be set"),
};

// make the cache type and create statement
let (cache_ty, cache_create) = match (&args.unbound, &args.size, &args.time) {
(true, None, None) => {
let (cache_ty, cache_create) = match (&args.unbound, &args.size, &args.time, &args.cache_type, &args.cache_create) {
(true, None, None, None, None) => {
let cache_ty = quote! {cached::UnboundCache<#cache_key_ty, #output_ty>};
let cache_create = quote! {cached::UnboundCache::new()};
(cache_ty, cache_create)
}
(false, Some(size), None) => {
(false, Some(size), None, None, None) => {
let cache_ty = quote! {cached::SizedCache<#cache_key_ty, #output_ty>};
let cache_create = quote! {cached::SizedCache::with_size(#size)};
(cache_ty, cache_create)
}
(false, None, Some(time)) => {
(false, None, Some(time), None, None) => {
let cache_ty = quote! {cached::TimedCache<#cache_key_ty, #output_ty>};
let cache_create = quote! {cached::TimedCache::with_lifespan(#time)};
(cache_ty, cache_create)
}
(false, None, None) => {
(false, None, None, None, None) => {
let cache_ty = quote! {cached::UnboundCache<#cache_key_ty, #output_ty>};
let cache_create = quote! {cached::UnboundCache::new()};
(cache_ty, cache_create)
}
_ => panic!("cache types (unbound, size, or time) are mutually exclusive"),
},
(false, None, None, Some(type_str), Some(create_str)) => {
let cache_type = parse_str::<Type>(type_str).expect("unable to parse cache type");

let cache_create =
parse_str::<Block>(create_str).expect("unable to parse cache create block");

(quote! { #cache_type }, quote! { #cache_create })
},
(false, None, None, Some(_), None) => panic!("type requires create to also be set"),
(false, None, None, None, Some(_)) => panic!("create requires type to also be set"),
_ => panic!("cache types (unbound, size, time, or cache_type and cache_create) are mutually exclusive"),
};

// make the set cache block
Expand Down
149 changes: 149 additions & 0 deletions examples/kitchen_sink_proc_macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
use std::cmp::Eq;
use std::collections::HashMap;
use std::hash::Hash;

use std::thread::sleep;
use std::time::Duration;

use cached::proc_macro::cached;
use cached::{Cached, SizedCache, UnboundCache};

// cached shorthand, uses the default unbounded cache.
// Equivalent to specifying `type = "UnboundCache<(u32), u32>", create= "{ UnboundCache::new() }"`
#[cached]
fn fib(n: u32) -> u32 {
if n == 0 || n == 1 {
return n;
}
fib(n - 1) + fib(n - 2)
}

// Same as above, but preallocates some space.
#[cached(
type = "UnboundCache<u32, u32>",
create = "{ UnboundCache::with_capacity(50) }"
)]
fn fib_specific(n: u32) -> u32 {
if n == 0 || n == 1 {
return n;
}
fib_specific(n - 1) + fib_specific(n - 2)
}

// Specify a specific cache type
// Note that the cache key type is a tuple of function argument types.
#[cached(
type = "SizedCache<(u32, u32), u32>",
create = "{ SizedCache::with_size(100) }"
)]
fn slow(a: u32, b: u32) -> u32 {
sleep(Duration::new(2, 0));
return a * b;
}

// Specify a specific cache type and an explicit key expression
// Note that the cache key type is a `String` created from the borrow arguments
// Note that key is not used, convert requires either key or type to be set.
#[cached(
type = "SizedCache<String, usize>",
create = "{ SizedCache::with_size(100) }",
convert = r#"{ format!("{}{}", a, b) }"#
)]
fn keyed(a: &str, b: &str) -> usize {
let size = a.len() + b.len();
sleep(Duration::new(size as u64, 0));
size
}

// Implement our own cache type
struct MyCache<K: Hash + Eq, V> {
store: HashMap<K, V>,
capacity: usize,
}
impl<K: Hash + Eq, V> MyCache<K, V> {
pub fn with_capacity(size: usize) -> MyCache<K, V> {
MyCache {
store: HashMap::with_capacity(size),
capacity: size,
}
}
}
impl<K: Hash + Eq, V> Cached<K, V> for MyCache<K, V> {
fn cache_get(&mut self, k: &K) -> Option<&V> {
self.store.get(k)
}
fn cache_get_mut(&mut self, k: &K) -> Option<&mut V> {
self.store.get_mut(k)
}
fn cache_set(&mut self, k: K, v: V) {
self.store.insert(k, v);
}
fn cache_remove(&mut self, k: &K) -> Option<V> {
self.store.remove(k)
}
fn cache_clear(&mut self) {
self.store.clear();
}
fn cache_reset(&mut self) {
self.store = HashMap::with_capacity(self.capacity);
}
fn cache_size(&self) -> usize {
self.store.len()
}
}

// Specify our custom cache and supply an instance to use
#[cached(type = "MyCache<u32, ()>", create = "{ MyCache::with_capacity(50) }")]
fn custom(n: u32) -> () {
if n == 0 {
return;
}
custom(n - 1)
}

pub fn main() {
println!("\n ** default cache **");
fib(3);
fib(3);
{
let cache = FIB.lock().unwrap();
println!("hits: {:?}", cache.cache_hits());
println!("misses: {:?}", cache.cache_misses());
// make sure lock is dropped
}
fib(10);
fib(10);

println!("\n ** specific cache **");
fib_specific(20);
fib_specific(20);
{
let cache = FIB_SPECIFIC.lock().unwrap();
println!("hits: {:?}", cache.cache_hits());
println!("misses: {:?}", cache.cache_misses());
// make sure lock is dropped
}
fib_specific(20);
fib_specific(20);

println!("\n ** custom cache **");
custom(25);
{
let cache = CUSTOM.lock().unwrap();
println!("hits: {:?}", cache.cache_hits());
println!("misses: {:?}", cache.cache_misses());
// make sure lock is dropped
}

println!("\n ** slow func **");
println!(" - first run `slow(10)`");
slow(10, 10);
println!(" - second run `slow(10)`");
slow(10, 10);
{
let cache = SLOW.lock().unwrap();
println!("hits: {:?}", cache.cache_hits());
println!("misses: {:?}", cache.cache_misses());
// make sure the cache-lock is dropped
}
}

0 comments on commit 6a0de22

Please sign in to comment.