Skip to main content

salvo_oapi/
endpoint.rs

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/// Represents an endpoint.
13///
14/// View [module level documentation](index.html) for more details.
15#[derive(Clone, Debug)]
16pub struct Endpoint {
17    /// The operation information of the endpoint.
18    pub operation: Operation,
19    /// The OpenApi components section of the endpoint.
20    pub components: Components,
21}
22
23impl Endpoint {
24    /// Creates a new `Endpoint` with the given operation and components.
25    #[must_use]
26    pub fn new(operation: Operation, components: Components) -> Self {
27        Self {
28            operation,
29            components,
30        }
31    }
32}
33
34/// A trait for endpoint argument register.
35pub trait EndpointArgRegister {
36    /// Modify the OpenApi components section or current operation information with given argument.
37    /// This function is called by macros internal.
38    fn register(components: &mut Components, operation: &mut Operation, arg: &str);
39}
40/// A trait for endpoint return type register.
41pub trait EndpointOutRegister {
42    /// Modify the OpenApi components section or current operation information with given argument.
43    /// This function is called by macros internal.
44    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/// A registry for all endpoints.
177#[doc(hidden)]
178#[non_exhaustive]
179pub struct EndpointRegistry {
180    /// The type id of the endpoint.
181    pub type_id: fn() -> TypeId,
182    /// The creator of the endpoint.
183    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    /// Save the endpoint information to the registry.
194    pub const fn save(type_id: fn() -> TypeId, creator: fn() -> Endpoint) -> Self {
195        Self { type_id, creator }
196    }
197    /// Find the endpoint information from the registry.
198    #[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}