plugin/
lib.rs

1#![deny(missing_docs, warnings)]
2
3//! Lazily-Evaluated, Order-Independent Plugins for Extensible Types.
4
5extern crate typemap;
6
7use std::any::Any;
8use typemap::{TypeMap, Key};
9
10/// Implementers of this trait can act as plugins for other types, via `OtherType::get<P>()`.
11///
12/// To create a plugin, implement this trait and provide an empty implementation
13/// of `Key` to associate the plugin with its return type, `Key::Value`.
14pub trait Plugin<E: ?Sized>: Key {
15    /// The error type associated with this plugin.
16    type Error;
17
18    /// Create the plugin from an instance of the extended type.
19    ///
20    /// While `eval` is given a mutable reference to the extended
21    /// type, it is important for implementers to remember that
22    /// the result of `eval` is usually cached, so care should
23    /// be taken when doing mutation on the extended type.
24    fn eval(&mut E) -> Result<Self::Value, Self::Error>;
25}
26
27/// Defines an interface that extensible types must implement.
28///
29/// Extensible types must contain a TypeMap.
30pub trait Extensible {
31    /// Get a reference to the type's extension storage.
32    fn extensions(&self) -> &TypeMap;
33
34    /// Get a mutable reference to the type's extension storage.
35    fn extensions_mut(&mut self) -> &mut TypeMap;
36}
37
38/// An interface for plugins that cache values between calls.
39///
40/// `R` is the type of the plugin's return value, which must be cloneable.
41pub trait Pluggable {
42    /// Return a copy of the plugin's produced value.
43    ///
44    /// The plugin will be created if it doesn't exist already.
45    /// If plugin creation fails, an error is returned.
46    ///
47    /// `P` is the plugin type.
48    fn get<P: Plugin<Self>>(&mut self) -> Result<P::Value, P::Error>
49    where P::Value: Clone + Any, Self: Extensible {
50        self.get_ref::<P>().map(|v| v.clone())
51    }
52
53    /// Return a reference to the plugin's produced value.
54    ///
55    /// The plugin will be created if it doesn't exist already.
56    /// If plugin creation fails an error is returned.
57    ///
58    /// `P` is the plugin type.
59    fn get_ref<P: Plugin<Self>>(&mut self) -> Result<&P::Value, P::Error>
60    where P::Value: Any, Self: Extensible {
61        self.get_mut::<P>().map(|mutref| &*mutref)
62    }
63
64    /// Return a mutable reference to the plugin's produced value.
65    ///
66    /// The plugin will be created if it doesn't exist already.
67    /// If plugin creation fail an error is returned.
68    ///
69    /// `P` is the plugin type.
70    fn get_mut<P: Plugin<Self>>(&mut self) -> Result<&mut P::Value, P::Error>
71    where P::Value: Any, Self: Extensible {
72        use typemap::Entry::{Occupied, Vacant};
73
74        if self.extensions().contains::<P>() {
75            return Ok(self.extensions_mut().get_mut::<P>().unwrap());
76        }
77
78        P::eval(self).map(move |data| {
79            match self.extensions_mut().entry::<P>() {
80                Vacant(entry) => entry.insert(data),
81                Occupied(..) => panic!("Unreachable.")
82            }
83        })
84    }
85
86    /// Create and evaluate a once-off instance of a plugin.
87    fn compute<P: Plugin<Self>>(&mut self) -> Result<P::Value, P::Error> {
88        <P as Plugin<Self>>::eval(self)
89    }
90}
91
92#[cfg(test)]
93mod test {
94    extern crate void;
95
96    use test::void::{Void, ResultVoidExt};
97
98    use typemap::{TypeMap, Key};
99    use super::{Extensible, Plugin, Pluggable};
100
101    struct Extended {
102        map: TypeMap
103    }
104
105    impl Extended {
106        fn new() -> Extended {
107            Extended { map: TypeMap::new() }
108        }
109    }
110
111    impl Extensible for Extended {
112        fn extensions(&self) -> &TypeMap { &self.map }
113        fn extensions_mut(&mut self) -> &mut TypeMap { &mut self.map }
114    }
115
116    impl Pluggable for Extended {}
117
118    macro_rules! generate_simple_plugin (
119        ($t:ty, $v:ident, $v2:expr) => {
120            #[derive(PartialEq, Debug, Clone)]
121            struct $v(i32);
122
123            impl Key for $t { type Value = $t; }
124
125            impl Plugin<Extended> for $t {
126                type Error = Void;
127
128                fn eval(_: &mut Extended) -> Result<$t, Void> {
129                    Ok($v($v2))
130                }
131            }
132        }
133    );
134
135    generate_simple_plugin!(One, One, 1);
136    generate_simple_plugin!(Two, Two, 2);
137    generate_simple_plugin!(Three, Three, 3);
138    generate_simple_plugin!(Four, Four, 4);
139    generate_simple_plugin!(Five, Five, 5);
140    generate_simple_plugin!(Six, Six, 6);
141    generate_simple_plugin!(Seven, Seven, 7);
142    generate_simple_plugin!(Eight, Eight, 8);
143    generate_simple_plugin!(Nine, Nine, 9);
144    generate_simple_plugin!(Ten, Ten, 10);
145
146    #[test] fn test_simple() {
147        let mut extended = Extended::new();
148        assert_eq!(extended.get::<One>(),   Ok(One(1)));
149        assert_eq!(extended.get::<Two>(),   Ok(Two(2)));
150        assert_eq!(extended.get::<Three>(), Ok(Three(3)));
151    }
152
153    #[test] fn test_resize() {
154        let mut extended = Extended::new();
155        extended.get::<One>().void_unwrap();
156        extended.get::<Two>().void_unwrap();
157        extended.get::<Three>().void_unwrap();
158        extended.get::<Four>().void_unwrap();
159        extended.get::<Five>().void_unwrap();
160        extended.get::<Six>().void_unwrap();
161        extended.get::<Seven>().void_unwrap();
162        extended.get::<Eight>().void_unwrap();
163        extended.get::<Nine>().void_unwrap();
164        extended.get::<Ten>().void_unwrap();
165        assert_eq!(extended.get_ref::<One>(), Ok(&One(1)))
166    }
167
168    #[test] fn test_custom_return_type() {
169        let mut extended = Extended::new();
170
171        // Define a struct.
172        struct IntPlugin;
173
174        // Map it onto an `i32` value.
175        impl Key for IntPlugin { type Value = i32; }
176
177        // Define the plugin evaluation function.
178        impl Plugin<Extended> for IntPlugin {
179            type Error = Void;
180
181            fn eval(_: &mut Extended) -> Result<i32, Void> {
182                Ok(0i32)
183            }
184        }
185        assert_eq!(extended.get::<IntPlugin>().void_unwrap(), 0i32);
186    }
187}
188