Skip to content

Commit 96c2aee

Browse files
committed
Update guide to describe configuring book for external crates;
Also refactor the `mdBook test` section and add a new "writing doc tests" chapter.
1 parent 3eb20b3 commit 96c2aee

File tree

5 files changed

+153
-23
lines changed

5 files changed

+153
-23
lines changed

guide/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- [Installation](guide/installation.md)
88
- [Reading Books](guide/reading.md)
99
- [Creating a Book](guide/creating.md)
10+
- [Writing Code Samples](guide/writing.md)
1011

1112
# Reference Guide
1213

guide/src/cli/test.md

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,16 @@
11
# The test command
22

3-
When writing a book, you sometimes need to automate some tests. For example,
3+
When writing a book, you may want to provide some code samples,
4+
and it's important that these be accurate.
5+
For example,
46
[The Rust Programming Book](https://doc.rust-lang.org/stable/book/) uses a lot
5-
of code examples that could get outdated. Therefore it is very important for
7+
of code samples that could get outdated as the language evolves. Therefore it is very important for
68
them to be able to automatically test these code examples.
79

8-
mdBook supports a `test` command that will run all available tests in a book. At
9-
the moment, only Rust tests are supported.
10+
mdBook supports a `test` command that will run code samples as doc tests for your book. At
11+
the moment, only Rust doc tests are supported.
1012

11-
#### Disable tests on a code block
12-
13-
rustdoc doesn't test code blocks which contain the `ignore` attribute:
14-
15-
```rust,ignore
16-
fn main() {}
17-
```
18-
19-
rustdoc also doesn't test code blocks which specify a language other than Rust:
20-
21-
```markdown
22-
**Foo**: _bar_
23-
```
24-
25-
rustdoc *does* test code blocks which have no language specified:
26-
27-
```
28-
This is going to cause an error!
29-
```
13+
For details on writing code samples and runnable code samples in your book, see [Writing](../guide/writing.md).
3014

3115
#### Specify a directory
3216

@@ -39,6 +23,10 @@ mdbook test path/to/book
3923

4024
#### `--library-path`
4125

26+
> Note: This argument doesn't provide sufficient information for current Rust compilers.
27+
Instead, add `package-dir` to your ***book.toml***, as described in [configuration](/format/configuration/general.md#rust-options).
28+
29+
4230
The `--library-path` (`-L`) option allows you to add directories to the library
4331
search path used by `rustdoc` when it builds and tests the examples. Multiple
4432
directories can be specified with multiple options (`-L foo -L bar`) or with a

guide/src/format/configuration/general.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,16 @@ integration.
6868

6969
```toml
7070
[rust]
71+
package-dir = "folder/for/Cargo.toml"
7172
edition = "2015" # the default edition for code blocks
7273
```
7374

75+
- **package-dir**: Folder containing a Cargo package whose targets and dependencies
76+
you want to use in your book's code samples.
77+
It must be specified if you want to test code samples with `use` statements, even if
78+
there is a `Cargo.toml` in the folder containing the `book.toml`.
79+
This can be a relative path, relative to the folder containing `book.toml`.
80+
7481
- **edition**: Rust edition to use by default for the code snippets. Default
7582
is `"2015"`. Individual code blocks can be controlled with the `edition2015`,
7683
`edition2018` or `edition2021` annotations, such as:
@@ -82,6 +89,7 @@ edition = "2015" # the default edition for code blocks
8289
```
8390
~~~
8491

92+
8593
### Build options
8694

8795
This controls the build process of your book.

guide/src/guide/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ This user guide provides an introduction to basic concepts of using mdBook.
55
- [Installation](installation.md)
66
- [Reading Books](reading.md)
77
- [Creating a Book](creating.md)
8+
- [Writing Code Samples](writing.md)

guide/src/guide/writing.md

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Writing code samples and documentation tests
2+
3+
If your book is about software, a short code sample may communicate the point better than many words of explanation.
4+
This section describes how to format samples and, perhaps more importantly, how to verify they compile and run
5+
to ensue they stay aligned with the software APIs they describe.
6+
7+
Code blocks in your book are passed through mdBook and processed by rustdoc. For more details on structuring codeblocks and running doc tests,
8+
refer to the [rustdoc book](https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html)
9+
10+
### Code blocks for sample code
11+
12+
You include a code sample in your book as a markdown fenced code block specifying `rust`, like so:
13+
14+
`````markdown
15+
```rust
16+
let four = 2 + 2;
17+
assert_eq!(four, 4);
18+
```
19+
`````
20+
21+
This displays as:
22+
23+
```rust
24+
let four = 2 + 2;
25+
assert_eq!(four, 4);
26+
```
27+
28+
Rustdoc will wrap this sample in a `fn main() {}` so that it can be compiled and even run by `mdbook test`.
29+
30+
#### Disable tests on a code block
31+
32+
rustdoc does not test code blocks which contain the `ignore` attribute:
33+
34+
`````markdown
35+
```rust,ignore
36+
fn main() {}
37+
This would not compile anyway.
38+
```
39+
`````
40+
41+
rustdoc also doesn't test code blocks which specify a language other than Rust:
42+
43+
`````markdown
44+
```markdown
45+
**Foo**: _bar_
46+
```
47+
`````
48+
49+
rustdoc *does* test code blocks which have no language specified:
50+
51+
`````markdown
52+
```
53+
let four = 2 + 2;
54+
assert_eq!(four, 4);
55+
```
56+
`````
57+
58+
### Hiding source lines within a sample
59+
60+
A longer sample may contain sections of boilerplate code that are not relevant to the current section of your book.
61+
You can hide source lines within the code block prefixing them with `#_`
62+
(that is a line starting with `#` followed by a single space), like so:
63+
64+
`````markdown
65+
```rust
66+
# use std::fs::File;
67+
# use std::io::{Write,Result};
68+
# fn main() -> Result<()> {
69+
let mut file = File::create("foo.txt")?;
70+
file.write_all(b"Hello, world!")?;
71+
# Ok(())
72+
# }
73+
```
74+
`````
75+
76+
This displays as:
77+
78+
```rust
79+
# use std::fs::File;
80+
# use std::io::{Write,Result};
81+
# fn main() -> Result<()> {
82+
let mut file = File::create("foo.txt")?;
83+
file.write_all(b"Hello, world!")?;
84+
# Ok(())
85+
# }
86+
```
87+
88+
Note that the code block displays an "show hidden lines" button in the upper right of the code block (when hovered over).
89+
90+
Note, too, that the sample provided its own `fn main(){}`, so the `use` statements could be positioned outside it.
91+
When rustdoc sees the sample already provides `fn main`, it does *not* do its own wrapping.
92+
93+
94+
### Tests using external crates
95+
96+
The previous example shows that you can `use` a crate within your sample.
97+
But if the crate is an *external* crate, that is, one declared as a dependency in your
98+
package `Cargo.toml`, rustc (the compiler invoked by rustdoc) needs
99+
`-L` and `--extern` switches in order to compile it.
100+
Cargo does this automatically for `cargo build` and `cargo rustdoc` and mdBook can as well.
101+
102+
To allow mdBook to determine the correct external crate information,
103+
add `package-dir` to your ***book.toml**, as described in [configuration](/format/configuration/general.md#rust-options).
104+
Note that mdBook runs a `cargo build` for the package to determine correct dependencies.
105+
106+
This example (borrowed from the `serde` crate documentation) compiles and runs in a properly configured book:
107+
108+
```rust
109+
use serde::{Deserialize, Serialize};
110+
111+
#[derive(Serialize, Deserialize, Debug)]
112+
struct Point {
113+
x: i32,
114+
y: i32,
115+
}
116+
117+
fn main() {
118+
let point = Point { x: 1, y: 2 };
119+
120+
// Convert the Point to a JSON string.
121+
let serialized = serde_json::to_string(&point).unwrap();
122+
123+
// Prints serialized = {"x":1,"y":2}
124+
println!("serialized = {}", serialized);
125+
126+
// Convert the JSON string back to a Point.
127+
let deserialized: Point = serde_json::from_str(&serialized).unwrap();
128+
129+
// Prints deserialized = Point { x: 1, y: 2 }
130+
println!("deserialized = {:?}", deserialized);
131+
}
132+
```

0 commit comments

Comments
 (0)