1pub mod transformer;
7pub mod validator;
8
9pub use transformer::SpecTransformer;
10pub use validator::SpecValidator;
11
12use crate::error::Error;
13use openapiv3::{OpenAPI, Operation, Parameter, PathItem, ReferenceOr};
14use std::collections::HashSet;
15
16pub type HttpMethodsIter<'a> = [(&'static str, &'a Option<Operation>); 8];
18
19#[must_use]
27pub const fn http_methods_iter(item: &PathItem) -> HttpMethodsIter<'_> {
28 [
29 ("GET", &item.get),
30 ("POST", &item.post),
31 ("PUT", &item.put),
32 ("DELETE", &item.delete),
33 ("PATCH", &item.patch),
34 ("HEAD", &item.head),
35 ("OPTIONS", &item.options),
36 ("TRACE", &item.trace),
37 ]
38}
39
40pub const MAX_REFERENCE_DEPTH: usize = 10;
42
43pub fn resolve_parameter_reference(spec: &OpenAPI, reference: &str) -> Result<Parameter, Error> {
60 let mut visited = HashSet::new();
61 resolve_parameter_reference_with_visited(spec, reference, &mut visited, 0)
62}
63
64fn resolve_parameter_reference_with_visited(
66 spec: &OpenAPI,
67 reference: &str,
68 visited: &mut HashSet<String>,
69 depth: usize,
70) -> Result<Parameter, Error> {
71 if depth >= MAX_REFERENCE_DEPTH {
73 return Err(Error::Validation(format!(
74 "Maximum reference depth ({MAX_REFERENCE_DEPTH}) exceeded while resolving '{reference}'"
75 )));
76 }
77
78 if !visited.insert(reference.to_string()) {
80 return Err(Error::Validation(format!(
81 "Circular reference detected: '{reference}' is part of a reference cycle"
82 )));
83 }
84
85 if !reference.starts_with("#/components/parameters/") {
88 return Err(Error::Validation(format!(
89 "Invalid parameter reference format: '{reference}'. Expected format: #/components/parameters/{{name}}"
90 )));
91 }
92
93 let param_name = reference
94 .strip_prefix("#/components/parameters/")
95 .ok_or_else(|| Error::Validation(format!("Invalid parameter reference: '{reference}'")))?;
96
97 let components = spec.components.as_ref().ok_or_else(|| {
99 Error::Validation(
100 "Cannot resolve parameter reference: OpenAPI spec has no components section"
101 .to_string(),
102 )
103 })?;
104
105 let param_ref = components.parameters.get(param_name).ok_or_else(|| {
106 Error::Validation(format!("Parameter '{param_name}' not found in components"))
107 })?;
108
109 match param_ref {
111 ReferenceOr::Item(param) => Ok(param.clone()),
112 ReferenceOr::Reference {
113 reference: nested_ref,
114 } => resolve_parameter_reference_with_visited(spec, nested_ref, visited, depth + 1),
115 }
116}