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 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
//! # restless core
//!
//! Core traits used by restless, crate which allows you to express HTTP request using the Rust
//! type system.
//!
//! If you are using restless, then you should be using the `restless` crate and not this one. This
//! crate is useful if you want to build plugins for `restless`.
#![warn(missing_docs)]
use std::borrow::Cow;
pub mod query;
use query::ToQuery;
pub mod methods;
use methods::*;
pub mod data;
pub use data::{Decodable, Encodable};
mod get;
pub use get::*;
mod head;
pub use head::*;
mod post;
pub use post::*;
mod patch;
pub use patch::*;
mod delete;
pub use delete::*;
/// HTTP Method.
///
/// HTTP defines a set of request methods. These define if a request mutates something, if it can
/// be cached, if it is idempotent and the semantics of the request. See [HTTP request
/// methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) for an explainer of these.
#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Method {
/// Requests a representation of the specified resource. Should only retrieve data.
Get,
/// Similar to a [`Get`] request, but without the response body. Only retrieves headers.
Head,
/// Submits an entry to the specified resource. Often causes a change in state or side-effects.
Post,
/// Replaces the target resource with the request payload. Is idempotent.
Put,
/// Deletes the specified resource on the server.
Delete,
/// Establishes a tunnel to the server identified by the target resource.
Connect,
/// Describes the communication options for the target resource.
Options,
/// Performs a message loop-back test to the target resource.
Trace,
/// Applies partial modifications to the target resource.
Patch,
}
#[cfg(feature = "http")]
impl From<Method> for http::Method {
fn from(method: Method) -> Self {
match method {
Method::Get => http::Method::GET,
Method::Head => http::Method::HEAD,
Method::Post => http::Method::POST,
Method::Put => http::Method::PUT,
Method::Delete => http::Method::DELETE,
Method::Connect => http::Method::CONNECT,
Method::Options => http::Method::OPTIONS,
Method::Trace => http::Method::TRACE,
Method::Patch => http::Method::PATCH,
}
}
}
/// HTTP Request.
///
/// This trait defines an arbitrary HTTP request.
pub trait Request {
/// Request body.
type Request: Encodable;
/// Response body.
type Response: Decodable;
/// Query string.
type Query: ToQuery;
/// URI path.
fn path(&self) -> Cow<'_, str>;
/// Request body.
fn body(&self) -> Self::Request;
/// Query.
fn query(&self) -> Self::Query;
/// HTTP method.
fn method(&self) -> Method;
/// Build URI for this request.
fn uri(&self) -> String {
let mut path = self.path().into_owned();
let query = self.query();
let query_string = query.encode();
if !query_string.is_empty() {
path.push('?');
path.push_str(&query_string);
}
path
}
}
/// Request type trait.
///
/// This allows types to implement [`Request`] by a proxy type.
pub trait RequestType: Sized {
/// Underlying type that implements [`Request`].
type Request: Request;
/// Borrow a reference to the underlying [`Request`] type.
fn borrow(&self) -> &Self::Request;
}
/// Specify the [`Request`] method that should be used for a type.
///
/// Due to the way the Rust type system works, you can implement both [`PostRequest`] and
/// [`GetRequest`] for the same type. To be able to implement [`Request`] for your type, this trait
/// helps by allowing you to specify which request type should be used.
///
/// For example:
///
/// ```rust
/// use restless_core::*;
/// use std::borrow::Cow;
///
/// struct MyRequest;
///
/// impl GetRequest for MyRequest {
/// type Response = Vec<u8>;
/// type Query = ();
///
/// fn path(&self) -> Cow<'_, str> {
/// "api/v1/request".into()
/// }
///
/// fn query(&self) -> Self::Query {}
/// }
///
/// impl RequestMethod for MyRequest {
/// type Method = methods::Get<Self>;
/// }
/// ```
pub trait RequestMethod: Sized
where
Self::Method: From<Self>,
for<'a> &'a Self::Method: From<&'a Self>,
{
/// Method to use for this request.
type Method: Request;
/// Borrow a [`Request`].
fn as_request(&self) -> &Self::Method {
self.into()
}
/// Turn this into an owned [`Request`].
fn into_request(self) -> Self::Method {
self.into()
}
}
impl<T: RequestMethod> RequestType for T
where
for<'a> &'a <T as RequestMethod>::Method: From<&'a T>,
{
type Request = <T as RequestMethod>::Method;
fn borrow(&self) -> &Self::Request {
self.into()
}
}
impl<T: RequestType> Request for T {
type Request = <<T as RequestType>::Request as Request>::Request;
type Response = <<T as RequestType>::Request as Request>::Response;
type Query = <<T as RequestType>::Request as Request>::Query;
fn path(&self) -> Cow<'_, str> {
self.borrow().path()
}
fn body(&self) -> Self::Request {
self.borrow().body()
}
fn query(&self) -> Self::Query {
self.borrow().query()
}
fn method(&self) -> Method {
self.borrow().method()
}
}