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