1use super::{
2 models::{
3 Either, HttpMethod, Reference, Resolvable, ResolvableParameter, ResolvablePathItem,
4 ResolvableResponse,
5 },
6 Schema,
7};
8use crate::error::ValidationError;
9use heck::ToPascalCase;
10
11use std::{cell::RefCell, collections::BTreeMap, mem};
12
13const DEF_REF_PREFIX: &str = "#/definitions/";
16const PARAM_REF_PREFIX: &str = "#/parameters/";
17const RESP_REF_PREFIX: &str = "#/responses/";
18
19type DefinitionsMap<S> = BTreeMap<String, Resolvable<S>>;
20type OperationsMap<S> = BTreeMap<String, ResolvablePathItem<S>>;
21type ParametersMap<S> = BTreeMap<String, ResolvableParameter<S>>;
22type ResponsesMap<S> = BTreeMap<String, ResolvableResponse<S>>;
23
24pub(crate) struct Resolver<S> {
29 cyclic_defs: RefCell<Vec<Resolvable<S>>>,
31 pub defs: DefinitionsMap<S>,
33 pub paths: OperationsMap<S>,
35 pub params: ParametersMap<S>,
37 pub resp: ResponsesMap<S>,
39}
40
41impl<S>
42 From<(
43 DefinitionsMap<S>,
44 OperationsMap<S>,
45 ParametersMap<S>,
46 ResponsesMap<S>,
47 )> for Resolver<S>
48{
49 fn from(
50 (defs, paths, params, resp): (
51 DefinitionsMap<S>,
52 OperationsMap<S>,
53 ParametersMap<S>,
54 ResponsesMap<S>,
55 ),
56 ) -> Self {
57 Resolver {
58 cyclic_defs: vec![].into(),
59 defs,
60 paths,
61 params,
62 resp,
63 }
64 }
65}
66
67impl<S> Resolver<S>
68where
69 S: Schema + Default,
70{
71 pub fn resolve(&mut self) -> Result<(), ValidationError> {
73 let mut paths = mem::take(&mut self.paths);
77 paths.iter_mut().try_for_each(|(path, map)| {
78 log::trace!("Checking path: {}", path);
79 self.resolve_operations(path, map)
80 })?;
81 self.paths = paths;
82
83 for (name, schema) in &self.defs {
85 schema.write().unwrap().set_name(name);
86 }
87
88 for (name, schema) in &self.defs {
89 log::trace!("Entering: {}", name);
90 self.resolve_definitions_no_root_ref(schema)?;
91
92 for def in self.cyclic_defs.borrow_mut().drain(..) {
93 log::debug!(
94 "Cyclic definition detected: {:?}",
95 def.read().unwrap().name().unwrap()
96 );
97 def.write().unwrap().set_cyclic(true);
98 }
99 }
100
101 Ok(())
102 }
103
104 fn resolve_definitions_no_root_ref(
107 &self,
108 schema: &Resolvable<S>,
109 ) -> Result<(), ValidationError> {
110 let mut schema = match schema.try_write().ok() {
111 Some(s) => s,
112 None => {
113 self.cyclic_defs.borrow_mut().push(schema.clone());
114 return Ok(());
115 }
116 };
117
118 if let Some(inner) = schema.items_mut() {
119 return self.resolve_definitions(inner);
120 }
121
122 if let Some(props) = schema.properties_mut() {
123 props.iter_mut().try_for_each(|(k, s)| {
124 log::trace!("Resolving property {:?}", k);
125 self.resolve_definitions(s)
126 })?;
127 }
128
129 if let Some(props) = schema
130 .additional_properties_mut()
131 .and_then(|s| s.right_mut())
132 {
133 self.resolve_definitions(props)?;
134 }
135
136 Ok(())
137 }
138
139 fn resolve_definitions(&self, schema: &mut Resolvable<S>) -> Result<(), ValidationError> {
142 let ref_def = {
143 let s = match schema.try_read().ok() {
144 Some(s) => s,
145 None => {
146 self.cyclic_defs.borrow_mut().push(schema.clone());
147 return Ok(());
148 }
149 };
150
151 if let Some(ref_name) = s.reference() {
152 log::trace!("Resolving definition {}", ref_name);
153 Some(self.resolve_definition_reference(ref_name)?)
154 } else {
155 None
156 }
157 };
158
159 if let Some(new) = ref_def {
160 *schema = match schema {
161 Resolvable::Raw(old) => Resolvable::Resolved {
162 old: old.clone(),
163 new: (*new).clone(),
164 },
165 _ => unimplemented!("schema already resolved?"),
166 };
167 }
168
169 self.resolve_definitions_no_root_ref(&*schema)
170 }
171
172 fn resolve_operations(
174 &mut self,
175 path: &str,
176 map: &mut ResolvablePathItem<S>,
177 ) -> Result<(), ValidationError> {
178 for (&method, op) in &mut map.methods {
179 self.resolve_parameters(Some(method), path, &mut op.parameters)?;
180 for resp in op.responses.values_mut() {
181 let ref_resp = if let Some(r) = resp.left() {
182 log::trace!("Resolving response {}", r.reference);
183 Some(self.resolve_response_reference(&r.reference)?)
184 } else {
185 None
186 };
187
188 if let Some(new) = ref_resp {
189 *resp = Either::Right(new);
190 }
191
192 let mut response = resp.write().unwrap();
193 self.resolve_operation_schema(
194 &mut response.schema,
195 Some(method),
196 path,
197 "Response",
198 )?;
199 }
200 }
201
202 self.resolve_parameters(None, path, &mut map.parameters)
203 }
204
205 fn resolve_parameters(
207 &mut self,
208 method: Option<HttpMethod>,
209 path: &str,
210 params: &mut [Either<Reference, ResolvableParameter<S>>],
211 ) -> Result<(), ValidationError> {
212 for p in params.iter_mut() {
213 let ref_param = if let Some(r) = p.left() {
214 log::trace!("Resolving parameter {}", r.reference);
215 Some(self.resolve_parameter_reference(&r.reference)?)
216 } else {
217 None
218 };
219
220 if let Some(new) = ref_param {
221 *p = Either::Right(new);
222 }
223
224 let mut param = p.write().unwrap();
225 self.resolve_operation_schema(&mut param.schema, method, path, "Body")?;
226 }
227
228 Ok(())
229 }
230
231 fn resolve_operation_schema(
233 &mut self,
234 s: &mut Option<Resolvable<S>>,
235 method: Option<HttpMethod>,
236 path: &str,
237 suffix: &str,
238 ) -> Result<(), ValidationError> {
239 let schema = match s.as_mut() {
240 Some(s) => s,
241 _ => return Ok(()),
242 };
243
244 match schema {
245 Resolvable::Raw(ref s) if s.read().unwrap().reference().is_none() => {
246 let prefix = method.map(|s| s.to_string()).unwrap_or_default();
249 let def_name = (prefix + path + suffix).to_pascal_case();
250 let mut ref_schema = S::default();
251 ref_schema.set_reference(format!("{}{}", DEF_REF_PREFIX, def_name));
252 let old_schema = mem::replace(schema, ref_schema.into());
253 self.defs.insert(def_name, old_schema);
254 }
255 _ => (),
256 }
257
258 self.resolve_definitions(schema)?;
259 Ok(())
260 }
261
262 fn resolve_definition_reference(&self, name: &str) -> Result<Resolvable<S>, ValidationError> {
264 if !name.starts_with(DEF_REF_PREFIX) {
265 return Err(ValidationError::InvalidRefUri(name.into()));
266 }
267
268 let name = &name[DEF_REF_PREFIX.len()..];
269 let schema = self
270 .defs
271 .get(name)
272 .ok_or_else(|| ValidationError::MissingReference(name.into()))?;
273 Ok(schema.clone())
274 }
275
276 fn resolve_parameter_reference(
278 &self,
279 name: &str,
280 ) -> Result<ResolvableParameter<S>, ValidationError> {
281 if !name.starts_with(PARAM_REF_PREFIX) {
282 return Err(ValidationError::InvalidRefUri(name.into()));
283 }
284
285 let name = &name[PARAM_REF_PREFIX.len()..];
286 let param = self
287 .params
288 .get(name)
289 .ok_or_else(|| ValidationError::MissingReference(name.into()))?;
290 Ok(param.clone())
291 }
292
293 fn resolve_response_reference(
295 &self,
296 name: &str,
297 ) -> Result<ResolvableResponse<S>, ValidationError> {
298 if !name.starts_with(RESP_REF_PREFIX) {
299 return Err(ValidationError::InvalidRefUri(name.into()));
300 }
301
302 let name = &name[RESP_REF_PREFIX.len()..];
303 let resp = self
304 .resp
305 .get(name)
306 .ok_or_else(|| ValidationError::MissingReference(name.into()))?;
307 Ok(resp.clone())
308 }
309}