Skip to content

Commit fb99276

Browse files
Merge pull request #457 from Michael-F-Bryan/config
Making configuration more flexible
2 parents 79dd03e + 5eff572 commit fb99276

File tree

19 files changed

+636
-1251
lines changed

19 files changed

+636
-1251
lines changed

book-example/src/format/config.md

Lines changed: 87 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,10 @@
22

33
You can configure the parameters for your book in the ***book.toml*** file.
44

5-
>**Note:**
6-
JSON configuration files were previously supported but have been deprecated in favor of
7-
the TOML configuration file. If you are still using JSON we strongly encourage you to migrate to
8-
the TOML configuration because JSON support will be removed in the future.
9-
105
Here is an example of what a ***book.toml*** file might look like:
116

127
```toml
8+
[book]
139
title = "Example book"
1410
author = "John Doe"
1511
description = "The example book covers examples."
@@ -24,66 +20,118 @@ additional-css = ["custom.css"]
2420
It is important to note that **any** relative path specified in the in the configuration will
2521
always be taken relative from the root of the book where the configuration file is located.
2622

27-
### General metadata
2823

29-
- **title:** The title of the book
30-
- **author:** The author of the book
31-
- **description:** A description for the book, which is added as meta information in the html `<head>` of each page
32-
33-
**book.toml**
34-
```toml
35-
title = "Example book"
36-
author = "John Doe"
37-
description = "The example book covers examples."
38-
```
24+
### General metadata
3925

40-
Some books may have multiple authors, there is an alternative key called `authors` plural that lets you specify an array
41-
of authors.
26+
This is general information about your book.
4227

43-
**book.toml**
44-
```toml
45-
title = "Example book"
46-
authors = ["John Doe", "Jane Doe"]
47-
description = "The example book covers examples."
48-
```
49-
50-
### Source directory
51-
By default, the source directory is found in the directory named `src` directly under the root folder. But this is configurable
52-
with the `source` key in the configuration file.
28+
- **title:** The title of the book
29+
- **authors:** The author(s) of the book
30+
- **description:** A description for the book, which is added as meta
31+
information in the html `<head>` of each page
32+
- **src:** By default, the source directory is found in the directory named
33+
`src` directly under the root folder. But this is configurable with the `src`
34+
key in the configuration file.
35+
- **build-dir:** The directory to put the rendered book in. By default this is
36+
`book/` in the book's root directory.
5337

5438
**book.toml**
5539
```toml
40+
[book]
5641
title = "Example book"
5742
authors = ["John Doe", "Jane Doe"]
5843
description = "The example book covers examples."
59-
60-
source = "my-src" # the source files will be found in `root/my-src` instead of `root/src`
44+
src = "my-src" # the source files will be found in `root/my-src` instead of `root/src`
45+
build-dir = "build"
6146
```
6247

6348
### HTML renderer options
64-
The HTML renderer has a couple of options aswell. All the options for the renderer need to be specified under the TOML table `[output.html]`.
49+
The HTML renderer has a couple of options as well. All the options for the
50+
renderer need to be specified under the TOML table `[output.html]`.
51+
6552
The following configuration options are available:
6653

67-
- **`destination`:** By default, the HTML book will be rendered in the `root/book` directory, but this option lets you specify another
68-
destination fodler.
69-
- **`theme`:** mdBook comes with a default theme and all the resource files needed for it. But if this option is set, mdBook will selectively overwrite the theme files with the ones found in the specified folder.
70-
- **`curly-quotes`:** Convert straight quotes to curly quotes, except for those that occur in code blocks and code spans. Defaults to `false`.
71-
- **`google-analytics`:** If you use Google Analytics, this option lets you enable it by simply specifying your ID in the configuration file.
72-
- **`additional-css`:** If you need to slightly change the appearance of your book without overwriting the whole style, you can specify a set of stylesheets that will be loaded after the default ones where you can surgically change the style.
73-
- **`additional-js`:** If you need to add some behaviour to your book without removing the current behaviour, you can specify a set of javascript files that will be loaded alongside the default one.
54+
pub playpen: Playpen,
55+
56+
- **theme:** mdBook comes with a default theme and all the resource files
57+
needed for it. But if this option is set, mdBook will selectively overwrite
58+
the theme files with the ones found in the specified folder.
59+
- **curly-quotes:** Convert straight quotes to curly quotes, except for
60+
those that occur in code blocks and code spans. Defaults to `false`.
61+
- **google-analytics:** If you use Google Analytics, this option lets you
62+
enable it by simply specifying your ID in the configuration file.
63+
- **additional-css:** If you need to slightly change the appearance of your
64+
book without overwriting the whole style, you can specify a set of
65+
stylesheets that will be loaded after the default ones where you can
66+
surgically change the style.
67+
- **additional-js:** If you need to add some behaviour to your book without
68+
removing the current behaviour, you can specify a set of javascript files
69+
that will be loaded alongside the default one.
70+
- **playpen:** A subtable for configuring various playpen settings.
7471

7572
**book.toml**
7673
```toml
74+
[book]
7775
title = "Example book"
7876
authors = ["John Doe", "Jane Doe"]
7977
description = "The example book covers examples."
8078

8179
[output.html]
82-
destination = "my-book" # the output files will be generated in `root/my-book` instead of `root/book`
8380
theme = "my-theme"
8481
curly-quotes = true
8582
google-analytics = "123456"
8683
additional-css = ["custom.css", "custom2.css"]
8784
additional-js = ["custom.js"]
85+
86+
[output.html.playpen]
87+
editor = "./path/to/editor"
88+
editable = false
8889
```
8990

91+
92+
## For Developers
93+
94+
If you are developing a plugin or alternate backend then whenever your code is
95+
called you will almost certainly be passed a reference to the book's `Config`.
96+
This can be treated roughly as a nested hashmap which lets you call methods like
97+
`get()` and `get_mut()` to get access to the config's contents.
98+
99+
By convention, plugin developers will have their settings as a subtable inside
100+
`plugins` (e.g. a link checker would put its settings in `plugins.link_check`)
101+
and backends should put their configuration under `output`, like the HTML
102+
renderer does in the previous examples.
103+
104+
As an example, some hypothetical `random` renderer would typically want to load
105+
its settings from the `Config` at the very start of its rendering process. The
106+
author can take advantage of serde to deserialize the generic `toml::Value`
107+
object retrieved from `Config` into a struct specific to its use case.
108+
109+
```rust
110+
#[derive(Debug, Deserialize, PartialEq)]
111+
struct RandomOutput {
112+
foo: u32,
113+
bar: String,
114+
baz: Vec<bool>,
115+
}
116+
117+
let src = r#"
118+
[output.random]
119+
foo = 5
120+
bar = "Hello World"
121+
baz = [true, true, false]
122+
"#;
123+
124+
let book_config = Config::from_str(src)?; // usually passed in by mdbook
125+
let random: Value = book_config.get("output.random").unwrap_or_default();
126+
let got: RandomOutput = random.try_into()?;
127+
128+
assert_eq!(got, should_be);
129+
130+
if let Some(baz) = book_config.get_deserialized::<Vec<bool>>("output.random.baz") {
131+
println!("{:?}", baz); // prints [true, true, false]
132+
133+
// do something interesting with baz
134+
}
135+
136+
// start the rendering process
137+
```

src/bin/build.rs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use clap::{App, ArgMatches, SubCommand};
1+
use std::path::PathBuf;
2+
use clap::{ArgMatches, SubCommand, App};
23
use mdbook::MDBook;
34
use mdbook::errors::Result;
45
use {get_book_dir, open};
@@ -15,10 +16,6 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
1516
.arg_from_usage(
1617
"--no-create 'Will not create non-existent files linked from SUMMARY.md'",
1718
)
18-
.arg_from_usage(
19-
"--curly-quotes 'Convert straight quotes to curly quotes, except for those \
20-
that occur in code blocks and code spans'",
21-
)
2219
.arg_from_usage(
2320
"[dir] 'A directory for your book{n}(Defaults to Current Directory \
2421
when omitted)'",
@@ -28,21 +25,16 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
2825
// Build command implementation
2926
pub fn execute(args: &ArgMatches) -> Result<()> {
3027
let book_dir = get_book_dir(args);
31-
let book = MDBook::new(&book_dir).read_config()?;
28+
let mut book = MDBook::new(&book_dir).read_config()?;
3229

33-
let mut book = match args.value_of("dest-dir") {
34-
Some(dest_dir) => book.with_destination(dest_dir),
35-
None => book,
36-
};
30+
if let Some(dest_dir) = args.value_of("dest-dir") {
31+
book.config.book.build_dir = PathBuf::from(dest_dir);
32+
}
3733

3834
if args.is_present("no-create") {
3935
book.create_missing = false;
4036
}
4137

42-
if args.is_present("curly-quotes") {
43-
book = book.with_curly_quotes(true);
44-
}
45-
4638
book.build()?;
4739

4840
if args.is_present("open") {

src/bin/init.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
4747
}
4848

4949
// Because of `src/book/mdbook.rs#L37-L39`, `dest` will always start with `root`
50-
let is_dest_inside_root = book.get_destination().starts_with(book.get_root());
50+
let is_dest_inside_root = book.get_destination().starts_with(&book.root);
5151

5252
if !args.is_present("force") && is_dest_inside_root {
5353
println!("\nDo you want a .gitignore to be created? (y/n)");

src/bin/serve.rs

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ extern crate staticfile;
33
extern crate ws;
44

55
use std;
6-
use std::path::Path;
6+
use std::path::PathBuf;
77
use self::iron::{status, AfterMiddleware, Chain, Iron, IronError, IronResult, Request, Response,
88
Set};
99
use clap::{App, ArgMatches, SubCommand};
@@ -29,10 +29,6 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
2929
"-d, --dest-dir=[dest-dir] 'The output directory for \
3030
your book{n}(Defaults to ./book when omitted)'",
3131
)
32-
.arg_from_usage(
33-
"--curly-quotes 'Convert straight quotes to curly quotes, except \
34-
for those that occur in code blocks and code spans'",
35-
)
3632
.arg_from_usage("-p, --port=[port] 'Use another port{n}(Defaults to 3000)'")
3733
.arg_from_usage(
3834
"-w, --websocket-port=[ws-port] 'Use another port for the \
@@ -53,15 +49,10 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
5349
const RELOAD_COMMAND: &'static str = "reload";
5450

5551
let book_dir = get_book_dir(args);
56-
let book = MDBook::new(&book_dir).read_config()?;
57-
58-
let mut book = match args.value_of("dest-dir") {
59-
Some(dest_dir) => book.with_destination(Path::new(dest_dir)),
60-
None => book,
61-
};
52+
let mut book = MDBook::new(&book_dir).read_config()?;
6253

63-
if args.is_present("curly-quotes") {
64-
book = book.with_curly_quotes(true);
54+
if let Some(dest_dir) = args.value_of("dest-dir") {
55+
book.config.book.build_dir = PathBuf::from(dest_dir);
6556
}
6657

6758
let port = args.value_of("port").unwrap_or("3000");
@@ -73,8 +64,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
7364
let address = format!("{}:{}", interface, port);
7465
let ws_address = format!("{}:{}", interface, ws_port);
7566

76-
book.set_livereload(format!(
77-
r#"
67+
book.livereload = Some(format!(r#"
7868
<script type="text/javascript">
7969
var socket = new WebSocket("ws://{}:{}");
8070
socket.onmessage = function (event) {{

src/bin/watch.rs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
extern crate notify;
22

3-
use std::path::Path;
3+
use std::path::{Path, PathBuf};
44
use self::notify::Watcher;
55
use std::time::Duration;
66
use std::sync::mpsc::channel;
@@ -18,10 +18,6 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
1818
"-d, --dest-dir=[dest-dir] 'The output directory for \
1919
your book{n}(Defaults to ./book when omitted)'",
2020
)
21-
.arg_from_usage(
22-
"--curly-quotes 'Convert straight quotes to curly quotes, except \
23-
for those that occur in code blocks and code spans'",
24-
)
2521
.arg_from_usage(
2622
"[dir] 'A directory for your book{n}(Defaults to \
2723
Current Directory when omitted)'",
@@ -31,15 +27,10 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
3127
// Watch command implementation
3228
pub fn execute(args: &ArgMatches) -> Result<()> {
3329
let book_dir = get_book_dir(args);
34-
let book = MDBook::new(&book_dir).read_config()?;
30+
let mut book = MDBook::new(&book_dir).read_config()?;
3531

36-
let mut book = match args.value_of("dest-dir") {
37-
Some(dest_dir) => book.with_destination(dest_dir),
38-
None => book,
39-
};
40-
41-
if args.is_present("curly-quotes") {
42-
book = book.with_curly_quotes(true);
32+
if let Some(dest_dir) = args.value_of("dest-dir") {
33+
book.config.book.build_dir = PathBuf::from(dest_dir);
4334
}
4435

4536
if args.is_present("open") {
@@ -84,18 +75,17 @@ where
8475
};
8576

8677
// Add the theme directory to the watcher
87-
watcher.watch(book.get_theme_path(), Recursive)
78+
watcher.watch(book.theme_dir(), Recursive)
8879
.unwrap_or_default();
8980

90-
9181
// Add the book.{json,toml} file to the watcher if it exists, because it's not
9282
// located in the source directory
93-
if watcher.watch(book.get_root().join("book.json"), NonRecursive)
83+
if watcher.watch(book.root.join("book.json"), NonRecursive)
9484
.is_err()
9585
{
9686
// do nothing if book.json is not found
9787
}
98-
if watcher.watch(book.get_root().join("book.toml"), NonRecursive)
88+
if watcher.watch(book.root.join("book.toml"), NonRecursive)
9989
.is_err()
10090
{
10191
// do nothing if book.toml is not found

0 commit comments

Comments
 (0)