use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::Arc;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum DepsError {
#[error("missing dependency: {0}")]
MissingDependency(String),
}
#[derive(Default, Clone)]
pub struct Deps(HashMap<TypeId, Arc<dyn Any + Send + Sync>>);
impl Deps {
pub fn insert<T: Any + Send + Sync + 'static>(&mut self, val: Arc<T>) -> &mut Self {
self.0.insert(TypeId::of::<T>(), Arc::new(val));
self
}
pub fn get<T: Any + Send + Sync + 'static>(&self) -> Option<&T> {
self.0
.get(&TypeId::of::<T>())
.and_then(|any| any.downcast_ref::<Arc<T>>())
.map(Arc::as_ref)
}
pub fn get_arc<T: Any + Send + Sync + 'static>(&self) -> Option<Arc<T>> {
self.0
.get(&TypeId::of::<T>())
.and_then(|any| any.downcast_ref::<Arc<T>>())
.cloned()
}
pub fn require<T: Any + Send + Sync + 'static>(&self) -> Result<&T, DepsError> {
self.get::<T>()
.ok_or_else(|| DepsError::MissingDependency(std::any::type_name::<T>().to_owned()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
struct MyService {
value: u32,
}
#[test]
fn get_returns_registered_value() {
let mut deps = Deps::default();
deps.insert(Arc::new(MyService { value: 42 }));
assert_eq!(deps.get::<MyService>().unwrap().value, 42);
}
#[test]
fn get_arc_returns_cloned_arc() {
let mut deps = Deps::default();
deps.insert(Arc::new(MyService { value: 7 }));
let arc = deps.get_arc::<MyService>().unwrap();
assert_eq!(arc.value, 7);
}
#[test]
fn require_missing_returns_error() {
let deps = Deps::default();
let err = deps.require::<MyService>().unwrap_err();
assert!(matches!(err, DepsError::MissingDependency(_)));
}
#[test]
fn get_absent_returns_none() {
let deps = Deps::default();
assert!(deps.get::<MyService>().is_none());
}
#[test]
fn deps_clone_shares_arcs() {
let mut deps = Deps::default();
deps.insert(Arc::new(MyService { value: 99 }));
let cloned = deps.clone();
assert_eq!(cloned.get::<MyService>().unwrap().value, 99);
}
}