Put the `create_missing` feature back in
This commit is contained in:
parent
9950f69c48
commit
ace0b51fb6
|
@ -2,26 +2,59 @@ use std::fmt::{self, Display, Formatter};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
use super::summary::{parse_summary, Link, SectionNumber, Summary, SummaryItem};
|
use super::summary::{parse_summary, Link, SectionNumber, Summary, SummaryItem};
|
||||||
|
use config::BuildConfig;
|
||||||
use errors::*;
|
use errors::*;
|
||||||
|
|
||||||
|
|
||||||
/// Load a book into memory from its `src/` directory.
|
/// Load a book into memory from its `src/` directory.
|
||||||
pub fn load_book<P: AsRef<Path>>(src_dir: P) -> Result<Book> {
|
pub fn load_book<P: AsRef<Path>>(src_dir: P, cfg: &BuildConfig) -> Result<Book> {
|
||||||
let src_dir = src_dir.as_ref();
|
let src_dir = src_dir.as_ref();
|
||||||
let summary_md = src_dir.join("SUMMARY.md");
|
let summary_md = src_dir.join("SUMMARY.md");
|
||||||
|
|
||||||
let mut summary_content = String::new();
|
let mut summary_content = String::new();
|
||||||
File::open(summary_md).chain_err(|| "Couldn't open SUMMARY.md")?
|
File::open(summary_md)
|
||||||
.read_to_string(&mut summary_content)?;
|
.chain_err(|| "Couldn't open SUMMARY.md")?
|
||||||
|
.read_to_string(&mut summary_content)?;
|
||||||
|
|
||||||
let summary = parse_summary(&summary_content).chain_err(|| "Summary parsing failed")?;
|
let summary = parse_summary(&summary_content).chain_err(|| "Summary parsing failed")?;
|
||||||
|
|
||||||
|
if cfg.create_missing {
|
||||||
|
create_missing(&src_dir, &summary).chain_err(|| "Unable to create missing chapters")?;
|
||||||
|
}
|
||||||
|
|
||||||
load_book_from_disk(&summary, src_dir)
|
load_book_from_disk(&summary, src_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_missing(src_dir: &Path, summary: &Summary) -> Result<()> {
|
||||||
|
let mut items: Vec<_> = summary
|
||||||
|
.prefix_chapters
|
||||||
|
.iter()
|
||||||
|
.chain(summary.numbered_chapters.iter())
|
||||||
|
.chain(summary.suffix_chapters.iter())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
while !items.is_empty() {
|
||||||
|
let next = items.pop().expect("already checked");
|
||||||
|
|
||||||
|
if let SummaryItem::Link(ref link) = *next {
|
||||||
|
let filename = src_dir.join(&link.location);
|
||||||
|
if !filename.exists() {
|
||||||
|
debug!("[*] Creating missing file {}", filename.display());
|
||||||
|
|
||||||
|
let mut f = File::create(&filename)?;
|
||||||
|
writeln!(f, "# {}", link.name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
items.extend(&link.nested_items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// A dumb tree structure representing a book.
|
/// A dumb tree structure representing a book.
|
||||||
///
|
///
|
||||||
|
@ -124,22 +157,23 @@ fn load_chapter<P: AsRef<Path>>(link: &Link, src_dir: P) -> Result<Chapter> {
|
||||||
src_dir.join(&link.location)
|
src_dir.join(&link.location)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut f = File::open(&location).chain_err(|| {
|
let mut f = File::open(&location)
|
||||||
format!("Chapter file not found, {}", link.location.display())
|
.chain_err(|| format!("Chapter file not found, {}", link.location.display()))?;
|
||||||
})?;
|
|
||||||
|
|
||||||
let mut content = String::new();
|
let mut content = String::new();
|
||||||
f.read_to_string(&mut content)?;
|
f.read_to_string(&mut content)?;
|
||||||
|
|
||||||
let stripped = location.strip_prefix(&src_dir)
|
let stripped = location
|
||||||
.expect("Chapters are always inside a book");
|
.strip_prefix(&src_dir)
|
||||||
|
.expect("Chapters are always inside a book");
|
||||||
|
|
||||||
let mut ch = Chapter::new(&link.name, content, stripped);
|
let mut ch = Chapter::new(&link.name, content, stripped);
|
||||||
ch.number = link.number.clone();
|
ch.number = link.number.clone();
|
||||||
|
|
||||||
let sub_items = link.nested_items.iter()
|
let sub_items = link.nested_items
|
||||||
.map(|i| load_summary_item(i, src_dir))
|
.iter()
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.map(|i| load_summary_item(i, src_dir))
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
ch.sub_items = sub_items;
|
ch.sub_items = sub_items;
|
||||||
|
|
||||||
|
@ -206,9 +240,10 @@ And here is some \
|
||||||
let temp = TempDir::new("book").unwrap();
|
let temp = TempDir::new("book").unwrap();
|
||||||
|
|
||||||
let chapter_path = temp.path().join("chapter_1.md");
|
let chapter_path = temp.path().join("chapter_1.md");
|
||||||
File::create(&chapter_path).unwrap()
|
File::create(&chapter_path)
|
||||||
.write(DUMMY_SRC.as_bytes())
|
.unwrap()
|
||||||
.unwrap();
|
.write(DUMMY_SRC.as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let link = Link::new("Chapter 1", chapter_path);
|
let link = Link::new("Chapter 1", chapter_path);
|
||||||
|
|
||||||
|
@ -221,9 +256,10 @@ And here is some \
|
||||||
|
|
||||||
let second_path = temp_dir.path().join("second.md");
|
let second_path = temp_dir.path().join("second.md");
|
||||||
|
|
||||||
File::create(&second_path).unwrap()
|
File::create(&second_path)
|
||||||
.write_all("Hello World!".as_bytes())
|
.unwrap()
|
||||||
.unwrap();
|
.write_all("Hello World!".as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
|
||||||
let mut second = Link::new("Nested Chapter 1", &second_path);
|
let mut second = Link::new("Nested Chapter 1", &second_path);
|
||||||
|
@ -356,11 +392,12 @@ And here is some \
|
||||||
assert_eq!(got.len(), 5);
|
assert_eq!(got.len(), 5);
|
||||||
|
|
||||||
// checking the chapter names are in the order should be sufficient here...
|
// checking the chapter names are in the order should be sufficient here...
|
||||||
let chapter_names: Vec<String> = got.into_iter().filter_map(|i| match *i {
|
let chapter_names: Vec<String> = got.into_iter()
|
||||||
BookItem::Chapter(ref ch) => Some(ch.name.clone()),
|
.filter_map(|i| match *i {
|
||||||
_ => None,
|
BookItem::Chapter(ref ch) => Some(ch.name.clone()),
|
||||||
})
|
_ => None,
|
||||||
.collect();
|
})
|
||||||
|
.collect();
|
||||||
let should_be: Vec<_> = vec![
|
let should_be: Vec<_> = vec![
|
||||||
String::from("Chapter 1"),
|
String::from("Chapter 1"),
|
||||||
String::from("Hello World"),
|
String::from("Hello World"),
|
||||||
|
|
|
@ -3,6 +3,7 @@ mod book;
|
||||||
mod init;
|
mod init;
|
||||||
|
|
||||||
pub use self::book::{Book, BookItem, BookItems, Chapter};
|
pub use self::book::{Book, BookItem, BookItems, Chapter};
|
||||||
|
pub use self::summary::SectionNumber;
|
||||||
pub use self::init::BookBuilder;
|
pub use self::init::BookBuilder;
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
@ -39,8 +40,15 @@ impl MDBook {
|
||||||
Config::default()
|
Config::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MDBook::load_with_config(book_root, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a book from its root directory using a custom config.
|
||||||
|
pub fn load_with_config<P: Into<PathBuf>>(book_root: P, config: Config) -> Result<MDBook> {
|
||||||
|
let book_root = book_root.into();
|
||||||
|
|
||||||
let src_dir = book_root.join(&config.book.src);
|
let src_dir = book_root.join(&config.book.src);
|
||||||
let book = book::load_book(&src_dir)?;
|
let book = book::load_book(&src_dir, &config.build)?;
|
||||||
|
|
||||||
Ok(MDBook {
|
Ok(MDBook {
|
||||||
root: book_root,
|
root: book_root,
|
||||||
|
|
|
@ -2,7 +2,6 @@ extern crate mdbook;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate pretty_assertions;
|
extern crate pretty_assertions;
|
||||||
extern crate select;
|
extern crate select;
|
||||||
extern crate tempdir;
|
|
||||||
extern crate walkdir;
|
extern crate walkdir;
|
||||||
|
|
||||||
mod dummy_book;
|
mod dummy_book;
|
||||||
|
@ -15,9 +14,9 @@ use std::ffi::OsStr;
|
||||||
use walkdir::{DirEntry, WalkDir, WalkDirIterator};
|
use walkdir::{DirEntry, WalkDir, WalkDirIterator};
|
||||||
use select::document::Document;
|
use select::document::Document;
|
||||||
use select::predicate::{Class, Name, Predicate};
|
use select::predicate::{Class, Name, Predicate};
|
||||||
use tempdir::TempDir;
|
|
||||||
use mdbook::errors::*;
|
use mdbook::errors::*;
|
||||||
use mdbook::utils::fs::file_to_string;
|
use mdbook::utils::fs::file_to_string;
|
||||||
|
use mdbook::config::Config;
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
|
|
||||||
|
|
||||||
|
@ -254,33 +253,29 @@ fn check_spacers() {
|
||||||
/// Ensure building fails if `create-missing` is false and one of the files does
|
/// Ensure building fails if `create-missing` is false and one of the files does
|
||||||
/// not exist.
|
/// not exist.
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn failure_on_missing_file() {
|
fn failure_on_missing_file() {
|
||||||
let (mut md, _temp) = create_missing_setup(false);
|
let temp = DummyBook::new().build().unwrap();
|
||||||
|
fs::remove_file(temp.path().join("src").join("intro.md")).unwrap();
|
||||||
|
|
||||||
// On failure, `build()` does not return a specific error, so assume
|
let mut cfg = Config::default();
|
||||||
// any error is a failure due to a missing file.
|
cfg.build.create_missing = false;
|
||||||
assert!(md.build().is_err());
|
|
||||||
|
let got = MDBook::load_with_config(temp.path(), cfg);
|
||||||
|
assert!(got.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure a missing file is created if `create-missing` is true.
|
/// Ensure a missing file is created if `create-missing` is true.
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn create_missing_file_with_config() {
|
fn create_missing_file_with_config() {
|
||||||
let (mut md, temp) = create_missing_setup(true);
|
|
||||||
|
|
||||||
md.build().unwrap();
|
|
||||||
assert!(temp.path().join("src").join("intro.md").exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_missing_setup(create_missing: bool) -> (MDBook, TempDir) {
|
|
||||||
let temp = DummyBook::new().build().unwrap();
|
let temp = DummyBook::new().build().unwrap();
|
||||||
let mut md = MDBook::load(temp.path()).unwrap();
|
|
||||||
|
|
||||||
md.config.build.create_missing = create_missing;
|
|
||||||
fs::remove_file(temp.path().join("src").join("intro.md")).unwrap();
|
fs::remove_file(temp.path().join("src").join("intro.md")).unwrap();
|
||||||
|
|
||||||
(md, temp)
|
let mut cfg = Config::default();
|
||||||
|
cfg.build.create_missing = true;
|
||||||
|
|
||||||
|
assert!(!temp.path().join("src").join("intro.md").exists());
|
||||||
|
let _md = MDBook::load_with_config(temp.path(), cfg).unwrap();
|
||||||
|
assert!(temp.path().join("src").join("intro.md").exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This makes sure you can include a Rust file with `{{#playpen example.rs}}`.
|
/// This makes sure you can include a Rust file with `{{#playpen example.rs}}`.
|
||||||
|
|
Loading…
Reference in New Issue