token_store/
lib.rs

1//! # Token Store
2//!
3//! This crate provides a small abstraction of a type allowing
4//! you to stroe values of arbitrary types and retrieving them
5//! using tokens values that are cheap to move around and clone.
6//!
7//! The typical use-case is a data store shared by large portions
8//! of an application requiring sequential access to parts of this
9//! store, but while not caring nor being able to know what else
10//! is stored in it.
11//!
12//! ## How to use it
13//!
14//! ```
15//! # extern crate token_store;
16//! use token_store::Store;
17//!
18//! # fn main(){
19//! // create a store
20//! let mut store = Store::new();
21//!
22//! // insert some things in it, you are given tokens
23//! let token1 = store.insert(42);
24//! // you can store any type as log as it is `Any + 'static`
25//! let token2 = store.insert(String::from("I like trains"));
26//! // the tokens keep the information of the store type,
27//! // as such you don't need any annotation to retrieve a value:
28//! store.get_mut(&token2).push_str(", and cars too!");
29//! # }
30//! ```
31//!
32//! The retrieved tokens can be cloned and shared as you like between various
33//! parts of your code.
34//!
35//! Note however that, as it is possible to store `!Send` types in the `token_store`,
36//! neither the store nor its tokens can be shared accross threads.
37//!
38//! ## Value scopes and genericity
39//!
40//! It is also possible to access simultaneously several values of the store using
41//! a scoped access:
42//!
43//! ```
44//! # extern crate token_store;
45//! # use token_store::{Store, StoreProxy};
46//! # fn main() {
47//! let mut store = Store::new();
48//! let token = store.insert(42);
49//! store.with_value(&token, |proxy, value| {
50//!     // Here, proxy is a `StoreProxy`, it allows you to to all you want with the
51//!     // store, as long as you do not try to access again the value guarded by
52//!     // the token provided to `with_value`.
53//!     // Also, value is a mutable reference to the value guarded by this token.
54//!
55//!     // You can nest calls to `with_value` to access several values simultaneously
56//!     let token2 = proxy.insert(String::new());
57//!     proxy.with_value(&token2, |proxy, value2| {
58//!         // Here you can access value, value2, as well as a proxy tracking that
59//!         // both values are borrowed
60//!     });
61//! });
62//! # }
63//! ```
64//!
65//! Two implementations of the `From` trait are also provided, allowing you to convert
66//! both a `&mut Store` and a `&mut StoreProxy` into a `StoreProxy`. This is to help
67//! generic code like this:
68//!
69//! ```
70//! # extern crate token_store;
71//! # use token_store::{Store, StoreProxy};
72//! # fn main() {}
73//! fn do_stuff<'store, S: Into<StoreProxy<'store>>>(s: S) {
74//!     let proxy = s.into();
75//!     // we now have a store proxy, and can do our stuff with it
76//!     // and the caller can call us directly with a `&mut Store` or
77//!     // from within a value scope.
78//! }
79//! ```
80#![warn(missing_docs)]
81
82use std::any::Any;
83use std::borrow::Cow;
84use std::cell::Cell;
85use std::marker::PhantomData;
86use std::rc::Rc;
87
88/// A token store
89///
90/// This struct allows you to store various values in a store
91/// and access them back using the provided tokens.
92pub struct Store {
93    values: Vec<Option<(Box<Any>, Rc<Cell<bool>>)>>,
94}
95
96/// A token for accessing the store contents
97pub struct Token<V> {
98    id: usize,
99    live: Rc<Cell<bool>>,
100    _type: PhantomData<V>,
101}
102
103impl<V> Token<V> {
104    /// Check wether this token is still valid
105    ///
106    /// If it is not, trying to use it to access contents
107    /// will panic.
108    pub fn valid(&self) -> bool {
109        self.live.get()
110    }
111}
112
113impl<V> Clone for Token<V> {
114    fn clone(&self) -> Token<V> {
115        Token {
116            id: self.id,
117            live: self.live.clone(),
118            _type: PhantomData,
119        }
120    }
121}
122
123impl Store {
124    /// Create a new store
125    pub fn new() -> Store {
126        Store { values: Vec::new() }
127    }
128
129    /// Insert a new value in this store
130    ///
131    /// Returns a clonable token that you can later use to access this
132    /// value.
133    pub fn insert<V: Any + 'static>(&mut self, value: V) -> Token<V> {
134        let boxed = Box::new(value) as Box<Any>;
135        let live = Rc::new(Cell::new(true));
136        {
137            // artificial scope to make the borrow checker happy
138            let empty_slot = self.values
139                .iter_mut()
140                .enumerate()
141                .find(|&(_, ref s)| s.is_none());
142            if let Some((id, slot)) = empty_slot {
143                *slot = Some((boxed, live.clone()));
144                return Token {
145                    id: id,
146                    live: live,
147                    _type: PhantomData,
148                };
149            }
150        }
151        self.values.push(Some((boxed, live.clone())));
152        Token {
153            id: self.values.len() - 1,
154            live: live,
155            _type: PhantomData,
156        }
157    }
158
159    /// Access value previously inserted in this store
160    ///
161    /// Panics if the provided token corresponds to a value that was removed.
162    pub fn get<V: Any + 'static>(&self, token: &Token<V>) -> &V {
163        if !token.live.get() {
164            panic!("Attempted to access a state value that was already removed!");
165        }
166        self.values[token.id]
167            .as_ref()
168            .and_then(|t| t.0.downcast_ref::<V>())
169            .unwrap()
170    }
171
172    /// Mutably access value previously inserted in this store
173    ///
174    /// Panics if the provided token corresponds to a value that was removed.
175    pub fn get_mut<V: Any + 'static>(&mut self, token: &Token<V>) -> &mut V {
176        if !token.live.get() {
177            panic!("Attempted to access a state value that was already removed!");
178        }
179        self.values[token.id]
180            .as_mut()
181            .and_then(|t| t.0.downcast_mut::<V>())
182            .unwrap()
183    }
184
185    /// Remove a value previously inserted in this store
186    ///
187    /// Panics if the provided token corresponds to a value that was already
188    /// removed.
189    pub fn remove<V: Any + 'static>(&mut self, token: Token<V>) -> V {
190        if !token.live.get() {
191            panic!("Attempted to remove a state value that was already removed!");
192        }
193        let (boxed, live) = self.values[token.id].take().unwrap();
194        live.set(false);
195        *boxed.downcast().unwrap()
196    }
197
198    /// Create a sub-scope with access to a value
199    ///
200    /// In the closure you provide, the value represented by `token`
201    /// will be available as an argument, as well as a `StoreProxy`,
202    /// which allows you to manipulate the other values of the store
203    /// while this one is mutably borrowed.
204    ///
205    /// Attempting to access again the same value from its token from
206    /// within this closure is forbidden, and attempting to do so will
207    /// result in a panic.
208    ///
209    /// The `StoreProxy` provides the same access methods as the `Store`,
210    /// including `with_value`, allowing you to create nested sub-scopes
211    /// accessing multiple store values at the same time.
212    pub fn with_value<V: Any + 'static, T, F>(&mut self, token: &Token<V>, f: F) -> T
213    where
214        F: FnOnce(&mut StoreProxy, &mut V) -> T,
215    {
216        self.as_proxy().with_value(token, f)
217    }
218
219    /// See this `Store` as a `StoreProxy` with no ongoing borrow
220    ///
221    /// This can be usefull for code requiering access to a store,
222    /// but wanting to be generic over being called from a value
223    /// scope or not.
224    ///
225    /// You can also use the `From` and `Into` traits to perform
226    /// this conversion.
227    pub fn as_proxy<'a>(&'a mut self) -> StoreProxy<'a> {
228        StoreProxy {
229            store: self,
230            borrowed: Cow::Owned(Vec::new()),
231        }
232    }
233}
234
235impl<'a> ::std::convert::From<&'a mut Store> for StoreProxy<'a> {
236    fn from(store: &'a mut Store) -> StoreProxy<'a> {
237        store.as_proxy()
238    }
239}
240
241impl<'a, 'b> ::std::convert::From<&'a mut StoreProxy<'b>> for StoreProxy<'a>
242where
243    'b: 'a,
244{
245    fn from(proxy: &'a mut StoreProxy<'b>) -> StoreProxy<'a> {
246        StoreProxy {
247            store: proxy.store,
248            borrowed: proxy.borrowed.clone(),
249        }
250    }
251}
252
253/// A Proxy representing a `Store` with ongoing borrows
254///
255/// This struct represents a handle to a store from which
256/// some values are already mutably borrowed, and as such
257/// cannot be touched.
258///
259/// See `Store::with_value` for detailed explanation of its
260/// use.
261pub struct StoreProxy<'store> {
262    store: &'store mut Store,
263    borrowed: Cow<'store, [usize]>,
264}
265
266impl<'store> StoreProxy<'store> {
267    /// Insert a new value in the proxified store
268    ///
269    /// Returns a clonable token that you can later use to access this
270    /// value.
271    pub fn insert<V: Any + 'static>(&mut self, value: V) -> Token<V> {
272        self.store.insert(value)
273    }
274
275    /// Access value previously inserted in the proxified store
276    ///
277    /// Panics if the provided token corresponds to a value that was removed, or
278    /// if this value is already borrowed.
279    pub fn get<V: Any + 'static>(&self, token: &Token<V>) -> &V {
280        if self.borrowed.contains(&token.id) {
281            panic!("Attempted to borrow twice the same value from the Store!");
282        }
283        self.store.get(token)
284    }
285
286    /// Mutably access value previously inserted in the proxified store
287    ///
288    /// Panics if the provided token corresponds to a value that was removed, or
289    /// if this value is already borrowed.
290    pub fn get_mut<V: Any + 'static>(&mut self, token: &Token<V>) -> &mut V {
291        if self.borrowed.contains(&token.id) {
292            panic!("Attempted to borrow twice the same value from the Store!");
293        }
294        self.store.get_mut(token)
295    }
296
297    /// Remove a value previously inserted in the proxified store
298    ///
299    /// Panics if the provided token corresponds to a value that was already
300    /// removed, or if this value is already borrowed.
301    pub fn remove<V: Any + 'static>(&mut self, token: Token<V>) -> V {
302        if self.borrowed.contains(&token.id) {
303            panic!("Attempted to remove a value from the Store while it was borrowed!");
304        }
305        self.store.remove(token)
306    }
307
308    /// Create a sub-scope with access to a value
309    ///
310    /// Panics if the provided token corresponds to a value that was removed, or
311    /// if this value is already borrowed.
312    ///
313    /// See `Store::with_value` for full documentation.
314    pub fn with_value<V: Any + 'static, T, F>(&mut self, token: &Token<V>, f: F) -> T
315    where
316        F: FnOnce(&mut StoreProxy, &mut V) -> T,
317    {
318        if self.borrowed.contains(&token.id) {
319            panic!("Attempted to borrow twice the same value from the Store!");
320        }
321        let value_ptr = { self.store.get_mut(token) as *mut V };
322        let value = unsafe { &mut *value_ptr };
323        let mut deeper_proxy = StoreProxy {
324            store: &mut *self.store,
325            borrowed: {
326                let mut my_borrowed = self.borrowed.clone().into_owned();
327                my_borrowed.push(token.id);
328                Cow::Owned(my_borrowed)
329            },
330        };
331        f(&mut deeper_proxy, value)
332    }
333}
334
335#[cfg(test)]
336mod tests {
337    use super::*;
338    #[test]
339    fn insert_and_retrieve() {
340        let mut store = Store::new();
341        let token1 = store.insert(42);
342        let token2 = store.insert("I like trains".to_owned());
343        assert_eq!(*store.get(&token1), 42);
344        assert_eq!(store.get(&token2), "I like trains");
345    }
346
347    #[test]
348    fn mutate() {
349        let mut store = Store::new();
350        let token = store.insert(42);
351        {
352            let v = store.get_mut(&token);
353            *v += 5;
354        }
355        assert_eq!(*store.get(&token), 47);
356    }
357
358    #[test]
359    #[should_panic]
360    fn no_access_removed() {
361        let mut store = Store::new();
362        let token = store.insert(42);
363        let token2 = token.clone();
364        store.remove(token2);
365        let _v = store.get(&token);
366    }
367
368    #[test]
369    #[should_panic]
370    fn no_mut_access_removed() {
371        let mut store = Store::new();
372        let token = store.insert(42);
373        let token2 = token.clone();
374        store.remove(token2);
375        let _v = store.get_mut(&token);
376    }
377
378    #[test]
379    #[should_panic]
380    fn no_double_remove() {
381        let mut store = Store::new();
382        let token = store.insert(42);
383        let token2 = token.clone();
384        store.remove(token2);
385        store.remove(token);
386    }
387
388
389    #[test]
390    fn place_reuse() {
391        let mut store = Store::new();
392        let token = store.insert(42);
393        store.remove(token);
394        let token = store.insert("I like trains");
395        assert_eq!(store.values.len(), 1);
396        assert_eq!(*store.get(&token), "I like trains");
397    }
398
399    #[test]
400    fn with_value_manipulate() {
401        let mut store = Store::new();
402        let token1 = store.insert("I like trains".to_owned());
403        let token2 = store.insert(42);
404        let len = store.with_value(&token1, |proxy, value1| {
405            *proxy.get_mut(&token2) += 10;
406            let token3 = proxy.with_value(&token2, |proxy, value2| {
407                *value2 *= 2;
408                proxy.insert(*value2 as f32 + 0.5)
409            });
410            let number = proxy.remove(token2);
411            value1.push_str(&format!(": {} = {}", number, proxy.get(&token3)));
412            value1.len()
413        });
414        assert_eq!(len, 26);
415        assert_eq!(store.get(&token1), "I like trains: 104 = 104.5");
416    }
417
418    #[test]
419    #[should_panic]
420    fn no_double_with_value() {
421        let mut store = Store::new();
422        let token = store.insert(42);
423        store.with_value(&token, |proxy, _| {
424            proxy.with_value(&token, |_, _| {});
425        });
426    }
427
428    #[test]
429    #[should_panic]
430    fn no_alias_get_and_with_value() {
431        let mut store = Store::new();
432        let token = store.insert(42);
433        store.with_value(&token, |proxy, _| {
434            let _v = proxy.get(&token);
435        });
436    }
437
438    #[test]
439    #[should_panic]
440    fn no_alias_get_mut_and_with_value() {
441        let mut store = Store::new();
442        let token = store.insert(42);
443        store.with_value(&token, |proxy, _| {
444            let _v = proxy.get_mut(&token);
445        });
446    }
447
448    #[test]
449    #[should_panic]
450    fn no_alias_remove_and_with_value() {
451        let mut store = Store::new();
452        let token = store.insert(42);
453        store.with_value(&token, |proxy, _| {
454            let _v = proxy.remove(token.clone());
455        });
456    }
457
458    #[test]
459    fn generic_into_store_proxy() {
460        fn insert_42<'a, S: Into<StoreProxy<'a>>>(s: S) -> Token<i32> {
461            let mut proxy = s.into();
462            proxy.insert(42)
463        }
464
465        let mut store = Store::new();
466        let token1 = insert_42(&mut store);
467        let token2 = store.with_value(&token1, |proxy, _| insert_42(proxy));
468        assert_eq!(*store.get(&token1), 42);
469        assert_eq!(*store.get(&token2), 42);
470    }
471
472    #[test]
473    fn token_validity() {
474        let mut store = Store::new();
475        let token = store.insert(42);
476        assert!(token.valid());
477        store.remove(token.clone());
478        assert!(!token.valid());
479    }
480}