salvo_oapi/
endpoint.rs

1use std::any::TypeId;
2
3use salvo_core::http::StatusCode;
4use salvo_core::{prelude::StatusError, writing};
5
6use crate::{Components, Operation, Response, ToResponse, ToResponses, ToSchema};
7
8/// Represents an endpoint.
9///
10/// View [module level documentation](index.html) for more details.
11pub struct Endpoint {
12    /// The operation information of the endpoint.
13    pub operation: Operation,
14    /// The OpenApi components section of the endpoint.
15    pub components: Components,
16}
17
18impl Endpoint {
19    /// Create new `Endpoint` with given operation and components.
20    pub fn new(operation: Operation, components: Components) -> Self {
21        Self {
22            operation,
23            components,
24        }
25    }
26}
27
28/// A trait for endpoint argument register.
29pub trait EndpointArgRegister {
30    /// Modify the OpenApi components section or current operation information with given argument. This function is called by macros internal.
31    fn register(components: &mut Components, operation: &mut Operation, arg: &str);
32}
33/// A trait for endpoint return type register.
34pub trait EndpointOutRegister {
35    /// Modify the OpenApi components section or current operation information with given argument. This function is called by macros internal.
36    fn register(components: &mut Components, operation: &mut Operation);
37}
38
39impl<C> EndpointOutRegister for writing::Json<C>
40where
41    C: ToSchema,
42{
43    #[inline]
44    fn register(components: &mut Components, operation: &mut Operation) {
45        operation
46            .responses
47            .insert("200", Self::to_response(components));
48    }
49}
50impl<T, E> EndpointOutRegister for Result<T, E>
51where
52    T: EndpointOutRegister + Send,
53    E: EndpointOutRegister + Send,
54{
55    #[inline]
56    fn register(components: &mut Components, operation: &mut Operation) {
57        T::register(components, operation);
58        E::register(components, operation);
59    }
60}
61impl<E> EndpointOutRegister for Result<(), E>
62where
63    E: EndpointOutRegister + Send,
64{
65    #[inline]
66    fn register(components: &mut Components, operation: &mut Operation) {
67        operation.responses.insert("200", Response::new("Ok"));
68        E::register(components, operation);
69    }
70}
71
72impl EndpointOutRegister for StatusError {
73    #[inline]
74    fn register(components: &mut Components, operation: &mut Operation) {
75        operation
76            .responses
77            .append(&mut Self::to_responses(components));
78    }
79}
80impl EndpointOutRegister for StatusCode {
81    fn register(components: &mut Components, operation: &mut Operation) {
82        for code in [
83            StatusCode::CONTINUE,
84            StatusCode::SWITCHING_PROTOCOLS,
85            StatusCode::PROCESSING,
86            StatusCode::OK,
87            StatusCode::CREATED,
88            StatusCode::ACCEPTED,
89            StatusCode::NON_AUTHORITATIVE_INFORMATION,
90            StatusCode::NO_CONTENT,
91            StatusCode::RESET_CONTENT,
92            StatusCode::PARTIAL_CONTENT,
93            StatusCode::MULTI_STATUS,
94            StatusCode::ALREADY_REPORTED,
95            StatusCode::IM_USED,
96            StatusCode::MULTIPLE_CHOICES,
97            StatusCode::MOVED_PERMANENTLY,
98            StatusCode::FOUND,
99            StatusCode::SEE_OTHER,
100            StatusCode::NOT_MODIFIED,
101            StatusCode::USE_PROXY,
102            StatusCode::TEMPORARY_REDIRECT,
103            StatusCode::PERMANENT_REDIRECT,
104        ] {
105            operation.responses.insert(
106                code.as_str(),
107                Response::new(
108                    code.canonical_reason()
109                        .unwrap_or("No further explanation is available."),
110                ),
111            )
112        }
113        operation
114            .responses
115            .append(&mut StatusError::to_responses(components));
116    }
117}
118impl EndpointOutRegister for salvo_core::Error {
119    #[inline]
120    fn register(components: &mut Components, operation: &mut Operation) {
121        operation
122            .responses
123            .append(&mut Self::to_responses(components));
124    }
125}
126
127impl EndpointOutRegister for &str {
128    #[inline]
129    fn register(components: &mut Components, operation: &mut Operation) {
130        operation.responses.insert(
131            "200",
132            Response::new("Ok").add_content("text/plain", String::to_schema(components)),
133        );
134    }
135}
136impl EndpointOutRegister for String {
137    #[inline]
138    fn register(components: &mut Components, operation: &mut Operation) {
139        operation.responses.insert(
140            "200",
141            Response::new("Ok").add_content("text/plain", String::to_schema(components)),
142        );
143    }
144}
145impl EndpointOutRegister for &String {
146    #[inline]
147    fn register(components: &mut Components, operation: &mut Operation) {
148        operation.responses.insert(
149            "200",
150            Response::new("Ok").add_content("text/plain", String::to_schema(components)),
151        );
152    }
153}
154
155/// A registry for all endpoints.
156#[doc(hidden)]
157#[non_exhaustive]
158pub struct EndpointRegistry {
159    /// The type id of the endpoint.
160    pub type_id: fn() -> TypeId,
161    /// The creator of the endpoint.
162    pub creator: fn() -> Endpoint,
163}
164
165impl EndpointRegistry {
166    /// Save the endpoint information to the registry.
167    pub const fn save(type_id: fn() -> TypeId, creator: fn() -> Endpoint) -> Self {
168        Self { type_id, creator }
169    }
170    /// Find the endpoint information from the registry.
171    pub fn find(type_id: &TypeId) -> Option<fn() -> Endpoint> {
172        for record in inventory::iter::<EndpointRegistry> {
173            if (record.type_id)() == *type_id {
174                return Some(record.creator);
175            }
176        }
177        None
178    }
179}
180inventory::collect!(EndpointRegistry);