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