Merge remote-tracking branch 'upstream/master' into split_commands
This commit is contained in:
commit
fe62d0c407
21
.travis.yml
21
.travis.yml
|
@ -28,6 +28,13 @@ matrix:
|
||||||
- nodejs
|
- nodejs
|
||||||
- os: linux
|
- os: linux
|
||||||
env: TARGET=x86_64-unknown-linux-musl CHANNEL=stable
|
env: TARGET=x86_64-unknown-linux-musl CHANNEL=stable
|
||||||
|
dist: trusty
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages: &musl_packages
|
||||||
|
- musl
|
||||||
|
- musl-dev
|
||||||
|
- musl-tools
|
||||||
# Beta channel
|
# Beta channel
|
||||||
- os: osx
|
- os: osx
|
||||||
env: TARGET=i686-apple-darwin CHANNEL=beta
|
env: TARGET=i686-apple-darwin CHANNEL=beta
|
||||||
|
@ -42,6 +49,13 @@ matrix:
|
||||||
env: TARGET=x86_64-unknown-linux-gnu CHANNEL=beta
|
env: TARGET=x86_64-unknown-linux-gnu CHANNEL=beta
|
||||||
- os: linux
|
- os: linux
|
||||||
env: TARGET=x86_64-unknown-linux-musl CHANNEL=beta
|
env: TARGET=x86_64-unknown-linux-musl CHANNEL=beta
|
||||||
|
dist: trusty
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages: &musl_packages
|
||||||
|
- musl
|
||||||
|
- musl-dev
|
||||||
|
- musl-tools
|
||||||
# Nightly channel
|
# Nightly channel
|
||||||
- os: osx
|
- os: osx
|
||||||
env: TARGET=i686-apple-darwin CHANNEL=nightly
|
env: TARGET=i686-apple-darwin CHANNEL=nightly
|
||||||
|
@ -56,6 +70,13 @@ matrix:
|
||||||
env: TARGET=x86_64-unknown-linux-gnu CHANNEL=nightly
|
env: TARGET=x86_64-unknown-linux-gnu CHANNEL=nightly
|
||||||
- os: linux
|
- os: linux
|
||||||
env: TARGET=x86_64-unknown-linux-musl CHANNEL=nightly
|
env: TARGET=x86_64-unknown-linux-musl CHANNEL=nightly
|
||||||
|
dist: trusty
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages: &musl_packages
|
||||||
|
- musl
|
||||||
|
- musl-dev
|
||||||
|
- musl-tools
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- export PATH="$PATH:$HOME/.cargo/bin"
|
- export PATH="$PATH:$HOME/.cargo/bin"
|
||||||
|
|
|
@ -19,6 +19,7 @@ clap = "2.24"
|
||||||
handlebars = "0.27"
|
handlebars = "0.27"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
|
error-chain = "0.10.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
pulldown-cmark = "0.0.14"
|
pulldown-cmark = "0.0.14"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use clap::{ArgMatches, SubCommand, App};
|
use clap::{ArgMatches, SubCommand, App};
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
|
use mdbook::errors::*;
|
||||||
use {get_book_dir, open};
|
use {get_book_dir, open};
|
||||||
|
|
||||||
// Build command implementation
|
// Build command implementation
|
||||||
pub fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
|
pub fn build(args: &ArgMatches) -> Result<()> {
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
let book = MDBook::new(&book_dir).read_config()?;
|
let book = MDBook::new(&book_dir).read_config()?;
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use clap::{ArgMatches, SubCommand, App};
|
use clap::{ArgMatches, SubCommand, App};
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
|
use mdbook::errors::*;
|
||||||
use get_book_dir;
|
use get_book_dir;
|
||||||
|
|
||||||
// Init command implementation
|
// Init command implementation
|
||||||
pub fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
|
pub fn init(args: &ArgMatches) -> Result<()> {
|
||||||
|
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
let mut book = MDBook::new(&book_dir);
|
let mut book = MDBook::new(&book_dir);
|
||||||
|
|
|
@ -9,7 +9,6 @@ use std::env;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use clap::{App, ArgMatches, AppSettings};
|
use clap::{App, ArgMatches, AppSettings};
|
||||||
|
|
||||||
pub mod build;
|
pub mod build;
|
||||||
|
|
|
@ -4,13 +4,11 @@ extern crate ws;
|
||||||
|
|
||||||
use std;
|
use std;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::error::Error;
|
|
||||||
use self::iron::{Iron, AfterMiddleware, IronResult, IronError, Request, Response, status, Set, Chain};
|
use self::iron::{Iron, AfterMiddleware, IronResult, IronError, Request, Response, status, Set, Chain};
|
||||||
use clap::{ArgMatches, SubCommand, App};
|
use clap::{ArgMatches, SubCommand, App};
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
|
use mdbook::errors::*;
|
||||||
use {get_book_dir, open};
|
use {get_book_dir, open};
|
||||||
|
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
use watch;
|
use watch;
|
||||||
|
|
||||||
|
@ -27,7 +25,7 @@ impl AfterMiddleware for ErrorRecover {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch command implementation
|
// Watch command implementation
|
||||||
pub fn serve(args: &ArgMatches) -> Result<(), Box<Error>> {
|
pub fn serve(args: &ArgMatches) -> Result<()> {
|
||||||
const RELOAD_COMMAND: &'static str = "reload";
|
const RELOAD_COMMAND: &'static str = "reload";
|
||||||
|
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use clap::{ArgMatches, SubCommand, App};
|
use clap::{ArgMatches, SubCommand, App};
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
|
use mdbook::errors::*;
|
||||||
use get_book_dir;
|
use get_book_dir;
|
||||||
|
|
||||||
// test command implementation
|
// test command implementation
|
||||||
pub fn test(args: &ArgMatches) -> Result<(), Box<Error>> {
|
pub fn test(args: &ArgMatches) -> Result<()> {
|
||||||
let book_dir = get_book_dir(args);
|
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()?;
|
||||||
|
|
||||||
|
|
|
@ -3,18 +3,16 @@ extern crate time;
|
||||||
extern crate crossbeam;
|
extern crate crossbeam;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use self::notify::Watcher;
|
use self::notify::Watcher;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
use clap::{ArgMatches, SubCommand, App};
|
use clap::{ArgMatches, SubCommand, App};
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
|
use mdbook::errors::*;
|
||||||
use {get_book_dir, open};
|
use {get_book_dir, open};
|
||||||
|
|
||||||
// Watch command implementation
|
// Watch command implementation
|
||||||
pub fn watch(args: &ArgMatches) -> Result<(), Box<Error>> {
|
pub fn watch(args: &ArgMatches) -> Result<()> {
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
let book = MDBook::new(&book_dir).read_config()?;
|
let book = MDBook::new(&book_dir).read_config()?;
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ use serde::{Serialize, Serializer};
|
||||||
use serde::ser::SerializeStruct;
|
use serde::ser::SerializeStruct;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum BookItem {
|
pub enum BookItem {
|
||||||
Chapter(String, Chapter), // String = section
|
Chapter(String, Chapter), // String = section
|
||||||
|
@ -37,7 +38,7 @@ impl Chapter {
|
||||||
|
|
||||||
|
|
||||||
impl Serialize for Chapter {
|
impl Serialize for Chapter {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
|
||||||
where S: Serializer
|
where S: Serializer
|
||||||
{
|
{
|
||||||
let mut struct_ = serializer.serialize_struct("Chapter", 2)?;
|
let mut struct_ = serializer.serialize_struct("Chapter", 2)?;
|
||||||
|
|
|
@ -4,14 +4,12 @@ pub use self::bookitem::{BookItem, BookItems};
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::error::Error;
|
|
||||||
use std::io;
|
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::io::ErrorKind;
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use {theme, parse, utils};
|
use {theme, parse, utils};
|
||||||
use renderer::{Renderer, HtmlHandlebars};
|
use renderer::{Renderer, HtmlHandlebars};
|
||||||
|
use errors::*;
|
||||||
|
|
||||||
use config::BookConfig;
|
use config::BookConfig;
|
||||||
use config::tomlconfig::TomlConfig;
|
use config::tomlconfig::TomlConfig;
|
||||||
|
@ -129,7 +127,7 @@ impl MDBook {
|
||||||
/// and adds a `SUMMARY.md` and a
|
/// and adds a `SUMMARY.md` and a
|
||||||
/// `chapter_1.md` to the source directory.
|
/// `chapter_1.md` to the source directory.
|
||||||
|
|
||||||
pub fn init(&mut self) -> Result<(), Box<Error>> {
|
pub fn init(&mut self) -> Result<()> {
|
||||||
|
|
||||||
debug!("[fn]: init");
|
debug!("[fn]: init");
|
||||||
|
|
||||||
|
@ -239,7 +237,7 @@ impl MDBook {
|
||||||
/// method of the current renderer.
|
/// method of the current renderer.
|
||||||
///
|
///
|
||||||
/// It is the renderer who generates all the output files.
|
/// It is the renderer who generates all the output files.
|
||||||
pub fn build(&mut self) -> Result<(), Box<Error>> {
|
pub fn build(&mut self) -> Result<()> {
|
||||||
debug!("[fn]: build");
|
debug!("[fn]: build");
|
||||||
|
|
||||||
self.init()?;
|
self.init()?;
|
||||||
|
@ -249,9 +247,7 @@ impl MDBook {
|
||||||
utils::fs::remove_dir_content(htmlconfig.get_destination())?;
|
utils::fs::remove_dir_content(htmlconfig.get_destination())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.renderer.render(&self)?;
|
self.renderer.render(&self)
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -259,7 +255,7 @@ impl MDBook {
|
||||||
self.config.get_root().join(".gitignore")
|
self.config.get_root().join(".gitignore")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_theme(&self) -> Result<(), Box<Error>> {
|
pub fn copy_theme(&self) -> Result<()> {
|
||||||
debug!("[fn]: copy_theme");
|
debug!("[fn]: copy_theme");
|
||||||
|
|
||||||
if let Some(htmlconfig) = self.config.get_html_config() {
|
if let Some(htmlconfig) = self.config.get_html_config() {
|
||||||
|
@ -298,16 +294,14 @@ impl MDBook {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_file<P: AsRef<Path>>(&self, filename: P, content: &[u8]) -> Result<(), Box<Error>> {
|
pub fn write_file<P: AsRef<Path>>(&self, filename: P, content: &[u8]) -> Result<()> {
|
||||||
let path = self.get_destination()
|
let path = self.get_destination()
|
||||||
.ok_or(String::from("HtmlConfig not set, could not find a destination"))?
|
.ok_or(String::from("HtmlConfig not set, could not find a destination"))?
|
||||||
.join(filename);
|
.join(filename);
|
||||||
|
|
||||||
utils::fs::create_file(&path)
|
utils::fs::create_file(&path)?
|
||||||
.and_then(|mut file| file.write_all(content))
|
.write_all(content)
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Could not create {}: {}", path.display(), e)))?;
|
.map_err(|e| e.into())
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the `book.json` file (if it exists) to extract
|
/// Parses the `book.json` file (if it exists) to extract
|
||||||
|
@ -315,7 +309,7 @@ impl MDBook {
|
||||||
/// The `book.json` file should be in the root directory of the book.
|
/// The `book.json` file should be in the root directory of the book.
|
||||||
/// The root directory is the one specified when creating a new `MDBook`
|
/// The root directory is the one specified when creating a new `MDBook`
|
||||||
|
|
||||||
pub fn read_config(mut self) -> Result<Self, Box<Error>> {
|
pub fn read_config(mut self) -> Result<Self> {
|
||||||
|
|
||||||
let toml = self.get_root().join("book.toml");
|
let toml = self.get_root().join("book.toml");
|
||||||
let json = self.get_root().join("book.json");
|
let json = self.get_root().join("book.json");
|
||||||
|
@ -369,9 +363,9 @@ impl MDBook {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test(&mut self) -> Result<(), Box<Error>> {
|
pub fn test(&mut self) -> Result<()> {
|
||||||
// read in the chapters
|
// read in the chapters
|
||||||
self.parse_summary()?;
|
self.parse_summary().chain_err(|| "Couldn't parse summary")?;
|
||||||
for item in self.iter() {
|
for item in self.iter() {
|
||||||
|
|
||||||
if let BookItem::Chapter(_, ref ch) = *item {
|
if let BookItem::Chapter(_, ref ch) = *item {
|
||||||
|
@ -381,15 +375,10 @@ impl MDBook {
|
||||||
|
|
||||||
println!("[*]: Testing file: {:?}", path);
|
println!("[*]: Testing file: {:?}", path);
|
||||||
|
|
||||||
let output_result = Command::new("rustdoc").arg(&path).arg("--test").output();
|
let output = Command::new("rustdoc").arg(&path).arg("--test").output()?;
|
||||||
let output = output_result?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(Box::new(io::Error::new(ErrorKind::Other,
|
bail!(ErrorKind::Subprocess("Rustdoc returned an error".to_string(), output));
|
||||||
format!("{}\n{}",
|
|
||||||
String::from_utf8_lossy(&output.stdout),
|
|
||||||
String::from_utf8_lossy(&output.stderr)))) as
|
|
||||||
Box<Error>);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -498,6 +487,23 @@ impl MDBook {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_mathjax_support(mut self, mathjax_support: bool) -> Self {
|
||||||
|
if let Some(htmlconfig) = self.config.get_mut_html_config() {
|
||||||
|
htmlconfig.set_mathjax_support(mathjax_support);
|
||||||
|
} else {
|
||||||
|
error!("There is no HTML renderer set...");
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mathjax_support(&self) -> bool {
|
||||||
|
if let Some(htmlconfig) = self.config.get_html_config() {
|
||||||
|
return htmlconfig.get_mathjax_support();
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_google_analytics_id(&self) -> Option<String> {
|
pub fn get_google_analytics_id(&self) -> Option<String> {
|
||||||
if let Some(htmlconfig) = self.config.get_html_config() {
|
if let Some(htmlconfig) = self.config.get_html_config() {
|
||||||
return htmlconfig.get_google_analytics_id();
|
return htmlconfig.get_google_analytics_id();
|
||||||
|
@ -539,7 +545,7 @@ impl MDBook {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct book
|
// Construct book
|
||||||
fn parse_summary(&mut self) -> Result<(), Box<Error>> {
|
fn parse_summary(&mut self) -> Result<()> {
|
||||||
// When append becomes stable, use self.content.append() ...
|
// When append becomes stable, use self.content.append() ...
|
||||||
self.content = parse::construct_bookitems(&self.get_source().join("SUMMARY.md"))?;
|
self.content = parse::construct_bookitems(&self.get_source().join("SUMMARY.md"))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -7,6 +7,7 @@ pub struct HtmlConfig {
|
||||||
destination: PathBuf,
|
destination: PathBuf,
|
||||||
theme: PathBuf,
|
theme: PathBuf,
|
||||||
curly_quotes: bool,
|
curly_quotes: bool,
|
||||||
|
mathjax_support: bool,
|
||||||
google_analytics: Option<String>,
|
google_analytics: Option<String>,
|
||||||
additional_css: Vec<PathBuf>,
|
additional_css: Vec<PathBuf>,
|
||||||
additional_js: Vec<PathBuf>,
|
additional_js: Vec<PathBuf>,
|
||||||
|
@ -30,6 +31,7 @@ impl HtmlConfig {
|
||||||
destination: root.clone().join("book"),
|
destination: root.clone().join("book"),
|
||||||
theme: root.join("theme"),
|
theme: root.join("theme"),
|
||||||
curly_quotes: false,
|
curly_quotes: false,
|
||||||
|
mathjax_support: false,
|
||||||
google_analytics: None,
|
google_analytics: None,
|
||||||
additional_css: Vec::new(),
|
additional_css: Vec::new(),
|
||||||
additional_js: Vec::new(),
|
additional_js: Vec::new(),
|
||||||
|
@ -51,6 +53,10 @@ impl HtmlConfig {
|
||||||
self.curly_quotes = curly_quotes;
|
self.curly_quotes = curly_quotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(mathjax_support) = tomlconfig.mathjax_support {
|
||||||
|
self.mathjax_support = mathjax_support;
|
||||||
|
}
|
||||||
|
|
||||||
if tomlconfig.google_analytics.is_some() {
|
if tomlconfig.google_analytics.is_some() {
|
||||||
self.google_analytics = tomlconfig.google_analytics;
|
self.google_analytics = tomlconfig.google_analytics;
|
||||||
}
|
}
|
||||||
|
@ -116,6 +122,14 @@ impl HtmlConfig {
|
||||||
self.curly_quotes = curly_quotes;
|
self.curly_quotes = curly_quotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_mathjax_support(&self) -> bool {
|
||||||
|
self.mathjax_support
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_mathjax_support(&mut self, mathjax_support: bool) {
|
||||||
|
self.mathjax_support = mathjax_support;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_google_analytics_id(&self) -> Option<String> {
|
pub fn get_google_analytics_id(&self) -> Option<String> {
|
||||||
self.google_analytics.clone()
|
self.google_analytics.clone()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use errors::*;
|
||||||
|
|
||||||
/// The JSON configuration is **deprecated** and will be removed in the near future.
|
/// The JSON configuration is **deprecated** and will be removed in the near future.
|
||||||
/// Please migrate to the TOML configuration.
|
/// Please migrate to the TOML configuration.
|
||||||
|
@ -32,9 +33,9 @@ pub struct JsonConfig {
|
||||||
/// assert_eq!(config.dest, Some(PathBuf::from("htmlbook")));
|
/// assert_eq!(config.dest, Some(PathBuf::from("htmlbook")));
|
||||||
/// ```
|
/// ```
|
||||||
impl JsonConfig {
|
impl JsonConfig {
|
||||||
pub fn from_json(input: &str) -> Result<Self, String> {
|
pub fn from_json(input: &str) -> Result<Self> {
|
||||||
let config: JsonConfig = serde_json::from_str(input)
|
let config: JsonConfig = serde_json::from_str(input)
|
||||||
.map_err(|e| format!("Could not parse JSON: {}", e))?;
|
.chain_err(|| format!("Could not parse JSON"))?;
|
||||||
|
|
||||||
return Ok(config);
|
return Ok(config);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
extern crate toml;
|
extern crate toml;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use errors::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct TomlConfig {
|
pub struct TomlConfig {
|
||||||
|
@ -25,6 +26,7 @@ pub struct TomlHtmlConfig {
|
||||||
pub theme: Option<PathBuf>,
|
pub theme: Option<PathBuf>,
|
||||||
pub google_analytics: Option<String>,
|
pub google_analytics: Option<String>,
|
||||||
pub curly_quotes: Option<bool>,
|
pub curly_quotes: Option<bool>,
|
||||||
|
pub mathjax_support: Option<bool>,
|
||||||
pub additional_css: Option<Vec<PathBuf>>,
|
pub additional_css: Option<Vec<PathBuf>>,
|
||||||
pub additional_js: Option<Vec<PathBuf>>,
|
pub additional_js: Option<Vec<PathBuf>>,
|
||||||
}
|
}
|
||||||
|
@ -43,9 +45,9 @@ pub struct TomlHtmlConfig {
|
||||||
/// assert_eq!(config.output.unwrap().html.unwrap().destination, Some(PathBuf::from("htmlbook")));
|
/// assert_eq!(config.output.unwrap().html.unwrap().destination, Some(PathBuf::from("htmlbook")));
|
||||||
/// ```
|
/// ```
|
||||||
impl TomlConfig {
|
impl TomlConfig {
|
||||||
pub fn from_toml(input: &str) -> Result<Self, String> {
|
pub fn from_toml(input: &str) -> Result<Self> {
|
||||||
let config: TomlConfig = toml::from_str(input)
|
let config: TomlConfig = toml::from_str(input)
|
||||||
.map_err(|e| format!("Could not parse TOML: {}", e))?;
|
.chain_err(|| "Could not parse TOML")?;
|
||||||
|
|
||||||
return Ok(config);
|
return Ok(config);
|
||||||
}
|
}
|
||||||
|
|
23
src/lib.rs
23
src/lib.rs
|
@ -69,10 +69,13 @@
|
||||||
//!
|
//!
|
||||||
//! Make sure to take a look at it.
|
//! Make sure to take a look at it.
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate error_chain;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
#[macro_use] extern crate serde_json;
|
#[macro_use]
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
extern crate handlebars;
|
extern crate handlebars;
|
||||||
extern crate pulldown_cmark;
|
extern crate pulldown_cmark;
|
||||||
|
@ -90,3 +93,21 @@ pub mod utils;
|
||||||
pub use book::MDBook;
|
pub use book::MDBook;
|
||||||
pub use book::BookItem;
|
pub use book::BookItem;
|
||||||
pub use renderer::Renderer;
|
pub use renderer::Renderer;
|
||||||
|
|
||||||
|
/// The error types used through out this crate.
|
||||||
|
pub mod errors {
|
||||||
|
error_chain!{
|
||||||
|
foreign_links {
|
||||||
|
Io(::std::io::Error);
|
||||||
|
HandlebarsRender(::handlebars::RenderError);
|
||||||
|
HandlebarsTemplate(::handlebars::TemplateError);
|
||||||
|
Utf8(::std::string::FromUtf8Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
errors {
|
||||||
|
Subprocess(message: String, output: ::std::process::Output) {
|
||||||
|
description("A subprocess failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,12 +4,12 @@ use book::MDBook;
|
||||||
use book::bookitem::{BookItem, Chapter};
|
use book::bookitem::{BookItem, Chapter};
|
||||||
use utils;
|
use utils;
|
||||||
use theme::{self, Theme};
|
use theme::{self, Theme};
|
||||||
|
use errors::*;
|
||||||
use regex::{Regex, Captures};
|
use regex::{Regex, Captures};
|
||||||
|
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::error::Error;
|
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -28,7 +28,7 @@ impl HtmlHandlebars {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_item(&self, item: &BookItem, mut ctx: RenderItemContext, print_content: &mut String)
|
fn render_item(&self, item: &BookItem, mut ctx: RenderItemContext, print_content: &mut String)
|
||||||
-> Result<(), Box<Error>> {
|
-> Result<()> {
|
||||||
// FIXME: This should be made DRY-er and rely less on mutable state
|
// FIXME: This should be made DRY-er and rely less on mutable state
|
||||||
match *item {
|
match *item {
|
||||||
BookItem::Chapter(_, ref ch) |
|
BookItem::Chapter(_, ref ch) |
|
||||||
|
@ -88,7 +88,7 @@ impl HtmlHandlebars {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an index.html from the first element in SUMMARY.md
|
/// Create an index.html from the first element in SUMMARY.md
|
||||||
fn render_index(&self, book: &MDBook, ch: &Chapter, destination: &Path) -> Result<(), Box<Error>> {
|
fn render_index(&self, book: &MDBook, ch: &Chapter, destination: &Path) -> Result<()> {
|
||||||
debug!("[*]: index.html");
|
debug!("[*]: index.html");
|
||||||
|
|
||||||
let mut content = String::new();
|
let mut content = String::new();
|
||||||
|
@ -129,7 +129,7 @@ impl HtmlHandlebars {
|
||||||
rendered
|
rendered
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_static_files(&self, book: &MDBook, theme: &Theme) -> Result<(), Box<Error>> {
|
fn copy_static_files(&self, book: &MDBook, theme: &Theme) -> Result<()> {
|
||||||
book.write_file("book.js", &theme.js)?;
|
book.write_file("book.js", &theme.js)?;
|
||||||
book.write_file("book.css", &theme.css)?;
|
book.write_file("book.css", &theme.css)?;
|
||||||
book.write_file("favicon.png", &theme.favicon)?;
|
book.write_file("favicon.png", &theme.favicon)?;
|
||||||
|
@ -180,7 +180,7 @@ impl HtmlHandlebars {
|
||||||
|
|
||||||
/// Helper function to write a file to the build directory, normalizing
|
/// Helper function to write a file to the build directory, normalizing
|
||||||
/// the path to be relative to the book root.
|
/// the path to be relative to the book root.
|
||||||
fn write_custom_file(&self, custom_file: &Path, book: &MDBook) -> Result<(), Box<Error>> {
|
fn write_custom_file(&self, custom_file: &Path, book: &MDBook) -> Result<()> {
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
let mut f = File::open(custom_file)?;
|
let mut f = File::open(custom_file)?;
|
||||||
f.read_to_end(&mut data)?;
|
f.read_to_end(&mut data)?;
|
||||||
|
@ -216,7 +216,7 @@ impl HtmlHandlebars {
|
||||||
|
|
||||||
/// Copy across any additional CSS and JavaScript files which the book
|
/// Copy across any additional CSS and JavaScript files which the book
|
||||||
/// has been configured to use.
|
/// has been configured to use.
|
||||||
fn copy_additional_css_and_js(&self, book: &MDBook) -> Result<(), Box<Error>> {
|
fn copy_additional_css_and_js(&self, book: &MDBook) -> Result<()> {
|
||||||
let custom_files = book.get_additional_css().iter().chain(
|
let custom_files = book.get_additional_css().iter().chain(
|
||||||
book.get_additional_js()
|
book.get_additional_js()
|
||||||
.iter(),
|
.iter(),
|
||||||
|
@ -232,7 +232,7 @@ impl HtmlHandlebars {
|
||||||
|
|
||||||
|
|
||||||
impl Renderer for HtmlHandlebars {
|
impl Renderer for HtmlHandlebars {
|
||||||
fn render(&self, book: &MDBook) -> Result<(), Box<Error>> {
|
fn render(&self, book: &MDBook) -> Result<()> {
|
||||||
debug!("[fn]: render");
|
debug!("[fn]: render");
|
||||||
let mut handlebars = Handlebars::new();
|
let mut handlebars = Handlebars::new();
|
||||||
|
|
||||||
|
@ -258,9 +258,7 @@ impl Renderer for HtmlHandlebars {
|
||||||
|
|
||||||
debug!("[*]: Check if destination directory exists");
|
debug!("[*]: Check if destination directory exists");
|
||||||
if fs::create_dir_all(&destination).is_err() {
|
if fs::create_dir_all(&destination).is_err() {
|
||||||
return Err(Box::new(
|
bail!("Unexpected error when constructing destination path");
|
||||||
io::Error::new(io::ErrorKind::Other, "Unexpected error when constructing destination path"),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, item) in book.iter().enumerate() {
|
for (i, item) in book.iter().enumerate() {
|
||||||
|
@ -301,7 +299,7 @@ impl Renderer for HtmlHandlebars {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_data(book: &MDBook) -> Result<serde_json::Map<String, serde_json::Value>, Box<Error>> {
|
fn make_data(book: &MDBook) -> Result<serde_json::Map<String, serde_json::Value>> {
|
||||||
debug!("[fn]: make_data");
|
debug!("[fn]: make_data");
|
||||||
|
|
||||||
let mut data = serde_json::Map::new();
|
let mut data = serde_json::Map::new();
|
||||||
|
@ -318,6 +316,10 @@ fn make_data(book: &MDBook) -> Result<serde_json::Map<String, serde_json::Value>
|
||||||
data.insert("google_analytics".to_owned(), json!(ga));
|
data.insert("google_analytics".to_owned(), json!(ga));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if book.get_mathjax_support() {
|
||||||
|
data.insert("mathjax_support".to_owned(), json!(true));
|
||||||
|
}
|
||||||
|
|
||||||
// Add check to see if there is an additional style
|
// Add check to see if there is an additional style
|
||||||
if book.has_additional_css() {
|
if book.has_additional_css() {
|
||||||
let mut css = Vec::new();
|
let mut css = Vec::new();
|
||||||
|
|
|
@ -2,8 +2,8 @@ pub use self::html_handlebars::HtmlHandlebars;
|
||||||
|
|
||||||
mod html_handlebars;
|
mod html_handlebars;
|
||||||
|
|
||||||
use std::error::Error;
|
use errors::*;
|
||||||
|
|
||||||
pub trait Renderer {
|
pub trait Renderer {
|
||||||
fn render(&self, book: &::book::MDBook) -> Result<(), Box<Error>>;
|
fn render(&self, book: &::book::MDBook) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,10 @@
|
||||||
<link rel="stylesheet" href="{{this}}">
|
<link rel="stylesheet" href="{{this}}">
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
|
{{#if mathjax_support}}
|
||||||
<!-- MathJax -->
|
<!-- MathJax -->
|
||||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<!-- Fetch Clipboard.js from CDN but have a local fallback -->
|
<!-- Fetch Clipboard.js from CDN but have a local fallback -->
|
||||||
<script src="https://cdn.jsdelivr.net/clipboard.js/1.6.1/clipboard.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/clipboard.js/1.6.1/clipboard.min.js"></script>
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
use std::path::{Path, PathBuf, Component};
|
use std::path::{Path, PathBuf, Component};
|
||||||
use std::error::Error;
|
use errors::*;
|
||||||
use std::io::{self, Read};
|
use std::io::Read;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
|
|
||||||
/// Takes a path to a file and try to read the file into a String
|
/// Takes a path to a file and try to read the file into a String
|
||||||
|
|
||||||
pub fn file_to_string(path: &Path) -> Result<String, Box<Error>> {
|
pub fn file_to_string(path: &Path) -> Result<String> {
|
||||||
let mut file = match File::open(path) {
|
let mut file = match File::open(path) {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!("[*]: Failed to open {:?}", path);
|
debug!("[*]: Failed to open {:?}", path);
|
||||||
return Err(Box::new(e));
|
bail!(e);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ pub fn file_to_string(path: &Path) -> Result<String, Box<Error>> {
|
||||||
|
|
||||||
if let Err(e) = file.read_to_string(&mut content) {
|
if let Err(e) = file.read_to_string(&mut content) {
|
||||||
debug!("[*]: Failed to read {:?}", path);
|
debug!("[*]: Failed to read {:?}", path);
|
||||||
return Err(Box::new(e));
|
bail!(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(content)
|
Ok(content)
|
||||||
|
@ -72,7 +72,7 @@ pub fn path_to_root<P: Into<PathBuf>>(path: P) -> String {
|
||||||
/// it checks every directory in the path to see if it exists,
|
/// it checks every directory in the path to see if it exists,
|
||||||
/// and if it does not it will be created.
|
/// and if it does not it will be created.
|
||||||
|
|
||||||
pub fn create_file(path: &Path) -> io::Result<File> {
|
pub fn create_file(path: &Path) -> Result<File> {
|
||||||
debug!("[fn]: create_file");
|
debug!("[fn]: create_file");
|
||||||
|
|
||||||
// Construct path
|
// Construct path
|
||||||
|
@ -83,12 +83,12 @@ pub fn create_file(path: &Path) -> io::Result<File> {
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("[*]: Create file: {:?}", path);
|
debug!("[*]: Create file: {:?}", path);
|
||||||
File::create(path)
|
File::create(path).map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes all the content of a directory but not the directory itself
|
/// Removes all the content of a directory but not the directory itself
|
||||||
|
|
||||||
pub fn remove_dir_content(dir: &Path) -> Result<(), Box<Error>> {
|
pub fn remove_dir_content(dir: &Path) -> Result<()> {
|
||||||
for item in fs::read_dir(dir)? {
|
for item in fs::read_dir(dir)? {
|
||||||
if let Ok(item) = item {
|
if let Ok(item) = item {
|
||||||
let item = item.path();
|
let item = item.path();
|
||||||
|
@ -108,7 +108,7 @@ pub fn remove_dir_content(dir: &Path) -> Result<(), Box<Error>> {
|
||||||
/// with the extensions given in the `ext_blacklist` array
|
/// with the extensions given in the `ext_blacklist` array
|
||||||
|
|
||||||
pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blacklist: &[&str])
|
pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blacklist: &[&str])
|
||||||
-> Result<(), Box<Error>> {
|
-> Result<()> {
|
||||||
debug!("[fn] copy_files_except_ext");
|
debug!("[fn] copy_files_except_ext");
|
||||||
// Check that from and to are different
|
// Check that from and to are different
|
||||||
if from == to {
|
if from == to {
|
||||||
|
|
|
@ -15,7 +15,8 @@ fn do_not_overwrite_unspecified_config_values() {
|
||||||
|
|
||||||
let book = MDBook::new(dir.path())
|
let book = MDBook::new(dir.path())
|
||||||
.with_source("bar")
|
.with_source("bar")
|
||||||
.with_destination("baz");
|
.with_destination("baz")
|
||||||
|
.with_mathjax_support(true);
|
||||||
|
|
||||||
assert_eq!(book.get_root(), dir.path());
|
assert_eq!(book.get_root(), dir.path());
|
||||||
assert_eq!(book.get_source(), dir.path().join("bar"));
|
assert_eq!(book.get_source(), dir.path().join("bar"));
|
||||||
|
@ -27,6 +28,7 @@ fn do_not_overwrite_unspecified_config_values() {
|
||||||
assert_eq!(book.get_root(), dir.path());
|
assert_eq!(book.get_root(), dir.path());
|
||||||
assert_eq!(book.get_source(), dir.path().join("bar"));
|
assert_eq!(book.get_source(), dir.path().join("bar"));
|
||||||
assert_eq!(book.get_destination().unwrap(), dir.path().join("baz"));
|
assert_eq!(book.get_destination().unwrap(), dir.path().join("baz"));
|
||||||
|
assert_eq!(book.get_mathjax_support(), true);
|
||||||
|
|
||||||
// Try with a partial config file
|
// Try with a partial config file
|
||||||
let file_path = dir.path().join("book.toml");
|
let file_path = dir.path().join("book.toml");
|
||||||
|
@ -39,5 +41,6 @@ fn do_not_overwrite_unspecified_config_values() {
|
||||||
assert_eq!(book.get_root(), dir.path());
|
assert_eq!(book.get_root(), dir.path());
|
||||||
assert_eq!(book.get_source(), dir.path().join("barbaz"));
|
assert_eq!(book.get_source(), dir.path().join("barbaz"));
|
||||||
assert_eq!(book.get_destination().unwrap(), dir.path().join("baz"));
|
assert_eq!(book.get_destination().unwrap(), dir.path().join("baz"));
|
||||||
|
assert_eq!(book.get_mathjax_support(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,20 @@ fn from_toml_output_html_curly_quotes() {
|
||||||
assert_eq!(htmlconfig.get_curly_quotes(), true);
|
assert_eq!(htmlconfig.get_curly_quotes(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that the `output.html.mathjax-support` key is correctly parsed in the TOML config
|
||||||
|
#[test]
|
||||||
|
fn from_toml_output_html_mathjax_support() {
|
||||||
|
let toml = r#"[output.html]
|
||||||
|
mathjax-support = true"#;
|
||||||
|
|
||||||
|
let parsed = TomlConfig::from_toml(&toml).expect("This should parse");
|
||||||
|
let config = BookConfig::from_tomlconfig("root", parsed);
|
||||||
|
|
||||||
|
let htmlconfig = config.get_html_config().expect("There should be an HtmlConfig");
|
||||||
|
|
||||||
|
assert_eq!(htmlconfig.get_mathjax_support(), true);
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that the `output.html.google-analytics` key is correctly parsed in the TOML config
|
// Tests that the `output.html.google-analytics` key is correctly parsed in the TOML config
|
||||||
#[test]
|
#[test]
|
||||||
fn from_toml_output_html_google_analytics() {
|
fn from_toml_output_html_google_analytics() {
|
||||||
|
|
Loading…
Reference in New Issue