1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! # mdBook
//!
//! **mdBook** is a tool for rendering a collection of markdown documents into
//! a form more suitable for end users like HTML or EPUB.  It offers a command
//! line interface, but this crate can be used if more control is required.
//!
//! This is the API doc, the [user guide] is also available if you want
//! information about the command line tool, format, structure etc. It is also
//! rendered with mdBook to showcase the features and default theme.
//!
//! Some reasons why you would want to use the crate (over the cli):
//!
//! - Integrate mdbook in a current project
//! - Extend the capabilities of mdBook
//! - Do some processing or test before building your book
//! - Accessing the public API to help create a new Renderer
//! - ...
//!
//! > **Note:** While we try to ensure `mdbook`'s command-line interface and
//! > behaviour are backwards compatible, the tool's internals are still
//! > evolving and being iterated on. If you wish to prevent accidental
//! > breakages it is recommended to pin any tools building on top of the
//! > `mdbook` crate to a specific release.
//!
//! # Examples
//!
//! If creating a new book from scratch, you'll want to get a `BookBuilder` via
//! the `MDBook::init()` method.
//!
//! ```rust,no_run
//! use mdbook::MDBook;
//! use mdbook::config::Config;
//!
//! let root_dir = "/path/to/book/root";
//!
//! // create a default config and change a couple things
//! let mut cfg = Config::default();
//! cfg.book.title = Some("My Book".to_string());
//! cfg.book.authors.push("Michael-F-Bryan".to_string());
//!
//! MDBook::init(root_dir)
//!     .create_gitignore(true)
//!     .with_config(cfg)
//!     .build()
//!     .expect("Book generation failed");
//! ```
//!
//! You can also load an existing book and build it.
//!
//! ```rust,no_run
//! use mdbook::MDBook;
//!
//! let root_dir = "/path/to/book/root";
//!
//! let mut md = MDBook::load(root_dir)
//!     .expect("Unable to load the book");
//! md.build().expect("Building failed");
//! ```
//!
//! ## Implementing a new Backend
//!
//! `mdbook` has a fairly flexible mechanism for creating additional backends
//! for your book. The general idea is you'll add an extra table in the book's
//! `book.toml` which specifies an executable to be invoked by `mdbook`. This
//! executable will then be called during a build, with an in-memory
//! representation ([`RenderContext`]) of the book being passed to the
//! subprocess via `stdin`.
//!
//! The [`RenderContext`] gives the backend access to the contents of
//! `book.toml` and lets it know which directory all generated artefacts should
//! be placed in. For a much more in-depth explanation, consult the [relevant
//! chapter] in the *For Developers* section of the user guide.
//!
//! To make creating a backend easier, the `mdbook` crate can be imported
//! directly, making deserializing the `RenderContext` easy and giving you
//! access to the various methods for working with the [`Config`].
//!
//! [user guide]: https://rust-lang.github.io/mdBook/
//! [`RenderContext`]: renderer/struct.RenderContext.html
//! [relevant chapter]: https://rust-lang.github.io/mdBook/for_developers/backends.html
//! [`Config`]: config/struct.Config.html

#![deny(missing_docs)]
#![deny(rust_2018_idioms)]

#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;

#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;

pub mod book;
pub mod config;
pub mod preprocess;
pub mod renderer;
pub mod theme;
pub mod utils;

/// The current version of `mdbook`.
///
/// This is provided as a way for custom preprocessors and renderers to do
/// compatibility checks.
pub const MDBOOK_VERSION: &str = env!("CARGO_PKG_VERSION");

pub use crate::book::BookItem;
pub use crate::book::MDBook;
pub use crate::config::Config;
pub use crate::renderer::Renderer;

/// The error types used through out this crate.
pub mod errors {
    use std::path::PathBuf;

    error_chain! {
        foreign_links {
            Io(std::io::Error) #[doc = "A wrapper around `std::io::Error`"];
            HandlebarsRender(handlebars::RenderError) #[doc = "Handlebars rendering failed"];
            HandlebarsTemplate(Box<handlebars::TemplateError>) #[doc = "Unable to parse the template"];
            Utf8(std::string::FromUtf8Error) #[doc = "Invalid UTF-8"];
            SerdeJson(serde_json::Error) #[doc = "JSON conversion failed"];
        }

        errors {
            /// A subprocess exited with an unsuccessful return code.
            Subprocess(message: String, output: std::process::Output) {
                description("A subprocess failed")
                display("{}: {}", message, String::from_utf8_lossy(&output.stdout))
            }

            /// An error was encountered while parsing the `SUMMARY.md` file.
            ParseError(line: usize, col: usize, message: String) {
                description("A SUMMARY.md parsing error")
                display("Error at line {}, column {}: {}", line, col, message)
            }

            /// The user tried to use a reserved filename.
            ReservedFilenameError(filename: PathBuf) {
                description("Reserved Filename")
                display("{} is reserved for internal use", filename.display())
            }

            /// Error with a TOML file.
            TomlQueryError(inner: toml_query::error::Error) {
                description("toml_query error")
                display("{}", inner)
            }
        }
    }

    // Box to halve the size of Error
    impl From<handlebars::TemplateError> for Error {
        fn from(e: handlebars::TemplateError) -> Error {
            From::from(Box::new(e))
        }
    }
}