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