salvo_oapi/
endpoint.rs

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