// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use serde_json; use serde::{Serialize, Deserialize}; use std::any::TypeId; use builder::Step; use std::fmt; use std::mem; use std::collections::HashMap; use std::cell::RefCell; /// This is essentially a HashMap which allows storing any type in its input and /// any type in its output. It is a write-once cache; values are never evicted, /// which means that references to the value can safely be returned from the /// get() method. // // FIXME: This type does not permit retrieving &Path from a PathBuf, primarily // due to a lack of any obvious way to ensure that this is safe, but also not // penalize other cases (e.g., deserializing u32 -> &u32, which is non-optimal). #[derive(Debug)] pub struct Cache(RefCell>>); fn to_json(element: &T) -> String { t!(serde_json::to_string(element)) } fn from_json<'a, O: Deserialize<'a>>(data: &'a str) -> O { t!(serde_json::from_str(data)) } #[derive(Clone, PartialEq, Eq, Hash)] pub struct Key(TypeId, String); impl fmt::Debug for Key { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.write_str(&format!("{:?}; ", self.0))?; fmt.write_str(&self.1) } } impl Cache { pub fn new() -> Cache { Cache(RefCell::new(HashMap::new())) } pub fn to_key<'a, K: Step<'a>>(key: &K) -> Key { Key(TypeId::of::(), to_json(key)) } /// Puts a value into the cache. Will panic if called more than once with /// the same key. /// /// Returns the internal key utilized, as an opaque structure, useful only /// for debugging. pub fn put(&self, key: Key, value: &V) where V: Serialize, { let mut cache = self.0.borrow_mut(); let value = to_json(value); assert!(!cache.contains_key(&key), "processing {:?} a second time", key); // Store a boxed str so that it's location in memory never changes and // it's safe for us to return references to it (so long as they live as // long as us). cache.insert(key, value.into_boxed_str()); } pub fn get<'a, V>(&'a self, key: &Key) -> Option where V: Deserialize<'a> + 'a, { let cache = self.0.borrow(); cache.get(key).map(|v| { // Change the lifetime. This borrow is valid for as long as self lives; // the data we're accessing will live as long as us and will be in a // stable location, since we use Box. let v = unsafe { mem::transmute::<&str, &'a str>(v) }; from_json(v) }) } }