Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add context::Builder #24

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions lib/src/context/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@

use tokio_core::reactor::Handle;
use std::sync::Arc;
use unsafe_any::UnsafeAny;
use hyper::{Method, error, HttpVersion, Body};
use hyper::header::Header;

use super::Context;
use state::State;
use util::typemap::TypeMap;
use request;
use data;

/// Helper struct for construct a [`Context`]
///
/// [`Context`]: struct.Context.html
pub struct Builder {
handle: Handle,

state: State,

// for request
request: request::Builder,
body: Body,
}

impl Builder {
/// Create a new `Builder` from a tokio_core `Handle`
pub fn new(handle: Handle) -> Self {
Self {
handle,
state: State::default(),
request: request::Builder::default(),
body: Body::default(),
}
}

/// Set the shared data.
pub fn shared(mut self, shared: Arc<TypeMap<UnsafeAny + Send + Sync>>) -> Self {
self.state.shared = shared;
self
}

/// Set the HTTP method to `method`
pub fn method(mut self, method: Method) -> Self {
self.request = self.request.method(method);
self
}

/// Set the uri
pub fn uri(mut self, uri: &str) -> Self {
self.request = self.request.uri(uri);
self
}

/// Set the HTTP version
pub fn version(mut self, ver: HttpVersion) -> Self {
self.request = self.request.version(ver);
self
}

/// Set an header
pub fn set_header<H: Header>(mut self, value: H) -> Self {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Methods on a builder should be just header and not set_header. See a similar method on http::request::Builder: https://docs.rs/http/0.1.0/http/request/struct.Builder.html#method.header

self.request = self.request.set_header(value);
self
}

/// Set the request data
pub fn set_data<B: Into<Body>>(mut self, body: B) -> Self {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as set_header.

self.body = body.into();
self
}

/// Create the `Context`, returning any error that occurs during build.
pub fn finalize(self) -> Result<Context, error::UriError> {
let Self {
handle, state,
request,
body
} = self;

Ok(Context::new(handle, request.finalize()?, state, data::Data::new(body)))
}
}

11 changes: 11 additions & 0 deletions lib/src/context.rs → lib/src/context/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod builder;

use std::ops::Deref;

use tokio_core::reactor::Handle;
Expand All @@ -9,6 +11,8 @@ use state::{FromState, State};
use Data;
pub use state::Key;

pub use self::builder::Builder;

/// `Context` represents the context of the current HTTP request.
///
/// A `Context` consists of:
Expand Down Expand Up @@ -79,6 +83,13 @@ impl Context {
pub fn deconstruct(self) -> (Handle, State, Request, Data) {
(self.handle, self.state, self.request, self.body)
}

/// Create a new context [`Builder`]
///
/// [`Builder`]: struct.Builder.html
pub fn builder(handle: Handle) -> Builder {
Builder::new(handle)
}
}

impl Deref for Context {
Expand Down
69 changes: 69 additions & 0 deletions lib/src/request/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@

use hyper::{Method, Uri, error, HttpVersion, Headers};
use hyper::header::Header;

use super::Request;

/// Helper struct for construct a [`Request`]
///
/// [`Request`]: struct.Context.html
pub struct Builder {
method: Method,
uri: Result<Uri, error::UriError>,
version: HttpVersion,
headers: Headers,
}

impl Default for Builder {
fn default() -> Self {
Self {
method: Method::Get,
uri: "/".parse::<Uri>(),
version: HttpVersion::Http11,
headers: Headers::new(),
}
}
}

impl Builder {
pub fn new() -> Self {
Self::default()
}

/// Set the HTTP method to `method`
pub fn method(mut self, method: Method) -> Self {
self.method = method;
self
}

/// Set the uri
pub fn uri(mut self, uri: &str) -> Self {
self.uri = uri.parse::<Uri>();
self
}

/// Set the HTTP version
pub fn version(mut self, ver: HttpVersion) -> Self {
self.version = ver;
self
}

/// Set an header
pub fn set_header<H: Header>(mut self, value: H) -> Self {
self.headers.set(value);
self
}

/// Create the `Context`, returning any error that occurs during build.
pub fn finalize(self) -> Result<Request, error::UriError> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be Result<Request, shio::Error> so we don't expose the hyper error type.

let Self {
method, uri,
version, headers,
} = self;

let uri = uri?;

Ok(Request::new((method, uri, version, headers)))
}
}

9 changes: 9 additions & 0 deletions lib/src/request/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
mod builder;

use hyper::{self, Method};

pub use self::builder::Builder;

pub struct Request {
method: Method,
uri: hyper::Uri,
Expand Down Expand Up @@ -49,4 +52,10 @@ impl Request {
pub fn path(&self) -> &str {
self.uri.path()
}

/// Create a new `Builder`
#[inline]
pub fn builder() -> Builder {
Builder::default()
}
}
18 changes: 7 additions & 11 deletions lib/src/router/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,9 @@ impl Handler for Router {
#[cfg(test)]
mod tests {
use tokio_core::reactor::Core;
use hyper;

use super::{Parameters, Router};
use {Context, Handler, Response, State, StatusCode};
use {Context, Handler, Response, StatusCode};
use Method::*;

// Empty handler to use for route tests
Expand Down Expand Up @@ -205,12 +204,9 @@ mod tests {
}));

let mut core = Core::new().unwrap();

// TODO: It should much easier to make a test context
// Perhaps `Request::build ( .. )` should be a thing?
// Proxied as `Context::build ( .. )` ?
let (request, data) = ::service::from_hyper_request(hyper::Request::new(Get, "/user/3289".parse().unwrap()));
let context = Context::new(core.handle(), request, State::default(), data);
let context = Context::builder(core.handle())
.uri("/user/3289")
.finalize().unwrap();

let work = router.call(context);

Expand Down Expand Up @@ -244,9 +240,9 @@ mod tests {
}));

let mut core = Core::new().unwrap();

let (request, data) = ::service::from_hyper_request(hyper::Request::new(Get, "/static/path/to/file/is/here".parse().unwrap()));
let context = Context::new(core.handle(), request, State::default(), data);
let context = Context::builder(core.handle())
.uri("/static/path/to/file/is/here")
.finalize().unwrap();

let work = router.call(context);

Expand Down
2 changes: 1 addition & 1 deletion lib/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub struct State {
request: TypeMap,

/// State shared across all requests.
shared: Arc<TypeMap<UnsafeAny + Send + Sync>>,
pub(crate) shared: Arc<TypeMap<UnsafeAny + Send + Sync>>,
}

impl Default for State {
Expand Down