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

Start documenting more #363

Merged
merged 5 commits into from
Dec 20, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
10 changes: 5 additions & 5 deletions src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
/// }
///
Expand All @@ -34,7 +34,7 @@ use crate::{response::IntoResponse, Request, Response};
///
/// ```no_run
/// # use core::future::Future;
/// fn hello(_cx: tide::Request<()>) -> impl Future<Output = String> {
/// fn hello(_req: tide::Request<()>) -> impl Future<Output = String> {
/// futures::future::ready(String::from("hello"))
/// }
///
Expand All @@ -50,7 +50,7 @@ pub trait Endpoint<State>: Send + Sync + 'static {
type Fut: Future<Output = Response> + Send + 'static;

/// Invoke the endpoint within the given context
fn call(&self, cx: Request<State>) -> Self::Fut;
fn call(&self, req: Request<State>) -> Self::Fut;
}

pub(crate) type DynEndpoint<State> =
Expand All @@ -63,8 +63,8 @@ where
Fut::Output: IntoResponse,
{
type Fut = BoxFuture<'static, Response>;
fn call(&self, cx: Request<State>) -> Self::Fut {
let fut = (self)(cx);
fn call(&self, req: Request<State>) -> Self::Fut {
let fut = (self)(req);
Box::pin(async move { fut.await.into_response() })
}
}
179 changes: 160 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
// #![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
//! 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__
Expand All @@ -23,7 +24,7 @@
//! app.listen("127.0.0.1:8080").await?;
//! #
//! # Ok(()) }) }
//! ````
//! ```
//!
//! __echo server__
//! ```no_run
Expand All @@ -38,28 +39,122 @@
//! ````
//!
//! __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 {
//! #
//! #[derive(Debug, Deserialize, Serialize)]
//! #[derive(Debug, serde::Deserialize, serde::Serialize)]
//! struct Counter { count: usize }
//!
//! 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<Response>;
//! ```
//!
//! ## 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:
//!
//! ```ignore
//! async fn log(req: Request, next: Next) -> Result<Response> {
//! 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<State>` 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:
//!
//! ```no_run
//! # use tide::Request;
//!
//! pub trait RequestExt {
//! fn bark(&self) -> String;
//! }
//!
//! impl<State> RequestExt for Request<State> {
//! fn bark(&self) -> String {
//! "woof".to_string()
//! }
//! }
//! ```
//!
//! Tide apps will then have access to the `bark` method on `Request`:
//!
//! ```no_run
//! # use tide::Request;
//! #
//! # pub trait RequestExt {
//! # fn bark(&self) -> String;
//! # }
//!
//! # impl<State> RequestExt for Request<State> {
//! # 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: Request<()>| 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
Expand All @@ -69,10 +164,13 @@
//!
//! 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!

// #![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;
Expand Down Expand Up @@ -101,11 +199,54 @@ 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();
/// app.at("/").get(|_| async move { "Hello, world!" });
/// app.listen("127.0.0.1:8080").await?;
/// #
/// # 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<State>| async move {
/// format!("Hello, {}!", &req.state().name)
/// });
/// app.listen("127.0.0.1:8080").await?;
/// #
/// # Ok(()) }) }
/// ```
pub fn with_state<State>(state: State) -> server::Server<State>
where
State: Send + Sync + 'static,
Expand Down
Loading