openapi_31/v31/
reference.rs1use std::{default, ops::Deref};
10
11use derive_more::{Display, Error};
12
13use crate::v31;
14
15#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
16pub struct Reference {
17 #[serde(rename = "$ref", skip_serializing_if = "Option::is_none")]
18 pub dollar_ref: Option<String>,
19 #[serde(rename = "summary", skip_serializing_if = "Option::is_none")]
20 pub summary: Option<String>,
21 #[serde(rename = "description", skip_serializing_if = "Option::is_none")]
22 pub description: Option<String>,
23}
24
25impl Reference {
26 pub fn new() -> Reference {
27 Reference { dollar_ref: None, summary: None, description: None }
28 }
29}
30
31#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
32#[serde(untagged)]
33pub enum ObjectOrRef<T> {
34 Object(T),
35 Ref {
36 #[serde(rename = "$ref")]
37 r#ref: String,
38 },
39}
40
41#[derive(Clone, Debug, PartialEq, Display, Error)]
42pub enum ObjectRefErr {
43 #[display(fmt = "unresolved reference")]
44 UnresolvedRef,
45
46 #[display(fmt = "mismatch reference")]
47 MismatchedRef,
48
49 #[display(fmt = "reference error")]
50 Ref,
51}
52
53impl<T> ObjectOrRef<T>
54where
55 T: Resolve + Clone,
56{
57 pub fn resolve(&self, openapi: &v31::Openapi) -> Result<T, ObjectRefErr> {
58 match self {
59 ObjectOrRef::Object(object) => Ok(object.clone()),
60 ObjectOrRef::Ref { r#ref } => T::resolve(r#ref, openapi),
61 }
62 }
63}
64
65pub trait Resolve {
66 fn resolve(r#ref: &str, openapi: &v31::Openapi) -> Result<Self, ObjectRefErr>
67 where
68 Self: Sized;
69}
70
71#[macro_export]
72macro_rules! default_resolve_strategy {
73 ($ref:ident, $openapi:ident, $component:ident, $postfix:literal) => {{
74 static REF_PREFIX: &str = concat!("#/components/", $postfix, "/");
75 if !$ref.starts_with(REF_PREFIX) {
76 return Err(v31::reference::ObjectRefErr::MismatchedRef);
77 }
78 let (_, item_name) = $ref.split_at(REF_PREFIX.len());
79 $openapi
80 .components
81 .as_ref()
82 .and_then(|components| components.$component.as_ref())
83 .and_then(|items| items.get(item_name))
84 .and_then(|item| Some(item.clone()))
85 .ok_or_else(|| v31::reference::ObjectRefErr::UnresolvedRef)
86 }};
87}
88
89impl Resolve for v31::Parameter {
90 fn resolve(r#ref: &str, openapi: &v31::Openapi) -> Result<Self, v31::reference::ObjectRefErr> {
91 default_resolve_strategy!(r#ref, openapi, parameters, "parameters")
92 }
93}
94
95impl Resolve for v31::Response {
96 fn resolve(r#ref: &str, openapi: &v31::Openapi) -> Result<Self, v31::reference::ObjectRefErr> {
97 default_resolve_strategy!(r#ref, openapi, responses, "responses")
98 }
99}
100
101impl Resolve for v31::Examples {
102 fn resolve(r#ref: &str, openapi: &v31::Openapi) -> Result<Self, v31::reference::ObjectRefErr> {
103 default_resolve_strategy!(r#ref, openapi, examples, "examples")
104 }
105}
106
107impl Resolve for v31::RequestBody {
108 fn resolve(r#ref: &str, openapi: &v31::Openapi) -> Result<Self, v31::reference::ObjectRefErr> {
109 default_resolve_strategy!(r#ref, openapi, request_bodies, "requestBodies")
110 }
111}
112
113impl Resolve for v31::Header {
114 fn resolve(r#ref: &str, openapi: &v31::Openapi) -> Result<Self, v31::reference::ObjectRefErr> {
115 default_resolve_strategy!(r#ref, openapi, headers, "headers")
116 }
117}
118
119impl Resolve for v31::SecurityScheme {
120 fn resolve(r#ref: &str, openapi: &v31::Openapi) -> Result<Self, ObjectRefErr> {
121 default_resolve_strategy!(r#ref, openapi, security_schemes, "securitySchemas")
122 }
123}
124
125impl Resolve for v31::Link {
126 fn resolve(r#ref: &str, openapi: &v31::Openapi) -> Result<Self, ObjectRefErr> {
127 default_resolve_strategy!(r#ref, openapi, links, "links")
128 }
129}
130
131impl Resolve for v31::PathItem {
132 fn resolve(r#ref: &str, openapi: &v31::Openapi) -> Result<Self, v31::reference::ObjectRefErr> {
133 default_resolve_strategy!(r#ref, openapi, path_items, "pathItems")
134 }
135}