Skip to main content

trpl/
lib.rs

1//! A support crate for [_The Rust Programming Language_][trpl].
2//!
3//! [trpl]: https://doc.rust-lang.org/book
4//!
5//! This crate mostly just re-exports items from *other* crates. It exists for
6//! two main reasons:
7//!
8//! 1. So that as you read along in _The Rust Programming Language_, you can
9//!    add just one dependency, rather than however many we end up with, and
10//!    likewise use only one set of imports.
11//!
12//! 2. So that we can more easily guarantee it keeps building and working. Since
13//!    we control the contents of this crate and when it changes, readers will
14//!    never be broken by upstream changes, e.g. if Tokio does a breaking 2.0
15//!    release at some point.
16
17// For direct use within the `trpl` crate, *not* re-exported.
18use std::{future::Future, pin::pin};
19
20use futures::future;
21
22// Re-exports, to be used like `trpl::join`.
23pub use futures::{
24    future::{Either, join, join_all, join3},
25    join,
26};
27pub use tokio::{
28    fs::read_to_string,
29    runtime::Runtime,
30    // We use the `unbounded` variants because they most closely match the APIs
31    // from `std::sync::mpsc::channel`. Tokio's API choices are interesting:
32    //
33    // | `tokio::sync::mpsc` | `std::sync::mpsc` |
34    // | ------------------- | ----------------- |
35    // | `channel`           | `sync_channel`    |
36    // | `unbounded_channel` | `channel`         |
37    //
38    // The book collapses these differences for pedagogical simplicity, so that
39    // readers are not asking why `unbounded` is now important and can focus on
40    // the more important differences between sync and async APIs.
41    sync::mpsc::{
42        UnboundedReceiver as Receiver, UnboundedSender as Sender,
43        unbounded_channel as channel,
44    },
45    task::{JoinHandle, spawn as spawn_task, yield_now},
46    time::{interval, sleep},
47};
48
49pub use tokio_stream::{
50    Stream, StreamExt, iter as stream_from_iter,
51    wrappers::{IntervalStream, UnboundedReceiverStream as ReceiverStream},
52};
53
54/// Run a single future to completion on a bespoke Tokio `Runtime`.
55///
56/// Every time you call this, a new instance of `tokio::runtime::Runtime` will
57/// be created (see the implementation for details: it is trivial). This is:
58///
59/// - Reasonable for teaching purposes, in that you do not generally need to set
60///   up more than one runtime anyway, and especially do not in basic code like
61///   we are showing!
62///
63/// - Not *that* far off from what Tokio itself does under the hood in its own
64///   `tokio::main` macro for supporting `async fn main`.
65pub fn block_on<F: Future>(future: F) -> F::Output {
66    let rt = Runtime::new().unwrap();
67    rt.block_on(future)
68}
69
70/// This function has been renamed to `block_on`; please see its documentation.
71/// This function remains to maintain compatibility with the online versions
72/// of the book that use the name `run`.
73pub fn run<F: Future>(future: F) -> F::Output {
74    block_on(future)
75}
76
77/// Run two futures, taking whichever finishes first and canceling the other.
78///
79/// Notice that this is built on [`futures::future::select`], which has the
80/// same overall semantics but does *not* drop the slower future. The idea there
81/// is that you can work with the first result and then later *also* continue
82/// waiting for the second future.
83///
84/// We drop the slower future for the sake of simplicity in the examples: no
85/// need to deal with the tuple and intentionally ignore the second future this
86/// way!
87///
88/// Note that this only works as “simply” as it does because:
89///
90/// - It takes ownership of the futures.
91/// - It internally *pins* the futures.
92/// - It throws away (rather than returning) the unused future (which is why it
93///   can get away with pinning them).
94pub async fn select<A, B, F1, F2>(f1: F1, f2: F2) -> Either<A, B>
95where
96    F1: Future<Output = A>,
97    F2: Future<Output = B>,
98{
99    let f1 = pin!(f1);
100    let f2 = pin!(f2);
101    match future::select(f1, f2).await {
102        Either::Left((a, _f2)) => Either::Left(a),
103        Either::Right((b, _f1)) => Either::Right(b),
104    }
105}
106
107/// This function has been renamed to `select`; please see its documentation.
108/// This function remains to maintain compatibility with the online versions
109/// of the book that use the name `race`.
110pub async fn race<A, B, F1, F2>(f1: F1, f2: F2) -> Either<A, B>
111where
112    F1: Future<Output = A>,
113    F2: Future<Output = B>,
114{
115    select(f1, f2).await
116}
117
118/// Fetch data from a URL. For more convenient use in _The Rust Programming
119/// Language_, panics instead of returning a [`Result`] if the request fails.
120pub async fn get(url: &str) -> Response {
121    Response(reqwest::get(url).await.unwrap())
122}
123
124/// A thin wrapper around [`reqwest::Response`] to make the demos in _The Rust
125/// Programming Language_ substantially nicer to use.
126pub struct Response(reqwest::Response);
127
128impl Response {
129    /// Get the full response text.
130    ///
131    /// If the response cannot be deserialized, this panics instead of returning
132    /// a [`Result`] (for convenience in the demo).
133    pub async fn text(self) -> String {
134        self.0.text().await.unwrap()
135    }
136}
137
138/// A thin wrapper around [`scraper::Html`] to make the demos in _The Rust
139/// Programming Language_ substantially nicer to use.
140pub struct Html {
141    inner: scraper::Html,
142}
143
144impl Html {
145    /// Parse an HTML document from a string.
146    ///
147    /// This is just a thin wrapper around `scraper::Html::parse_document` to
148    /// keep the exported API surface simpler.
149    pub fn parse(source: &str) -> Html {
150        Html {
151            inner: scraper::Html::parse_document(source),
152        }
153    }
154
155    /// Get the first item in the document matching a string selector. Returns
156    /// Some()
157    ///
158    /// If the selector is not a valid CSS selector, panics rather than
159    /// returning a [`Result`] for convenience.
160    pub fn select_first<'a>(
161        &'a self,
162        selector: &'a str,
163    ) -> Option<scraper::ElementRef<'a>> {
164        let selector = scraper::Selector::parse(selector).unwrap();
165        self.inner.select(&selector).nth(0)
166    }
167}