#![license = "MIT"]
#![deny(missing_docs, warnings)]
#![feature(macro_rules, default_type_params)]
extern crate typemap;
extern crate phantom;
use typemap::{TypeMap, Assoc};
pub use phantom::Phantom;
pub trait PluginFor<E, R = Self>: Assoc<R> {
fn eval(&mut E, Phantom<Self>) -> Option<R>;
}
pub trait Extensible {
fn extensions(&self) -> &TypeMap;
fn extensions_mut(&mut self) -> &mut TypeMap;
}
pub trait GetCached<R: Clone + 'static>: Extensible {
fn get<'a, P: PluginFor<Self, R> + 'static>(&'a mut self) -> Option<R> {
let f = |value: &'a mut R| -> R {
value.clone()
};
self.get_common::<P, R>(f)
}
fn get_ref<'a, P: PluginFor<Self, R> + 'static>(&'a mut self) -> Option<&'a R> {
let f = |value: &'a mut R| -> &'a R {
&*value
};
self.get_common::<P, &'a R>(f)
}
fn get_mut<'a, P: PluginFor<Self, R> + 'static>(&'a mut self) -> Option<&'a mut R> {
let f = |value: &'a mut R| -> &'a mut R {
value
};
self.get_common::<P, &'a mut R>(f)
}
#[doc(hidden)]
fn get_common<'a, P: PluginFor<Self, R> + 'static, S>(&'a mut self, f: |&'a mut R| -> S)
-> Option<S> {
let found = self.extensions().contains::<P, R>();
if found {
let result = self.extensions_mut().get_mut::<P, R>().unwrap();
return Some(f(result));
}
match PluginFor::eval(self, Phantom::<P>) {
Some(value) => {
self.extensions_mut().insert::<P, R>(value);
self.get_common::<P, S>(f)
},
None => None
}
}
}
pub trait Get<R> {
fn compute<P: PluginFor<Self, R> + Assoc<R>>(&mut self) -> Option<R> {
PluginFor::eval(self, Phantom::<P>)
}
}
impl<T, R> Get<R> for T {}
impl<E: Extensible, R: Clone+'static> GetCached<R> for E {}
#[cfg(test)]
mod test {
use typemap::{TypeMap, Assoc};
use phantom::Phantom;
use super::{Extensible, PluginFor, GetCached};
struct Extended {
map: TypeMap
}
impl Extended {
fn new() -> Extended {
Extended { map: TypeMap::new() }
}
}
impl Extensible for Extended {
fn extensions(&self) -> &TypeMap { &self.map }
fn extensions_mut(&mut self) -> &mut TypeMap { &mut self.map }
}
macro_rules! generate_simple_plugin (
($t:ty, $v:ident, $v2:expr) => {
#[deriving(PartialEq, Show, Clone)]
struct $v(uint);
impl Assoc<$t> for $t {}
impl PluginFor<Extended> for $t {
fn eval(_: &mut Extended, _: Phantom<$t>) -> Option<$t> {
Some($v($v2))
}
}
}
)
generate_simple_plugin!(One, One, 1)
generate_simple_plugin!(Two, Two, 2)
generate_simple_plugin!(Three, Three, 3)
generate_simple_plugin!(Four, Four, 4)
generate_simple_plugin!(Five, Five, 5)
generate_simple_plugin!(Six, Six, 6)
generate_simple_plugin!(Seven, Seven, 7)
generate_simple_plugin!(Eight, Eight, 8)
generate_simple_plugin!(Nine, Nine, 9)
generate_simple_plugin!(Ten, Ten, 10)
#[test] fn test_simple() {
let mut extended = Extended::new();
assert_eq!(extended.get::<One>(), Some(One(1)))
assert_eq!(extended.get::<Two>(), Some(Two(2)))
assert_eq!(extended.get::<Three>(), Some(Three(3)))
}
#[test] fn test_resize() {
let mut extended = Extended::new();
extended.get::<One>();
extended.get::<Two>();
extended.get::<Three>();
extended.get::<Four>();
extended.get::<Five>();
extended.get::<Six>();
extended.get::<Seven>();
extended.get::<Eight>();
extended.get::<Nine>();
extended.get::<Ten>();
assert_eq!(extended.get_ref::<One>(), Some(&One(1)))
}
#[test] fn test_custom_return_type() {
let mut extended = Extended::new();
struct IntPlugin;
impl Assoc<i32> for IntPlugin {}
impl PluginFor<Extended, i32> for IntPlugin {
fn eval(_: &mut Extended, _: Phantom<IntPlugin>) -> Option<i32> {
Some(0i32)
}
}
assert_eq!(extended.get::<IntPlugin>().unwrap(), 0i32);
}
}