mirror of
https://github.com/rust-lang/mdBook
synced 2024-11-15 01:07:13 +00:00
Put the create_missing
feature back in
This commit is contained in:
parent
9950f69c48
commit
ace0b51fb6
3 changed files with 83 additions and 43 deletions
|
@ -2,26 +2,59 @@ use std::fmt::{self, Display, Formatter};
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use super::summary::{parse_summary, Link, SectionNumber, Summary, SummaryItem};
|
||||
use config::BuildConfig;
|
||||
use errors::*;
|
||||
|
||||
|
||||
/// 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 summary_md = src_dir.join("SUMMARY.md");
|
||||
|
||||
let mut summary_content = String::new();
|
||||
File::open(summary_md).chain_err(|| "Couldn't open SUMMARY.md")?
|
||||
.read_to_string(&mut summary_content)?;
|
||||
File::open(summary_md)
|
||||
.chain_err(|| "Couldn't open SUMMARY.md")?
|
||||
.read_to_string(&mut summary_content)?;
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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.
|
||||
///
|
||||
|
@ -124,22 +157,23 @@ fn load_chapter<P: AsRef<Path>>(link: &Link, src_dir: P) -> Result<Chapter> {
|
|||
src_dir.join(&link.location)
|
||||
};
|
||||
|
||||
let mut f = File::open(&location).chain_err(|| {
|
||||
format!("Chapter file not found, {}", link.location.display())
|
||||
})?;
|
||||
let mut f = File::open(&location)
|
||||
.chain_err(|| format!("Chapter file not found, {}", link.location.display()))?;
|
||||
|
||||
let mut content = String::new();
|
||||
f.read_to_string(&mut content)?;
|
||||
|
||||
let stripped = location.strip_prefix(&src_dir)
|
||||
.expect("Chapters are always inside a book");
|
||||
let stripped = location
|
||||
.strip_prefix(&src_dir)
|
||||
.expect("Chapters are always inside a book");
|
||||
|
||||
let mut ch = Chapter::new(&link.name, content, stripped);
|
||||
ch.number = link.number.clone();
|
||||
|
||||
let sub_items = link.nested_items.iter()
|
||||
.map(|i| load_summary_item(i, src_dir))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let sub_items = link.nested_items
|
||||
.iter()
|
||||
.map(|i| load_summary_item(i, src_dir))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
ch.sub_items = sub_items;
|
||||
|
||||
|
@ -206,9 +240,10 @@ And here is some \
|
|||
let temp = TempDir::new("book").unwrap();
|
||||
|
||||
let chapter_path = temp.path().join("chapter_1.md");
|
||||
File::create(&chapter_path).unwrap()
|
||||
.write(DUMMY_SRC.as_bytes())
|
||||
.unwrap();
|
||||
File::create(&chapter_path)
|
||||
.unwrap()
|
||||
.write(DUMMY_SRC.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
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");
|
||||
|
||||
File::create(&second_path).unwrap()
|
||||
.write_all("Hello World!".as_bytes())
|
||||
.unwrap();
|
||||
File::create(&second_path)
|
||||
.unwrap()
|
||||
.write_all("Hello World!".as_bytes())
|
||||
.unwrap();
|
||||
|
||||
|
||||
let mut second = Link::new("Nested Chapter 1", &second_path);
|
||||
|
@ -356,11 +392,12 @@ And here is some \
|
|||
assert_eq!(got.len(), 5);
|
||||
|
||||
// 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 {
|
||||
BookItem::Chapter(ref ch) => Some(ch.name.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
let chapter_names: Vec<String> = got.into_iter()
|
||||
.filter_map(|i| match *i {
|
||||
BookItem::Chapter(ref ch) => Some(ch.name.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
let should_be: Vec<_> = vec![
|
||||
String::from("Chapter 1"),
|
||||
String::from("Hello World"),
|
||||
|
|
|
@ -3,6 +3,7 @@ mod book;
|
|||
mod init;
|
||||
|
||||
pub use self::book::{Book, BookItem, BookItems, Chapter};
|
||||
pub use self::summary::SectionNumber;
|
||||
pub use self::init::BookBuilder;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -39,8 +40,15 @@ impl MDBook {
|
|||
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 book = book::load_book(&src_dir)?;
|
||||
let book = book::load_book(&src_dir, &config.build)?;
|
||||
|
||||
Ok(MDBook {
|
||||
root: book_root,
|
||||
|
|
|
@ -2,7 +2,6 @@ extern crate mdbook;
|
|||
#[macro_use]
|
||||
extern crate pretty_assertions;
|
||||
extern crate select;
|
||||
extern crate tempdir;
|
||||
extern crate walkdir;
|
||||
|
||||
mod dummy_book;
|
||||
|
@ -15,9 +14,9 @@ use std::ffi::OsStr;
|
|||
use walkdir::{DirEntry, WalkDir, WalkDirIterator};
|
||||
use select::document::Document;
|
||||
use select::predicate::{Class, Name, Predicate};
|
||||
use tempdir::TempDir;
|
||||
use mdbook::errors::*;
|
||||
use mdbook::utils::fs::file_to_string;
|
||||
use mdbook::config::Config;
|
||||
use mdbook::MDBook;
|
||||
|
||||
|
||||
|
@ -254,33 +253,29 @@ fn check_spacers() {
|
|||
/// Ensure building fails if `create-missing` is false and one of the files does
|
||||
/// not exist.
|
||||
#[test]
|
||||
#[ignore]
|
||||
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
|
||||
// any error is a failure due to a missing file.
|
||||
assert!(md.build().is_err());
|
||||
let mut cfg = Config::default();
|
||||
cfg.build.create_missing = false;
|
||||
|
||||
let got = MDBook::load_with_config(temp.path(), cfg);
|
||||
assert!(got.is_err());
|
||||
}
|
||||
|
||||
/// Ensure a missing file is created if `create-missing` is true.
|
||||
#[test]
|
||||
#[ignore]
|
||||
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 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();
|
||||
|
||||
(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}}`.
|
||||
|
|
Loading…
Reference in a new issue