1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#[cfg(feature = "openapi")]
use crate::openapi::operation::OperationId;
use crate::{IntoResponse, RequestBody};
use futures_util::future::BoxFuture;
use gotham::{
	extractor::{PathExtractor, QueryStringExtractor},
	hyper::{Body, Method, Response},
	router::response::StaticResponseExtender,
	state::{State, StateData}
};
#[cfg(feature = "openapi")]
use openapi_type::{OpenapiType, Visitor};
use serde::{Deserialize, Deserializer};
use std::borrow::Cow;

/// A no-op extractor that can be used as a default type for [Endpoint::Placeholders] and
/// [Endpoint::Params].
#[derive(Debug, Clone, Copy)]
pub struct NoopExtractor;

impl<'de> Deserialize<'de> for NoopExtractor {
	fn deserialize<D: Deserializer<'de>>(_: D) -> Result<Self, D::Error> {
		Ok(Self)
	}
}

#[cfg(feature = "openapi")]
impl OpenapiType for NoopExtractor {
	fn visit_type<V: Visitor>(visitor: &mut V) {
		warn!(
			"You're asking for the OpenAPI Schema for gotham_restful::NoopExtractor. This is probably not what you want."
		);
		visitor.visit_unit();
	}
}

impl StateData for NoopExtractor {}

impl StaticResponseExtender for NoopExtractor {
	type ResBody = Body;
	fn extend(_: &mut State, _: &mut Response<Body>) {}
}

// TODO: Specify default types once https://github.com/rust-lang/rust/issues/29661 lands.
#[_private_openapi_trait(EndpointWithSchema)]
pub trait Endpoint {
	/// The HTTP Verb of this endpoint.
	fn http_method() -> Method;
	/// The URI that this endpoint listens on in gotham's format.
	fn uri() -> Cow<'static, str>;

	/// The verb used for generating an operation id if [Self::operation_id] returns [None].
	/// For example `read`, `read_all`, `create`, `update` etc.
	#[openapi_only]
	fn operation_verb() -> Option<&'static str>;

	/// The output type that provides the response.
	#[openapi_bound(Output: crate::ResponseSchema)]
	type Output: IntoResponse + Send;

	/// Returns `true` _iff_ the URI contains placeholders. `false` by default.
	fn has_placeholders() -> bool {
		false
	}
	/// The type that parses the URI placeholders. Use [NoopExtractor] if `has_placeholders()`
	/// returns `false`.
	#[openapi_bound(Placeholders: OpenapiType)]
	type Placeholders: PathExtractor<Body> + Clone + Sync;

	/// Returns `true` _iff_ the request parameters should be parsed. `false` by default.
	fn needs_params() -> bool {
		false
	}
	/// The type that parses the request parameters. Use [NoopExtractor] if `needs_params()`
	/// returns `false`.
	#[openapi_bound(Params: OpenapiType)]
	type Params: QueryStringExtractor<Body> + Clone + Sync;

	/// Returns `true` _iff_ the request body should be parsed. `false` by default.
	fn needs_body() -> bool {
		false
	}
	/// The type to parse the body into. Use `()` if `needs_body()` returns `false`.
	type Body: RequestBody + Send;

	/// Returns `true` if the request wants to know the auth status of the client. `false` by default.
	fn wants_auth() -> bool {
		false
	}

	/// Replace the automatically generated operation id with a custom one. Only relevant for the
	/// OpenAPI Specification.
	#[openapi_only]
	fn operation_id() -> OperationId {
		OperationId::FullAuto
	}

	/// Add a description to the openapi specification. Usually taken from the rustdoc comment
	/// when using the proc macro.
	#[openapi_only]
	fn description() -> Option<String> {
		None
	}

	/// The handler for this endpoint.
	fn handle(
		state: &mut State,
		placeholders: Self::Placeholders,
		params: Self::Params,
		body: Option<Self::Body>
	) -> BoxFuture<'_, Self::Output>;
}

#[cfg(feature = "openapi")]
impl<E: EndpointWithSchema> Endpoint for E {
	fn http_method() -> Method {
		E::http_method()
	}
	fn uri() -> Cow<'static, str> {
		E::uri()
	}

	type Output = E::Output;

	fn has_placeholders() -> bool {
		E::has_placeholders()
	}
	type Placeholders = E::Placeholders;

	fn needs_params() -> bool {
		E::needs_params()
	}
	type Params = E::Params;

	fn needs_body() -> bool {
		E::needs_body()
	}
	type Body = E::Body;

	fn wants_auth() -> bool {
		E::wants_auth()
	}

	fn handle<'a>(
		state: &'a mut State,
		placeholders: Self::Placeholders,
		params: Self::Params,
		body: Option<Self::Body>
	) -> BoxFuture<'a, Self::Output> {
		E::handle(state, placeholders, params, body)
	}
}