1use serde::{Deserialize, Serialize};
2use lazy_static::lazy_static;
3use regex::Regex;
4use crate::Spec;
5
6lazy_static! {
7 static ref PATH_EXPRESSION: Regex = Regex::new(r"^(?<source>[^#]*)#\/(?<location>[^/]+)\/(?<kind>[^/]+)\/*(?<name>\S*)$").unwrap();
8}
9
10pub trait Resolvable: Clone + Sized {
11 fn resolve(spec: &Spec, path: &String) -> Result<Self, ResolveError>;
12}
13
14#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
15#[serde(untagged)]
16pub enum RefOr<T> where T: Resolvable {
17 Reference {
18 #[serde(rename = "$ref")]
19 reference_path: String,
20 },
21 Item(T),
22}
23impl<T> RefOr<T> where T: Resolvable {
24 pub fn resolve(&self, spec: &Spec) -> Result<T, ResolveError> {
25 match self {
26 Self::Item(item) => Ok(item.clone()),
27 Self::Reference { reference_path } => T::resolve(spec, reference_path),
28 }
29 }
30}
31impl<T> Resolvable for Box<T> where T: Resolvable {
32 fn resolve(spec: &Spec, path: &String) -> Result<Self, ResolveError> {
33 Ok(Box::new(T::resolve(spec, path)?))
34 }
35}
36
37#[derive(Debug, Clone, Eq, PartialEq)]
38#[non_exhaustive]
39pub enum ReferenceType {
40 Callbacks,
41 Definitions,
42 Example,
43 Link,
44 Parameter,
45 PathItem,
46 RequestBody,
47 Response,
48 Schema,
49 SecurityScheme,
50}
51impl TryFrom<String> for ReferenceType {
52 type Error = ResolveError;
53
54 fn try_from(value: String) -> Result<Self, Self::Error> {
55 match value.as_str() {
56 "callback" => Ok(Self::Callbacks),
57 "definitions" => Ok(Self::Definitions),
58 "example" => Ok(Self::Example),
59 "link" => Ok(Self::Link),
60 "parameter" | "parameters" => Ok(Self::Parameter),
61 "path_item" => Ok(Self::PathItem),
62 "request_body" => Ok(Self::RequestBody),
63 "response" => Ok(Self::Response),
64 "schemas" => Ok(Self::Schema),
65 "security_scheme" => Ok(Self::SecurityScheme),
66
67 #[allow(unreachable_patterns)]
68 _ => Err(Self::Error::UnknownType)
69 }
70 }
71}
72
73#[derive(Debug)]
74pub enum ResolveError {
75 UnknownType,
76 MissingPathError,
77 MissingLocationError,
78 MissingKindError,
79 MissingMatchesError,
80 UnknownPathError(String),
81}
82
83#[derive(Clone, Debug)]
84pub struct Reference {
85 pub source: Option<String>,
86 pub location: String,
87 pub kind: ReferenceType,
88 pub name: String,
89}
90
91impl TryFrom<String> for Reference {
92 type Error = ResolveError;
93
94 fn try_from(value: String) -> Result<Self, Self::Error> {
95 let matches = PATH_EXPRESSION.captures(value.as_str())
96 .ok_or(Self::Error::MissingMatchesError)?;
97
98 let source = matches.name("source")
99 .map(|v| v.as_str().to_string().clone());
100
101 let location = matches.name("location")
102 .ok_or(Self::Error::MissingLocationError)?
103 .as_str().to_string().clone();
104
105 let kind_item = matches.name("kind")
106 .ok_or(Self::Error::MissingKindError)?
107 .as_str().to_string().clone();
108 let kind: ReferenceType = match kind_item.clone().try_into() {
109 Ok(v) => v,
110 Err(_) => location.clone().try_into()?,
111 };
112
113 let name_item = matches.name("name")
114 .map(|v| v.as_str().to_string().clone());
115 let name = match name_item {
116 Some(v) if !v.is_empty() => v,
117 _ => kind_item.clone()
118 };
119
120 Ok(Self {
121 source, location, kind, name
122 })
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::Reference;
129
130 #[test]
131 fn test_regex_captures_swagger_yaml() {
132 let hay = "#/parameters/nested_response";
133 let path = Reference::try_from(hay.to_string()).unwrap();
134 println!("{path:?}")
135 }
136 #[test]
137 fn test_regex_captures_swagger_json() {
138 let hay = "#/definitions/account-v1.AccountDto";
139 let path = Reference::try_from(hay.to_string()).unwrap();
140 println!("{path:?}")
141 }
142 #[test]
143 fn test_regex_captures_openapi_yaml() {
144 let hay = "#/components/schemas/Pets";
145 let path = Reference::try_from(hay.to_string()).unwrap();
146 println!("{path:?}")
147 }
148 #[test]
149 fn test_regex_captures_openapi_json() {
150 let hay = "#/components/schemas/account-v1.AccountDto";
151 let path = Reference::try_from(hay.to_string()).unwrap();
152 println!("{path:?}")
153 }
154}