1use crate::state::StateMap;
4use http::Method;
5use std::any::{Any, TypeId};
6use std::borrow::Cow;
7use std::collections::{HashMap, HashSet};
8use std::sync::Arc;
9
10pub trait RouteValue: Any + Send + Sync + 'static {
12 fn check(&self, _ctx: &BuildCtx<'_>) -> Result<(), String> {
14 Ok(())
15 }
16
17 fn label(&self) -> Cow<'static, str> {
19 Cow::Borrowed(std::any::type_name::<Self>())
20 }
21}
22
23pub struct BuildCtx<'a> {
25 pub state: &'a StateMap,
26 pub installed_plugins: &'a HashSet<&'static str>,
27 pub route_path: &'a str,
28 pub route_method: Option<&'a Method>,
29}
30
31type CheckFn = Arc<dyn Fn(&BuildCtx<'_>) -> Result<(), String> + Send + Sync>;
32
33#[derive(Default, Clone)]
35pub struct MetaMap {
36 map: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
37 checkers: HashMap<TypeId, CheckFn>,
38 labels: HashMap<TypeId, String>,
39}
40
41impl MetaMap {
42 pub fn new() -> Self {
43 Self::default()
44 }
45
46 pub fn insert<T: RouteValue>(&mut self, value: T) {
47 let id = TypeId::of::<T>();
48 let label = value.label().into_owned();
49 let arc = Arc::new(value);
50 let for_check = Arc::clone(&arc);
51 self.map.insert(id, arc);
52 self.checkers.insert(
53 id,
54 Arc::new(move |ctx| RouteValue::check(for_check.as_ref(), ctx)),
55 );
56 self.labels.insert(id, label);
57 }
58
59 pub fn get<T: RouteValue>(&self) -> Option<Arc<T>> {
60 self.map
61 .get(&TypeId::of::<T>())
62 .and_then(|v| v.clone().downcast::<T>().ok())
63 }
64
65 pub fn extend(&mut self, other: MetaMap) {
67 for (id, v) in other.map {
68 self.map.insert(id, v);
69 }
70 for (id, c) in other.checkers {
71 self.checkers.insert(id, c);
72 }
73 for (id, l) in other.labels {
74 self.labels.insert(id, l);
75 }
76 }
77
78 pub fn is_empty(&self) -> bool {
79 self.map.is_empty()
80 }
81
82 pub fn check_all(&self, ctx: &BuildCtx<'_>) -> Result<(), String> {
83 for check in self.checkers.values() {
84 check(ctx)?;
85 }
86 Ok(())
87 }
88
89 pub fn labels(&self) -> Vec<&str> {
91 let mut v: Vec<_> = self.labels.values().map(|s| s.as_str()).collect();
92 v.sort_unstable();
93 v
94 }
95}
96
97pub struct Needs<T>(std::marker::PhantomData<fn() -> T>);
99
100impl<T: Send + Sync + 'static> Needs<T> {
101 pub fn new() -> Self {
102 Self(std::marker::PhantomData)
103 }
104}
105
106impl<T: Send + Sync + 'static> Default for Needs<T> {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112impl<T: Send + Sync + 'static> RouteValue for Needs<T> {
113 fn check(&self, ctx: &BuildCtx<'_>) -> Result<(), String> {
114 if ctx.state.get::<T>().is_some() {
115 Ok(())
116 } else {
117 Err(format!(
118 "route {} needs state `{}` (missing in App::state)",
119 ctx.route_path,
120 std::any::type_name::<T>()
121 ))
122 }
123 }
124
125 fn label(&self) -> Cow<'static, str> {
126 Cow::Owned(format!("Needs<{}>", std::any::type_name::<T>()))
127 }
128}