use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt::{self, Debug};
use futures::future::FutureObj;
use crate::{Extract, Request, Response, RouteMatch};
mod default_config;
pub use self::default_config::{Configuration, ConfigurationBuilder, Environment};
trait StoreItem: Any + Send + Sync {
fn clone_any(&self) -> Box<dyn StoreItem>;
fn as_dyn_any(&self) -> &(dyn Any + Send + Sync);
fn as_dyn_any_mut(&mut self) -> &mut (dyn Any + Send + Sync);
fn fmt_debug(&self, fmt: &mut fmt::Formatter) -> fmt::Result;
}
impl<T> StoreItem for T
where
T: Any + Debug + Clone + Send + Sync,
{
fn clone_any(&self) -> Box<dyn StoreItem> {
Box::new(self.clone())
}
fn as_dyn_any(&self) -> &(dyn Any + Send + Sync) {
self
}
fn as_dyn_any_mut(&mut self) -> &mut (dyn Any + Send + Sync) {
self
}
fn fmt_debug(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
self.fmt(fmt)
}
}
impl Clone for Box<dyn StoreItem> {
fn clone(&self) -> Box<dyn StoreItem> {
(&**self).clone_any()
}
}
impl Debug for Box<dyn StoreItem> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
(&**self).fmt_debug(fmt)
}
}
#[derive(Clone)]
pub struct Store(HashMap<TypeId, Box<dyn StoreItem>>);
impl Debug for Store {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_set().entries(self.0.values()).finish()
}
}
impl Store {
pub(crate) fn new() -> Self {
Store(HashMap::new())
}
pub(crate) fn merge(&mut self, base: &Store) {
let overlay = std::mem::replace(&mut self.0, base.0.clone());
self.0.extend(overlay);
}
pub fn read<T: Any + Debug + Clone + Send + Sync>(&self) -> Option<&T> {
let id = TypeId::of::<T>();
self.0
.get(&id)
.and_then(|v| (**v).as_dyn_any().downcast_ref::<T>())
}
pub fn write<T: Any + Debug + Clone + Send + Sync>(&mut self, value: T) {
let id = TypeId::of::<T>();
self.0.insert(id, Box::new(value) as Box<dyn StoreItem>);
}
}
pub struct ExtractConfiguration<T>(pub Option<T>);
impl<S, T> Extract<S> for ExtractConfiguration<T>
where
S: 'static,
T: Any + Debug + Clone + Send + Sync + 'static,
{
type Fut = FutureObj<'static, Result<Self, Response>>;
fn extract(
data: &mut S,
req: &mut Request,
params: &Option<RouteMatch<'_>>,
store: &Store,
) -> Self::Fut {
let store_item = store.read().cloned();
FutureObj::new(Box::new(
async move { Ok(ExtractConfiguration(store_item)) },
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn store_read_write() {
let mut store = Store::new();
assert_eq!(store.read::<usize>(), None);
assert_eq!(store.read::<isize>(), None);
store.write(42usize);
store.write(-3isize);
assert_eq!(store.read::<usize>(), Some(&42));
assert_eq!(store.read::<isize>(), Some(&-3));
store.write(3usize);
assert_eq!(store.read::<usize>(), Some(&3));
}
#[test]
fn store_clone() {
let mut store = Store::new();
store.write(42usize);
store.write(String::from("foo"));
let mut new_store = store.clone();
new_store.write(3usize);
new_store.write(4u32);
assert_eq!(store.read::<usize>(), Some(&42));
assert_eq!(store.read::<u32>(), None);
assert_eq!(store.read::<String>(), Some(&"foo".into()));
assert_eq!(new_store.read::<usize>(), Some(&3));
assert_eq!(new_store.read::<u32>(), Some(&4));
assert_eq!(new_store.read::<String>(), Some(&"foo".into()));
}
}