Split Endpoint trait into OutgoingRequest, IncomingRequest

This commit is contained in:
Jonas Platte 2020-08-12 22:35:18 +02:00
parent abc34eeb7e
commit 987d48666c
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
11 changed files with 95 additions and 68 deletions

View File

@ -101,7 +101,7 @@ impl ToTokens for Api {
let request_type = &self.request;
let response_type = &self.response;
let request_try_from_type = if self.request.contains_lifetimes() {
let incoming_request_type = if self.request.contains_lifetimes() {
quote!(IncomingRequest)
} else {
quote!(Request)
@ -227,22 +227,25 @@ impl ToTokens for Api {
let request_lifetimes = self.request.combine_lifetimes();
let non_auth_endpoint_impl = if requires_authentication.value {
let non_auth_endpoint_impls = if requires_authentication.value {
TokenStream::new()
} else {
quote! {
impl #request_lifetimes #ruma_api_import::NonAuthEndpoint
impl #request_lifetimes #ruma_api_import::OutgoingNonAuthRequest
for Request #request_lifetimes
{}
impl #ruma_api_import::IncomingNonAuthRequest for #incoming_request_type {}
}
};
let api = quote! {
#[doc = #request_doc]
#request_type
impl ::std::convert::TryFrom<#ruma_api_import::exports::http::Request<Vec<u8>>> for #request_try_from_type {
impl ::std::convert::TryFrom<#ruma_api_import::exports::http::Request<Vec<u8>>>
for #incoming_request_type
{
type Error = #ruma_api_import::error::FromHttpRequestError;
#[allow(unused_variables)]
@ -308,21 +311,24 @@ impl ToTokens for Api {
}
}
impl #request_lifetimes #ruma_api_import::Endpoint for Request #request_lifetimes {
type Response = Response;
type ResponseError = #error;
type IncomingRequest = <Self as #ruma_api_import::Outgoing>::Incoming;
const __METADATA: #ruma_api_import::Metadata = #ruma_api_import::Metadata {
description: #description,
method: #ruma_api_import::exports::http::Method::#method,
name: #name,
path: #path,
rate_limited: #rate_limited,
requires_authentication: #requires_authentication,
};
impl #request_lifetimes #ruma_api_import::OutgoingRequest
for Request #request_lifetimes
{
type EndpointError = #error;
type IncomingResponse = <Response as #ruma_api_import::Outgoing>::Incoming;
// FIXME: Doc string interpolation
/// Metadata for the `#name` endpoint.
const METADATA: #ruma_api_import::Metadata = #ruma_api_import::Metadata {
description: #description,
method: #ruma_api_import::exports::http::Method::#method,
name: #name,
path: #path,
rate_limited: #rate_limited,
requires_authentication: #requires_authentication,
};
const METADATA: #ruma_api_import::Metadata = __METADATA;
#[allow(unused_mut, unused_variables)]
fn try_into_http_request(
@ -333,7 +339,7 @@ impl ToTokens for Api {
#ruma_api_import::exports::http::Request<Vec<u8>>,
#ruma_api_import::error::IntoHttpError,
> {
let metadata = Request::METADATA;
let metadata = <Self as #ruma_api_import::OutgoingRequest>::METADATA;
let http_request = #ruma_api_import::exports::http::Request::builder()
.method(#ruma_api_import::exports::http::Method::#method)
@ -350,7 +356,16 @@ impl ToTokens for Api {
}
}
#non_auth_endpoint_impl
impl #ruma_api_import::IncomingRequest for #incoming_request_type {
type EndpointError = #error;
type OutgoingResponse = Response;
// FIXME: Doc string interpolation
/// Metadata for the `#name` endpoint.
const METADATA: #ruma_api_import::Metadata = __METADATA;
}
#non_auth_endpoint_impls
};
api.to_tokens(tokens);

View File

@ -4,12 +4,14 @@
use std::fmt::{self, Display, Formatter};
use crate::EndpointError;
// FIXME when `!` becomes stable use it
/// Default `ResponseError` for `ruma_api!` macro
/// Default `EndpointError` for `ruma_api!` macro
#[derive(Clone, Copy, Debug)]
pub enum Void {}
impl crate::EndpointError for Void {
impl EndpointError for Void {
fn try_from_response(
response: http::Response<Vec<u8>>,
) -> Result<Self, ResponseDeserializationError> {

View File

@ -231,7 +231,7 @@ pub trait Outgoing {
type Incoming;
}
/// Gives users the ability to define their own serializable/deserializable errors.
/// Gives users the ability to define their own serializable / deserializable errors.
pub trait EndpointError: std::error::Error + Sized {
/// Tries to construct `Self` from an `http::Response`.
///
@ -242,22 +242,15 @@ pub trait EndpointError: std::error::Error + Sized {
) -> Result<Self, error::ResponseDeserializationError>;
}
/// A Matrix API endpoint.
///
/// The type implementing this trait contains any data needed to make a request to the endpoint.
pub trait Endpoint: Outgoing<Incoming = <Self as Endpoint>::IncomingRequest> {
/// Data returned in a successful response from the endpoint.
type Response: Outgoing<Incoming = Self::IncomingResponse>
+ TryInto<http::Response<Vec<u8>>, Error = IntoHttpError>;
/// Error type returned when response from endpoint fails.
type ResponseError: EndpointError;
/// A request type for a Matrix API endpoint. (trait used for sending requests)
pub trait OutgoingRequest {
/// A type capturing the expected error conditions the server can return.
type EndpointError: EndpointError;
/// Shorthand for `<Self as Outgoing>::Incoming`.
type IncomingRequest: TryFrom<http::Request<Vec<u8>>, Error = FromHttpRequestError>;
/// Shorthand for `<Self::Response as Outgoing>::Incoming`.
/// Response type returned when the request is successful.
type IncomingResponse: TryFrom<
http::Response<Vec<u8>>,
Error = FromHttpResponseError<<Self as Endpoint>::ResponseError>,
Error = FromHttpResponseError<Self::EndpointError>,
>;
/// Metadata about the endpoint.
@ -278,11 +271,23 @@ pub trait Endpoint: Outgoing<Incoming = <Self as Endpoint>::IncomingRequest> {
) -> Result<http::Request<Vec<u8>>, IntoHttpError>;
}
/// A Matrix API endpoint that doesn't require authentication.
///
/// This marker trait is to indicate that a type implementing `Endpoint` doesn't require any
/// authentication.
pub trait NonAuthEndpoint: Endpoint {}
/// A request type for a Matrix API endpoint. (trait used for receiving requests)
pub trait IncomingRequest: TryFrom<http::Request<Vec<u8>>, Error = FromHttpRequestError> {
/// A type capturing the error conditions that can be returned in the response.
type EndpointError: EndpointError;
/// Response type to return when the request is successful.
type OutgoingResponse: TryInto<http::Response<Vec<u8>>, Error = IntoHttpError>;
/// Metadata about the endpoint.
const METADATA: Metadata;
}
/// Marker trait for requests that don't require authentication. (for the client side)
pub trait OutgoingNonAuthRequest: OutgoingRequest {}
/// Marker trait for requests that don't require authentication. (for the server side)
pub trait IncomingNonAuthRequest: IncomingRequest {}
/// Metadata about an API endpoint.
#[derive(Clone, Debug)]

View File

@ -1,6 +1,6 @@
use std::convert::TryFrom;
use ruma_api::{ruma_api, Endpoint as _};
use ruma_api::{ruma_api, OutgoingRequest as _};
use ruma_identifiers::{user_id, UserId};
ruma_api! {

View File

@ -10,7 +10,7 @@ use ruma_api::{
FromHttpRequestError, FromHttpResponseError, IntoHttpError, RequestDeserializationError,
ResponseDeserializationError, ServerError, Void,
},
Endpoint, Metadata, Outgoing,
IncomingRequest, Metadata, Outgoing, OutgoingRequest,
};
/// A request to create a new room alias.
@ -24,35 +24,33 @@ impl Outgoing for Request {
type Incoming = Self;
}
impl Endpoint for Request {
type Response = Response;
type ResponseError = Void;
type IncomingRequest = Self;
const METADATA: Metadata = Metadata {
description: "Add an alias to a room.",
method: Method::PUT,
name: "create_alias",
path: "/_matrix/client/r0/directory/room/:room_alias",
rate_limited: false,
requires_authentication: false,
};
impl OutgoingRequest for Request {
type EndpointError = Void;
type IncomingResponse = Response;
const METADATA: Metadata = Metadata {
description: "Add an alias to a room.",
method: Method::PUT,
name: "create_alias",
path: "/_matrix/client/r0/directory/room/:room_alias",
rate_limited: false,
requires_authentication: false,
};
const METADATA: Metadata = METADATA;
fn try_into_http_request(
self,
base_url: &str,
_access_token: Option<&str>,
) -> Result<http::Request<Vec<u8>>, IntoHttpError> {
let metadata = Request::METADATA;
let url = (base_url.to_owned() + metadata.path)
let url = (base_url.to_owned() + METADATA.path)
.replace(":room_alias", &self.room_alias.to_string());
let request_body = RequestBody { room_id: self.room_id };
let http_request = http::Request::builder()
.method(metadata.method)
.method(METADATA.method)
.uri(url)
.body(serde_json::to_vec(&request_body)?)
// this cannot fail because we don't give user-supplied data to any of the
@ -63,6 +61,13 @@ impl Endpoint for Request {
}
}
impl IncomingRequest for Request {
type EndpointError = Void;
type OutgoingResponse = Response;
const METADATA: Metadata = METADATA;
}
impl TryFrom<http::Request<Vec<u8>>> for Request {
type Error = FromHttpRequestError;

View File

@ -1,6 +1,6 @@
use std::convert::TryFrom;
use ruma_api::{ruma_api, Endpoint};
use ruma_api::{ruma_api, OutgoingRequest as _};
ruma_api! {
metadata: {

View File

@ -56,7 +56,7 @@ mod tests {
use std::convert::TryInto;
use js_int::uint;
use ruma_api::Endpoint as _;
use ruma_api::OutgoingRequest as _;
use super::{Request, Response};

View File

@ -118,7 +118,7 @@ mod tests {
use super::{Direction, Request};
use js_int::uint;
use ruma_api::Endpoint;
use ruma_api::OutgoingRequest;
use ruma_identifiers::room_id;
use crate::r0::filter::{LazyLoadOptions, RoomEventFilter};

View File

@ -140,7 +140,7 @@ mod user_serde;
#[cfg(test)]
mod tests {
use ruma_api::Endpoint;
use ruma_api::OutgoingRequest;
use serde_json::{from_value as from_json_value, json, Value as JsonValue};
use super::{LoginInfo, Medium, Request, UserInfo};

View File

@ -403,7 +403,7 @@ impl DeviceLists {
mod tests {
use std::{convert::TryInto, time::Duration};
use ruma_api::Endpoint;
use ruma_api::OutgoingRequest;
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
use matches::assert_matches;

View File

@ -113,7 +113,7 @@ use http::{uri::Uri, Response as HttpResponse};
use hyper::{client::HttpConnector, Client as HyperClient};
#[cfg(feature = "hyper-tls")]
use hyper_tls::HttpsConnector;
use ruma_api::Endpoint;
use ruma_api::OutgoingRequest;
use ruma_client_api::r0::sync::sync_events::{
Filter as SyncFilter, Request as SyncRequest, Response as SyncResponse,
};
@ -350,19 +350,19 @@ where
}
/// Makes a request to a Matrix API endpoint.
pub async fn request<Request: Endpoint>(
pub async fn request<Request: OutgoingRequest>(
&self,
request: Request,
) -> Result<Request::IncomingResponse, Error<Request::ResponseError>> {
) -> Result<Request::IncomingResponse, Error<Request::EndpointError>> {
self.request_with_url_params(request, None).await
}
/// Makes a request to a Matrix API endpoint including additional URL parameters.
pub async fn request_with_url_params<Request: Endpoint>(
pub async fn request_with_url_params<Request: OutgoingRequest>(
&self,
request: Request,
extra_params: Option<BTreeMap<String, String>>,
) -> Result<Request::IncomingResponse, Error<Request::ResponseError>> {
) -> Result<Request::IncomingResponse, Error<Request::EndpointError>> {
let client = self.0.clone();
let mut http_request = {
let session;