From cf79c8a635a8b3dce8d2b94bd3f0562e703ed79a Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 28 Nov 2019 21:24:42 +0100 Subject: [PATCH 1/5] new server docs Signed-off-by: Yoshua Wuyts --- src/endpoint.rs | 10 +++++----- src/lib.rs | 43 ++++++++++++++++++++++++++++++++++++++++++- src/server/mod.rs | 45 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/src/endpoint.rs b/src/endpoint.rs index e8f53f756..795fd2884 100644 --- a/src/endpoint.rs +++ b/src/endpoint.rs @@ -20,7 +20,7 @@ use crate::{response::IntoResponse, Request, Response}; /// A simple endpoint that is invoked on a `GET` request and returns a `String`: /// /// ```no_run -/// async fn hello(_cx: tide::Request<()>) -> String { +/// async fn hello(_req: tide::Request<()>) -> String { /// String::from("hello") /// } /// @@ -34,7 +34,7 @@ use crate::{response::IntoResponse, Request, Response}; /// /// ```no_run /// # use core::future::Future; -/// fn hello(_cx: tide::Request<()>) -> impl Future { +/// fn hello(_req: tide::Request<()>) -> impl Future { /// futures::future::ready(String::from("hello")) /// } /// @@ -50,7 +50,7 @@ pub trait Endpoint: Send + Sync + 'static { type Fut: Future + Send + 'static; /// Invoke the endpoint within the given context - fn call(&self, cx: Request) -> Self::Fut; + fn call(&self, req: Request) -> Self::Fut; } pub(crate) type DynEndpoint = @@ -63,8 +63,8 @@ where Fut::Output: IntoResponse, { type Fut = BoxFuture<'static, Response>; - fn call(&self, cx: Request) -> Self::Fut { - let fut = (self)(cx); + fn call(&self, req: Request) -> Self::Fut { + let fut = (self)(req); Box::pin(async move { fut.await.into_response() }) } } diff --git a/src/lib.rs b/src/lib.rs index c8698bf46..d3c2579f2 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ //! app.listen("127.0.0.1:8080").await?; //! # //! # Ok(()) }) } -//! ```` +//! ``` //! //! __echo server__ //! ```no_run @@ -101,11 +101,52 @@ pub use server::{Route, Server}; pub use http; /// Create a new Tide server. +/// +/// # Examples +/// +/// ```no_run +/// # use futures::executor::block_on; +/// # fn main() -> Result<(), std::io::Error> { block_on(async { +/// # +/// let mut _app = tide::new(); +/// # +/// # Ok(()) }) } +/// ``` pub fn new() -> server::Server<()> { Server::new() } /// Create a new Tide server with shared global state. +/// +/// Global state is useful for storing items +/// +/// # Examples +/// +/// ```no_run +/// # use futures::executor::block_on; +/// # fn main() -> Result<(), std::io::Error> { block_on(async { +/// # +/// use tide::Request; +/// +/// /// The shared application state. +/// struct State { +/// name: String, +/// } +/// +/// // Define a new instance of the state. +/// let state = State { +/// name: "Nori".to_string() +/// }; +/// +/// // Initialize the application with state. +/// let mut app = tide::with_state(state); +/// app.at("/").get(|req: Request| async move { +/// format!("Hello, {}!", &req.state().name) +/// }); +/// app.listen("127.0.0.1:8080").await?; +/// # +/// # Ok(()) }) } +/// ``` pub fn with_state(state: State) -> server::Server where State: Send + Sync + 'static, diff --git a/src/server/mod.rs b/src/server/mod.rs index 20bfdeaec..1c56b29f3 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -143,7 +143,18 @@ pub struct Server { } impl Server<()> { - /// Create an empty `Server`, with no initial middleware or configuration. + /// Create a new Tide server. + /// + /// # Examples + /// + /// ```no_run + /// # use futures::executor::block_on; + /// # fn main() -> Result<(), std::io::Error> { block_on(async { + /// # + /// let mut _app = tide::new(); + /// # + /// # Ok(()) }) } + /// ``` pub fn new() -> Server<()> { Self::with_state(()) } @@ -156,7 +167,37 @@ impl Default for Server<()> { } impl Server { - /// Create an `Server`, with initial middleware or configuration. + /// Create a new Tide server with shared global state. + /// + /// Global state is useful for storing items + /// + /// # Examples + /// + /// ```no_run + /// # use futures::executor::block_on; + /// # fn main() -> Result<(), std::io::Error> { block_on(async { + /// # + /// use tide::Request; + /// + /// /// The shared application state. + /// struct State { + /// name: String, + /// } + /// + /// // Define a new instance of the state. + /// let state = State { + /// name: "Nori".to_string() + /// }; + /// + /// // Initialize the application with state. + /// let mut app = tide::with_state(state); + /// app.at("/").get(|req: Request| async move { + /// format!("Hello, {}!", &req.state().name) + /// }); + /// app.listen("127.0.0.1:8080").await?; + /// # + /// # Ok(()) }) } + /// ``` pub fn with_state(state: State) -> Server { Server { router: Router::new(), From de49c6acd991413acec8d0b7d2b3200df5c26fed Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 28 Nov 2019 22:07:56 +0100 Subject: [PATCH 2/5] fix docs Signed-off-by: Yoshua Wuyts --- src/lib.rs | 110 +++++++++++++++++++++++++++++++++++++++++----- src/server/mod.rs | 4 +- 2 files changed, 102 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d3c2579f2..9dbe7902b 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,14 @@ //! modular, and built directly for `async/await`. Whether it's a quick webhook, or an L7 load //! balancer, Tide will make it work. //! +//! # Features +//! +//! - __Fast:__ Written in Rust, and built on Futures, Tide is incredibly efficient. +//! - __Friendly:__ With thorough documentation, and a complete API, Tide helps cover your every +//! need. +//! - __Minimal:__ With only a few concepts to learn, Tide is easy to pick up and become productive +//! with. +//! //! # Examples //! //! __hello world__ @@ -38,10 +46,7 @@ //! ```` //! //! __send and receive json__ -//! _note: this example doesn't compile yet because we still need to work on -//! our error handling. Replace `?` with `.unwrap()` if you want to make this -//! compile_ -//! ```ignore +//! ```no_run //! # use futures::executor::block_on; //! # fn main() -> Result<(), std::io::Error> { block_on(async { //! # @@ -50,16 +55,99 @@ //! //! let mut app = tide::new(); //! app.at("/").get(|mut req: tide::Request<()>| async move { -//! let mut counter: Counter = req.body_json().await?; +//! let mut counter: Counter = req.body_json().await.unwrap(); //! println!("count is {}", counter.count); //! counter.count += 1; -//! tide::Response::new(200).body_json(&counter)? +//! tide::Response::new(200).body_json(&counter).unwrap() //! }); //! app.listen("127.0.0.1:8080").await?; //! # //! # Ok(()) }) } //! ``` //! +//! # Concepts +//! +//! ## Request-Response +//! +//! Each Tide endpoint takes a [`Request`] and returns a [`Response`]. Because async functions +//! allow us to wait without blocking, this makes Tide feel similar to synchronous servers. Except +//! it's incredibly efficient. +//! +//! ```txt +//! async fn endpoint(req: Request) -> Result; +//! ``` +//! +//! ## Middleware +//! +//! Middleware wrap each request and response pair, allowing code to be run before the endpoint, +//! and after each endpoint. Additionally each handler can choose to never yield to the endpoint +//! and abort early. This is useful for e.g. authentication middleware. Tide's middleware works +//! like a stack. A simplified example of the logger middleware is something like this: +//! +//! ```rust +//! async fn log(req: Request, next: Next) -> Result { +//! println!("Incoming request from {} on url {}", req.peer_addr(), req.url()); +//! let res = next().await?; +//! println!("Outgoing response with status {}", res.status()); +//! res +//! } +//! ``` +//! +//! As a new request comes in, we perform some logic. Then we yield to the next +//! middleware (or endpoint, we don't know when we yield to `next`), and once that's +//! done, we return the Response. We can decide to not yield to `next` at any stage, +//! and abort early. This can then be used in applications using the [`Server::middleware`] +//! method. +//! +//! ## State +//! +//! Middleware often needs to share values with the endpoint. This is done through "local state". +//! Local state is built using a typemap that's available through [`Request::local`]. +//! +//! Global state is used when a complete application needs access to a particular +//! value. Examples of this include: database connections, websocket connections, or +//! network-enabled config. Every `Request` has an inner value that must +//! implement `Send + Sync + Clone`, and can thus freely be shared between requests. +//! +//! By default `tide::new` will use `()` as the shared state. But if you want to +//! create a new app with shared state you can use the [`with_state`] function. +//! +//! ## Extension Traits +//! +//! Sometimes having global and local context can require a bit of setup. There are +//! cases where it'd be nice if things were a little easier. This is why Tide +//! encourages people to write _extension traits_. +//! +//! By using an _extension trait_ you can extend [`Request`] or [`Response`] with more +//! functionality. For example, an authentication package could implement a `user` method on +//! `Request`, to access the authenticated user provided by middleware. +//! +//! Extension traits are written by defining a trait + trait impl for the struct that's being +//! extended: +//! +//! ```rust +//! pub trait RequestExt { +//! pub fn bark(&self) -> String; +//! } +//! +//! impl RequestExt for Request { +//! pub fn bark(&self) -> String { +//! "woof".to_string() +//! } +//! } +//! ``` +//! +//! Tide apps will then have access to the `bark` method on `Request`: +//! +//! ```rust +//! #[async_std::main] +//! async fn main() -> Result<(), std::io::Error> { +//! let mut app = tide::new(); +//! app.at("/").get(|req| async move { req.bark() }); +//! app.listen("127.0.0.1:8080").await +//! } +//! ``` +//! //! # Stability //! //! It's still early in Tide's development cycle. While the general shape of Tide might have @@ -69,10 +157,8 @@ //! //! However we *are* committed to closely following semver, and documenting any and all breaking //! changes we make. Also as time goes on you may find that fewer and fewer changes occur, until we -//! eventually remove this notice entirely. -//! -//! The goal of Tide is to build a premier HTTP experience for Async Rust. We have a long journey -//! ahead of us. But we're excited you're here with us! +//! eventually remove this notice entirely. The goal of Tide is to build a premier HTTP experience +//! for Async Rust. We have a long journey ahead of us. But we're excited you're here with us! mod endpoint; mod error; @@ -108,7 +194,9 @@ pub use http; /// # use futures::executor::block_on; /// # fn main() -> Result<(), std::io::Error> { block_on(async { /// # -/// let mut _app = tide::new(); +/// let mut app = tide::new(); +/// app.at("/").get(|_| async move { "Hello, world!" }); +/// app.listen("127.0.0.1:8080").await?; /// # /// # Ok(()) }) } /// ``` diff --git a/src/server/mod.rs b/src/server/mod.rs index 1c56b29f3..da99b5168 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -151,7 +151,9 @@ impl Server<()> { /// # use futures::executor::block_on; /// # fn main() -> Result<(), std::io::Error> { block_on(async { /// # - /// let mut _app = tide::new(); + /// let mut app = tide::new(); + /// app.at("/").get(|_| async move { "Hello, world!" }); + /// app.listen("127.0.0.1:8080").await?; /// # /// # Ok(()) }) } /// ``` From 9caab8ef7d94a6471e6563530c63c76de19b16d6 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 28 Nov 2019 22:40:28 +0100 Subject: [PATCH 3/5] tests pass Signed-off-by: Yoshua Wuyts --- Cargo.toml | 1 + src/lib.rs | 28 +++++++++++++++++++++------- src/request.rs | 18 ++++++++++++++++++ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 39091db3d..79f86c922 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ pin-project-lite = "0.1.0" mime = "0.3.14" [dev-dependencies] +async-std = { version = "1.0.1", features = ["unstable", "attributes"] } basic-cookies = "0.1.3" bytes = "0.4.12" futures-fs = "0.0.5" diff --git a/src/lib.rs b/src/lib.rs index 9dbe7902b..80be38406 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,7 @@ //! # use futures::executor::block_on; //! # fn main() -> Result<(), std::io::Error> { block_on(async { //! # -//! #[derive(Debug, Deserialize, Serialize)] +//! #[derive(Debug, serde::Deserialize, serde::Serialize)] //! struct Counter { count: usize } //! //! let mut app = tide::new(); @@ -84,7 +84,7 @@ //! and abort early. This is useful for e.g. authentication middleware. Tide's middleware works //! like a stack. A simplified example of the logger middleware is something like this: //! -//! ```rust +//! ```ignore //! async fn log(req: Request, next: Next) -> Result { //! println!("Incoming request from {} on url {}", req.peer_addr(), req.url()); //! let res = next().await?; @@ -125,13 +125,15 @@ //! Extension traits are written by defining a trait + trait impl for the struct that's being //! extended: //! -//! ```rust +//! ```no_run +//! # use tide::Request; +//! //! pub trait RequestExt { -//! pub fn bark(&self) -> String; +//! fn bark(&self) -> String; //! } //! //! impl RequestExt for Request { -//! pub fn bark(&self) -> String { +//! fn bark(&self) -> String { //! "woof".to_string() //! } //! } @@ -139,11 +141,23 @@ //! //! Tide apps will then have access to the `bark` method on `Request`: //! -//! ```rust +//! ```no_run +//! # use tide::Request; +//! # +//! # pub trait RequestExt { +//! # fn bark(&self) -> String; +//! # } +//! +//! # impl RequestExt for Request { +//! # fn bark(&self) -> String { +//! # "woof".to_string() +//! # } +//! # } +//! //! #[async_std::main] //! async fn main() -> Result<(), std::io::Error> { //! let mut app = tide::new(); -//! app.at("/").get(|req| async move { req.bark() }); +//! app.at("/").get(|req: Request<()>| async move { req.bark() }); //! app.listen("127.0.0.1:8080").await //! } //! ``` diff --git a/src/request.rs b/src/request.rs index 0525be0e1..1595abf48 100644 --- a/src/request.rs +++ b/src/request.rs @@ -40,6 +40,24 @@ impl Request { } /// Access the request's HTTP method. + /// + /// # Examples + /// + /// ```no_run + /// # use futures::executor::block_on; + /// # fn main() -> Result<(), std::io::Error> { block_on(async { + /// # + /// use tide::Request; + /// + /// let mut app = tide::new(); + /// app.at("/").get(|req: Request<()>| async move { + /// assert_eq!(req.method(), http::Method::GET); + /// "Hello world!" + /// }); + /// app.listen("127.0.0.1:8080").await?; + /// # + /// # Ok(()) })} + /// ``` pub fn method(&self) -> &Method { self.request.method() } From 3489a010f3956348497953cae01779701035c702 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 28 Nov 2019 22:59:38 +0100 Subject: [PATCH 4/5] move prelude around Signed-off-by: Yoshua Wuyts --- src/lib.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 80be38406..2ebb9d2c0 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,3 @@ -// #![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))))] -#![cfg_attr(feature = "docs", feature(doc_cfg))] - //! # Serve the web //! //! Tide is a friendly HTTP server built for casual Rustaceans and veterans alike. It's completely @@ -174,6 +167,11 @@ //! eventually remove this notice entirely. The goal of Tide is to build a premier HTTP experience //! for Async Rust. We have a long journey ahead of us. But we're excited you're here with us! +// #![warn(missing_docs)] +#![warn(missing_debug_implementations, rust_2018_idioms)] +#![doc(test(attr(deny(rust_2018_idioms, warnings))))] +#![doc(test(attr(allow(unused_extern_crates, unused_variables))))] + mod endpoint; mod error; pub mod middleware; From 3dda33ffcaabb1d1d5e03f09c02c6881841f9811 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 29 Nov 2019 00:22:28 +0100 Subject: [PATCH 5/5] request docs Signed-off-by: Yoshua Wuyts --- src/request.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 10 deletions(-) diff --git a/src/request.rs b/src/request.rs index 1595abf48..4b04b6767 100644 --- a/src/request.rs +++ b/src/request.rs @@ -52,7 +52,7 @@ impl Request { /// let mut app = tide::new(); /// app.at("/").get(|req: Request<()>| async move { /// assert_eq!(req.method(), http::Method::GET); - /// "Hello world!" + /// "" /// }); /// app.listen("127.0.0.1:8080").await?; /// # @@ -63,11 +63,47 @@ impl Request { } /// Access the request's full URI method. + /// + /// # Examples + /// + /// ```no_run + /// # use futures::executor::block_on; + /// # fn main() -> Result<(), std::io::Error> { block_on(async { + /// # + /// use tide::Request; + /// + /// let mut app = tide::new(); + /// app.at("/").get(|req: Request<()>| async move { + /// assert_eq!(req.uri(), &"/".parse::().unwrap()); + /// "" + /// }); + /// app.listen("127.0.0.1:8080").await?; + /// # + /// # Ok(()) })} + /// ``` pub fn uri(&self) -> &Uri { self.request.uri() } /// Access the request's HTTP version. + /// + /// # Examples + /// + /// ```no_run + /// # use futures::executor::block_on; + /// # fn main() -> Result<(), std::io::Error> { block_on(async { + /// # + /// use tide::Request; + /// + /// let mut app = tide::new(); + /// app.at("/").get(|req: Request<()>| async move { + /// assert_eq!(req.version(), tide::http::Version::HTTP_11); + /// "" + /// }); + /// app.listen("127.0.0.1:8080").await?; + /// # + /// # Ok(()) })} + /// ``` pub fn version(&self) -> Version { self.request.version() } @@ -78,19 +114,28 @@ impl Request { } /// Get an HTTP header. + /// + /// # Examples + /// + /// ```no_run + /// # use futures::executor::block_on; + /// # fn main() -> Result<(), std::io::Error> { block_on(async { + /// # + /// use tide::Request; + /// + /// let mut app = tide::new(); + /// app.at("/").get(|req: Request<()>| async move { + /// assert_eq!(req.header("X-Forwarded-For"), Some("127.0.0.1")); + /// "" + /// }); + /// app.listen("127.0.0.1:8080").await?; + /// # + /// # Ok(()) })} + /// ``` pub fn header(&self, key: &'static str) -> Option<&'_ str> { self.request.headers().get(key).map(|h| h.to_str().unwrap()) } - /// Set an HTTP header. - pub fn set_header(mut self, key: &'static str, value: impl AsRef) -> Self { - let value = value.as_ref().to_owned(); - self.request - .headers_mut() - .insert(key, value.parse().unwrap()); - self - } - /// Get a local value. pub fn local(&self) -> Option<&T> { self.request.extensions().get() @@ -148,6 +193,24 @@ impl Request { /// /// Any I/O error encountered while reading the body is immediately returned /// as an `Err`. + /// + /// # Examples + /// + /// ```no_run + /// # use futures::executor::block_on; + /// # fn main() -> Result<(), std::io::Error> { block_on(async { + /// # + /// use tide::Request; + /// + /// let mut app = tide::new(); + /// app.at("/").get(|mut req: Request<()>| async move { + /// let _body: Vec = req.body_bytes().await.unwrap(); + /// "" + /// }); + /// app.listen("127.0.0.1:8080").await?; + /// # + /// # Ok(()) })} + /// ``` pub async fn body_bytes(&mut self) -> std::io::Result> { let mut buf = Vec::with_capacity(1024); self.request.body_mut().read_to_end(&mut buf).await?; @@ -165,6 +228,24 @@ impl Request { /// as an `Err`. /// /// If the body cannot be interpreted as valid UTF-8, an `Err` is returned. + /// + /// # Examples + /// + /// ```no_run + /// # use futures::executor::block_on; + /// # fn main() -> Result<(), std::io::Error> { block_on(async { + /// # + /// use tide::Request; + /// + /// let mut app = tide::new(); + /// app.at("/").get(|mut req: Request<()>| async move { + /// let _body: String = req.body_string().await.unwrap(); + /// "" + /// }); + /// app.listen("127.0.0.1:8080").await?; + /// # + /// # Ok(()) })} + /// ``` pub async fn body_string(&mut self) -> std::io::Result { let body_bytes = self.body_bytes().await?; Ok(String::from_utf8(body_bytes).map_err(|_| std::io::ErrorKind::InvalidData)?)