gotham_restful/
endpoint.rs

1#[cfg(feature = "openapi")]
2use crate::openapi::operation::OperationId;
3use crate::{IntoResponse, RequestBody};
4use futures_util::future::BoxFuture;
5use gotham::{
6	extractor::{PathExtractor, QueryStringExtractor},
7	hyper::{Body, Method, Response},
8	router::response::StaticResponseExtender,
9	state::{State, StateData}
10};
11#[cfg(feature = "openapi")]
12use openapi_type::{OpenapiType, Visitor};
13use serde::{Deserialize, Deserializer};
14use std::borrow::Cow;
15
16/// A no-op extractor that can be used as a default type for [Endpoint::Placeholders] and
17/// [Endpoint::Params].
18#[derive(Debug, Clone, Copy)]
19pub struct NoopExtractor;
20
21impl<'de> Deserialize<'de> for NoopExtractor {
22	fn deserialize<D: Deserializer<'de>>(_: D) -> Result<Self, D::Error> {
23		Ok(Self)
24	}
25}
26
27#[cfg(feature = "openapi")]
28impl OpenapiType for NoopExtractor {
29	fn visit_type<V: Visitor>(visitor: &mut V) {
30		warn!(
31			"You're asking for the OpenAPI Schema for gotham_restful::NoopExtractor. This is probably not what you want."
32		);
33		visitor.visit_unit();
34	}
35}
36
37impl StateData for NoopExtractor {}
38
39impl StaticResponseExtender for NoopExtractor {
40	type ResBody = Body;
41	fn extend(_: &mut State, _: &mut Response<Body>) {}
42}
43
44// TODO: Specify default types once https://github.com/rust-lang/rust/issues/29661 lands.
45#[_private_openapi_trait(EndpointWithSchema)]
46pub trait Endpoint {
47	/// The HTTP Verb of this endpoint.
48	fn http_method() -> Method;
49	/// The URI that this endpoint listens on in gotham's format.
50	fn uri() -> Cow<'static, str>;
51
52	/// The verb used for generating an operation id if [Self::operation_id] returns [None].
53	/// For example `read`, `read_all`, `create`, `update` etc.
54	#[openapi_only]
55	fn operation_verb() -> Option<&'static str>;
56
57	/// The output type that provides the response.
58	#[openapi_bound(Output: crate::ResponseSchema)]
59	type Output: IntoResponse + Send;
60
61	/// Returns `true` _iff_ the URI contains placeholders. `false` by default.
62	fn has_placeholders() -> bool {
63		false
64	}
65	/// The type that parses the URI placeholders. Use [NoopExtractor] if `has_placeholders()`
66	/// returns `false`.
67	#[openapi_bound(Placeholders: OpenapiType)]
68	type Placeholders: PathExtractor<Body> + Clone + Sync;
69
70	/// Returns `true` _iff_ the request parameters should be parsed. `false` by default.
71	fn needs_params() -> bool {
72		false
73	}
74	/// The type that parses the request parameters. Use [NoopExtractor] if `needs_params()`
75	/// returns `false`.
76	#[openapi_bound(Params: OpenapiType)]
77	type Params: QueryStringExtractor<Body> + Clone + Sync;
78
79	/// Returns `true` _iff_ the request body should be parsed. `false` by default.
80	fn needs_body() -> bool {
81		false
82	}
83	/// The type to parse the body into. Use `()` if `needs_body()` returns `false`.
84	type Body: RequestBody + Send;
85
86	/// Returns `true` if the request wants to know the auth status of the client. `false` by default.
87	fn wants_auth() -> bool {
88		false
89	}
90
91	/// Replace the automatically generated operation id with a custom one. Only relevant for the
92	/// OpenAPI Specification.
93	#[openapi_only]
94	fn operation_id() -> OperationId {
95		OperationId::FullAuto
96	}
97
98	/// Add a description to the openapi specification. Usually taken from the rustdoc comment
99	/// when using the proc macro.
100	#[openapi_only]
101	fn description() -> Option<String> {
102		None
103	}
104
105	/// The handler for this endpoint.
106	fn handle(
107		state: &mut State,
108		placeholders: Self::Placeholders,
109		params: Self::Params,
110		body: Option<Self::Body>
111	) -> BoxFuture<'_, Self::Output>;
112}
113
114#[cfg(feature = "openapi")]
115impl<E: EndpointWithSchema> Endpoint for E {
116	fn http_method() -> Method {
117		E::http_method()
118	}
119	fn uri() -> Cow<'static, str> {
120		E::uri()
121	}
122
123	type Output = E::Output;
124
125	fn has_placeholders() -> bool {
126		E::has_placeholders()
127	}
128	type Placeholders = E::Placeholders;
129
130	fn needs_params() -> bool {
131		E::needs_params()
132	}
133	type Params = E::Params;
134
135	fn needs_body() -> bool {
136		E::needs_body()
137	}
138	type Body = E::Body;
139
140	fn wants_auth() -> bool {
141		E::wants_auth()
142	}
143
144	fn handle<'a>(
145		state: &'a mut State,
146		placeholders: Self::Placeholders,
147		params: Self::Params,
148		body: Option<Self::Body>
149	) -> BoxFuture<'a, Self::Output> {
150		E::handle(state, placeholders, params, body)
151	}
152}