1use std::any::TypeId;
2use std::fmt::{self, Debug, Formatter};
3
4#[cfg(feature = "rfc9457")]
5use salvo_core::http::Problem;
6use salvo_core::http::StatusCode;
7use salvo_core::prelude::StatusError;
8use salvo_core::writing;
9
10use crate::{Components, Operation, Response, ToResponse, ToResponses, ToSchema};
11
12#[derive(Clone, Debug)]
16pub struct Endpoint {
17 pub operation: Operation,
19 pub components: Components,
21}
22
23impl Endpoint {
24 #[must_use]
26 pub fn new(operation: Operation, components: Components) -> Self {
27 Self {
28 operation,
29 components,
30 }
31 }
32}
33
34pub trait EndpointArgRegister {
36 fn register(components: &mut Components, operation: &mut Operation, arg: &str);
39}
40pub trait EndpointOutRegister {
42 fn register(components: &mut Components, operation: &mut Operation);
45}
46
47impl<C> EndpointOutRegister for writing::Json<C>
48where
49 C: ToSchema,
50{
51 #[inline]
52 fn register(components: &mut Components, operation: &mut Operation) {
53 operation
54 .responses
55 .insert("200", Self::to_response(components));
56 }
57}
58impl<T, E> EndpointOutRegister for Result<T, E>
59where
60 T: EndpointOutRegister + Send,
61 E: EndpointOutRegister + Send,
62{
63 #[inline]
64 fn register(components: &mut Components, operation: &mut Operation) {
65 T::register(components, operation);
66 E::register(components, operation);
67 }
68}
69impl<E> EndpointOutRegister for Result<(), E>
70where
71 E: EndpointOutRegister + Send,
72{
73 #[inline]
74 fn register(components: &mut Components, operation: &mut Operation) {
75 operation.responses.insert("200", Response::new("Ok"));
76 E::register(components, operation);
77 }
78}
79
80impl EndpointOutRegister for StatusError {
81 #[inline]
82 fn register(components: &mut Components, operation: &mut Operation) {
83 operation
84 .responses
85 .append(&mut Self::to_responses(components));
86 }
87}
88
89#[cfg(feature = "rfc9457")]
90impl<Extensions> EndpointOutRegister for Problem<Extensions>
91where
92 Extensions: ToSchema + 'static,
93{
94 #[inline]
95 fn register(components: &mut Components, operation: &mut Operation) {
96 operation
97 .responses
98 .append(&mut Self::to_responses(components));
99 }
100}
101impl EndpointOutRegister for StatusCode {
102 fn register(components: &mut Components, operation: &mut Operation) {
103 for code in [
104 Self::CONTINUE,
105 Self::SWITCHING_PROTOCOLS,
106 Self::PROCESSING,
107 Self::OK,
108 Self::CREATED,
109 Self::ACCEPTED,
110 Self::NON_AUTHORITATIVE_INFORMATION,
111 Self::NO_CONTENT,
112 Self::RESET_CONTENT,
113 Self::PARTIAL_CONTENT,
114 Self::MULTI_STATUS,
115 Self::ALREADY_REPORTED,
116 Self::IM_USED,
117 Self::MULTIPLE_CHOICES,
118 Self::MOVED_PERMANENTLY,
119 Self::FOUND,
120 Self::SEE_OTHER,
121 Self::NOT_MODIFIED,
122 Self::USE_PROXY,
123 Self::TEMPORARY_REDIRECT,
124 Self::PERMANENT_REDIRECT,
125 ] {
126 operation.responses.insert(
127 code.as_str(),
128 Response::new(
129 code.canonical_reason()
130 .unwrap_or("No further explanation is available."),
131 ),
132 )
133 }
134 operation
135 .responses
136 .append(&mut StatusError::to_responses(components));
137 }
138}
139impl EndpointOutRegister for salvo_core::Error {
140 #[inline]
141 fn register(components: &mut Components, operation: &mut Operation) {
142 operation
143 .responses
144 .append(&mut Self::to_responses(components));
145 }
146}
147
148impl EndpointOutRegister for &str {
149 #[inline]
150 fn register(components: &mut Components, operation: &mut Operation) {
151 operation.responses.insert(
152 "200",
153 Response::new("Ok").add_content("text/plain", String::to_schema(components)),
154 );
155 }
156}
157impl EndpointOutRegister for String {
158 #[inline]
159 fn register(components: &mut Components, operation: &mut Operation) {
160 operation.responses.insert(
161 "200",
162 Response::new("Ok").add_content("text/plain", Self::to_schema(components)),
163 );
164 }
165}
166impl EndpointOutRegister for &String {
167 #[inline]
168 fn register(components: &mut Components, operation: &mut Operation) {
169 operation.responses.insert(
170 "200",
171 Response::new("Ok").add_content("text/plain", String::to_schema(components)),
172 );
173 }
174}
175
176#[doc(hidden)]
178#[non_exhaustive]
179pub struct EndpointRegistry {
180 pub type_id: fn() -> TypeId,
182 pub creator: fn() -> Endpoint,
184}
185
186impl Debug for EndpointRegistry {
187 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
188 f.debug_struct("EndpointRegistry").finish()
189 }
190}
191
192impl EndpointRegistry {
193 pub const fn save(type_id: fn() -> TypeId, creator: fn() -> Endpoint) -> Self {
195 Self { type_id, creator }
196 }
197 #[must_use]
199 pub fn find(type_id: &TypeId) -> Option<fn() -> Endpoint> {
200 for record in inventory::iter::<Self> {
201 if (record.type_id)() == *type_id {
202 return Some(record.creator);
203 }
204 }
205 None
206 }
207}
208inventory::collect!(EndpointRegistry);
209
210#[cfg(feature = "anyhow")]
211impl EndpointOutRegister for anyhow::Error {
212 #[inline]
213 fn register(components: &mut Components, operation: &mut Operation) {
214 StatusError::register(components, operation);
215 }
216}
217
218#[cfg(feature = "eyre")]
219impl EndpointOutRegister for eyre::Report {
220 #[inline]
221 fn register(components: &mut Components, operation: &mut Operation) {
222 StatusError::register(components, operation);
223 }
224}