Revise API to convert between associated types and Hyper request/response types.

This commit is contained in:
Jimmy Cuadra 2017-05-11 02:39:22 -07:00
parent b6a743d821
commit 5a4661c408
2 changed files with 131 additions and 120 deletions

View File

@ -11,8 +11,13 @@ repository = "https://github.com/ruma/ruma-api"
version = "0.3.0"
[dependencies]
serde = "0.8.21"
[dependencies.hyper]
git = "https://github.com/hyperium/hyper"
rev = "fed04dfb58e19b408322d4e5ca7474871e848a35"
[dev-dependencies]
ruma-identifiers = "0.6.0"
serde_derive = "0.8.21"
ruma-identifiers = "0.11"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"

View File

@ -2,133 +2,139 @@
//! in the various [Matrix](https://matrix.org) API specifications.
//! These types can be shared by client and server code for all Matrix APIs.
//!
//! When implementing a new Matrix API, each endpoint have a type that implements `Endpoint`, plus
//! any necessary associated types.
//! When implementing a new Matrix API, each endpoint has a type that implements `Endpoint`, plus
//! the necessary associated types.
//! An implementation of `Endpoint` contains all the information about the HTTP method, the path and
//! input parameters for requests, and the structure of a successful response.
//! Such types can then be used by client code to make requests, and by server code to fulfill
//! those requests.
//!
//! # Example
//!
//! ```rust,no_run
//! # #![feature(proc_macro)]
//! #
//! # extern crate ruma_api;
//! # extern crate ruma_identifiers;
//! # #[macro_use]
//! # extern crate serde_derive;
//! #
//! # fn main() {
//! /// PUT /_matrix/client/r0/directory/room/:room_alias
//! pub mod create {
//! use ruma_api;
//! use ruma_identifiers::{RoomAliasId, RoomId};
//!
//! /// This API endpoint's body parameters.
//! #[derive(Clone, Debug, Deserialize, Serialize)]
//! pub struct BodyParams {
//! pub room_id: RoomId,
//! }
//!
//! /// This API endpoint's path parameters.
//! #[derive(Clone, Debug)]
//! pub struct PathParams {
//! pub room_alias: RoomAliasId,
//! }
//!
//! /// Details about this API endpoint.
//! pub struct Endpoint;
//!
//! impl ruma_api::Endpoint for Endpoint {
//! type BodyParams = BodyParams;
//! type PathParams = PathParams;
//! type QueryParams = ();
//! type Response = ();
//!
//! fn method() -> ruma_api::Method {
//! ruma_api::Method::Put
//! }
//!
//! fn request_path(params: Self::PathParams) -> String {
//! format!("/_matrix/client/r0/directory/room/{}", params.room_alias)
//! }
//!
//! fn router_path() -> &'static str {
//! "/_matrix/client/r0/directory/room/:room_alias"
//! }
//!
//! fn name() -> &'static str {
//! "room_directory"
//! }
//!
//! fn description() -> &'static str {
//! "Matrix implementation of room directory."
//! }
//!
//! fn requires_authentication() -> bool {
//! true
//! }
//!
//! fn rate_limited() -> bool {
//! false
//! }
//! }
//! }
//! # }
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![feature(associated_consts, try_from)]
extern crate serde;
extern crate hyper;
#[cfg(test)] extern crate ruma_identifiers;
#[cfg(test)] extern crate serde;
#[cfg(test)] #[macro_use] extern crate serde_derive;
#[cfg(test)] extern crate serde_json;
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
/// HTTP request methods used in Matrix APIs.
#[derive(Clone, Copy, Debug)]
pub enum Method {
/// DELETE
Delete,
/// GET
Get,
/// POST
Post,
/// PUT
Put,
}
use hyper::{Method, Request, Response};
/// An API endpoint.
/// A Matrix API endpoint.
pub trait Endpoint {
/// Request parameters supplied via the body of the HTTP request.
type BodyParams: Deserialize + Serialize;
/// Data needed to make a request to the endpoint.
type Request: TryInto<Request>;
/// Data returned from the endpoint.
type Response: TryFrom<Response>;
/// Request parameters supplied via the URL's path.
type PathParams;
/// Parameters supplied via the URL's query string.
type QueryParams: Deserialize + Serialize;
/// The body of the response.
type Response: Deserialize + Serialize;
/// Returns the HTTP method used by this endpoint.
fn method() -> Method;
/// Generates the path component of the URL for this endpoint using the supplied parameters.
fn request_path(params: Self::PathParams) -> String;
/// Generates a generic path component of the URL for this endpoint, suitable for `Router` from
/// the router crate.
fn router_path() -> &'static str;
/// A unique identifier for this endpoint, suitable for `Router` from the router crate.
fn name() -> &'static str;
/// A human-readable description of the endpoint.
fn description() -> &'static str;
/// Whether or not this endpoint requires an authenticated user.
fn requires_authentication() -> bool;
/// Whether or not this endpoint is rate limited.
fn rate_limited() -> bool;
/// Metadata about the endpoint.
const METADATA: Metadata;
}
/// Metadata about an API endpoint.
#[derive(Clone, Debug)]
pub struct Metadata {
/// A human-readable description of the endpoint.
pub description: &'static str,
/// The HTTP method used by this endpoint.
pub method: Method,
/// A unique identifier for this endpoint.
pub name: &'static str,
/// The path of this endpoint's URL, with variable names where path parameters should be filled
/// in during a request.
pub path: &'static str,
/// Whether or not this endpoint is rate limited by the server.
pub rate_limited: bool,
/// Whether or not the server requires an authenticated user for this endpoint.
pub requires_authentication: bool,
}
#[cfg(test)]
mod tests {
/// PUT /_matrix/client/r0/directory/room/:room_alias
pub mod create {
use std::convert::TryFrom;
use hyper::{Method, Request as HyperRequest, Response as HyperResponse, StatusCode};
use ruma_identifiers::{RoomAliasId, RoomId};
use serde_json;
use super::super::{Endpoint as ApiEndpoint, Metadata};
#[derive(Debug)]
pub struct Endpoint;
#[derive(Debug)]
pub struct Error;
impl ApiEndpoint for Endpoint {
type Request = Request;
type Response = 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: true,
};
}
/// A request to create a new room alias.
#[derive(Debug)]
pub struct Request {
pub room_id: RoomId, // body
pub room_alias: RoomAliasId, // path
}
#[derive(Debug, Serialize)]
struct RequestBody {
room_id: RoomId,
}
impl TryFrom<Request> for HyperRequest {
type Error = Error;
fn try_from(request: Request) -> Result<HyperRequest, Self::Error> {
let metadata = Endpoint::METADATA;
let path = metadata.path
.to_string()
.replace(":room_alias", &request.room_alias.to_string());
let mut hyper_request = HyperRequest::new(
metadata.method,
path.parse().map_err(|_| Error)?,
);
let request_body = RequestBody {
room_id: request.room_id,
};
hyper_request.set_body(serde_json::to_vec(&request_body).map_err(|_| Error)?);
Ok(hyper_request)
}
}
/// The response to a request to create a new room alias.
pub struct Response;
impl TryFrom<HyperResponse> for Response {
type Error = Error;
fn try_from(hyper_response: HyperResponse) -> Result<Response, Self::Error> {
if hyper_response.status() == StatusCode::Ok {
Ok(Response)
} else {
Err(Error)
}
}
}
}
}