restless_core/lib.rs
1//! # restless core
2//!
3//! Core traits used by restless, crate which allows you to express HTTP request using the Rust
4//! type system.
5//!
6//! If you are using restless, then you should be using the `restless` crate and not this one. This
7//! crate is useful if you want to build plugins for `restless`.
8#![warn(missing_docs)]
9
10use std::borrow::Cow;
11
12pub mod query;
13use query::ToQuery;
14
15pub mod methods;
16use methods::*;
17
18pub mod data;
19pub use data::{Decodable, Encodable};
20
21mod get;
22pub use get::*;
23mod head;
24pub use head::*;
25mod post;
26pub use post::*;
27mod patch;
28pub use patch::*;
29mod delete;
30pub use delete::*;
31
32/// HTTP Method.
33///
34/// HTTP defines a set of request methods. These define if a request mutates something, if it can
35/// be cached, if it is idempotent and the semantics of the request. See [HTTP request
36/// methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) for an explainer of these.
37#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
38pub enum Method {
39 /// Requests a representation of the specified resource. Should only retrieve data.
40 Get,
41 /// Similar to a [`Get`] request, but without the response body. Only retrieves headers.
42 Head,
43 /// Submits an entry to the specified resource. Often causes a change in state or side-effects.
44 Post,
45 /// Replaces the target resource with the request payload. Is idempotent.
46 Put,
47 /// Deletes the specified resource on the server.
48 Delete,
49 /// Establishes a tunnel to the server identified by the target resource.
50 Connect,
51 /// Describes the communication options for the target resource.
52 Options,
53 /// Performs a message loop-back test to the target resource.
54 Trace,
55 /// Applies partial modifications to the target resource.
56 Patch,
57}
58
59#[cfg(feature = "http")]
60impl From<Method> for http::Method {
61 fn from(method: Method) -> Self {
62 match method {
63 Method::Get => http::Method::GET,
64 Method::Head => http::Method::HEAD,
65 Method::Post => http::Method::POST,
66 Method::Put => http::Method::PUT,
67 Method::Delete => http::Method::DELETE,
68 Method::Connect => http::Method::CONNECT,
69 Method::Options => http::Method::OPTIONS,
70 Method::Trace => http::Method::TRACE,
71 Method::Patch => http::Method::PATCH,
72 }
73 }
74}
75
76/// HTTP Request.
77///
78/// This trait defines an arbitrary HTTP request.
79pub trait Request {
80 /// Request body.
81 type Request: Encodable;
82 /// Response body.
83 type Response: Decodable;
84 /// Query string.
85 type Query: ToQuery;
86
87 /// URI path.
88 fn path(&self) -> Cow<'_, str>;
89 /// Request body.
90 fn body(&self) -> Self::Request;
91 /// Query.
92 fn query(&self) -> Self::Query;
93 /// HTTP method.
94 fn method(&self) -> Method;
95
96 /// Build URI for this request.
97 fn uri(&self) -> String {
98 let mut path = self.path().into_owned();
99 let query = self.query();
100 let query_string = query.encode();
101 if !query_string.is_empty() {
102 path.push('?');
103 path.push_str(&query_string);
104 }
105 path
106 }
107}
108
109/// Request type trait.
110///
111/// This allows types to implement [`Request`] by a proxy type.
112pub trait RequestType: Sized {
113 /// Underlying type that implements [`Request`].
114 type Request: Request;
115
116 /// Borrow a reference to the underlying [`Request`] type.
117 fn borrow(&self) -> &Self::Request;
118}
119
120/// Specify the [`Request`] method that should be used for a type.
121///
122/// Due to the way the Rust type system works, you can implement both [`PostRequest`] and
123/// [`GetRequest`] for the same type. To be able to implement [`Request`] for your type, this trait
124/// helps by allowing you to specify which request type should be used.
125///
126/// For example:
127///
128/// ```rust
129/// use restless_core::*;
130/// use std::borrow::Cow;
131///
132/// struct MyRequest;
133///
134/// impl GetRequest for MyRequest {
135/// type Response = Vec<u8>;
136/// type Query = ();
137///
138/// fn path(&self) -> Cow<'_, str> {
139/// "api/v1/request".into()
140/// }
141///
142/// fn query(&self) -> Self::Query {}
143/// }
144///
145/// impl RequestMethod for MyRequest {
146/// type Method = methods::Get<Self>;
147/// }
148/// ```
149pub trait RequestMethod: Sized
150where
151 Self::Method: From<Self>,
152 for<'a> &'a Self::Method: From<&'a Self>,
153{
154 /// Method to use for this request.
155 type Method: Request;
156
157 /// Borrow a [`Request`].
158 fn as_request(&self) -> &Self::Method {
159 self.into()
160 }
161
162 /// Turn this into an owned [`Request`].
163 fn into_request(self) -> Self::Method {
164 self.into()
165 }
166}
167
168impl<T: RequestMethod> RequestType for T
169where
170 for<'a> &'a <T as RequestMethod>::Method: From<&'a T>,
171{
172 type Request = <T as RequestMethod>::Method;
173
174 fn borrow(&self) -> &Self::Request {
175 self.into()
176 }
177}
178
179impl<T: RequestType> Request for T {
180 type Request = <<T as RequestType>::Request as Request>::Request;
181 type Response = <<T as RequestType>::Request as Request>::Response;
182 type Query = <<T as RequestType>::Request as Request>::Query;
183
184 fn path(&self) -> Cow<'_, str> {
185 self.borrow().path()
186 }
187
188 fn body(&self) -> Self::Request {
189 self.borrow().body()
190 }
191
192 fn query(&self) -> Self::Query {
193 self.borrow().query()
194 }
195
196 fn method(&self) -> Method {
197 self.borrow().method()
198 }
199}