1mod openapi;
2
3use std::{cell::RefCell, collections::HashSet, convert::TryFrom, fmt::Display, rc::Rc};
4
5use indexmap::IndexMap;
6use jsona::{
7 dom::{Key, KeyOrIndex, Keys, Node, Object},
8 error::ErrorObject,
9 util::mapper::Mapper,
10};
11pub use jsona_schema::Schema;
12use jsona_schema::SchemaParser;
13pub use openapi::*;
14use serde_json::Value;
15
16const ERROR_SOURCE: &str = "openapi";
17
18#[derive(Clone, Debug)]
19pub struct OpenapiError {
20 pub keys: Keys,
21 pub message: String,
22}
23
24impl OpenapiError {
25 pub fn new<T: ToString>(keys: Keys, message: T) -> Self {
26 Self {
27 keys,
28 message: message.to_string(),
29 }
30 }
31 pub fn to_error_object(&self, node: &Node, mapper: &Mapper) -> ErrorObject {
32 let message = self.message.clone();
33 ErrorObject::new(
34 ERROR_SOURCE,
35 "InvalidOpenapi",
36 message,
37 self.keys.mapper_range(node, mapper),
38 )
39 }
40}
41
42impl Display for OpenapiError {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 if self.keys.is_empty() {
45 write!(f, "{}", self.message)
46 } else {
47 write!(f, "{} at {}", self.message, self.keys)
48 }
49 }
50}
51
52type OpenapiResult<T> = std::result::Result<T, OpenapiError>;
53
54impl TryFrom<&Node> for Openapi {
55 type Error = Vec<OpenapiError>;
56
57 fn try_from(value: &Node) -> Result<Self, Self::Error> {
58 OpenapiParser::parse(value)
59 }
60}
61
62struct OpenapiParser {
63 openapi: Openapi,
64 routes: HashSet<String>,
65 errors: Vec<OpenapiError>,
66 defs: Rc<RefCell<IndexMap<String, Schema>>>,
67}
68
69impl OpenapiParser {
70 fn parse(node: &Node) -> Result<Openapi, Vec<OpenapiError>> {
71 let mut errors: Vec<OpenapiError> = vec![];
72 let routes: HashSet<String> = HashSet::default();
73 let mut openapi = Self::parse_openapi(&mut errors, node);
74 let schemas = get_components_mut(&mut openapi)
75 .schemas
76 .take()
77 .unwrap_or_default();
78 let mut parser = OpenapiParser {
79 openapi,
80 routes,
81 errors,
82 defs: Rc::new(RefCell::new(schemas)),
83 };
84 parser.parse_paths(node);
85 let OpenapiParser {
86 mut openapi,
87 errors,
88 defs,
89 ..
90 } = parser;
91 if errors.is_empty() {
92 if !defs.borrow().is_empty() {
93 get_components_mut(&mut openapi).schemas = Some(defs.take());
94 }
95 Ok(openapi)
96 } else {
97 Err(errors)
98 }
99 }
100
101 fn parse_openapi(errors: &mut Vec<OpenapiError>, value: &Node) -> Openapi {
102 let mut spec = Openapi {
103 openapi: "3.0.0".into(),
104 info: Info {
105 title: "openapi".into(),
106 version: "0.1.0".into(),
107 ..Default::default()
108 },
109 ..Default::default()
110 };
111 match value.get_as_object("@openapi") {
112 Some((key, Some(value))) => {
113 let keys = Keys::single(key);
114 let mut value = Node::from(value).to_plain_json();
115 if let Value::Object(ref mut obj) = value {
116 if obj.get("info").is_none() {
117 obj.insert(
118 "info".into(),
119 serde_json::to_value(spec.info.clone()).unwrap(),
120 );
121 }
122 if obj.get("openapi").is_none() {
123 obj.insert(
124 "openapi".into(),
125 serde_json::to_value(spec.openapi.clone()).unwrap(),
126 );
127 }
128 }
129 if let Value::Object(ref mut v) = value {
130 v.insert("paths".into(), Value::Object(serde_json::Map::new()));
131 }
132 match serde_json::from_value(value) {
133 Ok(v) => spec = v,
134 Err(error) => errors.push(OpenapiError::new(
135 keys,
136 format!("invalid spec value, {error}"),
137 )),
138 }
139 }
140 Some((key, None)) => {
141 errors.push(OpenapiError::new(Keys::single(key), "must be object"))
142 }
143 None => {}
144 }
145 spec
146 }
147
148 fn parse_paths(&mut self, node: &Node) {
149 if let Some(object) = node.as_object() {
150 for (key, value) in object.value().read().iter() {
151 if let Err(error) = self.parse_endpoint(key, value) {
152 self.errors.push(error);
153 }
154 }
155 } else {
156 self.errors
157 .push(OpenapiError::new(Keys::default(), "must be object"))
158 }
159 }
160
161 fn parse_endpoint(&mut self, key: &Key, value: &Node) -> OpenapiResult<()> {
162 let operation_id = key.value();
163 let keys = Keys::single(key.clone());
164 if !value.is_object() {
165 return Err(OpenapiError::new(keys, "must be object"));
166 }
167 let mut operation = self.parse_endpoint_annotation(&keys, value)?;
168 operation.operation_id = Some(operation_id.into());
169 let (method, path_parts) = self.parse_route(&keys, value)?;
170 let pathname = self.parse_req(&mut operation, &keys, value, &path_parts)?;
171 self.parse_res(&mut operation, &keys, value)?;
172 let path_item = self
173 .openapi
174 .paths
175 .entry(pathname)
176 .or_insert(Default::default());
177 method.add_operation(path_item, operation);
178 Ok(())
179 }
180
181 fn parse_endpoint_annotation(&mut self, keys: &Keys, value: &Node) -> OpenapiResult<Operation> {
182 match value.get_as_object("@endpoint") {
183 Some((key, Some(value))) => {
184 let mut value = Node::from(value).to_plain_json();
185 value
186 .as_object_mut()
187 .unwrap()
188 .insert("responses".into(), Value::Object(Default::default()));
189 serde_json::from_value(value).map_err(|error| {
190 OpenapiError::new(keys.join(key), format!("invalid endpoint value, {error}"))
191 })
192 }
193 Some((key, None)) => Err(OpenapiError::new(keys.join(key), "must be object")),
194 None => Ok(Operation::default()),
195 }
196 }
197
198 fn parse_route(
199 &mut self,
200 keys: &Keys,
201 value: &Node,
202 ) -> OpenapiResult<(MethodKind, Vec<String>)> {
203 match value.get_as_string("route") {
204 Some((key, Some(value))) => {
205 let keys = keys.join(key);
206 let splitted_route: Vec<&str> = value.value().split(' ').collect();
207 let err = || OpenapiError::new(keys.clone(), "is invalid");
208 if splitted_route.len() != 2 {
209 return Err(err());
210 }
211 let method = MethodKind::from_str(splitted_route[0]).ok_or_else(err)?;
212 let path = splitted_route[1].trim();
213 let path_parts: Vec<String> = path.split('/').map(|v| v.to_string()).collect();
214 let canonical_route = format!("{} {}", method, path);
215 if !self.routes.insert(canonical_route) {
216 return Err(OpenapiError::new(keys, "is conflict"));
217 }
218 Ok((method, path_parts))
219 }
220 Some((key, None)) => Err(OpenapiError::new(keys.join(key), "must be string")),
221 None => Err(OpenapiError::new(keys.clone(), "miss route")),
222 }
223 }
224
225 fn parse_req(
226 &mut self,
227 operation: &mut Operation,
228 keys: &Keys,
229 value: &Node,
230 path_parts: &[String],
231 ) -> OpenapiResult<String> {
232 match value.get_as_object("req") {
233 Some((key, Some(value))) => {
234 let keys = keys.join(key);
235 let pathname = self.parse_req_params(operation, &keys, &value, path_parts)?;
236 for (key, value) in value.value().read().iter() {
237 match key.value() {
238 "query" => {
239 self.parse_req_parameters(
240 operation,
241 "query",
242 &keys.join(key.clone()),
243 value,
244 )?;
245 }
246 "headers" => {
247 self.parse_req_parameters(
248 operation,
249 "header",
250 &keys.join(key.clone()),
251 value,
252 )?;
253 }
254 "body" => self.parse_req_body(operation, &keys.join(key.clone()), value)?,
255 _ => {}
256 }
257 }
258 Ok(pathname)
259 }
260 Some((key, None)) => Err(OpenapiError::new(keys.join(key), "must be object")),
261 None => {
262 if path_parts.iter().any(|v| v.as_str() == "{}") {
263 return Err(OpenapiError::new(keys.clone(), "req.params is required"));
264 }
265 Ok(path_parts.join("/"))
266 }
267 }
268 }
269
270 fn parse_req_params(
271 &mut self,
272 operation: &mut Operation,
273 keys: &Keys,
274 value: &Object,
275 path_parts: &[String],
276 ) -> OpenapiResult<String> {
277 match Node::from(value.clone()).get_as_object("params") {
278 Some((key, Some(value))) => {
279 let keys = keys.join(key);
280 let num_params = path_parts.iter().filter(|v| v.as_str() == "{}").count();
281 let map = value.value().read();
282 if num_params != map.len() {
283 return Err(OpenapiError::new(keys, "does not match route"));
284 }
285 let mut new_path_parts: Vec<String> = vec![];
286 let mut idx = 0;
287 let names: Vec<&str> = map.iter().map(|(k, _)| k.value()).collect();
288 for part in path_parts {
289 if *part == "{}" {
290 new_path_parts.push(format!("{{{}}}", names[idx]));
291 idx += 1;
292 } else {
293 new_path_parts.push(part.to_string())
294 }
295 }
296 self.parse_req_parameters(operation, "path", &keys, &value.into())?;
297 Ok(new_path_parts.join("/"))
298 }
299 Some((key, None)) => Err(OpenapiError::new(keys.join(key), "must be object")),
300 None => {
301 if path_parts.iter().any(|v| v.as_str() == "{}") {
302 return Err(OpenapiError::new(keys.clone(), "params is required"));
303 }
304 Ok(path_parts.join("/"))
305 }
306 }
307 }
308
309 fn parse_req_parameters(
310 &mut self,
311 operation: &mut Operation,
312 location: &str,
313 keys: &Keys,
314 value: &Node,
315 ) -> OpenapiResult<()> {
316 match value.as_object() {
317 Some(object) => {
318 let mut parameters = vec![];
319 for (key, value) in object.value().read().iter() {
320 let parameter = Parameter {
321 name: key.value().to_string(),
322 location: location.into(),
323 ..Default::default()
324 };
325 parameters.push(self.parse_parameter(
326 parameter,
327 &keys.join(key.clone()),
328 value,
329 )?);
330 }
331 if let Some(v) = operation.parameters.as_mut() {
332 v.extend(parameters)
333 } else {
334 operation.parameters = Some(parameters);
335 }
336 Ok(())
337 }
338 None => Err(OpenapiError::new(keys.clone(), "must be object")),
339 }
340 }
341
342 fn parse_req_body(
343 &mut self,
344 operation: &mut Operation,
345 keys: &Keys,
346 value: &Node,
347 ) -> OpenapiResult<()> {
348 let content_type = parse_string_annotation(keys, value, "@contentType")?
349 .unwrap_or_else(|| "application/json".into());
350 let schema = self.parse_schema(keys, value)?;
351 let media_type = MediaType {
352 schema: Some(schema),
353 examples: if exist_annotation(value, "@example") {
354 Some(OneOrMultiExample::Example {
355 example: value.to_plain_json(),
356 })
357 } else {
358 None
359 },
360 ..Default::default()
361 };
362 let mut content = IndexMap::default();
363 content.insert(content_type, media_type);
364 let request_body = RequestBody {
365 description: parse_string_annotation(keys, value, "@describe")?,
366 required: Some(true),
367 content,
368 };
369 operation.request_body = Some(ObjectOrReference::Object(request_body));
370 Ok(())
371 }
372
373 fn parse_res(
374 &mut self,
375 operation: &mut Operation,
376 keys: &Keys,
377 value: &Node,
378 ) -> OpenapiResult<()> {
379 match value.get_as_object("res") {
380 Some((key, Some(value))) => {
381 let keys = keys.join(key);
382 for (key, value) in value.value().read().iter() {
383 let keys = keys.join(key.clone());
384 let status = key
385 .value()
386 .parse::<u32>()
387 .map_err(|_| OpenapiError::new(keys.clone(), "should be status code"))?;
388 if !(100..=599).contains(&status) {
389 return Err(OpenapiError::new(keys, "must be integer in [100, 600)"));
390 }
391 let description =
392 parse_string_annotation(&keys, value, "@describe")?.unwrap_or_default();
393 let mut response = Response {
394 description,
395 ..Default::default()
396 };
397
398 let with_header = exist_annotation(value, "@withHeader");
399
400 if with_header {
401 match value.as_object() {
402 Some(object) => {
403 for (key, value) in object.value().read().iter() {
404 match key.value() {
405 "headers" => self.parse_res_header(
406 &mut response,
407 &keys.join(key.clone()),
408 value,
409 )?,
410 "body" => self.parse_res_body(
411 &mut response,
412 &keys.join(key.clone()),
413 value,
414 )?,
415 _ => {}
416 }
417 }
418 }
419 None => {
420 return Err(OpenapiError::new(keys, "must be object"));
421 }
422 }
423 } else {
424 self.parse_res_body(&mut response, &keys, value)?;
425 }
426
427 operation.responses.insert(status.to_string(), response);
428 }
429 Ok(())
430 }
431 Some((key, None)) => Err(OpenapiError::new(keys.join(key), "must be object")),
432 None => {
433 let default_response = Response {
434 description: Default::default(),
435 ..Default::default()
436 };
437 operation.responses.insert("200".into(), default_response);
438 Ok(())
439 }
440 }
441 }
442
443 fn parse_parameter(
444 &mut self,
445 mut parameter: Parameter,
446 keys: &Keys,
447 value: &Node,
448 ) -> OpenapiResult<ObjectOrReference<Parameter>> {
449 if let Some(ref_val) = parse_ref_annotation(keys, value, "#/components/parameters/")? {
450 return Ok(ref_val);
451 }
452 parameter.description = parse_string_annotation(keys, value, "@describe")?;
453 parameter.required = Some(!exist_annotation(value, "@optional"));
454 parameter.schema = Some(self.parse_schema(keys, value)?);
455 parameter.examples = if exist_annotation(value, "@example") {
456 Some(OneOrMultiExample::Example {
457 example: value.to_plain_json(),
458 })
459 } else {
460 None
461 };
462
463 let parameter_object = ObjectOrReference::Object(parameter);
464
465 if let Some(name) = parse_string_annotation(keys, value, "@def")? {
466 return self.def_parameters(name, parameter_object);
467 }
468 Ok(parameter_object)
469 }
470
471 fn parse_res_header(
472 &mut self,
473 response: &mut Response,
474 keys: &Keys,
475 value: &Node,
476 ) -> OpenapiResult<()> {
477 match value.as_object() {
478 Some(value) => {
479 for (key, value) in value.value().read().iter() {
480 let keys = keys.join(key.clone());
481 let header = Header {
482 description: parse_string_annotation(&keys, value, "@describe")?,
483 required: Some(!exist_annotation(value, "@optional")),
484 schema: Some(self.parse_schema(&keys, value)?),
485 ..Default::default()
486 };
487 let header_object = ObjectOrReference::Object(header);
488 response
489 .headers
490 .get_or_insert(Default::default())
491 .insert(key.value().to_string(), header_object);
492 }
493 Ok(())
494 }
495 None => Err(OpenapiError::new(keys.clone(), "must be object")),
496 }
497 }
498
499 fn parse_res_body(
500 &mut self,
501 response: &mut Response,
502 keys: &Keys,
503 value: &Node,
504 ) -> OpenapiResult<()> {
505 let content_type = parse_string_annotation(keys, value, "@contentType")?
506 .unwrap_or_else(|| "application/json".into());
507 let schema = self.parse_schema(keys, value)?;
508 let media_type = MediaType {
509 schema: Some(schema),
510 examples: if exist_annotation(value, "@example") {
511 Some(OneOrMultiExample::Example {
512 example: value.to_plain_json(),
513 })
514 } else {
515 None
516 },
517 ..Default::default()
518 };
519 response
520 .content
521 .get_or_insert(Default::default())
522 .insert(content_type, media_type);
523 Ok(())
524 }
525
526 fn parse_schema(&mut self, keys: &Keys, value: &Node) -> OpenapiResult<Schema> {
527 let scope = SchemaParser {
528 keys: keys.clone(),
529 node: value.clone(),
530 defs: self.defs.clone(),
531 ref_prefix: Rc::new("#/components/schemas/".to_string()),
532 prefer_optional: false,
533 };
534 let mut schema = scope
535 .parse()
536 .map_err(|_| OpenapiError::new(keys.clone(), "invalid schema"))?;
537 schema.description = None;
538 Ok(schema)
539 }
540
541 fn def_parameters(
542 &mut self,
543 name: String,
544 value: ObjectOrReference<Parameter>,
545 ) -> OpenapiResult<ObjectOrReference<Parameter>> {
546 let components = get_components_mut(&mut self.openapi);
547 if components.parameters.is_none() {
548 components.parameters = Some(Default::default());
549 }
550 components
551 .parameters
552 .as_mut()
553 .unwrap()
554 .insert(name.clone(), value);
555 return Ok(ObjectOrReference::Ref {
556 ref_path: format!("#/components/parameters/{}", name),
557 });
558 }
559}
560
561fn get_components_mut(spec: &mut Openapi) -> &mut Components {
562 if spec.components.is_none() {
563 spec.components = Some(Default::default());
564 }
565 spec.components.as_mut().unwrap()
566}
567
568fn exist_annotation(value: &Node, name: &str) -> bool {
569 value.get(&KeyOrIndex::annotation(name)).is_some()
570}
571
572fn parse_string_annotation(keys: &Keys, value: &Node, name: &str) -> OpenapiResult<Option<String>> {
573 match value.get_as_string(name) {
574 Some((_, Some(value))) => Ok(Some(value.value().to_string())),
575 Some((key, None)) => Err(OpenapiError::new(keys.join(key), "must be string")),
576 None => Ok(None),
577 }
578}
579
580fn parse_ref_annotation<T>(
581 keys: &Keys,
582 value: &Node,
583 ref_prefix: &str,
584) -> OpenapiResult<Option<ObjectOrReference<T>>> {
585 match parse_string_annotation(keys, value, "@ref")? {
586 Some(ref_value) => Ok(Some(ObjectOrReference::Ref {
587 ref_path: format!("{}{}", ref_prefix, ref_value),
588 })),
589 None => Ok(None),
590 }
591}
592
593enum MethodKind {
594 Get,
595 Post,
596 Put,
597 Delete,
598 Patch,
599}
600
601impl MethodKind {
602 pub fn from_str(value: &str) -> Option<Self> {
603 match value.trim().to_lowercase().as_str() {
604 "get" => Some(MethodKind::Get),
605 "post" => Some(MethodKind::Post),
606 "put" => Some(MethodKind::Put),
607 "delete" => Some(MethodKind::Delete),
608 "patch" => Some(MethodKind::Patch),
609 _ => None,
610 }
611 }
612 pub fn add_operation(&self, path_item: &mut PathItem, operation: Operation) {
613 match self {
614 MethodKind::Get => path_item.get = Some(operation),
615 MethodKind::Post => path_item.post = Some(operation),
616 MethodKind::Put => path_item.put = Some(operation),
617 MethodKind::Delete => path_item.delete = Some(operation),
618 MethodKind::Patch => path_item.patch = Some(operation),
619 };
620 }
621}
622impl Display for MethodKind {
623 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
624 match self {
625 MethodKind::Get => write!(f, "get"),
626 MethodKind::Post => write!(f, "post"),
627 MethodKind::Put => write!(f, "put"),
628 MethodKind::Delete => write!(f, "delete"),
629 MethodKind::Patch => write!(f, "patch"),
630 }
631 }
632}