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 request_type = &self.request;
let response_type = &self.response; 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) quote!(IncomingRequest)
} else { } else {
quote!(Request) quote!(Request)
@ -227,22 +227,25 @@ impl ToTokens for Api {
let request_lifetimes = self.request.combine_lifetimes(); 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() TokenStream::new()
} else { } else {
quote! { quote! {
impl #request_lifetimes #ruma_api_import::NonAuthEndpoint impl #request_lifetimes #ruma_api_import::OutgoingNonAuthRequest
for Request #request_lifetimes for Request #request_lifetimes
{} {}
impl #ruma_api_import::IncomingNonAuthRequest for #incoming_request_type {}
} }
}; };
let api = quote! { let api = quote! {
#[doc = #request_doc] #[doc = #request_doc]
#request_type #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; type Error = #ruma_api_import::error::FromHttpRequestError;
#[allow(unused_variables)] #[allow(unused_variables)]
@ -308,14 +311,7 @@ impl ToTokens for Api {
} }
} }
impl #request_lifetimes #ruma_api_import::Endpoint for Request #request_lifetimes { const __METADATA: #ruma_api_import::Metadata = #ruma_api_import::Metadata {
type Response = Response;
type ResponseError = #error;
type IncomingRequest = <Self as #ruma_api_import::Outgoing>::Incoming;
type IncomingResponse = <Response as #ruma_api_import::Outgoing>::Incoming;
/// Metadata for the `#name` endpoint.
const METADATA: #ruma_api_import::Metadata = #ruma_api_import::Metadata {
description: #description, description: #description,
method: #ruma_api_import::exports::http::Method::#method, method: #ruma_api_import::exports::http::Method::#method,
name: #name, name: #name,
@ -324,6 +320,16 @@ impl ToTokens for Api {
requires_authentication: #requires_authentication, 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 = __METADATA;
#[allow(unused_mut, unused_variables)] #[allow(unused_mut, unused_variables)]
fn try_into_http_request( fn try_into_http_request(
self, self,
@ -333,7 +339,7 @@ impl ToTokens for Api {
#ruma_api_import::exports::http::Request<Vec<u8>>, #ruma_api_import::exports::http::Request<Vec<u8>>,
#ruma_api_import::error::IntoHttpError, #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() let http_request = #ruma_api_import::exports::http::Request::builder()
.method(#ruma_api_import::exports::http::Method::#method) .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); api.to_tokens(tokens);

View File

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

View File

@ -242,22 +242,15 @@ pub trait EndpointError: std::error::Error + Sized {
) -> Result<Self, error::ResponseDeserializationError>; ) -> Result<Self, error::ResponseDeserializationError>;
} }
/// A Matrix API endpoint. /// A request type for a Matrix API endpoint. (trait used for sending requests)
/// pub trait OutgoingRequest {
/// The type implementing this trait contains any data needed to make a request to the endpoint. /// A type capturing the expected error conditions the server can return.
pub trait Endpoint: Outgoing<Incoming = <Self as Endpoint>::IncomingRequest> { type EndpointError: EndpointError;
/// 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;
/// Shorthand for `<Self as Outgoing>::Incoming`. /// Response type returned when the request is successful.
type IncomingRequest: TryFrom<http::Request<Vec<u8>>, Error = FromHttpRequestError>;
/// Shorthand for `<Self::Response as Outgoing>::Incoming`.
type IncomingResponse: TryFrom< type IncomingResponse: TryFrom<
http::Response<Vec<u8>>, http::Response<Vec<u8>>,
Error = FromHttpResponseError<<Self as Endpoint>::ResponseError>, Error = FromHttpResponseError<Self::EndpointError>,
>; >;
/// Metadata about the endpoint. /// Metadata about the endpoint.
@ -278,11 +271,23 @@ pub trait Endpoint: Outgoing<Incoming = <Self as Endpoint>::IncomingRequest> {
) -> Result<http::Request<Vec<u8>>, IntoHttpError>; ) -> Result<http::Request<Vec<u8>>, IntoHttpError>;
} }
/// A Matrix API endpoint that doesn't require authentication. /// A request type for a Matrix API endpoint. (trait used for receiving requests)
/// pub trait IncomingRequest: TryFrom<http::Request<Vec<u8>>, Error = FromHttpRequestError> {
/// This marker trait is to indicate that a type implementing `Endpoint` doesn't require any /// A type capturing the error conditions that can be returned in the response.
/// authentication. type EndpointError: EndpointError;
pub trait NonAuthEndpoint: Endpoint {}
/// 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. /// Metadata about an API endpoint.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -403,7 +403,7 @@ impl DeviceLists {
mod tests { mod tests {
use std::{convert::TryInto, time::Duration}; 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 serde_json::{from_value as from_json_value, json, to_value as to_json_value};
use matches::assert_matches; use matches::assert_matches;

View File

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