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}