From 269d405bb51ab82d00973b42bf430c26618d6f68 Mon Sep 17 00:00:00 2001 From: Glenn Griffin Date: Tue, 10 Dec 2019 12:40:57 -0800 Subject: [PATCH] Add documentation --- src/lib.rs | 47 ++++++++++++++++++--------- src/mappers.rs | 72 ++++++++++++++++++++++++++++++++++------- src/mappers/request.rs | 33 ++++++++++--------- src/mappers/response.rs | 19 ++++++----- src/responders.rs | 22 +++++++++++++ 5 files changed, 143 insertions(+), 50 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 69e6cc1..bdd3282 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,18 +41,19 @@ //! //! # Server behavior //! -//! The Server is started with [Server::run()](struct.Server.html#method.run). +//! The Server is started with [run()](struct.Server.html#method.run). //! //! The server will run in a background thread until it's dropped. Once dropped //! it will assert that every configured expectation has been met or will panic. -//! You can also use [server.verify_and_clear()](struct.Server.html#method.verify_and_clear) +//! You can also use [verify_and_clear()](struct.Server.html#method.verify_and_clear) //! to assert and clear the expectations while keeping the server running. //! -//! [server.addr()](struct.Server.html#method.addr) will return the address the +//! [addr()](struct.Server.html#method.addr) will return the address the //! server is listening on. //! -//! [server.url()](struct.Server.html#method.url) will +//! [url()](struct.Server.html#method.url) will //! construct a fully formed http url to the path provided i.e. +//! //! `server.url("/foo?key=value") == "https:///foo?key=value"`. //! //! # Defining Expecations @@ -127,18 +128,22 @@ //! //! ## Times //! -//! Each expectation defines how many times a matching requests is expected to +//! Each expectation defines how many times a matching request is expected to //! be received. The [Times](enum.Times.html) enum defines the possibility. //! `Times::Exactly(1)` is the default value of an `Expectation` if one is not //! specified with the -//! [Expectation.times()](struct.Expectation.html#method.times) method. +//! [times()](struct.ExpectationBuilder.html#method.times) method. +//! +//! The server will respond to any requests that violate the times request with +//! a 500 status code and the server will subsequently panic on Drop. //! //! ## Responder //! -//! responders define how the server will respond to a matched request. There +//! Responders define how the server will respond to a matched request. There //! are a number of implemented responders within the responders module. In //! addition to the predefined responders you can provide any -//! hyper::Response> or obviously implement your own Responder. +//! `hyper::Response` with a body that can be cloned or implement your own +//! Responder. //! //! ## Responder example //! @@ -165,26 +170,38 @@ //! //! ``` -//#![deny(missing_docs)] +#![deny(missing_docs)] -// hidden from docs here because it's re-rexported from the mappers module. -#[doc(hidden)] +/// true if all the provided matchers return true. +/// +/// The macro exists to conveniently box a list of mappers and put them into a +/// `Vec>`. The translation is: +/// +/// `all_of![a, b] => all_of(vec![Box::new(a), Box::new(b)])` #[macro_export] macro_rules! all_of { ($($x:expr),*) => ($crate::mappers::all_of($crate::vec_of_boxes![$($x),*])); ($($x:expr,)*) => ($crate::all_of![$($x),*]); } -// hidden from docs here because it's re-rexported from the mappers module. -#[doc(hidden)] +/// true if any of the provided matchers return true. +/// +/// The macro exists to conveniently box a list of mappers and put them into a +/// `Vec>`. The translation is: +/// +/// `any_of![a, b] => any_of(vec![Box::new(a), Box::new(b)])` #[macro_export] macro_rules! any_of { ($($x:expr),*) => ($crate::mappers::any_of($crate::vec_of_boxes![$($x),*])); ($($x:expr,)*) => ($crate::any_of![$($x),*]); } -// hidden from docs here because it's re-rexported from the responders module. -#[doc(hidden)] +/// a Responder that cycles through a list of responses. +/// +/// The macro exists to conveniently box a list of responders and put them into a +/// `Vec>`. The translation is: +/// +/// `cycle![a, b] => cycle(vec![Box::new(a), Box::new(b)])` #[macro_export] macro_rules! cycle { ($($x:expr),*) => ($crate::responders::cycle($crate::vec_of_boxes![$($x),*])); diff --git a/src/mappers.rs b/src/mappers.rs index 34daf7f..f96f353 100644 --- a/src/mappers.rs +++ b/src/mappers.rs @@ -1,30 +1,54 @@ +//! Mapper implementations. +//! +//! This module contains mappers for composing a set of operations. The result +//! of the composition usually results in a boolean. Any `Mapper` that results in a +//! boolean value also implemens `Matcher`. + use std::borrow::Borrow; use std::fmt; // import the any_of and all_of macros from crate root so they are accessible if // people glob import this module. +/// Accept a list of matchers and returns true if all matchers are true. +#[doc(inline)] pub use crate::all_of; +/// Accept a list of matchers and returns true if any matcher is true. +#[doc(inline)] pub use crate::any_of; + pub mod request; pub mod response; pub mod sequence; +/// The core trait. Defines how an input value should be turned into an output +/// value. This allows for a flexible pattern of composition where two or more +/// mappers are chained together to form a readable and flexible manipulation. +/// +/// There is a special case of a Mapper that outputs a bool that is called a +/// Matcher. pub trait Mapper: Send + fmt::Debug where IN: ?Sized, { + /// The output type. type Out; + /// Map an input to output. fn map(&mut self, input: &IN) -> Self::Out; } -// Matcher is just a special case of Mapper that returns a boolean. Simply -// provides the `matches` method rather than `map` as that reads a little -// better. +/// Matcher is just a special case of Mapper that returns a boolean. It simply +/// provides the `matches` method rather than `map` as that reads a little +/// better. +/// +/// There is a blanket implementation for all Mappers that output bool values. +/// You should never implement Matcher yourself, instead implement Mapper with a +/// bool Out parameter. pub trait Matcher: Send + fmt::Debug where IN: ?Sized, { + /// true if the input matches. fn matches(&mut self, input: &IN) -> bool; } impl Matcher for T @@ -36,9 +60,11 @@ where } } +/// Always true. pub fn any() -> Any { Any } +/// The `Any` mapper returned by [any()](fn.any.html) #[derive(Debug)] pub struct Any; impl Mapper for Any @@ -52,9 +78,11 @@ where } } +/// true if the input is equal to value. pub fn eq(value: T) -> Eq { Eq(value) } +/// The `Eq` mapper returned by [eq()](fn.eq.html) #[derive(Debug)] pub struct Eq(T); impl Mapper for Eq @@ -69,9 +97,11 @@ where } } +/// Call Deref::deref() on the input and pass it to the next mapper. pub fn deref(inner: C) -> Deref { Deref(inner) } +/// The `Deref` mapper returned by [deref()](fn.deref.html) #[derive(Debug)] pub struct Deref(C); impl Mapper for Deref @@ -86,7 +116,11 @@ where } } +/// Create a regex. +/// +/// This trait may panic if the regex failed to build. pub trait IntoRegex { + /// turn self into a regex. fn into_regex(self) -> regex::bytes::Regex; } impl IntoRegex for &str { @@ -110,10 +144,12 @@ impl IntoRegex for regex::bytes::Regex { } } +/// true if the input matches the regex provided. pub fn matches(value: impl IntoRegex) -> Matches { //let regex = regex::bytes::Regex::new(value).expect("failed to create regex"); Matches(value.into_regex()) } +/// The `Matches` mapper returned by [matches()](fn.matches.html) #[derive(Debug)] pub struct Matches(regex::bytes::Regex); impl Mapper for Matches @@ -127,9 +163,11 @@ where } } +/// invert the result of the inner mapper. pub fn not(inner: C) -> Not { Not(inner) } +/// The `Not` mapper returned by [not()](fn.not.html) pub struct Not(C); impl Mapper for Not where @@ -151,6 +189,8 @@ where } } +/// true if all the provided matchers return true. See the `all_of!` macro for +/// convenient usage. pub fn all_of(inner: Vec>>) -> AllOf where IN: fmt::Debug + ?Sized, @@ -158,6 +198,7 @@ where AllOf(inner) } +/// The `AllOf` mapper returned by [all_of()](fn.all_of.html) #[derive(Debug)] pub struct AllOf(Vec>>) where @@ -173,12 +214,15 @@ where } } +/// true if any of the provided matchers returns true. See the `any_of!` macro +/// for convenient usage. pub fn any_of(inner: Vec>>) -> AnyOf where IN: fmt::Debug + ?Sized, { AnyOf(inner) } +/// The `AnyOf` mapper returned by [any_of()](fn.any_of.html) #[derive(Debug)] pub struct AnyOf(Vec>>) where @@ -194,12 +238,11 @@ where } } -pub fn url_decoded(inner: C) -> UrlDecoded -where - C: Mapper<[(String, String)]>, -{ +/// url decode the input and pass the resulting slice of key-value pairs to the next mapper. +pub fn url_decoded(inner: C) -> UrlDecoded { UrlDecoded(inner) } +/// The `UrlDecoded` mapper returned by [url_decoded()](fn.url_decoded.html) #[derive(Debug)] pub struct UrlDecoded(C); impl Mapper for UrlDecoded @@ -217,12 +260,15 @@ where } } -pub fn json_decoded(inner: C) -> JsonDecoded -where - C: Mapper, -{ +/// json decode the input and pass the resulting serde_json::Value to the next +/// mapper. +/// +/// If the input can't be decoded a serde_json::Value::Null is passed to the next +/// mapper. +pub fn json_decoded(inner: C) -> JsonDecoded { JsonDecoded(inner) } +/// The `JsonDecoded` mapper returned by [json_decoded()](fn.json_decoded.html) #[derive(Debug)] pub struct JsonDecoded(C); impl Mapper for JsonDecoded @@ -239,12 +285,14 @@ where } } +/// lowercase the input and pass it to the next mapper. pub fn lowercase(inner: C) -> Lowercase where C: Mapper<[u8]>, { Lowercase(inner) } +/// The `Lowercase` mapper returned by [lowercase()](fn.lowercase.html) #[derive(Debug)] pub struct Lowercase(C); impl Mapper for Lowercase @@ -260,9 +308,11 @@ where } } +/// pass the input to the provided `Fn(T) -> bool` and return the result. pub fn map_fn(f: F) -> MapFn { MapFn(f) } +/// The `MapFn` mapper returned by [map_fn()](fn.map_fn.html) pub struct MapFn(F); impl Mapper for MapFn where diff --git a/src/mappers/request.rs b/src/mappers/request.rs index 957cdf7..5f3c95a 100644 --- a/src/mappers/request.rs +++ b/src/mappers/request.rs @@ -1,11 +1,12 @@ +//! Mappers that extract information from HTTP requests. + use super::Mapper; -pub fn method(inner: C) -> Method -where - C: Mapper, -{ +/// Extract the method from the HTTP request and pass it to the next mapper. +pub fn method(inner: C) -> Method { Method(inner) } +/// The `Method` mapper returned by [method()](fn.method.html) #[derive(Debug)] pub struct Method(C); impl Mapper> for Method @@ -19,12 +20,11 @@ where } } -pub fn path(inner: C) -> Path -where - C: Mapper, -{ +/// Extract the path from the HTTP request and pass it to the next mapper. +pub fn path(inner: C) -> Path { Path(inner) } +/// The `Path` mapper returned by [path()](fn.path.html) #[derive(Debug)] pub struct Path(C); impl Mapper> for Path @@ -38,12 +38,11 @@ where } } -pub fn query(inner: C) -> Query -where - C: Mapper, -{ +/// Extract the query from the HTTP request and pass it to the next mapper. +pub fn query(inner: C) -> Query { Query(inner) } +/// The `Query` mapper returned by [query()](fn.query.html) #[derive(Debug)] pub struct Query(C); impl Mapper> for Query @@ -57,12 +56,12 @@ where } } -pub fn headers(inner: C) -> Headers -where - C: Mapper<[(Vec, Vec)]>, -{ +/// Extract the headers from the HTTP request and pass the sequence to the next +/// mapper. +pub fn headers(inner: C) -> Headers { Headers(inner) } +/// The `Headers` mapper returned by [headers()](fn.headers.html) #[derive(Debug)] pub struct Headers(C); impl Mapper> for Headers @@ -81,9 +80,11 @@ where } } +/// Extract the body from the HTTP request and pass it to the next mapper. pub fn body(inner: C) -> Body { Body(inner) } +/// The `Body` mapper returned by [body()](fn.body.html) #[derive(Debug)] pub struct Body(C); impl Mapper> for Body diff --git a/src/mappers/response.rs b/src/mappers/response.rs index dd68880..8deec74 100644 --- a/src/mappers/response.rs +++ b/src/mappers/response.rs @@ -1,11 +1,12 @@ +//! Mappers that extract information from HTTP responses. + use super::Mapper; -pub fn status_code(inner: C) -> StatusCode -where - C: Mapper, -{ +/// Extract the status code from the HTTP response and pass it to the next mapper. +pub fn status_code(inner: C) -> StatusCode { StatusCode(inner) } +/// The `StatusCode` mapper returned by [status_code()](fn.status_code.html) #[derive(Debug)] pub struct StatusCode(C); impl Mapper> for StatusCode @@ -19,12 +20,12 @@ where } } -pub fn headers(inner: C) -> Headers -where - C: Mapper<[(Vec, Vec)]>, -{ +/// Extract the headers from the HTTP response and pass the sequence to the next +/// mapper. +pub fn headers(inner: C) -> Headers { Headers(inner) } +/// The `Headers` mapper returned by [headers()](fn.headers.html) #[derive(Debug)] pub struct Headers(C); impl Mapper> for Headers @@ -43,9 +44,11 @@ where } } +/// Extract the body from the HTTP response and pass it to the next mapper. pub fn body(inner: C) -> Body { Body(inner) } +/// The `Body` mapper returned by [body()](fn.body.html) #[derive(Debug)] pub struct Body(C); impl Mapper> for Body diff --git a/src/responders.rs b/src/responders.rs index c252125..9d1a6d1 100644 --- a/src/responders.rs +++ b/src/responders.rs @@ -1,16 +1,26 @@ +//! Responder implementations. +//! +//! Reponders determine how the server will respond. + use std::fmt; use std::future::Future; use std::pin::Pin; +// import the cycle macro so that it's available if people glob import this module. +#[doc(inline)] pub use crate::cycle; +/// Respond with an HTTP response. pub trait Responder: Send + fmt::Debug { + /// Return a future that outputs an HTTP response. fn respond(&mut self) -> Pin> + Send>>; } +/// respond with the provided status code. pub fn status_code(code: u16) -> impl Responder { StatusCode(code) } +/// The `StatusCode` responder returned by [status_code()](fn.status_code.html) #[derive(Debug)] pub struct StatusCode(u16); impl Responder for StatusCode { @@ -25,12 +35,17 @@ impl Responder for StatusCode { } } +/// respond with a body that is the json encoding of data. +/// +/// The status code will be `200` and the content-type will be +/// `application/json`. pub fn json_encoded(data: T) -> impl Responder where T: serde::Serialize, { JsonEncoded(serde_json::to_string(&data).unwrap()) } +/// The `JsonEncoded` responder returned by [json_encoded()](fn.json_encoded.html) #[derive(Debug)] pub struct JsonEncoded(String); impl Responder for JsonEncoded { @@ -46,12 +61,17 @@ impl Responder for JsonEncoded { } } +/// respond with a body that is the url encoding of data. +/// +/// The status code will be `200` and the content-type will be +/// `application/x-www-form-urlencoded`. pub fn url_encoded(data: T) -> impl Responder where T: serde::Serialize, { UrlEncoded(serde_urlencoded::to_string(&data).unwrap()) } +/// The `UrlEncoded` responder returned by [url_encoded()](fn.url_encoded.html) #[derive(Debug)] pub struct UrlEncoded(String); impl Responder for UrlEncoded { @@ -87,12 +107,14 @@ where } } +/// Cycle through the provided list of responders. pub fn cycle(responders: Vec>) -> impl Responder { if responders.is_empty() { panic!("empty vector provided to cycle"); } Cycle { idx: 0, responders } } +/// The `Cycle` responder returned by [cycle()](fn.cycle.html) #[derive(Debug)] pub struct Cycle { idx: usize,