forked from x52dev/oas3-rs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathref.rs
144 lines (116 loc) · 3.7 KB
/
ref.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use std::str::FromStr;
use derive_more::derive::{Display, Error};
use log::trace;
use once_cell::sync::Lazy;
use regex::Regex;
use serde::{Deserialize, Serialize};
use super::Spec;
static RE_REF: Lazy<Regex> = Lazy::new(|| {
Regex::new("^(?P<source>[^#]*)#/components/(?P<type>[^/]+)/(?P<name>.+)$").unwrap()
});
/// Container for a type of OpenAPI object, or a reference to one.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(untagged)]
pub enum ObjectOrReference<T> {
/// Object reference.
Ref {
/// Path, file reference, or URL pointing to object.
#[serde(rename = "$ref")]
ref_path: String,
},
/// Inline object.
Object(T),
}
impl<T> ObjectOrReference<T>
where
T: FromRef,
{
/// Resolves the object (if needed) from the given `spec` and returns it.
pub fn resolve(&self, spec: &Spec) -> Result<T, RefError> {
match self {
Self::Object(component) => Ok(component.clone()),
Self::Ref { ref_path } => T::from_ref(spec, ref_path),
}
}
}
/// Object reference error.
#[derive(Clone, Debug, PartialEq, Display, Error)]
pub enum RefError {
/// Referenced object has unknown type.
#[display("Invalid type: {}", _0)]
UnknownType(#[error(not(source))] String),
/// Referenced object was not of expected type.
#[display("Mismatched type: cannot reference a {} as a {}", _0, _1)]
MismatchedType(RefType, RefType),
/// Reference path points outside the given spec file.
#[display("Unresolvable path: {}", _0)]
Unresolvable(#[error(not(source))] String), // TODO: use some kind of path structure
}
/// Component type of a reference.
#[derive(Debug, Clone, Copy, PartialEq, Display)]
pub enum RefType {
/// Schema component type.
Schema,
/// Response component type.
Response,
/// Parameter component type.
Parameter,
/// Example component type.
Example,
/// Request body component type.
RequestBody,
/// Header component type.
Header,
/// Security scheme component type.
SecurityScheme,
/// Link component type.
Link,
/// Callback component type.
Callback,
}
impl FromStr for RefType {
type Err = RefError;
fn from_str(typ: &str) -> Result<Self, Self::Err> {
Ok(match typ {
"schemas" => Self::Schema,
"responses" => Self::Response,
"parameters" => Self::Parameter,
"examples" => Self::Example,
"requestBodies" => Self::RequestBody,
"headers" => Self::Header,
"securitySchemes" => Self::SecurityScheme,
"links" => Self::Link,
"callbacks" => Self::Callback,
typ => return Err(RefError::UnknownType(typ.to_owned())),
})
}
}
/// Parsed reference path.
#[derive(Debug, Clone)]
pub struct Ref {
/// Source file of the object being references.
pub source: String,
/// Type of object being referenced.
pub kind: RefType,
/// Name of object being referenced.
pub name: String,
}
impl FromStr for Ref {
type Err = RefError;
fn from_str(path: &str) -> Result<Self, Self::Err> {
let parts = RE_REF.captures(path).unwrap();
trace!("creating Ref: {}/{}", &parts["type"], &parts["name"]);
Ok(Self {
source: parts["source"].to_owned(),
kind: parts["type"].parse()?,
name: parts["name"].to_owned(),
})
}
}
/// Find an object from a reference path (`$ref`).
///
/// Implemented for object types which can be shared via a spec's `components` object.
pub trait FromRef: Clone {
/// Finds an object in `spec` using the given `path`.
fn from_ref(spec: &Spec, path: &str) -> Result<Self, RefError>;
}