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}