Convert project to use RelativePath where appropriate.
This commit is contained in:
parent
3ba71c570c
commit
dcb7946110
|
@ -39,6 +39,7 @@ tempdir = "0.3.4"
|
||||||
itertools = "0.7"
|
itertools = "0.7"
|
||||||
shlex = "0.1"
|
shlex = "0.1"
|
||||||
toml-query = "0.6"
|
toml-query = "0.6"
|
||||||
|
relative-path = { version = "0.3", features = ["serde"] }
|
||||||
|
|
||||||
# Watch feature
|
# Watch feature
|
||||||
notify = { version = "4.0", optional = true }
|
notify = { version = "4.0", optional = true }
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
@ -7,6 +7,7 @@ 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 config::BuildConfig;
|
||||||
use errors::*;
|
use errors::*;
|
||||||
|
use relative_path::RelativePathBuf;
|
||||||
|
|
||||||
/// 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, cfg: &BuildConfig) -> Result<Book> {
|
pub fn load_book<P: AsRef<Path>>(src_dir: P, cfg: &BuildConfig) -> Result<Book> {
|
||||||
|
@ -39,7 +40,8 @@ fn create_missing(src_dir: &Path, summary: &Summary) -> Result<()> {
|
||||||
let next = items.pop().expect("already checked");
|
let next = items.pop().expect("already checked");
|
||||||
|
|
||||||
if let SummaryItem::Link(ref link) = *next {
|
if let SummaryItem::Link(ref link) = *next {
|
||||||
let filename = src_dir.join(&link.location);
|
let filename = link.location.to_path(src_dir);
|
||||||
|
|
||||||
if !filename.exists() {
|
if !filename.exists() {
|
||||||
if let Some(parent) = filename.parent() {
|
if let Some(parent) = filename.parent() {
|
||||||
if !parent.exists() {
|
if !parent.exists() {
|
||||||
|
@ -150,13 +152,15 @@ pub struct Chapter {
|
||||||
pub number: Option<SectionNumber>,
|
pub number: Option<SectionNumber>,
|
||||||
/// Nested items.
|
/// Nested items.
|
||||||
pub sub_items: Vec<BookItem>,
|
pub sub_items: Vec<BookItem>,
|
||||||
/// The chapter's location, relative to the `SUMMARY.md` file.
|
/// The chapter's relative location.
|
||||||
pub path: PathBuf,
|
pub path: RelativePathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chapter {
|
impl Chapter {
|
||||||
/// Create a new chapter with the provided content.
|
/// Create a new chapter with the provided content.
|
||||||
pub fn new<P: Into<PathBuf>>(name: &str, content: String, path: P) -> Chapter {
|
pub fn new<P: Into<RelativePathBuf>>(
|
||||||
|
name: &str, content: String, path: P,
|
||||||
|
) -> Chapter {
|
||||||
Chapter {
|
Chapter {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
content: content,
|
content: content,
|
||||||
|
@ -201,24 +205,16 @@ fn load_chapter<P: AsRef<Path>>(link: &Link, src_dir: P) -> Result<Chapter> {
|
||||||
debug!("Loading {} ({})", link.name, link.location.display());
|
debug!("Loading {} ({})", link.name, link.location.display());
|
||||||
let src_dir = src_dir.as_ref();
|
let src_dir = src_dir.as_ref();
|
||||||
|
|
||||||
let location = if link.location.is_absolute() {
|
let location = &link.location;
|
||||||
link.location.clone()
|
|
||||||
} else {
|
|
||||||
src_dir.join(&link.location)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut f = File::open(&location)
|
let mut f = File::open(location.to_path(src_dir))
|
||||||
.chain_err(|| 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)
|
||||||
.chain_err(|| format!("Unable to read \"{}\" ({})", link.name, location.display()))?;
|
.chain_err(|| format!("Unable to read \"{}\" ({})", link.name, location.display()))?;
|
||||||
|
|
||||||
let stripped = location
|
let mut ch = Chapter::new(&link.name, content, &link.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();
|
ch.number = link.number.clone();
|
||||||
|
|
||||||
let sub_items = link.nested_items
|
let sub_items = link.nested_items
|
||||||
|
@ -289,8 +285,9 @@ And here is some \
|
||||||
fn dummy_link() -> (Link, TempDir) {
|
fn dummy_link() -> (Link, TempDir) {
|
||||||
let temp = TempDir::new("book").unwrap();
|
let temp = TempDir::new("book").unwrap();
|
||||||
|
|
||||||
let chapter_path = temp.path().join("chapter_1.md");
|
let chapter_path = RelativePathBuf::from("chapter_1.md");
|
||||||
File::create(&chapter_path)
|
|
||||||
|
File::create(&chapter_path.to_path(temp.path()))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write(DUMMY_SRC.as_bytes())
|
.write(DUMMY_SRC.as_bytes())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -304,9 +301,9 @@ And here is some \
|
||||||
fn nested_links() -> (Link, TempDir) {
|
fn nested_links() -> (Link, TempDir) {
|
||||||
let (mut root, temp_dir) = dummy_link();
|
let (mut root, temp_dir) = dummy_link();
|
||||||
|
|
||||||
let second_path = temp_dir.path().join("second.md");
|
let second_path = RelativePathBuf::from("second.md");
|
||||||
|
|
||||||
File::create(&second_path)
|
File::create(&second_path.to_path(&temp_dir))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all("Hello World!".as_bytes())
|
.write_all("Hello World!".as_bytes())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -332,7 +329,7 @@ And here is some \
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cant_load_a_nonexistent_chapter() {
|
fn cant_load_a_nonexistent_chapter() {
|
||||||
let link = Link::new("Chapter 1", "/foo/bar/baz.md");
|
let link = Link::new("Chapter 1", "foo/bar/baz.md");
|
||||||
|
|
||||||
let got = load_chapter(&link, "");
|
let got = load_chapter(&link, "");
|
||||||
assert!(got.is_err());
|
assert!(got.is_err());
|
||||||
|
@ -346,14 +343,14 @@ And here is some \
|
||||||
name: String::from("Nested Chapter 1"),
|
name: String::from("Nested Chapter 1"),
|
||||||
content: String::from("Hello World!"),
|
content: String::from("Hello World!"),
|
||||||
number: Some(SectionNumber(vec![1, 2])),
|
number: Some(SectionNumber(vec![1, 2])),
|
||||||
path: PathBuf::from("second.md"),
|
path: RelativePathBuf::from("second.md"),
|
||||||
sub_items: Vec::new(),
|
sub_items: Vec::new(),
|
||||||
};
|
};
|
||||||
let should_be = BookItem::Chapter(Chapter {
|
let should_be = BookItem::Chapter(Chapter {
|
||||||
name: String::from("Chapter 1"),
|
name: String::from("Chapter 1"),
|
||||||
content: String::from(DUMMY_SRC),
|
content: String::from(DUMMY_SRC),
|
||||||
number: None,
|
number: None,
|
||||||
path: PathBuf::from("chapter_1.md"),
|
path: RelativePathBuf::from("chapter_1.md"),
|
||||||
sub_items: vec![
|
sub_items: vec![
|
||||||
BookItem::Chapter(nested.clone()),
|
BookItem::Chapter(nested.clone()),
|
||||||
BookItem::Separator,
|
BookItem::Separator,
|
||||||
|
@ -377,7 +374,7 @@ And here is some \
|
||||||
BookItem::Chapter(Chapter {
|
BookItem::Chapter(Chapter {
|
||||||
name: String::from("Chapter 1"),
|
name: String::from("Chapter 1"),
|
||||||
content: String::from(DUMMY_SRC),
|
content: String::from(DUMMY_SRC),
|
||||||
path: PathBuf::from("chapter_1.md"),
|
path: RelativePathBuf::from("chapter_1.md"),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -416,7 +413,7 @@ And here is some \
|
||||||
name: String::from("Chapter 1"),
|
name: String::from("Chapter 1"),
|
||||||
content: String::from(DUMMY_SRC),
|
content: String::from(DUMMY_SRC),
|
||||||
number: None,
|
number: None,
|
||||||
path: PathBuf::from("Chapter_1/index.md"),
|
path: RelativePathBuf::from("Chapter_1/index.md"),
|
||||||
sub_items: vec![
|
sub_items: vec![
|
||||||
BookItem::Chapter(Chapter::new(
|
BookItem::Chapter(Chapter::new(
|
||||||
"Hello World",
|
"Hello World",
|
||||||
|
@ -463,7 +460,7 @@ And here is some \
|
||||||
name: String::from("Chapter 1"),
|
name: String::from("Chapter 1"),
|
||||||
content: String::from(DUMMY_SRC),
|
content: String::from(DUMMY_SRC),
|
||||||
number: None,
|
number: None,
|
||||||
path: PathBuf::from("Chapter_1/index.md"),
|
path: RelativePathBuf::from("Chapter_1/index.md"),
|
||||||
sub_items: vec![
|
sub_items: vec![
|
||||||
BookItem::Chapter(Chapter::new(
|
BookItem::Chapter(Chapter::new(
|
||||||
"Hello World",
|
"Hello World",
|
||||||
|
@ -497,7 +494,7 @@ And here is some \
|
||||||
numbered_chapters: vec![
|
numbered_chapters: vec![
|
||||||
SummaryItem::Link(Link {
|
SummaryItem::Link(Link {
|
||||||
name: String::from("Empty"),
|
name: String::from("Empty"),
|
||||||
location: PathBuf::from(""),
|
location: RelativePathBuf::from(""),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -511,14 +508,13 @@ And here is some \
|
||||||
#[test]
|
#[test]
|
||||||
fn cant_load_chapters_when_the_link_is_a_directory() {
|
fn cant_load_chapters_when_the_link_is_a_directory() {
|
||||||
let (_, temp) = dummy_link();
|
let (_, temp) = dummy_link();
|
||||||
let dir = temp.path().join("nested");
|
fs::create_dir(temp.path().join("nested")).unwrap();
|
||||||
fs::create_dir(&dir).unwrap();
|
|
||||||
|
|
||||||
let summary = Summary {
|
let summary = Summary {
|
||||||
numbered_chapters: vec![
|
numbered_chapters: vec![
|
||||||
SummaryItem::Link(Link {
|
SummaryItem::Link(Link {
|
||||||
name: String::from("nested"),
|
name: String::from("nested"),
|
||||||
location: dir,
|
location: RelativePathBuf::from("nested"),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
|
@ -221,13 +221,13 @@ impl MDBook {
|
||||||
|
|
||||||
for item in self.iter() {
|
for item in self.iter() {
|
||||||
if let BookItem::Chapter(ref ch) = *item {
|
if let BookItem::Chapter(ref ch) = *item {
|
||||||
if !ch.path.as_os_str().is_empty() {
|
if !ch.path.as_str().is_empty() {
|
||||||
let path = self.source_dir().join(&ch.path);
|
let path = ch.path.to_path(self.source_dir());
|
||||||
let content = utils::fs::file_to_string(&path)?;
|
let content = utils::fs::file_to_string(&path)?;
|
||||||
info!("Testing file: {:?}", path);
|
info!("Testing file: {:?}", path);
|
||||||
|
|
||||||
// write preprocessed file to tempdir
|
// write preprocessed file to tempdir
|
||||||
let path = temp_dir.path().join(&ch.path);
|
let path = ch.path.to_path(temp_dir.path());
|
||||||
let mut tmpf = utils::fs::create_file(&path)?;
|
let mut tmpf = utils::fs::create_file(&path)?;
|
||||||
tmpf.write_all(content.as_bytes())?;
|
tmpf.write_all(content.as_bytes())?;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use memchr::{self, Memchr};
|
use memchr::{self, Memchr};
|
||||||
use pulldown_cmark::{self, Alignment, Event, Tag};
|
use pulldown_cmark::{self, Alignment, Event, Tag};
|
||||||
|
use relative_path::{RelativePath, RelativePathBuf};
|
||||||
use errors::*;
|
use errors::*;
|
||||||
|
|
||||||
/// Parse the text from a `SUMMARY.md` file into a sort of "recipe" to be
|
/// Parse the text from a `SUMMARY.md` file into a sort of "recipe" to be
|
||||||
|
@ -71,7 +71,7 @@ pub struct Link {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// The location of the chapter's source file, taking the book's `src`
|
/// The location of the chapter's source file, taking the book's `src`
|
||||||
/// directory as the root.
|
/// directory as the root.
|
||||||
pub location: PathBuf,
|
pub location: RelativePathBuf,
|
||||||
/// The section number, if this chapter is in the numbered section.
|
/// The section number, if this chapter is in the numbered section.
|
||||||
pub number: Option<SectionNumber>,
|
pub number: Option<SectionNumber>,
|
||||||
/// Any nested items this chapter may contain.
|
/// Any nested items this chapter may contain.
|
||||||
|
@ -80,10 +80,10 @@ pub struct Link {
|
||||||
|
|
||||||
impl Link {
|
impl Link {
|
||||||
/// Create a new link with no nested items.
|
/// Create a new link with no nested items.
|
||||||
pub fn new<S: Into<String>, P: AsRef<Path>>(name: S, location: P) -> Link {
|
pub fn new<S: Into<String>, P: AsRef<RelativePath>>(name: S, location: P) -> Link {
|
||||||
Link {
|
Link {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
location: location.as_ref().to_path_buf(),
|
location: location.as_ref().to_relative_path_buf(),
|
||||||
number: None,
|
number: None,
|
||||||
nested_items: Vec::new(),
|
nested_items: Vec::new(),
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ impl Default for Link {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Link {
|
Link {
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
location: PathBuf::new(),
|
location: RelativePathBuf::new(),
|
||||||
number: None,
|
number: None,
|
||||||
nested_items: Vec::new(),
|
nested_items: Vec::new(),
|
||||||
}
|
}
|
||||||
|
@ -277,7 +277,7 @@ impl<'a> SummaryParser<'a> {
|
||||||
} else {
|
} else {
|
||||||
Ok(Link {
|
Ok(Link {
|
||||||
name: name,
|
name: name,
|
||||||
location: PathBuf::from(href.to_string()),
|
location: RelativePathBuf::from(href.to_string()),
|
||||||
number: None,
|
number: None,
|
||||||
nested_items: Vec::new(),
|
nested_items: Vec::new(),
|
||||||
})
|
})
|
||||||
|
@ -617,12 +617,12 @@ mod tests {
|
||||||
let should_be = vec![
|
let should_be = vec![
|
||||||
SummaryItem::Link(Link {
|
SummaryItem::Link(Link {
|
||||||
name: String::from("First"),
|
name: String::from("First"),
|
||||||
location: PathBuf::from("./first.md"),
|
location: RelativePathBuf::from("./first.md"),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
SummaryItem::Link(Link {
|
SummaryItem::Link(Link {
|
||||||
name: String::from("Second"),
|
name: String::from("Second"),
|
||||||
location: PathBuf::from("./second.md"),
|
location: RelativePathBuf::from("./second.md"),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
@ -661,7 +661,7 @@ mod tests {
|
||||||
let src = "[First](./first.md)";
|
let src = "[First](./first.md)";
|
||||||
let should_be = Link {
|
let should_be = Link {
|
||||||
name: String::from("First"),
|
name: String::from("First"),
|
||||||
location: PathBuf::from("./first.md"),
|
location: RelativePathBuf::from("./first.md"),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -682,7 +682,7 @@ mod tests {
|
||||||
let src = "- [First](./first.md)\n";
|
let src = "- [First](./first.md)\n";
|
||||||
let link = Link {
|
let link = Link {
|
||||||
name: String::from("First"),
|
name: String::from("First"),
|
||||||
location: PathBuf::from("./first.md"),
|
location: RelativePathBuf::from("./first.md"),
|
||||||
number: Some(SectionNumber(vec![1])),
|
number: Some(SectionNumber(vec![1])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
@ -703,12 +703,12 @@ mod tests {
|
||||||
let should_be = vec![
|
let should_be = vec![
|
||||||
SummaryItem::Link(Link {
|
SummaryItem::Link(Link {
|
||||||
name: String::from("First"),
|
name: String::from("First"),
|
||||||
location: PathBuf::from("./first.md"),
|
location: RelativePathBuf::from("./first.md"),
|
||||||
number: Some(SectionNumber(vec![1])),
|
number: Some(SectionNumber(vec![1])),
|
||||||
nested_items: vec![
|
nested_items: vec![
|
||||||
SummaryItem::Link(Link {
|
SummaryItem::Link(Link {
|
||||||
name: String::from("Nested"),
|
name: String::from("Nested"),
|
||||||
location: PathBuf::from("./nested.md"),
|
location: RelativePathBuf::from("./nested.md"),
|
||||||
number: Some(SectionNumber(vec![1, 1])),
|
number: Some(SectionNumber(vec![1, 1])),
|
||||||
nested_items: Vec::new(),
|
nested_items: Vec::new(),
|
||||||
}),
|
}),
|
||||||
|
@ -716,7 +716,7 @@ mod tests {
|
||||||
}),
|
}),
|
||||||
SummaryItem::Link(Link {
|
SummaryItem::Link(Link {
|
||||||
name: String::from("Second"),
|
name: String::from("Second"),
|
||||||
location: PathBuf::from("./second.md"),
|
location: RelativePathBuf::from("./second.md"),
|
||||||
number: Some(SectionNumber(vec![2])),
|
number: Some(SectionNumber(vec![2])),
|
||||||
nested_items: Vec::new(),
|
nested_items: Vec::new(),
|
||||||
}),
|
}),
|
||||||
|
@ -740,13 +740,13 @@ mod tests {
|
||||||
let should_be = vec![
|
let should_be = vec![
|
||||||
SummaryItem::Link(Link {
|
SummaryItem::Link(Link {
|
||||||
name: String::from("First"),
|
name: String::from("First"),
|
||||||
location: PathBuf::from("./first.md"),
|
location: RelativePathBuf::from("./first.md"),
|
||||||
number: Some(SectionNumber(vec![1])),
|
number: Some(SectionNumber(vec![1])),
|
||||||
nested_items: Vec::new(),
|
nested_items: Vec::new(),
|
||||||
}),
|
}),
|
||||||
SummaryItem::Link(Link {
|
SummaryItem::Link(Link {
|
||||||
name: String::from("Second"),
|
name: String::from("Second"),
|
||||||
location: PathBuf::from("./second.md"),
|
location: RelativePathBuf::from("./second.md"),
|
||||||
number: Some(SectionNumber(vec![2])),
|
number: Some(SectionNumber(vec![2])),
|
||||||
nested_items: Vec::new(),
|
nested_items: Vec::new(),
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -95,6 +95,7 @@ extern crate shlex;
|
||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
extern crate toml;
|
extern crate toml;
|
||||||
extern crate toml_query;
|
extern crate toml_query;
|
||||||
|
extern crate relative_path;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -114,7 +115,7 @@ pub use config::Config;
|
||||||
|
|
||||||
/// The error types used through out this crate.
|
/// The error types used through out this crate.
|
||||||
pub mod errors {
|
pub mod errors {
|
||||||
use std::path::PathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
|
|
||||||
error_chain!{
|
error_chain!{
|
||||||
foreign_links {
|
foreign_links {
|
||||||
|
@ -142,7 +143,7 @@ pub mod errors {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The user tried to use a reserved filename.
|
/// The user tried to use a reserved filename.
|
||||||
ReservedFilenameError(filename: PathBuf) {
|
ReservedFilenameError(filename: RelativePathBuf) {
|
||||||
description("Reserved Filename")
|
description("Reserved Filename")
|
||||||
display("{} is reserved for internal use", filename.display())
|
display("{} is reserved for internal use", filename.display())
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,10 +31,10 @@ impl Preprocessor for LinkPreprocessor {
|
||||||
|
|
||||||
book.for_each_mut(|section: &mut BookItem| {
|
book.for_each_mut(|section: &mut BookItem| {
|
||||||
if let BookItem::Chapter(ref mut ch) = *section {
|
if let BookItem::Chapter(ref mut ch) = *section {
|
||||||
let base = ch.path
|
let base = ch.path.to_path(&src_dir)
|
||||||
.parent()
|
.parent()
|
||||||
.map(|dir| src_dir.join(dir))
|
.expect("All book items have a parent")
|
||||||
.expect("All book items have a parent");
|
.to_owned();
|
||||||
|
|
||||||
let content = replace_all(&ch.content, base);
|
let content = replace_all(&ch.content, base);
|
||||||
ch.content = content;
|
ch.content = content;
|
||||||
|
|
|
@ -15,6 +15,7 @@ use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use handlebars::Handlebars;
|
use handlebars::Handlebars;
|
||||||
|
use relative_path::RelativePath;
|
||||||
|
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
|
@ -26,13 +27,13 @@ impl HtmlHandlebars {
|
||||||
HtmlHandlebars
|
HtmlHandlebars
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_file<P: AsRef<Path>>(
|
fn write_file<P: AsRef<RelativePath>>(
|
||||||
&self,
|
&self,
|
||||||
build_dir: &Path,
|
build_dir: &Path,
|
||||||
filename: P,
|
path: P,
|
||||||
content: &[u8],
|
content: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let path = build_dir.join(filename);
|
let path = path.as_ref().to_path(build_dir);
|
||||||
|
|
||||||
utils::fs::create_file(&path)?
|
utils::fs::create_file(&path)?
|
||||||
.write_all(content)
|
.write_all(content)
|
||||||
|
@ -50,15 +51,11 @@ impl HtmlHandlebars {
|
||||||
BookItem::Chapter(ref ch) => {
|
BookItem::Chapter(ref ch) => {
|
||||||
let content = ch.content.clone();
|
let content = ch.content.clone();
|
||||||
let content = utils::render_markdown(&content, ctx.html_config.curly_quotes);
|
let content = utils::render_markdown(&content, ctx.html_config.curly_quotes);
|
||||||
|
|
||||||
print_content.push_str(&content);
|
print_content.push_str(&content);
|
||||||
|
|
||||||
// Update the context with data for this file
|
|
||||||
let path = ch.path
|
|
||||||
.to_str()
|
|
||||||
.chain_err(|| "Could not convert path to str")?;
|
|
||||||
|
|
||||||
// "print.html" is used for the print page.
|
// "print.html" is used for the print page.
|
||||||
if ch.path == Path::new("print.md") {
|
if ch.path == RelativePath::new("print.md") {
|
||||||
bail!(ErrorKind::ReservedFilenameError(ch.path.clone()));
|
bail!(ErrorKind::ReservedFilenameError(ch.path.clone()));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,23 +69,35 @@ impl HtmlHandlebars {
|
||||||
title = ch.name.clone() + " - " + book_title;
|
title = ch.name.clone() + " - " + book_title;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.data.insert("path".to_owned(), json!(path));
|
// NB: normalize would translate something like: `a/.././b/c/d` to
|
||||||
|
// `b/c/d`, if we then skip one and translate we get `../..`.
|
||||||
|
let path_to_root = ch.path
|
||||||
|
.normalize().components().skip(1).map(|_| "..").collect::<Vec<_>>()
|
||||||
|
.join("/");
|
||||||
|
|
||||||
|
// TODO: remove trailing slash (and this block), it is only needed to have
|
||||||
|
// string-perfect backwards compatibility, but `<base>` works the same without it.
|
||||||
|
let path_to_root = if !path_to_root.is_empty() {
|
||||||
|
format!("{}/", path_to_root)
|
||||||
|
} else {
|
||||||
|
path_to_root
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.data.insert("path".to_owned(), json!(ch.path));
|
||||||
ctx.data.insert("content".to_owned(), json!(content));
|
ctx.data.insert("content".to_owned(), json!(content));
|
||||||
ctx.data.insert("chapter_title".to_owned(), json!(ch.name));
|
ctx.data.insert("chapter_title".to_owned(), json!(ch.name));
|
||||||
ctx.data.insert("title".to_owned(), json!(title));
|
ctx.data.insert("title".to_owned(), json!(title));
|
||||||
ctx.data.insert("path_to_root".to_owned(),
|
ctx.data.insert("path_to_root".to_owned(), json!(path_to_root));
|
||||||
json!(utils::fs::path_to_root(&ch.path)));
|
|
||||||
|
|
||||||
// Render the handlebars template with the data
|
// Render the handlebars template with the data
|
||||||
debug!("Render template");
|
debug!("Render template");
|
||||||
let rendered = ctx.handlebars.render("index", &ctx.data)?;
|
let rendered = ctx.handlebars.render("index", &ctx.data)?;
|
||||||
|
|
||||||
let filepath = Path::new(&ch.path).with_extension("html");
|
let filepath = ch.path.with_extension("html");
|
||||||
|
|
||||||
let rendered = self.post_process(
|
let rendered = self.post_process(
|
||||||
rendered,
|
rendered,
|
||||||
&normalize_path(filepath.to_str().ok_or_else(|| {
|
filepath.as_str(),
|
||||||
Error::from(format!("Bad file name: {}", filepath.display()))
|
|
||||||
})?),
|
|
||||||
&ctx.html_config.playpen,
|
&ctx.html_config.playpen,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -112,8 +121,9 @@ impl HtmlHandlebars {
|
||||||
|
|
||||||
let mut content = String::new();
|
let mut content = String::new();
|
||||||
|
|
||||||
File::open(destination.join(&ch.path.with_extension("html")))?
|
let path = ch.path.with_extension("html").to_path(destination);
|
||||||
.read_to_string(&mut content)?;
|
|
||||||
|
File::open(&path)?.read_to_string(&mut content)?;
|
||||||
|
|
||||||
// This could cause a problem when someone displays
|
// This could cause a problem when someone displays
|
||||||
// code containing <base href=...>
|
// code containing <base href=...>
|
||||||
|
@ -125,10 +135,7 @@ impl HtmlHandlebars {
|
||||||
|
|
||||||
self.write_file(destination, "index.html", content.as_bytes())?;
|
self.write_file(destination, "index.html", content.as_bytes())?;
|
||||||
|
|
||||||
debug!(
|
debug!("Creating index.html from {} ✓", path.display());
|
||||||
"Creating index.html from {} ✓",
|
|
||||||
destination.join(&ch.path.with_extension("html")).display()
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -226,8 +233,7 @@ impl HtmlHandlebars {
|
||||||
data.insert("is_print".to_owned(), json!(true));
|
data.insert("is_print".to_owned(), json!(true));
|
||||||
data.insert("path".to_owned(), json!("print.md"));
|
data.insert("path".to_owned(), json!("print.md"));
|
||||||
data.insert("content".to_owned(), json!(print_content));
|
data.insert("content".to_owned(), json!(print_content));
|
||||||
data.insert("path_to_root".to_owned(),
|
data.insert("path_to_root".to_owned(), json!(""));
|
||||||
json!(utils::fs::path_to_root(Path::new("print.md"))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_hbs_helpers(&self, handlebars: &mut Handlebars, html_config: &HtmlConfig) {
|
fn register_hbs_helpers(&self, handlebars: &mut Handlebars, html_config: &HtmlConfig) {
|
||||||
|
@ -329,9 +335,7 @@ impl Renderer for HtmlHandlebars {
|
||||||
|
|
||||||
let rendered = handlebars.render("index", &data)?;
|
let rendered = handlebars.render("index", &data)?;
|
||||||
|
|
||||||
let rendered = self.post_process(rendered,
|
let rendered = self.post_process(rendered, "print.html", &html_config.playpen);
|
||||||
"print.html",
|
|
||||||
&html_config.playpen);
|
|
||||||
|
|
||||||
self.write_file(&destination, "print.html", &rendered.into_bytes())?;
|
self.write_file(&destination, "print.html", &rendered.into_bytes())?;
|
||||||
debug!("Creating print.html ✓");
|
debug!("Creating print.html ✓");
|
||||||
|
@ -428,10 +432,7 @@ fn make_data(root: &Path, book: &Book, config: &Config, html_config: &HtmlConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
chapter.insert("name".to_owned(), json!(ch.name));
|
chapter.insert("name".to_owned(), json!(ch.name));
|
||||||
let path = ch.path
|
chapter.insert("path".to_owned(), json!(ch.path));
|
||||||
.to_str()
|
|
||||||
.chain_err(|| "Could not convert path to str")?;
|
|
||||||
chapter.insert("path".to_owned(), json!(path));
|
|
||||||
}
|
}
|
||||||
BookItem::Separator => {
|
BookItem::Separator => {
|
||||||
chapter.insert("spacer".to_owned(), json!("_spacer_"));
|
chapter.insert("spacer".to_owned(), json!("_spacer_"));
|
||||||
|
@ -624,13 +625,6 @@ struct RenderItemContext<'a> {
|
||||||
html_config: HtmlConfig,
|
html_config: HtmlConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn normalize_path(path: &str) -> String {
|
|
||||||
use std::path::is_separator;
|
|
||||||
path.chars()
|
|
||||||
.map(|ch| if is_separator(ch) { '/' } else { ch })
|
|
||||||
.collect::<String>()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn normalize_id(content: &str) -> String {
|
pub fn normalize_id(content: &str) -> String {
|
||||||
content.chars()
|
content.chars()
|
||||||
.filter_map(|ch| if ch.is_alphanumeric() || ch == '_' || ch == '-' {
|
.filter_map(|ch| if ch.is_alphanumeric() || ch == '_' || ch == '-' {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::path::{Component, Path, PathBuf};
|
use std::path::Path;
|
||||||
use errors::*;
|
use errors::*;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
|
@ -16,48 +16,6 @@ pub fn file_to_string<P: AsRef<Path>>(path: P) -> Result<String> {
|
||||||
Ok(content)
|
Ok(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes a path and returns a path containing just enough `../` to point to
|
|
||||||
/// the root of the given path.
|
|
||||||
///
|
|
||||||
/// This is mostly interesting for a relative path to point back to the
|
|
||||||
/// directory from where the path starts.
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate mdbook;
|
|
||||||
/// #
|
|
||||||
/// # use std::path::Path;
|
|
||||||
/// # use mdbook::utils::fs::path_to_root;
|
|
||||||
/// #
|
|
||||||
/// # fn main() {
|
|
||||||
/// let path = Path::new("some/relative/path");
|
|
||||||
/// assert_eq!(path_to_root(path), "../../");
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// **note:** it's not very fool-proof, if you find a situation where
|
|
||||||
/// it doesn't return the correct path.
|
|
||||||
/// Consider [submitting a new issue](https://github.com/rust-lang-nursery/mdBook/issues)
|
|
||||||
/// or a [pull-request](https://github.com/rust-lang-nursery/mdBook/pulls) to improve it.
|
|
||||||
|
|
||||||
pub fn path_to_root<P: Into<PathBuf>>(path: P) -> String {
|
|
||||||
debug!("path_to_root");
|
|
||||||
// Remove filename and add "../" for every directory
|
|
||||||
|
|
||||||
path.into()
|
|
||||||
.parent()
|
|
||||||
.expect("")
|
|
||||||
.components()
|
|
||||||
.fold(String::new(), |mut s, c| {
|
|
||||||
match c {
|
|
||||||
Component::Normal(_) => s.push_str("../"),
|
|
||||||
_ => {
|
|
||||||
debug!("Other path component... {:?}", c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function creates a file and returns it. But before creating the file
|
/// This function creates a file and returns it. But before creating the file
|
||||||
/// 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.
|
||||||
|
|
Loading…
Reference in New Issue