diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index f3cf36093..99b78ddac 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -71,7 +71,7 @@ jobs:
run: cargo fmt --all -- --check
- name: Docs
- run: cargo doc --features docs
+ run: cargo doc
clippy_check:
name: Clippy check
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 309e8a8d6..eca4cf96e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview
## [Unreleased]
+### Added
+
+- Added `logger::RequestLogger` based on `log` (replaces `logger:RootLogger`)
+
+### Changed
+
+- Resolved an `#[allow(unused_mut)]` workaround.
+- Renamed `ExtractForms` to `ContextExt`.
+
+### Removed
+
+- Removed `logger::RootLogger` (replaced by `logger:RequestLogger`)
+- Removed internal use of the `box_async` macro
+
## [0.3.0] - 2019-10-31
This is the first release in almost 6 months; introducing a snapshot of where we
diff --git a/Cargo.toml b/Cargo.toml
index 1b28d5818..11e2b7088 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,11 +29,9 @@ route-recognizer = "0.1.13"
serde = "1.0.102"
serde_derive = "1.0.102"
serde_json = "1.0.41"
-slog = "2.5.2"
-slog-async = "2.3.0"
-slog-term = "2.4.2"
typemap = "0.3.3"
serde_urlencoded = "0.6.1"
+log = "0.4.8"
[dependencies.http-service-hyper]
optional = true
diff --git a/README.md b/README.md
index 604bd44a1..758271e0b 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@
API Docs
|
-
+
Contributing
|
@@ -60,23 +60,23 @@ fn main() -> Result<(), std::io::Error> {
**More Examples**
-- [Hello World](https://github.com/rustasync/tide/tree/master/examples/hello.rs)
-- [Messages](https://github.com/rustasync/tide/blob/master/examples/messages.rs)
-- [Body Types](https://github.com/rustasync/tide/blob/master/examples/body_types.rs)
-- [Multipart Form](https://github.com/rustasync/tide/tree/master/examples/multipart-form/main.rs)
-- [Catch All](https://github.com/rustasync/tide/tree/master/examples/catch_all.rs)
-- [Cookies](https://github.com/rustasync/tide/tree/master/examples/cookies.rs)
-- [Default Headers](https://github.com/rustasync/tide/tree/master/examples/default_headers.rs)
-- [GraphQL](https://github.com/rustasync/tide/tree/master/examples/graphql.rs)
+- [Hello World](https://github.com/http-rs/tide/tree/master/examples/hello.rs)
+- [Messages](https://github.com/http-rs/tide/blob/master/examples/messages.rs)
+- [Body Types](https://github.com/http-rs/tide/blob/master/examples/body_types.rs)
+- [Multipart Form](https://github.com/http-rs/tide/tree/master/examples/multipart-form/main.rs)
+- [Catch All](https://github.com/http-rs/tide/tree/master/examples/catch_all.rs)
+- [Cookies](https://github.com/http-rs/tide/tree/master/examples/cookies.rs)
+- [Default Headers](https://github.com/http-rs/tide/tree/master/examples/default_headers.rs)
+- [GraphQL](https://github.com/http-rs/tide/tree/master/examples/graphql.rs)
## Resources
Read about the design here:
-- [Rising Tide: building a modular web framework in the open](https://rustasync.github.io/team/2018/09/11/tide.html)
-- [Routing and extraction in Tide: a first sketch](https://rustasync.github.io/team/2018/10/16/tide-routing.html)
-- [Middleware in Tide](https://rustasync.github.io/team/2018/11/07/tide-middleware.html)
-- [Tide's evolving middleware approach](https://rustasync.github.io/team/2018/11/27/tide-middleware-evolution.html)
+- [Rising Tide: building a modular web framework in the open](https://http-rs.github.io/team/2018/09/11/tide.html)
+- [Routing and extraction in Tide: a first sketch](https://http-rs.github.io/team/2018/10/16/tide-routing.html)
+- [Middleware in Tide](https://http-rs.github.io/team/2018/11/07/tide-middleware.html)
+- [Tide's evolving middleware approach](https://http-rs.github.io/team/2018/11/27/tide-middleware-evolution.html)
## Contributing
@@ -89,7 +89,7 @@ guide][contributing] and take a look at some of these issues:
#### Conduct
The Tide project adheres to the [Contributor Covenant Code of
-Conduct](https://github.com/rustasync/tide/blob/master/.github/CODE_OF_CONDUCT.md). This
+Conduct](https://github.com/http-rs/tide/blob/master/.github/CODE_OF_CONDUCT.md). This
describes the minimum behavior expected from all contributors.
## License
@@ -107,7 +107,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
-[releases]: https://github.com/rustasync/tide/releases
-[contributing]: https://github.com/rustasync/tide/blob/master/.github/CONTRIBUTING.md
-[good-first-issue]: https://github.com/rustasync/tide/labels/good%20first%20issue
-[help-wanted]: https://github.com/rustasync/tide/labels/help%20wanted
+[releases]: https://github.com/http-rs/tide/releases
+[contributing]: https://github.com/http-rs/tide/blob/master/.github/CONTRIBUTING.md
+[good-first-issue]: https://github.com/http-rs/tide/labels/good%20first%20issue
+[help-wanted]: https://github.com/http-rs/tide/labels/help%20wanted
diff --git a/examples/body_types.rs b/examples/body_types.rs
index fb85fbe1e..deed774ae 100644
--- a/examples/body_types.rs
+++ b/examples/body_types.rs
@@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use tide::{
error::ResultExt,
- forms::{self, ExtractForms},
+ forms::{self, ContextExt},
response, App, Context, EndpointResult,
};
@@ -11,28 +11,24 @@ struct Message {
contents: String,
}
-#[allow(unused_mut)] // Workaround clippy bug
async fn echo_string(mut cx: Context<()>) -> String {
let msg = cx.body_string().await.unwrap();
println!("String: {}", msg);
msg
}
-#[allow(unused_mut)] // Workaround clippy bug
async fn echo_bytes(mut cx: Context<()>) -> Vec {
let msg = cx.body_bytes().await.unwrap();
println!("Bytes: {:?}", msg);
msg
}
-#[allow(unused_mut)] // Workaround clippy bug
async fn echo_json(mut cx: Context<()>) -> EndpointResult {
let msg = cx.body_json().await.client_err()?;
println!("JSON: {:?}", msg);
Ok(response::json(msg))
}
-#[allow(unused_mut)] // Workaround clippy bug
async fn echo_form(mut cx: Context<()>) -> EndpointResult {
let msg = cx.body_form().await?;
println!("Form: {:?}", msg);
diff --git a/examples/cookies.rs b/examples/cookies.rs
index 1f8fee097..751efe8c3 100644
--- a/examples/cookies.rs
+++ b/examples/cookies.rs
@@ -7,12 +7,10 @@ async fn retrieve_cookie(mut cx: Context<()>) -> String {
format!("hello cookies: {:?}", cx.get_cookie("hello").unwrap())
}
-#[allow(unused_mut)] // Workaround clippy bug
async fn set_cookie(mut cx: Context<()>) {
cx.set_cookie(Cookie::new("hello", "world")).unwrap();
}
-#[allow(unused_mut)] // Workaround clippy bug
async fn remove_cookie(mut cx: Context<()>) {
cx.remove_cookie(Cookie::named("hello")).unwrap();
}
diff --git a/examples/messages.rs b/examples/messages.rs
index 3cbb75d7f..ffb84f160 100644
--- a/examples/messages.rs
+++ b/examples/messages.rs
@@ -37,13 +37,11 @@ impl Database {
}
}
-#[allow(unused_mut)] // Workaround clippy bug
async fn new_message(mut cx: Context) -> EndpointResult {
let msg = cx.body_json().await.client_err()?;
Ok(cx.state().insert(msg).to_string())
}
-#[allow(unused_mut)] // Workaround clippy bug
async fn set_message(mut cx: Context) -> EndpointResult<()> {
let msg = cx.body_json().await.client_err()?;
let id = cx.param("id").client_err()?;
diff --git a/examples/multipart-form/main.rs b/examples/multipart-form/main.rs
index 6ed732ab0..1bf6e7495 100644
--- a/examples/multipart-form/main.rs
+++ b/examples/multipart-form/main.rs
@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
use std::io::Read;
-use tide::{forms::ExtractForms, response, App, Context, EndpointResult};
+use tide::{forms::ContextExt, response, App, Context, EndpointResult};
#[derive(Serialize, Deserialize, Clone)]
struct Message {
@@ -9,7 +9,6 @@ struct Message {
file: Option,
}
-#[allow(unused_mut)] // Workaround clippy bug
async fn upload_file(mut cx: Context<()>) -> EndpointResult {
// https://stackoverflow.com/questions/43424982/how-to-parse-multipart-forms-using-abonander-multipart-with-rocket
let mut message = Message {
diff --git a/src/app.rs b/src/app.rs
index a3c9ba8cf..b76ce47d2 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -33,7 +33,7 @@ use crate::{
///
/// let mut app = tide::App::new();
/// app.at("/hello").get(|_| async move {"Hello, world!"});
-/// app.serve("127.0.0.1:8000");
+/// app.serve("127.0.0.1:8000").unwrap();
/// ```
///
/// # Routing and parameters
@@ -44,7 +44,6 @@ use crate::{
/// segments as parameters to endpoints:
///
/// ```rust, no_run
-///
/// use tide::error::ResultExt;
///
/// async fn hello(cx: tide::Context<()>) -> tide::EndpointResult {
@@ -65,7 +64,7 @@ use crate::{
/// "Use /hello/{your name} or /goodbye/{your name}"
/// });
///
-/// app.serve("127.0.0.1:8000");
+/// app.serve("127.0.0.1:8000").unwrap();
/// ```
///
/// You can learn more about routing in the [`App::at`] documentation.
@@ -272,7 +271,7 @@ impl HttpService for Server {
let middleware = self.middleware.clone();
let data = self.data.clone();
- box_async! {
+ Box::pin(async move {
let fut = {
let Selection { endpoint, params } = router.route(&path, method);
let cx = Context::new(data, req, params);
@@ -286,7 +285,7 @@ impl HttpService for Server {
};
Ok(fut.await)
- }
+ })
}
}
diff --git a/src/endpoint.rs b/src/endpoint.rs
index dc02d5e88..176e1e15f 100644
--- a/src/endpoint.rs
+++ b/src/endpoint.rs
@@ -66,8 +66,6 @@ where
type Fut = BoxFuture<'static, Response>;
fn call(&self, cx: Context) -> Self::Fut {
let fut = (self)(cx);
- box_async! {
- fut.await.into_response()
- }
+ Box::pin(async move { fut.await.into_response() })
}
}
diff --git a/src/forms.rs b/src/forms.rs
index b7338dab5..697027973 100644
--- a/src/forms.rs
+++ b/src/forms.rs
@@ -8,7 +8,7 @@ use crate::{
};
/// An extension trait for `Context`, providing form extraction.
-pub trait ExtractForms {
+pub trait ContextExt {
/// Asynchronously extract the entire body as a single form.
fn body_form(&mut self) -> BoxTryFuture;
@@ -16,13 +16,15 @@ pub trait ExtractForms {
fn body_multipart(&mut self) -> BoxTryFuture>>>;
}
-impl ExtractForms for Context {
+impl ContextExt for Context {
fn body_form(&mut self) -> BoxTryFuture {
let body = self.take_body();
- box_async! {
+ Box::pin(async move {
let body = body.into_vec().await.client_err()?;
- Ok(serde_urlencoded::from_bytes(&body).map_err(|e| err_fmt!("could not decode form: {}", e)).client_err()?)
- }
+ Ok(serde_urlencoded::from_bytes(&body)
+ .map_err(|e| err_fmt!("could not decode form: {}", e))
+ .client_err()?)
+ })
}
fn body_multipart(&mut self) -> BoxTryFuture>>> {
@@ -35,11 +37,13 @@ impl ExtractForms for Context {
let body = self.take_body();
- box_async! {
+ Box::pin(async move {
let body = body.into_vec().await.client_err()?;
- let boundary = boundary.ok_or_else(|| err_fmt!("no boundary found")).client_err()?;
+ let boundary = boundary
+ .ok_or_else(|| err_fmt!("no boundary found"))
+ .client_err()?;
Ok(Multipart::with_body(Cursor::new(body), boundary))
- }
+ })
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 6ddac489a..af4d64f84 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,31 +1,15 @@
-#![cfg_attr(feature = "nightly", deny(missing_docs))]
-#![cfg_attr(feature = "nightly", feature(external_doc))]
-#![cfg_attr(feature = "nightly", doc(include = "../README.md"))]
-#![cfg_attr(test, deny(warnings))]
-#![allow(unused_variables)]
-#![deny(
- nonstandard_style,
- rust_2018_idioms,
- future_incompatible,
- missing_debug_implementations
-)]
-// TODO: Remove this after clippy bug due to async await is resolved.
-// ISSUE: https://github.com/rust-lang/rust-clippy/issues/3988
-#![allow(clippy::needless_lifetimes)]
+// #![warn(missing_docs)]
+#![warn(missing_debug_implementations, rust_2018_idioms)]
+#![allow(clippy::mutex_atomic, clippy::module_inception)]
+#![doc(test(attr(deny(rust_2018_idioms, warnings))))]
+#![doc(test(attr(allow(unused_extern_crates, unused_variables))))]
-//!
//! Welcome to Tide.
//!
//! The [`App`](struct.App.html) docs are a good place to get started.
//!
//!
-macro_rules! box_async {
- {$($t:tt)*} => {
- ::futures::future::FutureExt::boxed(async move { $($t)* })
- };
-}
-
#[macro_use]
pub mod error;
diff --git a/src/middleware/cookies.rs b/src/middleware/cookies.rs
index b33443b51..639ea4032 100644
--- a/src/middleware/cookies.rs
+++ b/src/middleware/cookies.rs
@@ -31,7 +31,7 @@ impl Middleware for CookiesMiddleware {
mut cx: Context,
next: Next<'a, Data>,
) -> BoxFuture<'a, Response> {
- box_async! {
+ Box::pin(async move {
let cookie_data = cx
.extensions_mut()
.remove()
@@ -56,7 +56,7 @@ impl Middleware for CookiesMiddleware {
}
}
res
- }
+ })
}
}
@@ -72,23 +72,19 @@ mod tests {
static COOKIE_NAME: &str = "testCookie";
/// Tide will use the the `Cookies`'s `Extract` implementation to build this parameter.
- #[allow(unused_mut)] // Workaround clippy bug
async fn retrieve_cookie(mut cx: Context<()>) -> String {
format!("{}", cx.get_cookie(COOKIE_NAME).unwrap().unwrap().value())
}
- #[allow(unused_mut)] // Workaround clippy bug
async fn set_cookie(mut cx: Context<()>) {
cx.set_cookie(Cookie::new(COOKIE_NAME, "NewCookieValue"))
.unwrap();
}
- #[allow(unused_mut)] // Workaround clippy bug
async fn remove_cookie(mut cx: Context<()>) {
cx.remove_cookie(Cookie::named(COOKIE_NAME)).unwrap();
}
- #[allow(unused_mut)] // Workaround clippy bug
async fn set_multiple_cookie(mut cx: Context<()>) {
cx.set_cookie(Cookie::new("C1", "V1")).unwrap();
cx.set_cookie(Cookie::new("C2", "V2")).unwrap();
diff --git a/src/middleware/default_headers.rs b/src/middleware/default_headers.rs
index d7b5bffe4..e6627f82b 100644
--- a/src/middleware/default_headers.rs
+++ b/src/middleware/default_headers.rs
@@ -41,7 +41,7 @@ impl DefaultHeaders {
impl Middleware for DefaultHeaders {
fn handle<'a>(&'a self, cx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> {
- box_async! {
+ Box::pin(async move {
let mut res = next.run(cx).await;
let headers = res.headers_mut();
@@ -49,6 +49,6 @@ impl Middleware for DefaultHeaders {
headers.entry(key).unwrap().or_insert_with(|| value.clone());
}
res
- }
+ })
}
}
diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs
index 3becf7928..defaed905 100644
--- a/src/middleware/logger.rs
+++ b/src/middleware/logger.rs
@@ -1,50 +1,50 @@
-use slog::{info, o, Drain};
-use slog_async;
-use slog_term;
-
-use futures::future::BoxFuture;
-
use crate::{
middleware::{Middleware, Next},
Context, Response,
};
+use futures::future::BoxFuture;
-/// Root logger for Tide. Wraps over logger provided by slog.SimpleLogger
-#[derive(Debug)]
-pub struct RootLogger {
- // drain: dyn slog::Drain,
- inner_logger: slog::Logger,
-}
-
-impl RootLogger {
- pub fn new() -> RootLogger {
- let decorator = slog_term::TermDecorator::new().build();
- let drain = slog_term::CompactFormat::new(decorator).build().fuse();
- let drain = slog_async::Async::new(drain).build().fuse();
-
- let log = slog::Logger::root(drain, o!());
- RootLogger { inner_logger: log }
+/// A simple requests logger
+///
+/// # Examples
+///
+/// ```rust
+///
+/// let mut app = tide::App::new();
+/// app.middleware(tide::middleware::RequestLogger::new());
+/// ```
+#[derive(Debug, Clone, Default)]
+pub struct RequestLogger;
+
+impl RequestLogger {
+ pub fn new() -> Self {
+ Self::default()
}
-}
-impl Default for RootLogger {
- fn default() -> Self {
- Self::new()
+ async fn log_basic<'a, Data: Send + Sync + 'static>(
+ &'a self,
+ ctx: Context,
+ next: Next<'a, Data>,
+ ) -> Response {
+ let path = ctx.uri().path().to_owned();
+ let method = ctx.method().as_str().to_owned();
+ log::trace!("IN => {} {}", method, path);
+ let start = std::time::Instant::now();
+ let res = next.run(ctx).await;
+ let status = res.status();
+ log::info!(
+ "{} {} {} {}ms",
+ method,
+ path,
+ status.as_str(),
+ start.elapsed().as_millis()
+ );
+ res
}
}
-/// Stores information during request phase and logs information once the response
-/// is generated.
-impl Middleware for RootLogger {
- fn handle<'a>(&'a self, cx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> {
- box_async! {
- let path = cx.uri().path().to_owned();
- let method = cx.method().as_str().to_owned();
-
- let res = next.run(cx).await;
- let status = res.status();
- info!(self.inner_logger, "{} {} {}", method, path, status.as_str());
- res
- }
+impl Middleware for RequestLogger {
+ fn handle<'a>(&'a self, ctx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> {
+ Box::pin(async move { self.log_basic(ctx, next).await })
}
}
diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs
index 64c6c5692..343a21b17 100644
--- a/src/middleware/mod.rs
+++ b/src/middleware/mod.rs
@@ -7,7 +7,9 @@ mod cookies;
mod default_headers;
mod logger;
-pub use self::{cookies::CookiesMiddleware, default_headers::DefaultHeaders, logger::RootLogger};
+pub use self::{
+ cookies::CookiesMiddleware, default_headers::DefaultHeaders, logger::RequestLogger,
+};
/// Middleware that wraps around remaining middleware chain.
pub trait Middleware: 'static + Send + Sync {
diff --git a/src/router.rs b/src/router.rs
index 34710110d..d3dfc1723 100644
--- a/src/router.rs
+++ b/src/router.rs
@@ -62,7 +62,10 @@ impl Router {
}
fn not_found_endpoint(_cx: Context) -> BoxFuture<'static, Response> {
- box_async! {
- http::Response::builder().status(http::StatusCode::NOT_FOUND).body(Body::empty()).unwrap()
- }
+ Box::pin(async move {
+ http::Response::builder()
+ .status(http::StatusCode::NOT_FOUND)
+ .body(Body::empty())
+ .unwrap()
+ })
}