Skip to content

Commit 4e1152d

Browse files
authored
Merge branch 'main' into param-error
2 parents 528fe8c + 904a45e commit 4e1152d

File tree

14 files changed

+271
-330
lines changed

14 files changed

+271
-330
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ jobs:
7878

7979
- uses: actions-rs/toolchain@v1
8080
with:
81-
toolchain: stable
81+
toolchain: nightly
8282
override: true
8383

8484
- name: setup

Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "tide"
33
version = "0.13.0"
4-
description = "Serve the web – HTTP server framework"
4+
description = "A minimal and pragmatic Rust web application framework built for rapid development"
55
authors = [
66
"Aaron Turon <[email protected]>",
77
"Yoshua Wuyts <[email protected]>",
@@ -41,9 +41,10 @@ async-std = { version = "1.6.0", features = ["unstable"] }
4141
async-trait = "0.1.36"
4242
femme = { version = "2.0.1", optional = true }
4343
futures-util = "0.3.5"
44-
log = { version = "0.4.8", features = ["std"] }
45-
http-types = "2.4.0"
44+
http-client = { version = "6.0.0", default-features = false }
45+
http-types = "2.5.0"
4646
kv-log-macro = "1.0.4"
47+
log = { version = "0.4.8", features = ["std"] }
4748
pin-project-lite = "0.1.7"
4849
route-recognizer = "0.2.0"
4950
serde = "1.0.102"
@@ -57,7 +58,7 @@ lazy_static = "1.4.0"
5758
logtest = "2.0.0"
5859
portpicker = "0.1.0"
5960
serde = { version = "1.0.102", features = ["derive"] }
60-
surf = { version = "2.0.0-alpha.3", default-features = false, features = ["h1-client"] }
61+
surf = { version = "2.0.0-alpha.7", default-features = false, features = ["h1-client"] }
6162
tempfile = "3.1.0"
6263

6364
[[test]]

README.md

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,16 @@
4141
</h3>
4242
</div>
4343

44-
A modular web framework built around async/await
44+
Tide is a minimal and pragmatic Rust web application framework built for
45+
rapid development. It comes with a robust set of features that make building
46+
async web applications and APIs easier and more fun.
4547

4648
## Getting started
4749

48-
Add two dependencies to your project's `Cargo.toml` file: `tide` itself, and `async-std` with the feature `attributes` enabled:
50+
In order to build a web app in Rust you need an HTTP server, and an async
51+
runtime. After running `cargo init` add the following lines to your
52+
`Cargo.toml` file:
53+
4954
```toml
5055
# Example, use the version numbers you need
5156
tide = "0.13.0"
@@ -54,25 +59,43 @@ async-std = { version = "1.6.0", features = ["attributes"] }
5459

5560
## Examples
5661

57-
**Hello World**
62+
Create an HTTP server that receives a JSON body, validates it, and responds
63+
with a confirmation message.
5864

5965
```rust
66+
use tide::Request;
67+
use tide::prelude::*;
68+
69+
#[derive(Debug, Deserialize)]
70+
struct Animal {
71+
name: String,
72+
legs: u8,
73+
}
74+
6075
#[async_std::main]
61-
async fn main() -> Result<(), std::io::Error> {
62-
tide::log::start();
76+
async fn main() -> tide::Result<()> {
6377
let mut app = tide::new();
64-
app.at("/").get(|_| async { Ok("Hello, world!") });
78+
app.at("/orders/shoes").post(order_shoes);
6579
app.listen("127.0.0.1:8080").await?;
6680
Ok(())
6781
}
82+
83+
async fn order_shoes(mut req: Request<()>) -> tide::Result {
84+
let Animal { name, legs } = req.body_json().await?;
85+
Ok(format!("Hello, {}! I've put in an order for {} shoes", name, legs).into())
86+
}
6887
```
6988

70-
To try [the included examples](https://github.com/http-rs/tide/tree/main/examples), check out this repository and run
7189
```sh
72-
$ cargo run --example # shows a list of available examples
73-
$ cargo run --example hello
90+
$ curl localhost:8000/orders/shoes -d '{ "name": "Chashu", "legs": 4 }'
91+
Hello, Chashu! I've put in an order for 4 shoes
92+
93+
$ curl localhost:8000/orders/shoes -d '{ "name": "Mary Millipede", "legs": 750 }'
94+
number too large to fit in target type
7495
```
7596
97+
See more examples in the [examples](https://github.com/http-rs/tide/tree/main/examples) directory.
98+
7699
## Tide's design:
77100
- [Rising Tide: building a modular web framework in the open](https://rustasync.github.io/team/2018/09/11/tide.html)
78101
- [Routing and extraction in Tide: a first sketch](https://rustasync.github.io/team/2018/10/16/tide-routing.html)

src/lib.rs

Lines changed: 32 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -1,197 +1,56 @@
1-
//! # Serve the web
2-
//!
3-
//! Tide is a friendly HTTP server built for casual Rustaceans and veterans alike. It's completely
4-
//! modular, and built directly for `async/await`. Whether it's a quick webhook, or an L7 load
5-
//! balancer, Tide will make it work.
6-
//!
7-
//! # Features
8-
//!
9-
//! - __Fast:__ Written in Rust, and built on Futures, Tide is incredibly efficient.
10-
//! - __Friendly:__ With thorough documentation, and a complete API, Tide helps cover your every
11-
//! need.
12-
//! - __Minimal:__ With only a few concepts to learn, Tide is easy to pick up and become productive
13-
//! with.
1+
//! Tide is a minimal and pragmatic Rust web application framework built for
2+
//! rapid development. It comes with a robust set of features that make
3+
//! building async web applications and APIs easier and more fun.
144
//!
155
//! # Getting started
166
//!
17-
//! Add two dependencies to your project's `Cargo.toml` file: `tide` itself, and `async-std` with the feature `attributes` enabled:
7+
//! In order to build a web app in Rust you need an HTTP server, and an async
8+
//! runtime. After running `cargo init` add the following lines to your
9+
//! `Cargo.toml` file:
10+
//!
1811
//! ```toml
1912
//! # Example, use the version numbers you need
20-
//! tide = "0.7.0"
21-
//! async-std = { version = "1.5.0", features = ["attributes"] }
13+
//! tide = "0.13.0"
14+
//! async-std = { version = "1.6.0", features = ["attributes"] }
2215
//!```
2316
//!
2417
//! # Examples
2518
//!
26-
//! __hello world__
27-
//! ```no_run
28-
//! # use async_std::task::block_on;
29-
//! # fn main() -> Result<(), std::io::Error> { block_on(async {
30-
//! #
31-
//! let mut app = tide::new();
32-
//! app.at("/").get(|_| async { Ok("Hello, world!") });
33-
//! app.listen("127.0.0.1:8080").await?;
34-
//! #
35-
//! # Ok(()) }) }
36-
//! ```
19+
//! Create an HTTP server that receives a JSON body, validates it, and responds with a
20+
//! confirmation message.
3721
//!
38-
//! __echo server__
3922
//! ```no_run
40-
//! # use async_std::task::block_on;
41-
//! # fn main() -> Result<(), std::io::Error> { block_on(async {
42-
//! #
43-
//! let mut app = tide::new();
44-
//! app.at("/").get(|req| async { Ok(req) });
45-
//! app.listen("127.0.0.1:8080").await?;
46-
//! #
47-
//! # Ok(()) }) }
48-
//! ````
49-
//!
50-
//! __send and receive json__
51-
//! ```no_run
52-
//! # use async_std::task::block_on;
53-
//! # fn main() -> Result<(), std::io::Error> { block_on(async {
54-
//! # use tide::{Body, Request, Response};
55-
//! #
56-
//! #[derive(Debug, serde::Deserialize, serde::Serialize)]
57-
//! struct Counter { count: usize }
58-
//!
59-
//! let mut app = tide::new();
60-
//! app.at("/").get(|mut req: Request<()>| async move {
61-
//! let mut counter: Counter = req.body_json().await?;
62-
//! println!("count is {}", counter.count);
63-
//! counter.count += 1;
64-
//! let mut res = Response::new(200);
65-
//! res.set_body(Body::from_json(&counter)?);
66-
//! Ok(res)
67-
//! });
68-
//! app.listen("127.0.0.1:8080").await?;
69-
//! #
70-
//! # Ok(()) }) }
71-
//! ```
23+
//! use tide::Request;
24+
//! use tide::prelude::*;
7225
//!
73-
//! # Concepts
74-
//!
75-
//! ## Request-Response
76-
//!
77-
//! Each Tide endpoint takes a [`Request`] and returns a [`Response`]. Because async functions
78-
//! allow us to wait without blocking, this makes Tide feel similar to synchronous servers. Except
79-
//! it's incredibly efficient.
80-
//!
81-
//! ```txt
82-
//! async fn endpoint(req: Request) -> Result;
83-
//! ```
84-
//!
85-
//! ## Middleware
86-
//!
87-
//! Middleware wrap each request and response pair, allowing code to be run before the endpoint,
88-
//! and after each endpoint. Additionally each handler can choose to never yield to the endpoint
89-
//! and abort early. This is useful for e.g. authentication middleware. Tide's middleware works
90-
//! like a stack. A simplified example of the logger middleware is something like this:
91-
//!
92-
//! ```ignore
93-
//! async fn log(req: Request, next: Next) -> tide::Result {
94-
//! println!("Incoming request from {} on url {}", req.peer_addr(), req.url());
95-
//! let res = next().await?;
96-
//! println!("Outgoing response with status {}", res.status());
97-
//! res
26+
//! #[derive(Debug, Deserialize)]
27+
//! struct Animal {
28+
//! name: String,
29+
//! legs: u8,
9830
//! }
99-
//! ```
100-
//!
101-
//! As a new request comes in, we perform some logic. Then we yield to the next
102-
//! middleware (or endpoint, we don't know when we yield to `next`), and once that's
103-
//! done, we return the Response. We can decide to not yield to `next` at any stage,
104-
//! and abort early. This can then be used in applications using the [`Server::middleware`]
105-
//! method.
106-
//!
107-
//! ## State
108-
//!
109-
//! Middleware often needs to share values with the endpoint. This is done through "request scoped
110-
//! state". Request scoped state is built using a typemap that's available through
111-
//! [`Request::ext`].
11231
//!
113-
//! If the endpoint needs to share values with middleware, response scoped state can be set via
114-
//! [`Response::insert_ext`] and is available through [`Response::ext`].
115-
//!
116-
//! Application scoped state is used when a complete application needs access to a particular
117-
//! value. Examples of this include: database connections, websocket connections, or
118-
//! network-enabled config. Every `Request<State>` has an inner value that must
119-
//! implement `Send + Sync + Clone`, and can thus freely be shared between requests.
120-
//!
121-
//! By default `tide::new` will use `()` as the shared state. But if you want to
122-
//! create a new app with shared state you can use the [`with_state`] function.
123-
//!
124-
//! ## Extension Traits
125-
//!
126-
//! Sometimes having application and request scoped context can require a bit of setup. There are
127-
//! cases where it'd be nice if things were a little easier. This is why Tide
128-
//! encourages people to write _extension traits_.
129-
//!
130-
//! By using an _extension trait_ you can extend [`Request`] or [`Response`] with more
131-
//! functionality. For example, an authentication package could implement a `user` method on
132-
//! `Request`, to access the authenticated user provided by middleware.
133-
//!
134-
//! Extension traits are written by defining a trait + trait impl for the struct that's being
135-
//! extended:
136-
//!
137-
//! ```no_run
138-
//! # use tide::Request;
139-
//! #
140-
//! pub trait RequestExt {
141-
//! fn bark(&self) -> String;
142-
//! }
143-
//!
144-
//! impl<State> RequestExt for Request<State> {
145-
//! fn bark(&self) -> String {
146-
//! "woof".to_string()
147-
//! }
148-
//! }
149-
//! ```
150-
//!
151-
//! Tide apps will then have access to the `bark` method on `Request`:
152-
//!
153-
//! ```no_run
154-
//! # use tide::Request;
155-
//! #
156-
//! # pub trait RequestExt {
157-
//! # fn bark(&self) -> String;
158-
//! # }
159-
//! #
160-
//! # impl<State> RequestExt for Request<State> {
161-
//! # fn bark(&self) -> String {
162-
//! # "woof".to_string()
163-
//! # }
164-
//! # }
165-
//! #
16632
//! #[async_std::main]
167-
//! async fn main() -> Result<(), std::io::Error> {
33+
//! async fn main() -> tide::Result<()> {
16834
//! let mut app = tide::new();
169-
//! app.at("/").get(|req: Request<()>| async move { Ok(req.bark()) });
170-
//! app.listen("127.0.0.1:8080").await
35+
//! app.at("/orders/shoes").post(order_shoes);
36+
//! app.listen("127.0.0.1:8080").await?;
37+
//! Ok(())
17138
//! }
172-
//! ```
17339
//!
174-
//! # HTTP Version 1.1 only
40+
//! async fn order_shoes(mut req: Request<()>) -> tide::Result {
41+
//! let Animal { name, legs } = req.body_json().await?;
42+
//! Ok(format!("Hello, {}! I've put in an order for {} shoes", name, legs).into())
43+
//! }
44+
//! ````
17545
//!
176-
//! Tide's default backend currently only supports HTTP/1.1. In order
177-
//! to use nginx as reverse proxy for Tide, your upstream proxy
178-
//! configuration must include this line:
46+
//! ```sh
47+
//! $ curl localhost:8000/orders/shoes -d '{ "name": "Chashu", "legs": 4 }'
48+
//! Hello, Chashu! I've put in an order for 4 shoes
17949
//!
180-
//! ```text
181-
//! proxy_http_version 1.1;
50+
//! $ curl localhost:8000/orders/shoes -d '{ "name": "Mary Millipede", "legs": 750 }'
51+
//! number too large to fit in target type
18252
//! ```
183-
//!
184-
//! # API Stability
185-
//!
186-
//! It's still early in Tide's development cycle. While the general shape of Tide might have
187-
//! roughly established, the exact traits and function parameters may change between versions. In
188-
//! practice this means that building your core business on Tide is probably not a wise idea...
189-
//! yet.
190-
//!
191-
//! However we *are* committed to closely following semver, and documenting any and all breaking
192-
//! changes we make. Also as time goes on you may find that fewer and fewer changes occur, until we
193-
//! eventually remove this notice entirely. The goal of Tide is to build a premier HTTP experience
194-
//! for Async Rust. We have a long journey ahead of us. But we're excited you're here with us!
53+
//! See more examples in the [examples](https://github.com/http-rs/tide/tree/main/examples) directory.
19554
19655
#![cfg_attr(feature = "docs", feature(doc_cfg))]
19756
// #![warn(missing_docs)]

src/route.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl<'a, State: Clone + Send + Sync + 'static> Route<'a, State> {
4343
let mut p = self.path.clone();
4444

4545
if !p.ends_with('/') && !path.starts_with('/') {
46-
p.push_str("/");
46+
p.push('/');
4747
}
4848

4949
if path != "/" {
@@ -289,7 +289,7 @@ where
289289
route_params,
290290
} = req;
291291

292-
let rest = crate::request::rest(&route_params).unwrap_or_else(|| "");
292+
let rest = crate::request::rest(&route_params).unwrap_or("");
293293
req.url_mut().set_path(&rest);
294294

295295
self.0

0 commit comments

Comments
 (0)