1use http::Method;
4use schemars::schema::RootSchema;
5use schemars::JsonSchema;
6use std::collections::HashSet;
7
8use crate::error::Error;
9use crate::Result;
10
11#[derive(Debug, Clone)]
13pub struct EndpointSchema {
14 pub path: String,
16 pub method: Method,
18 pub request_schema: Option<RootSchema>,
20 pub response_schema: Option<RootSchema>,
22 pub query_schema: Option<RootSchema>,
24 pub params_schema: Option<RootSchema>,
26}
27
28#[derive(Debug, Clone)]
30pub struct SchemaRegistry {
31 entries: Vec<EndpointSchema>,
32 strict: bool,
33 routes: HashSet<String>,
34}
35
36impl Default for SchemaRegistry {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl SchemaRegistry {
43 pub fn new() -> Self {
45 Self {
46 entries: Vec::new(),
47 strict: false,
48 routes: HashSet::new(),
49 }
50 }
51
52 pub fn strict(mut self, strict: bool) -> Self {
54 self.strict = strict;
55 self
56 }
57
58 pub fn is_strict(&self) -> bool {
60 self.strict
61 }
62
63 pub fn register_endpoint(
65 &mut self,
66 path: impl Into<String>,
67 method: Method,
68 request_schema: Option<RootSchema>,
69 response_schema: Option<RootSchema>,
70 ) {
71 let path = path.into();
72 self.routes.insert(route_key(&path, &method));
73 self.entries.push(EndpointSchema {
74 path,
75 method,
76 request_schema,
77 response_schema,
78 query_schema: None,
79 params_schema: None,
80 });
81 }
82
83 pub fn register_full(
85 &mut self,
86 path: impl Into<String>,
87 method: Method,
88 request_schema: Option<RootSchema>,
89 response_schema: Option<RootSchema>,
90 query_schema: Option<RootSchema>,
91 params_schema: Option<RootSchema>,
92 ) {
93 let path = path.into();
94 self.routes.insert(route_key(&path, &method));
95 self.entries.push(EndpointSchema {
96 path,
97 method,
98 request_schema,
99 response_schema,
100 query_schema,
101 params_schema,
102 });
103 }
104
105 pub fn register_typed<E, Req, Res>(&mut self)
107 where
108 E: crate::Endpoint,
109 Req: JsonSchema + 'static,
110 Res: JsonSchema + 'static,
111 E::Params: JsonSchema,
112 E::Query: JsonSchema,
113 {
114 self.register_full(
115 E::PATH,
116 E::METHOD,
117 Some(schemars::schema_for!(Req)),
118 Some(schemars::schema_for!(Res)),
119 Some(schemars::schema_for!(E::Query)),
120 Some(schemars::schema_for!(E::Params)),
121 );
122 }
123
124 pub fn ensure_route(&self, path: &str, method: &Method) -> Result<()> {
126 if !self.strict {
127 return Ok(());
128 }
129 let key = route_key(path, method);
130 if self.routes.contains(&key) {
131 Ok(())
132 } else {
133 Err(Error::Other(format!(
134 "route not in schema registry: {method} {path}"
135 )))
136 }
137 }
138
139 pub fn entries(&self) -> &[EndpointSchema] {
141 &self.entries
142 }
143}
144
145fn route_key(path: &str, method: &Method) -> String {
146 format!("{method}:{path}")
147}