Skip to main content

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