kynos_openapi/model/reference.rs
1//! The Reference Object, and the "either a reference or the thing" wrapper.
2
3use serde::{Deserialize, Serialize};
4
5/// A reference to another part of this or another description.
6///
7/// # This is not a JSON Schema `$ref`
8///
9/// The specification draws a sharp line that is easy to miss. A *Reference
10/// Object* — this type — has exactly three fields, and any other property
11/// present alongside them **shall be ignored**. A *Schema Object* `$ref` is
12/// plain JSON Schema 2020-12, where sibling keywords are fully applied.
13///
14/// [`Schema`](crate::Schema) therefore models `$ref` as an ordinary keyword,
15/// and does not use this type.
16///
17/// Accordingly this object carries no [`Extensions`](crate::Extensions): it
18/// cannot be extended.
19#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
20pub struct Ref {
21 /// The URI of the referenced component.
22 #[serde(rename = "$ref")]
23 pub location: String,
24
25 /// A short summary, overriding that of the referenced component.
26 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub summary: Option<String>,
28
29 /// A description, overriding that of the referenced component.
30 ///
31 /// [CommonMark] syntax may be used.
32 ///
33 /// [CommonMark]: https://spec.commonmark.org/
34 #[serde(default, skip_serializing_if = "Option::is_none")]
35 pub description: Option<String>,
36}
37
38impl Ref {
39 /// References an arbitrary URI.
40 pub fn new(location: impl Into<String>) -> Self {
41 Self {
42 location: location.into(),
43 summary: None,
44 description: None,
45 }
46 }
47
48 /// References a named entry under `#/components/schemas`.
49 #[must_use]
50 pub fn schema(name: &str) -> Self {
51 Self::new(format!("#/components/schemas/{name}"))
52 }
53
54 /// References a named entry under `#/components/responses`.
55 #[must_use]
56 pub fn response(name: &str) -> Self {
57 Self::new(format!("#/components/responses/{name}"))
58 }
59
60 /// References a named entry under `#/components/parameters`.
61 #[must_use]
62 pub fn parameter(name: &str) -> Self {
63 Self::new(format!("#/components/parameters/{name}"))
64 }
65
66 /// References a named entry under `#/components/requestBodies`.
67 #[must_use]
68 pub fn request_body(name: &str) -> Self {
69 Self::new(format!("#/components/requestBodies/{name}"))
70 }
71
72 /// References a named entry under `#/components/securitySchemes`.
73 #[must_use]
74 pub fn security_scheme(name: &str) -> Self {
75 Self::new(format!("#/components/securitySchemes/{name}"))
76 }
77
78 /// Sets the overriding summary.
79 #[must_use]
80 pub fn with_summary(mut self, summary: impl Into<String>) -> Self {
81 self.summary = Some(summary.into());
82 self
83 }
84
85 /// Sets the overriding description.
86 #[must_use]
87 pub fn with_description(mut self, description: impl Into<String>) -> Self {
88 self.description = Some(description.into());
89 self
90 }
91}
92
93/// Either an inline `T` or a [`Ref`] standing in for one.
94///
95/// Deserialization prefers the reference: an object carrying `$ref` is always
96/// read as a [`Ref`], matching the specification's rule that the remaining
97/// properties of a Reference Object are ignored.
98#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
99#[serde(untagged)]
100pub enum RefOr<T> {
101 /// A reference to a component defined elsewhere.
102 Ref(Ref),
103 /// The object itself, inline.
104 Item(T),
105}
106
107impl<T> RefOr<T> {
108 /// Returns the inline item, or `None` when this is a reference.
109 pub fn as_item(&self) -> Option<&T> {
110 match self {
111 Self::Item(item) => Some(item),
112 Self::Ref(_) => None,
113 }
114 }
115
116 /// Returns the reference, or `None` when this is an inline item.
117 pub fn as_ref_object(&self) -> Option<&Ref> {
118 match self {
119 Self::Ref(reference) => Some(reference),
120 Self::Item(_) => None,
121 }
122 }
123
124 /// Returns `true` when this is a reference rather than an inline item.
125 pub fn is_ref(&self) -> bool {
126 matches!(self, Self::Ref(_))
127 }
128}
129
130impl<T> From<Ref> for RefOr<T> {
131 fn from(reference: Ref) -> Self {
132 Self::Ref(reference)
133 }
134}
135
136#[cfg(test)]
137mod tests;