1use crate::constants;
7
8pub mod parser;
9pub mod transformer;
10pub mod validator;
11
12pub use parser::parse_openapi;
13pub use transformer::SpecTransformer;
14pub use validator::SpecValidator;
15
16use crate::error::Error;
17use openapiv3::{OpenAPI, Operation, Parameter, PathItem, ReferenceOr};
18use std::collections::HashSet;
19
20pub type HttpMethodsIter<'a> = [(&'static str, &'a Option<Operation>); 8];
22
23#[must_use]
31pub const fn http_methods_iter(item: &PathItem) -> HttpMethodsIter<'_> {
32 [
33 (constants::HTTP_METHOD_GET, &item.get),
34 (constants::HTTP_METHOD_POST, &item.post),
35 (constants::HTTP_METHOD_PUT, &item.put),
36 (constants::HTTP_METHOD_DELETE, &item.delete),
37 (constants::HTTP_METHOD_PATCH, &item.patch),
38 (constants::HTTP_METHOD_HEAD, &item.head),
39 (constants::HTTP_METHOD_OPTIONS, &item.options),
40 ("TRACE", &item.trace),
41 ]
42}
43
44pub const MAX_REFERENCE_DEPTH: usize = 10;
46
47pub fn resolve_parameter_reference(spec: &OpenAPI, reference: &str) -> Result<Parameter, Error> {
64 let mut visited = HashSet::new();
65 resolve_parameter_reference_with_visited(spec, reference, &mut visited, 0)
66}
67
68fn resolve_parameter_reference_with_visited(
70 spec: &OpenAPI,
71 reference: &str,
72 visited: &mut HashSet<String>,
73 depth: usize,
74) -> Result<Parameter, Error> {
75 if depth >= MAX_REFERENCE_DEPTH {
77 return Err(Error::validation_error(format!(
78 "Maximum reference depth ({MAX_REFERENCE_DEPTH}) exceeded while resolving '{reference}'"
79 )));
80 }
81
82 if !visited.insert(reference.to_string()) {
84 return Err(Error::validation_error(format!(
85 "Circular reference detected: '{reference}' is part of a reference cycle"
86 )));
87 }
88
89 if !reference.starts_with("#/components/parameters/") {
92 return Err(Error::validation_error(format!(
93 "Invalid parameter reference format: '{reference}'. Expected format: #/components/parameters/{{name}}"
94 )));
95 }
96
97 let param_name = reference
98 .strip_prefix("#/components/parameters/")
99 .ok_or_else(|| {
100 Error::validation_error(format!("Invalid parameter reference: '{reference}'"))
101 })?;
102
103 let components = spec.components.as_ref().ok_or_else(|| {
105 Error::validation_error(
106 "Cannot resolve parameter reference: OpenAPI spec has no components section"
107 .to_string(),
108 )
109 })?;
110
111 let param_ref = components.parameters.get(param_name).ok_or_else(|| {
112 Error::validation_error(format!("Parameter '{param_name}' not found in components"))
113 })?;
114
115 match param_ref {
117 ReferenceOr::Item(param) => Ok(param.clone()),
118 ReferenceOr::Reference {
119 reference: nested_ref,
120 } => resolve_parameter_reference_with_visited(spec, nested_ref, visited, depth + 1),
121 }
122}