Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bring back draft chapters #1153

Merged
merged 2 commits into from
May 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions book-example/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [clean](cli/clean.md)
- [Format](format/README.md)
- [SUMMARY.md](format/summary.md)
- [Draft chapter]()
- [Configuration](format/config.md)
- [Theme](format/theme/README.md)
- [index.hbs](format/theme/index-hbs.md)
Expand Down
18 changes: 17 additions & 1 deletion book-example/src/format/summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ are. Without this file, there is no book.
Even though `SUMMARY.md` is a markdown file, the formatting is very strict to
allow for easy parsing. Let's see how you should format your `SUMMARY.md` file.

#### Allowed elements
#### Structure

1. ***Title*** It's common practice to begin with a title, generally <code
class="language-markdown"># Summary</code>. But it is not mandatory, the
Expand Down Expand Up @@ -36,3 +36,19 @@ allow for easy parsing. Let's see how you should format your `SUMMARY.md` file.

All other elements are unsupported and will be ignored at best or result in an
error.

#### Other elements

- ***Separators*** In between chapters you can add a separator. In the HTML renderer
this will result in a line being rendered in the table of contents. A separator is
a line containing exclusively dashes and at least three of them: `---`.
- ***Draft chapters*** Draft chapters are chapters without a file and thus content.
The purpose of a draft chapter is to signal future chapters still to be written.
Or when still laying out the structure of the book to avoid creating the files
while you are still changing the structure of the book a lot.
Draft chapters will be rendered in the HTML renderer as disabled links in the table
of contents, as you can see for the next chapter in the table of contents on the left.
Draft chapters are written like normal chapters but without writing the path to the file
```markdown
- [Draft chapter]()
```
97 changes: 63 additions & 34 deletions src/book/book.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,19 @@ fn create_missing(src_dir: &Path, summary: &Summary) -> Result<()> {
let next = items.pop().expect("already checked");

if let SummaryItem::Link(ref link) = *next {
let filename = src_dir.join(&link.location);
if !filename.exists() {
if let Some(parent) = filename.parent() {
if !parent.exists() {
fs::create_dir_all(parent)?;
if let Some(ref location) = link.location {
let filename = src_dir.join(location);
if !filename.exists() {
if let Some(parent) = filename.parent() {
if !parent.exists() {
fs::create_dir_all(parent)?;
}
}
}
debug!("Creating missing file {}", filename.display());
debug!("Creating missing file {}", filename.display());

let mut f = File::create(&filename)?;
writeln!(f, "# {}", link.name)?;
let mut f = File::create(&filename)?;
writeln!(f, "# {}", link.name)?;
}
}

items.extend(&link.nested_items);
Expand Down Expand Up @@ -152,7 +154,7 @@ pub struct Chapter {
/// Nested items.
pub sub_items: Vec<BookItem>,
/// The chapter's location, relative to the `SUMMARY.md` file.
pub path: PathBuf,
pub path: Option<PathBuf>,
/// An ordered list of the names of each chapter above this one, in the hierarchy.
pub parent_names: Vec<String>,
}
Expand All @@ -168,11 +170,31 @@ impl Chapter {
Chapter {
name: name.to_string(),
content,
path: path.into(),
path: Some(path.into()),
parent_names,
..Default::default()
}
}

/// Create a new draft chapter that is not attached to a source markdown file and has
/// thus no content.
pub fn new_draft(name: &str, parent_names: Vec<String>) -> Self {
Chapter {
name: name.to_string(),
content: String::new(),
path: None,
parent_names,
..Default::default()
}
}

/// Check if the chapter is a draft chapter, meaning it has no path to a source markdown file
pub fn is_draft_chapter(&self) -> bool {
match self.path {
Some(_) => false,
None => true,
}
}
}

/// Use the provided `Summary` to load a `Book` from disk.
Expand Down Expand Up @@ -202,7 +224,7 @@ pub(crate) fn load_book_from_disk<P: AsRef<Path>>(summary: &Summary, src_dir: P)
})
}

fn load_summary_item<P: AsRef<Path>>(
fn load_summary_item<P: AsRef<Path> + Clone>(
item: &SummaryItem,
src_dir: P,
parent_names: Vec<String>,
Expand All @@ -220,28 +242,35 @@ fn load_chapter<P: AsRef<Path>>(
src_dir: P,
parent_names: Vec<String>,
) -> Result<Chapter> {
debug!("Loading {} ({})", link.name, link.location.display());
let src_dir = src_dir.as_ref();

let location = if link.location.is_absolute() {
link.location.clone()
} else {
src_dir.join(&link.location)
};
let mut ch = if let Some(ref link_location) = link.location {
debug!("Loading {} ({})", link.name, link_location.display());

let location = if link_location.is_absolute() {
link_location.clone()
} else {
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)
.chain_err(|| format!("Unable to read \"{}\" ({})", link.name, location.display()))?;
let mut content = String::new();
f.read_to_string(&mut content)
.chain_err(|| format!("Unable to read \"{}\" ({})", link.name, location.display()))?;

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");

Chapter::new(&link.name, content, stripped, parent_names.clone())
} else {
Chapter::new_draft(&link.name, parent_names.clone())
};

let mut sub_item_parents = parent_names.clone();
let mut ch = Chapter::new(&link.name, content, stripped, parent_names);

ch.number = link.number.clone();

sub_item_parents.push(link.name.clone());
Expand Down Expand Up @@ -376,15 +405,15 @@ And here is some \
name: String::from("Nested Chapter 1"),
content: String::from("Hello World!"),
number: Some(SectionNumber(vec![1, 2])),
path: PathBuf::from("second.md"),
path: Some(PathBuf::from("second.md")),
parent_names: vec![String::from("Chapter 1")],
sub_items: Vec::new(),
};
let should_be = BookItem::Chapter(Chapter {
name: String::from("Chapter 1"),
content: String::from(DUMMY_SRC),
number: None,
path: PathBuf::from("chapter_1.md"),
path: Some(PathBuf::from("chapter_1.md")),
parent_names: Vec::new(),
sub_items: vec![
BookItem::Chapter(nested.clone()),
Expand All @@ -408,7 +437,7 @@ And here is some \
sections: vec![BookItem::Chapter(Chapter {
name: String::from("Chapter 1"),
content: String::from(DUMMY_SRC),
path: PathBuf::from("chapter_1.md"),
path: Some(PathBuf::from("chapter_1.md")),
..Default::default()
})],
..Default::default()
Expand Down Expand Up @@ -448,7 +477,7 @@ And here is some \
name: String::from("Chapter 1"),
content: String::from(DUMMY_SRC),
number: None,
path: PathBuf::from("Chapter_1/index.md"),
path: Some(PathBuf::from("Chapter_1/index.md")),
parent_names: Vec::new(),
sub_items: vec![
BookItem::Chapter(Chapter::new(
Expand Down Expand Up @@ -500,7 +529,7 @@ And here is some \
name: String::from("Chapter 1"),
content: String::from(DUMMY_SRC),
number: None,
path: PathBuf::from("Chapter_1/index.md"),
path: Some(PathBuf::from("Chapter_1/index.md")),
parent_names: Vec::new(),
sub_items: vec![
BookItem::Chapter(Chapter::new(
Expand Down Expand Up @@ -537,7 +566,7 @@ And here is some \
let summary = Summary {
numbered_chapters: vec![SummaryItem::Link(Link {
name: String::from("Empty"),
location: PathBuf::from(""),
location: Some(PathBuf::from("")),
..Default::default()
})],
..Default::default()
Expand All @@ -556,7 +585,7 @@ And here is some \
let summary = Summary {
numbered_chapters: vec![SummaryItem::Link(Link {
name: String::from("nested"),
location: dir,
location: Some(dir),
..Default::default()
})],
..Default::default()
Expand Down
57 changes: 30 additions & 27 deletions src/book/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,37 +251,40 @@ impl MDBook {

for item in book.iter() {
if let BookItem::Chapter(ref ch) = *item {
if !ch.path.as_os_str().is_empty() {
let path = self.source_dir().join(&ch.path);
info!("Testing file: {:?}", path);

// write preprocessed file to tempdir
let path = temp_dir.path().join(&ch.path);
let mut tmpf = utils::fs::create_file(&path)?;
tmpf.write_all(ch.content.as_bytes())?;

let mut cmd = Command::new("rustdoc");
cmd.arg(&path).arg("--test").args(&library_args);

if let Some(edition) = self.config.rust.edition {
match edition {
RustEdition::E2015 => {
cmd.args(&["--edition", "2015"]);
}
RustEdition::E2018 => {
cmd.args(&["--edition", "2018"]);
}
let chapter_path = match ch.path {
Some(ref path) if !path.as_os_str().is_empty() => path,
_ => continue,
};

let path = self.source_dir().join(&chapter_path);
info!("Testing file: {:?}", path);

// write preprocessed file to tempdir
let path = temp_dir.path().join(&chapter_path);
let mut tmpf = utils::fs::create_file(&path)?;
tmpf.write_all(ch.content.as_bytes())?;

let mut cmd = Command::new("rustdoc");
cmd.arg(&path).arg("--test").args(&library_args);

if let Some(edition) = self.config.rust.edition {
match edition {
RustEdition::E2015 => {
cmd.args(&["--edition", "2015"]);
}
RustEdition::E2018 => {
cmd.args(&["--edition", "2018"]);
}
}
}

let output = cmd.output()?;
let output = cmd.output()?;

if !output.status.success() {
bail!(ErrorKind::Subprocess(
"Rustdoc returned an error".to_string(),
output
));
}
if !output.status.success() {
bail!(ErrorKind::Subprocess(
"Rustdoc returned an error".to_string(),
output
));
}
}
}
Expand Down
Loading