oag_core/parse/
ref_resolve.rs1use indexmap::IndexMap;
2
3use super::components::Components;
4use super::media_type::MediaType;
5use super::operation::{Operation, PathItem};
6use super::parameter::{Parameter, ParameterOrRef};
7use super::request_body::{RequestBody, RequestBodyOrRef};
8use super::response::{Response, ResponseOrRef};
9use super::schema::{Schema, SchemaOrRef};
10use super::spec::OpenApiSpec;
11use crate::error::ResolveError;
12
13pub struct RefResolver<'a> {
16 components: Option<&'a Components>,
17}
18
19impl<'a> RefResolver<'a> {
20 pub fn new(spec: &'a OpenApiSpec) -> Self {
21 Self {
22 components: spec.components.as_ref(),
23 }
24 }
25
26 pub fn resolve_spec(&mut self, spec: &OpenApiSpec) -> Result<OpenApiSpec, ResolveError> {
28 let mut resolved = spec.clone();
29
30 for (_path, item) in &mut resolved.paths {
32 self.resolve_path_item(item)?;
33 }
34
35 if let Some(ref mut components) = resolved.components {
37 let schema_names: Vec<String> = components.schemas.keys().cloned().collect();
38 for name in schema_names {
39 let schema = components.schemas.get(&name).unwrap().clone();
40 let resolved_schema = self.resolve_schema_or_ref(&schema)?;
41 components.schemas.insert(name, resolved_schema);
42 }
43 }
44
45 Ok(resolved)
46 }
47
48 fn resolve_path_item(&mut self, item: &mut PathItem) -> Result<(), ResolveError> {
49 let mut resolved_params = Vec::new();
51 for p in &item.parameters {
52 resolved_params.push(self.resolve_parameter_or_ref(p)?);
53 }
54 item.parameters = resolved_params;
55
56 macro_rules! resolve_op {
58 ($op:expr) => {
59 if let Some(ref mut op) = $op {
60 self.resolve_operation(op)?;
61 }
62 };
63 }
64 resolve_op!(item.get);
65 resolve_op!(item.post);
66 resolve_op!(item.put);
67 resolve_op!(item.delete);
68 resolve_op!(item.patch);
69 resolve_op!(item.options);
70 resolve_op!(item.head);
71 resolve_op!(item.trace);
72 Ok(())
73 }
74
75 fn resolve_operation(&mut self, op: &mut Operation) -> Result<(), ResolveError> {
76 let mut resolved_params = Vec::new();
78 for p in &op.parameters {
79 resolved_params.push(self.resolve_parameter_or_ref(p)?);
80 }
81 op.parameters = resolved_params;
82
83 if let Some(ref body) = op.request_body {
85 let resolved = self.resolve_request_body_or_ref(body)?;
86 op.request_body = Some(resolved);
87 }
88
89 let mut resolved_responses = IndexMap::new();
91 for (status, resp) in &op.responses {
92 resolved_responses.insert(status.clone(), self.resolve_response_or_ref(resp)?);
93 }
94 op.responses = resolved_responses;
95
96 Ok(())
97 }
98
99 pub fn resolve_schema_or_ref(
100 &mut self,
101 schema_or_ref: &SchemaOrRef,
102 ) -> Result<SchemaOrRef, ResolveError> {
103 match schema_or_ref {
104 SchemaOrRef::Ref { .. } => {
105 Ok(schema_or_ref.clone())
110 }
111 SchemaOrRef::Schema(schema) => {
112 let resolved = self.resolve_schema(schema)?;
113 Ok(SchemaOrRef::Schema(Box::new(resolved)))
114 }
115 }
116 }
117
118 fn resolve_schema(&mut self, schema: &Schema) -> Result<Schema, ResolveError> {
119 let mut resolved = schema.clone();
120
121 let mut resolved_props = IndexMap::new();
123 for (name, prop) in &schema.properties {
124 resolved_props.insert(name.clone(), self.resolve_schema_or_ref(prop)?);
125 }
126 resolved.properties = resolved_props;
127
128 if let Some(ref items) = schema.items {
130 resolved.items = Some(Box::new(self.resolve_schema_or_ref(items)?));
131 }
132
133 resolved.all_of = schema
135 .all_of
136 .iter()
137 .map(|s| self.resolve_schema_or_ref(s))
138 .collect::<Result<Vec<_>, _>>()?;
139 resolved.one_of = schema
140 .one_of
141 .iter()
142 .map(|s| self.resolve_schema_or_ref(s))
143 .collect::<Result<Vec<_>, _>>()?;
144 resolved.any_of = schema
145 .any_of
146 .iter()
147 .map(|s| self.resolve_schema_or_ref(s))
148 .collect::<Result<Vec<_>, _>>()?;
149
150 if let Some(super::schema::AdditionalProperties::Schema(ref s)) =
152 schema.additional_properties
153 {
154 resolved.additional_properties = Some(super::schema::AdditionalProperties::Schema(
155 Box::new(self.resolve_schema_or_ref(s)?),
156 ));
157 }
158
159 Ok(resolved)
160 }
161
162 fn resolve_parameter_or_ref(
163 &mut self,
164 param: &ParameterOrRef,
165 ) -> Result<ParameterOrRef, ResolveError> {
166 match param {
167 ParameterOrRef::Ref { ref_path } => {
168 let resolved = self.lookup_parameter(ref_path)?;
169 Ok(ParameterOrRef::Parameter(resolved))
170 }
171 ParameterOrRef::Parameter(p) => {
172 let mut resolved = p.clone();
173 if let Some(ref s) = p.schema {
174 resolved.schema = Some(self.resolve_schema_or_ref(s)?);
175 }
176 Ok(ParameterOrRef::Parameter(resolved))
177 }
178 }
179 }
180
181 fn resolve_request_body_or_ref(
182 &mut self,
183 body: &RequestBodyOrRef,
184 ) -> Result<RequestBodyOrRef, ResolveError> {
185 match body {
186 RequestBodyOrRef::Ref { ref_path } => {
187 let resolved = self.lookup_request_body(ref_path)?;
188 let mut rb = resolved;
189 self.resolve_media_types(&mut rb.content)?;
190 Ok(RequestBodyOrRef::RequestBody(rb))
191 }
192 RequestBodyOrRef::RequestBody(rb) => {
193 let mut resolved = rb.clone();
194 self.resolve_media_types(&mut resolved.content)?;
195 Ok(RequestBodyOrRef::RequestBody(resolved))
196 }
197 }
198 }
199
200 fn resolve_response_or_ref(
201 &mut self,
202 resp: &ResponseOrRef,
203 ) -> Result<ResponseOrRef, ResolveError> {
204 match resp {
205 ResponseOrRef::Ref { ref_path } => {
206 let resolved = self.lookup_response(ref_path)?;
207 let mut r = resolved;
208 self.resolve_media_types(&mut r.content)?;
209 Ok(ResponseOrRef::Response(r))
210 }
211 ResponseOrRef::Response(r) => {
212 let mut resolved = r.clone();
213 self.resolve_media_types(&mut resolved.content)?;
214 Ok(ResponseOrRef::Response(resolved))
215 }
216 }
217 }
218
219 fn resolve_media_types(
220 &mut self,
221 content: &mut IndexMap<String, MediaType>,
222 ) -> Result<(), ResolveError> {
223 let keys: Vec<String> = content.keys().cloned().collect();
224 for key in keys {
225 let mt = content.get(&key).unwrap().clone();
226 let mut resolved_mt = mt;
227 if let Some(ref s) = resolved_mt.schema {
228 resolved_mt.schema = Some(self.resolve_schema_or_ref(s)?);
229 }
230 if let Some(ref s) = resolved_mt.item_schema {
231 resolved_mt.item_schema = Some(self.resolve_schema_or_ref(s)?);
232 }
233 content.insert(key, resolved_mt);
234 }
235 Ok(())
236 }
237
238 fn lookup_parameter(&self, ref_path: &str) -> Result<Parameter, ResolveError> {
241 let name = parse_ref_name(ref_path, "parameters")?;
242 self.components
243 .and_then(|c| c.parameters.get(name))
244 .and_then(|p| match p {
245 ParameterOrRef::Parameter(param) => Some(param.clone()),
246 _ => None,
247 })
248 .ok_or_else(|| ResolveError::RefTargetNotFound(ref_path.to_string()))
249 }
250
251 fn lookup_request_body(&self, ref_path: &str) -> Result<RequestBody, ResolveError> {
252 let name = parse_ref_name(ref_path, "requestBodies")?;
253 self.components
254 .and_then(|c| c.request_bodies.get(name))
255 .and_then(|rb| match rb {
256 RequestBodyOrRef::RequestBody(body) => Some(body.clone()),
257 _ => None,
258 })
259 .ok_or_else(|| ResolveError::RefTargetNotFound(ref_path.to_string()))
260 }
261
262 fn lookup_response(&self, ref_path: &str) -> Result<Response, ResolveError> {
263 let name = parse_ref_name(ref_path, "responses")?;
264 self.components
265 .and_then(|c| c.responses.get(name))
266 .and_then(|r| match r {
267 ResponseOrRef::Response(resp) => Some(resp.clone()),
268 _ => None,
269 })
270 .ok_or_else(|| ResolveError::RefTargetNotFound(ref_path.to_string()))
271 }
272}
273
274fn parse_ref_name<'a>(ref_path: &'a str, expected_section: &str) -> Result<&'a str, ResolveError> {
276 let stripped = ref_path
277 .strip_prefix("#/components/")
278 .ok_or_else(|| ResolveError::InvalidRefFormat(ref_path.to_string()))?;
279 let (section, name) = stripped
280 .split_once('/')
281 .ok_or_else(|| ResolveError::InvalidRefFormat(ref_path.to_string()))?;
282 if section != expected_section {
283 return Err(ResolveError::InvalidRefFormat(format!(
284 "expected section '{}', got '{}' in {}",
285 expected_section, section, ref_path
286 )));
287 }
288 Ok(name)
289}