Skip to content

Revamp config code #287

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

Merged
merged 9 commits into from
Jun 5, 2017
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 @@ -18,6 +18,7 @@ exclude = [
clap = "2.24"
handlebars = "0.26"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
pulldown-cmark = "0.0.14"
log = "0.3"
Expand Down
83 changes: 69 additions & 14 deletions book-example/src/format/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,83 @@

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

We encourage using the TOML format, but JSON is also recognized and parsed.
**Note:** JSON configuration files were previously supported but have been deprecated in favor of
the TOML configuration file. If you are still using JSON we strongly encourage you to migrate to
the TOML configuration because JSON support will be removed in the future.

Here is an example of what a ***book.toml*** file might look like:

```toml
title = "Example book"
author = "Name"
author = "John Doe"
description = "The example book covers examples."
dest = "output/my-book"

[output.html]
destination = "my-example-book"
additional-css = ["custom.css"]
```

## Supported configuration options

It is important to note that **any** relative path specified in the in the configuration will
always be taken relative from the root of the book where the configuration file is located.

### General metadata

- **title:** The title of the book
- **author:** The author of the book
- **description:** A description for the book, which is added as meta information in the html `<head>` of each page

**book.toml**
```toml
title = "Example book"
author = "John Doe"
description = "The example book covers examples."
```

Some books may have multiple authors, there is an alternative key called `authors` plural that lets you specify an array
of authors.

**book.toml**
```toml
title = "Example book"
authors = ["John Doe", "Jane Doe"]
description = "The example book covers examples."
```

### Source directory
By default, the source directory is found in the directory named `src` directly under the root folder. But this is configurable
with the `source` key in the configuration file.

**book.toml**
```toml
title = "Example book"
authors = ["John Doe", "Jane Doe"]
description = "The example book covers examples."

source = "my-src" # the source files will be found in `root/my-src` instead of `root/src`
```

#### Supported variables
### HTML renderer options
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]`.
The following configuration options are available:

If relative paths are given, they will be relative to the book's root, i.e. the
parent directory of the source directory.
- **destination:** By default, the HTML book will be rendered in the `root/book` directory, but this option lets you specify another
destination fodler.
- **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.
- **google-analytics:** If you use Google Analytics, this option lets you enable it by simply specifying your ID in the configuration file.
- **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.

- **title:** The title of the book.
- **author:** The author of the book.
- **description:** The description, which is added as meta in the html head of each page.
- **src:** The path to the book's source files (chapters in Markdown, SUMMARY.md, etc.). Defaults to `root/src`.
- **dest:** The path to the directory where you want your book to be rendered. Defaults to `root/book`.
- **theme_path:** The path to a custom theme directory. Defaults to `root/theme`.
- **google_analytics_id:** If included, google analytics will be added to each page and use the provided ID.
**book.toml**
```toml
title = "Example book"
authors = ["John Doe", "Jane Doe"]
description = "The example book covers examples."

[output.html]
destination = "my-book" # the output files will be generated in `root/my-book` instead of `root/book`
theme = "my-theme"
google-analytics = "123456"
additional-css = ["custom.css", "custom2.css"]
```

***note:*** *the supported configurable parameters are scarce at the moment, but more will be added in the future*
46 changes: 30 additions & 16 deletions src/bin/mdbook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
// Skip this if `--force` is present
if !args.is_present("force") {
// Print warning
print!("\nCopying the default theme to {:?}", book.get_src());
print!("\nCopying the default theme to {:?}", book.get_source());
println!("could potentially overwrite files already present in that directory.");
print!("\nAre you sure you want to continue? (y/n) ");

Expand All @@ -148,7 +148,9 @@ fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
}

// Because of `src/book/mdbook.rs#L37-L39`, `dest` will always start with `root`
let is_dest_inside_root = book.get_dest().starts_with(book.get_root());
let is_dest_inside_root = book.get_destination()
.map(|p| p.starts_with(book.get_root()))
.unwrap_or(false);

if !args.is_present("force") && is_dest_inside_root {
println!("\nDo you want a .gitignore to be created? (y/n)");
Expand All @@ -168,10 +170,10 @@ fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
// Build command implementation
fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
let book_dir = get_book_dir(args);
let book = MDBook::new(&book_dir).read_config();
let book = MDBook::new(&book_dir).read_config()?;

let mut book = match args.value_of("dest-dir") {
Some(dest_dir) => book.set_dest(Path::new(dest_dir)),
Some(dest_dir) => book.with_destination(Path::new(dest_dir)),
None => book,
};

Expand All @@ -181,8 +183,10 @@ fn build(args: &ArgMatches) -> Result<(), Box<Error>> {

book.build()?;

if args.is_present("open") {
open(book.get_dest().join("index.html"));
if let Some(d) = book.get_destination() {
if args.is_present("open") {
open(d.join("index.html"));
}
}

Ok(())
Expand All @@ -193,16 +197,18 @@ fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
#[cfg(feature = "watch")]
fn watch(args: &ArgMatches) -> Result<(), Box<Error>> {
let book_dir = get_book_dir(args);
let book = MDBook::new(&book_dir).read_config();
let book = MDBook::new(&book_dir).read_config()?;

let mut book = match args.value_of("dest-dir") {
Some(dest_dir) => book.set_dest(Path::new(dest_dir)),
Some(dest_dir) => book.with_destination(Path::new(dest_dir)),
None => book,
};

if args.is_present("open") {
book.build()?;
open(book.get_dest().join("index.html"));
if let Some(d) = book.get_destination() {
open(d.join("index.html"));
}
}

trigger_on_change(&mut book, |path, book| {
Expand All @@ -223,13 +229,18 @@ fn serve(args: &ArgMatches) -> Result<(), Box<Error>> {
const RELOAD_COMMAND: &'static str = "reload";

let book_dir = get_book_dir(args);
let book = MDBook::new(&book_dir).read_config();
let book = MDBook::new(&book_dir).read_config()?;

let mut book = match args.value_of("dest-dir") {
Some(dest_dir) => book.set_dest(Path::new(dest_dir)),
Some(dest_dir) => book.with_destination(Path::new(dest_dir)),
None => book,
};

if let None = book.get_destination() {
println!("The HTML renderer is not set up, impossible to serve the files.");
std::process::exit(2);
}

let port = args.value_of("port").unwrap_or("3000");
let ws_port = args.value_of("websocket-port").unwrap_or("3001");
let interface = args.value_of("interface").unwrap_or("localhost");
Expand Down Expand Up @@ -260,7 +271,7 @@ fn serve(args: &ArgMatches) -> Result<(), Box<Error>> {

book.build()?;

let staticfile = staticfile::Static::new(book.get_dest());
let staticfile = staticfile::Static::new(book.get_destination().expect("destination is present, checked before"));
let iron = iron::Iron::new(staticfile);
let _iron = iron.http(&*address).unwrap();

Expand Down Expand Up @@ -292,7 +303,7 @@ fn serve(args: &ArgMatches) -> Result<(), Box<Error>> {

fn test(args: &ArgMatches) -> Result<(), Box<Error>> {
let book_dir = get_book_dir(args);
let mut book = MDBook::new(&book_dir).read_config();
let mut book = MDBook::new(&book_dir).read_config()?;

book.test()?;

Expand Down Expand Up @@ -341,13 +352,16 @@ fn trigger_on_change<F>(book: &mut MDBook, closure: F) -> ()
};

// Add the source directory to the watcher
if let Err(e) = watcher.watch(book.get_src(), Recursive) {
println!("Error while watching {:?}:\n {:?}", book.get_src(), e);
if let Err(e) = watcher.watch(book.get_source(), Recursive) {
println!("Error while watching {:?}:\n {:?}", book.get_source(), e);
::std::process::exit(0);
};

// Add the theme directory to the watcher
watcher.watch(book.get_theme_path(), Recursive).unwrap_or_default();
if let Some(t) = book.get_theme_path() {
watcher.watch(t, Recursive).unwrap_or_default();
}


// Add the book.{json,toml} file to the watcher if it exists, because it's not
// located in the source directory
Expand Down
Loading