Skip to content

Commit ace0b51

Browse files
Put the create_missing feature back in
1 parent 9950f69 commit ace0b51

File tree

3 files changed

+83
-43
lines changed

3 files changed

+83
-43
lines changed

src/book/book.rs

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,59 @@ use std::fmt::{self, Display, Formatter};
22
use std::path::{Path, PathBuf};
33
use std::collections::VecDeque;
44
use std::fs::File;
5-
use std::io::Read;
5+
use std::io::{Read, Write};
66

77
use super::summary::{parse_summary, Link, SectionNumber, Summary, SummaryItem};
8+
use config::BuildConfig;
89
use errors::*;
910

1011

1112
/// Load a book into memory from its `src/` directory.
12-
pub fn load_book<P: AsRef<Path>>(src_dir: P) -> Result<Book> {
13+
pub fn load_book<P: AsRef<Path>>(src_dir: P, cfg: &BuildConfig) -> Result<Book> {
1314
let src_dir = src_dir.as_ref();
1415
let summary_md = src_dir.join("SUMMARY.md");
1516

1617
let mut summary_content = String::new();
17-
File::open(summary_md).chain_err(|| "Couldn't open SUMMARY.md")?
18-
.read_to_string(&mut summary_content)?;
18+
File::open(summary_md)
19+
.chain_err(|| "Couldn't open SUMMARY.md")?
20+
.read_to_string(&mut summary_content)?;
1921

2022
let summary = parse_summary(&summary_content).chain_err(|| "Summary parsing failed")?;
2123

24+
if cfg.create_missing {
25+
create_missing(&src_dir, &summary).chain_err(|| "Unable to create missing chapters")?;
26+
}
27+
2228
load_book_from_disk(&summary, src_dir)
2329
}
2430

31+
fn create_missing(src_dir: &Path, summary: &Summary) -> Result<()> {
32+
let mut items: Vec<_> = summary
33+
.prefix_chapters
34+
.iter()
35+
.chain(summary.numbered_chapters.iter())
36+
.chain(summary.suffix_chapters.iter())
37+
.collect();
38+
39+
while !items.is_empty() {
40+
let next = items.pop().expect("already checked");
41+
42+
if let SummaryItem::Link(ref link) = *next {
43+
let filename = src_dir.join(&link.location);
44+
if !filename.exists() {
45+
debug!("[*] Creating missing file {}", filename.display());
46+
47+
let mut f = File::create(&filename)?;
48+
writeln!(f, "# {}", link.name)?;
49+
}
50+
51+
items.extend(&link.nested_items);
52+
}
53+
}
54+
55+
Ok(())
56+
}
57+
2558

2659
/// A dumb tree structure representing a book.
2760
///
@@ -124,22 +157,23 @@ fn load_chapter<P: AsRef<Path>>(link: &Link, src_dir: P) -> Result<Chapter> {
124157
src_dir.join(&link.location)
125158
};
126159

127-
let mut f = File::open(&location).chain_err(|| {
128-
format!("Chapter file not found, {}", link.location.display())
129-
})?;
160+
let mut f = File::open(&location)
161+
.chain_err(|| format!("Chapter file not found, {}", link.location.display()))?;
130162

131163
let mut content = String::new();
132164
f.read_to_string(&mut content)?;
133165

134-
let stripped = location.strip_prefix(&src_dir)
135-
.expect("Chapters are always inside a book");
166+
let stripped = location
167+
.strip_prefix(&src_dir)
168+
.expect("Chapters are always inside a book");
136169

137170
let mut ch = Chapter::new(&link.name, content, stripped);
138171
ch.number = link.number.clone();
139172

140-
let sub_items = link.nested_items.iter()
141-
.map(|i| load_summary_item(i, src_dir))
142-
.collect::<Result<Vec<_>>>()?;
173+
let sub_items = link.nested_items
174+
.iter()
175+
.map(|i| load_summary_item(i, src_dir))
176+
.collect::<Result<Vec<_>>>()?;
143177

144178
ch.sub_items = sub_items;
145179

@@ -206,9 +240,10 @@ And here is some \
206240
let temp = TempDir::new("book").unwrap();
207241

208242
let chapter_path = temp.path().join("chapter_1.md");
209-
File::create(&chapter_path).unwrap()
210-
.write(DUMMY_SRC.as_bytes())
211-
.unwrap();
243+
File::create(&chapter_path)
244+
.unwrap()
245+
.write(DUMMY_SRC.as_bytes())
246+
.unwrap();
212247

213248
let link = Link::new("Chapter 1", chapter_path);
214249

@@ -221,9 +256,10 @@ And here is some \
221256

222257
let second_path = temp_dir.path().join("second.md");
223258

224-
File::create(&second_path).unwrap()
225-
.write_all("Hello World!".as_bytes())
226-
.unwrap();
259+
File::create(&second_path)
260+
.unwrap()
261+
.write_all("Hello World!".as_bytes())
262+
.unwrap();
227263

228264

229265
let mut second = Link::new("Nested Chapter 1", &second_path);
@@ -356,11 +392,12 @@ And here is some \
356392
assert_eq!(got.len(), 5);
357393

358394
// checking the chapter names are in the order should be sufficient here...
359-
let chapter_names: Vec<String> = got.into_iter().filter_map(|i| match *i {
360-
BookItem::Chapter(ref ch) => Some(ch.name.clone()),
361-
_ => None,
362-
})
363-
.collect();
395+
let chapter_names: Vec<String> = got.into_iter()
396+
.filter_map(|i| match *i {
397+
BookItem::Chapter(ref ch) => Some(ch.name.clone()),
398+
_ => None,
399+
})
400+
.collect();
364401
let should_be: Vec<_> = vec![
365402
String::from("Chapter 1"),
366403
String::from("Hello World"),

src/book/mod.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod book;
33
mod init;
44

55
pub use self::book::{Book, BookItem, BookItems, Chapter};
6+
pub use self::summary::SectionNumber;
67
pub use self::init::BookBuilder;
78

89
use std::path::{Path, PathBuf};
@@ -39,8 +40,15 @@ impl MDBook {
3940
Config::default()
4041
};
4142

43+
MDBook::load_with_config(book_root, config)
44+
}
45+
46+
/// Load a book from its root directory using a custom config.
47+
pub fn load_with_config<P: Into<PathBuf>>(book_root: P, config: Config) -> Result<MDBook> {
48+
let book_root = book_root.into();
49+
4250
let src_dir = book_root.join(&config.book.src);
43-
let book = book::load_book(&src_dir)?;
51+
let book = book::load_book(&src_dir, &config.build)?;
4452

4553
Ok(MDBook {
4654
root: book_root,

tests/rendered_output.rs

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ extern crate mdbook;
22
#[macro_use]
33
extern crate pretty_assertions;
44
extern crate select;
5-
extern crate tempdir;
65
extern crate walkdir;
76

87
mod dummy_book;
@@ -15,9 +14,9 @@ use std::ffi::OsStr;
1514
use walkdir::{DirEntry, WalkDir, WalkDirIterator};
1615
use select::document::Document;
1716
use select::predicate::{Class, Name, Predicate};
18-
use tempdir::TempDir;
1917
use mdbook::errors::*;
2018
use mdbook::utils::fs::file_to_string;
19+
use mdbook::config::Config;
2120
use mdbook::MDBook;
2221

2322

@@ -254,33 +253,29 @@ fn check_spacers() {
254253
/// Ensure building fails if `create-missing` is false and one of the files does
255254
/// not exist.
256255
#[test]
257-
#[ignore]
258256
fn failure_on_missing_file() {
259-
let (mut md, _temp) = create_missing_setup(false);
257+
let temp = DummyBook::new().build().unwrap();
258+
fs::remove_file(temp.path().join("src").join("intro.md")).unwrap();
259+
260+
let mut cfg = Config::default();
261+
cfg.build.create_missing = false;
260262

261-
// On failure, `build()` does not return a specific error, so assume
262-
// any error is a failure due to a missing file.
263-
assert!(md.build().is_err());
263+
let got = MDBook::load_with_config(temp.path(), cfg);
264+
assert!(got.is_err());
264265
}
265266

266267
/// Ensure a missing file is created if `create-missing` is true.
267268
#[test]
268-
#[ignore]
269269
fn create_missing_file_with_config() {
270-
let (mut md, temp) = create_missing_setup(true);
271-
272-
md.build().unwrap();
273-
assert!(temp.path().join("src").join("intro.md").exists());
274-
}
275-
276-
fn create_missing_setup(create_missing: bool) -> (MDBook, TempDir) {
277270
let temp = DummyBook::new().build().unwrap();
278-
let mut md = MDBook::load(temp.path()).unwrap();
279-
280-
md.config.build.create_missing = create_missing;
281271
fs::remove_file(temp.path().join("src").join("intro.md")).unwrap();
282272

283-
(md, temp)
273+
let mut cfg = Config::default();
274+
cfg.build.create_missing = true;
275+
276+
assert!(!temp.path().join("src").join("intro.md").exists());
277+
let _md = MDBook::load_with_config(temp.path(), cfg).unwrap();
278+
assert!(temp.path().join("src").join("intro.md").exists());
284279
}
285280

286281
/// This makes sure you can include a Rust file with `{{#playpen example.rs}}`.

0 commit comments

Comments
 (0)