mirror of
https://github.com/rust-lang/mdBook
synced 2024-12-14 14:52:37 +00:00
Merge pull request #288 from budziq/rustfmt_update
Made changes with rustfmt including `use_try_shorthand`
This commit is contained in:
commit
f038dcb404
14 changed files with 402 additions and 320 deletions
3
build.rs
3
build.rs
|
@ -20,7 +20,8 @@ fn main() {
|
||||||
.arg(format!("{}", theme_dir.to_str().unwrap()))
|
.arg(format!("{}", theme_dir.to_str().unwrap()))
|
||||||
.arg("--use")
|
.arg("--use")
|
||||||
.arg("nib")
|
.arg("nib")
|
||||||
.status().unwrap()
|
.status()
|
||||||
|
.unwrap()
|
||||||
.success() {
|
.success() {
|
||||||
panic!("Stylus encoutered an error");
|
panic!("Stylus encoutered an error");
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ enum_trailing_comma = true
|
||||||
match_block_trailing_comma = true
|
match_block_trailing_comma = true
|
||||||
struct_trailing_comma = "Always"
|
struct_trailing_comma = "Always"
|
||||||
wrap_comments = true
|
wrap_comments = true
|
||||||
|
use_try_shorthand = true
|
||||||
|
|
||||||
report_todo = "Always"
|
report_todo = "Always"
|
||||||
report_fixme = "Always"
|
report_fixme = "Always"
|
||||||
|
|
|
@ -121,7 +121,7 @@ fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
let mut book = MDBook::new(&book_dir);
|
let mut book = MDBook::new(&book_dir);
|
||||||
|
|
||||||
// Call the function that does the initialization
|
// Call the function that does the initialization
|
||||||
try!(book.init());
|
book.init()?;
|
||||||
|
|
||||||
// If flag `--theme` is present, copy theme to src
|
// If flag `--theme` is present, copy theme to src
|
||||||
if args.is_present("theme") {
|
if args.is_present("theme") {
|
||||||
|
@ -142,7 +142,7 @@ fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the function that copies the theme
|
// Call the function that copies the theme
|
||||||
try!(book.copy_theme());
|
book.copy_theme()?;
|
||||||
println!("\nTheme copied.");
|
println!("\nTheme copied.");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -172,14 +172,14 @@ fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
|
|
||||||
let mut book = match args.value_of("dest-dir") {
|
let mut book = match args.value_of("dest-dir") {
|
||||||
Some(dest_dir) => book.set_dest(Path::new(dest_dir)),
|
Some(dest_dir) => book.set_dest(Path::new(dest_dir)),
|
||||||
None => book
|
None => book,
|
||||||
};
|
};
|
||||||
|
|
||||||
if args.is_present("no-create") {
|
if args.is_present("no-create") {
|
||||||
book.create_missing = false;
|
book.create_missing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try!(book.build());
|
book.build()?;
|
||||||
|
|
||||||
if args.is_present("open") {
|
if args.is_present("open") {
|
||||||
open(book.get_dest().join("index.html"));
|
open(book.get_dest().join("index.html"));
|
||||||
|
@ -197,11 +197,11 @@ fn watch(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
|
|
||||||
let mut book = match args.value_of("dest-dir") {
|
let mut book = match args.value_of("dest-dir") {
|
||||||
Some(dest_dir) => book.set_dest(Path::new(dest_dir)),
|
Some(dest_dir) => book.set_dest(Path::new(dest_dir)),
|
||||||
None => book
|
None => book,
|
||||||
};
|
};
|
||||||
|
|
||||||
if args.is_present("open") {
|
if args.is_present("open") {
|
||||||
try!(book.build());
|
book.build()?;
|
||||||
open(book.get_dest().join("index.html"));
|
open(book.get_dest().join("index.html"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +227,7 @@ fn serve(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
|
|
||||||
let mut book = match args.value_of("dest-dir") {
|
let mut book = match args.value_of("dest-dir") {
|
||||||
Some(dest_dir) => book.set_dest(Path::new(dest_dir)),
|
Some(dest_dir) => book.set_dest(Path::new(dest_dir)),
|
||||||
None => book
|
None => book,
|
||||||
};
|
};
|
||||||
|
|
||||||
let port = args.value_of("port").unwrap_or("3000");
|
let port = args.value_of("port").unwrap_or("3000");
|
||||||
|
@ -253,25 +253,22 @@ fn serve(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
socket.close();
|
socket.close();
|
||||||
}}
|
}}
|
||||||
</script>
|
</script>
|
||||||
"#, public_address, ws_port, RELOAD_COMMAND).to_owned());
|
"#,
|
||||||
|
public_address,
|
||||||
|
ws_port,
|
||||||
|
RELOAD_COMMAND));
|
||||||
|
|
||||||
try!(book.build());
|
book.build()?;
|
||||||
|
|
||||||
let staticfile = staticfile::Static::new(book.get_dest());
|
let staticfile = staticfile::Static::new(book.get_dest());
|
||||||
let iron = iron::Iron::new(staticfile);
|
let iron = iron::Iron::new(staticfile);
|
||||||
let _iron = iron.http(&*address).unwrap();
|
let _iron = iron.http(&*address).unwrap();
|
||||||
|
|
||||||
let ws_server = ws::WebSocket::new(|_| {
|
let ws_server = ws::WebSocket::new(|_| |_| Ok(())).unwrap();
|
||||||
|_| {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}).unwrap();
|
|
||||||
|
|
||||||
let broadcaster = ws_server.broadcaster();
|
let broadcaster = ws_server.broadcaster();
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || { ws_server.listen(&*ws_address).unwrap(); });
|
||||||
ws_server.listen(&*ws_address).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
println!("\nServing on {}", address);
|
println!("\nServing on {}", address);
|
||||||
|
|
||||||
|
@ -296,7 +293,7 @@ fn test(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
let book_dir = get_book_dir(args);
|
let book_dir = get_book_dir(args);
|
||||||
let mut book = MDBook::new(&book_dir).read_config();
|
let mut book = MDBook::new(&book_dir).read_config();
|
||||||
|
|
||||||
try!(book.test());
|
book.test()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -339,7 +336,7 @@ fn trigger_on_change<F>(book: &mut MDBook, closure: F) -> ()
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error while trying to watch the files:\n\n\t{:?}", e);
|
println!("Error while trying to watch the files:\n\n\t{:?}", e);
|
||||||
::std::process::exit(0);
|
::std::process::exit(0);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add the source directory to the watcher
|
// Add the source directory to the watcher
|
||||||
|
@ -350,10 +347,14 @@ fn trigger_on_change<F>(book: &mut MDBook, closure: F) -> ()
|
||||||
|
|
||||||
// Add the book.{json,toml} file to the watcher if it exists, because it's not
|
// Add the book.{json,toml} file to the watcher if it exists, because it's not
|
||||||
// located in the source directory
|
// located in the source directory
|
||||||
if watcher.watch(book.get_root().join("book.json"), NonRecursive).is_err() {
|
if watcher
|
||||||
|
.watch(book.get_root().join("book.json"), NonRecursive)
|
||||||
|
.is_err() {
|
||||||
// do nothing if book.json is not found
|
// do nothing if book.json is not found
|
||||||
}
|
}
|
||||||
if watcher.watch(book.get_root().join("book.toml"), NonRecursive).is_err() {
|
if watcher
|
||||||
|
.watch(book.get_root().join("book.toml"), NonRecursive)
|
||||||
|
.is_err() {
|
||||||
// do nothing if book.toml is not found
|
// do nothing if book.toml is not found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,16 +362,18 @@ fn trigger_on_change<F>(book: &mut MDBook, closure: F) -> ()
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match rx.recv() {
|
match rx.recv() {
|
||||||
Ok(event) => match event {
|
Ok(event) => {
|
||||||
NoticeWrite(path) |
|
match event {
|
||||||
NoticeRemove(path) |
|
NoticeWrite(path) |
|
||||||
Create(path) |
|
NoticeRemove(path) |
|
||||||
Write(path) |
|
Create(path) |
|
||||||
Remove(path) |
|
Write(path) |
|
||||||
Rename(_, path) => {
|
Remove(path) |
|
||||||
closure(&path, book);
|
Rename(_, path) => {
|
||||||
|
closure(&path, book);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("An error occured: {:?}", e);
|
println!("An error occured: {:?}", e);
|
||||||
|
|
|
@ -53,7 +53,7 @@ impl BookConfig {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!("[*]: Failed to open {:?}", &path);
|
error!("[*]: Failed to open {:?}", &path);
|
||||||
exit(2);
|
exit(2);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
if f.read_to_string(&mut data).is_err() {
|
if f.read_to_string(&mut data).is_err() {
|
||||||
error!("[*]: Failed to read {:?}", &path);
|
error!("[*]: Failed to read {:?}", &path);
|
||||||
|
@ -85,11 +85,11 @@ impl BookConfig {
|
||||||
|
|
||||||
pub fn parse_from_toml_string(&mut self, data: &str) -> &mut Self {
|
pub fn parse_from_toml_string(&mut self, data: &str) -> &mut Self {
|
||||||
let config = match toml::from_str(data) {
|
let config = match toml::from_str(data) {
|
||||||
Ok(x) => {x},
|
Ok(x) => x,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("[*]: Toml parse errors in book.toml: {:?}", e);
|
error!("[*]: Toml parse errors in book.toml: {:?}", e);
|
||||||
exit(2);
|
exit(2);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
self.parse_from_btreemap(&config);
|
self.parse_from_btreemap(&config);
|
||||||
|
@ -97,7 +97,8 @@ impl BookConfig {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the string to JSON and converts it to BTreeMap<String, toml::Value>.
|
/// Parses the string to JSON and converts it
|
||||||
|
/// to BTreeMap<String, toml::Value>.
|
||||||
pub fn parse_from_json_string(&mut self, data: &str) -> &mut Self {
|
pub fn parse_from_json_string(&mut self, data: &str) -> &mut Self {
|
||||||
|
|
||||||
let c: serde_json::Value = match serde_json::from_str(data) {
|
let c: serde_json::Value = match serde_json::from_str(data) {
|
||||||
|
@ -105,7 +106,7 @@ impl BookConfig {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("[*]: JSON parse errors in book.json: {:?}", e);
|
error!("[*]: JSON parse errors in book.json: {:?}", e);
|
||||||
exit(2);
|
exit(2);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let config = json_object_to_btreemap(c.as_object().unwrap());
|
let config = json_object_to_btreemap(c.as_object().unwrap());
|
||||||
|
@ -205,10 +206,7 @@ pub fn json_object_to_btreemap(json: &serde_json::Map<String, serde_json::Value>
|
||||||
let mut config: BTreeMap<String, toml::Value> = BTreeMap::new();
|
let mut config: BTreeMap<String, toml::Value> = BTreeMap::new();
|
||||||
|
|
||||||
for (key, value) in json.iter() {
|
for (key, value) in json.iter() {
|
||||||
config.insert(
|
config.insert(String::from_str(key).unwrap(), json_value_to_toml_value(value.to_owned()));
|
||||||
String::from_str(key).unwrap(),
|
|
||||||
json_value_to_toml_value(value.to_owned())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config
|
config
|
||||||
|
@ -223,10 +221,10 @@ pub fn json_value_to_toml_value(json: serde_json::Value) -> toml::Value {
|
||||||
serde_json::Value::Number(x) => toml::Value::Float(x.as_f64().unwrap()),
|
serde_json::Value::Number(x) => toml::Value::Float(x.as_f64().unwrap()),
|
||||||
serde_json::Value::String(x) => toml::Value::String(x),
|
serde_json::Value::String(x) => toml::Value::String(x),
|
||||||
serde_json::Value::Array(x) => {
|
serde_json::Value::Array(x) => {
|
||||||
toml::Value::Array(x.iter().map(|v| json_value_to_toml_value(v.to_owned())).collect())
|
toml::Value::Array(x.iter()
|
||||||
},
|
.map(|v| json_value_to_toml_value(v.to_owned()))
|
||||||
serde_json::Value::Object(x) => {
|
.collect())
|
||||||
toml::Value::Table(json_object_to_btreemap(&x))
|
|
||||||
},
|
},
|
||||||
|
serde_json::Value::Object(x) => toml::Value::Table(json_object_to_btreemap(&x)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,12 @@ impl Chapter {
|
||||||
|
|
||||||
|
|
||||||
impl Serialize for Chapter {
|
impl Serialize for Chapter {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
let mut struct_ = try!(serializer.serialize_struct("Chapter", 2));
|
where S: Serializer
|
||||||
try!(struct_.serialize_field("name", &self.name));
|
{
|
||||||
try!(struct_.serialize_field("path", &self.path));
|
let mut struct_ = serializer.serialize_struct("Chapter", 2)?;
|
||||||
|
struct_.serialize_field("name", &self.name)?;
|
||||||
|
struct_.serialize_field("path", &self.path)?;
|
||||||
struct_.end()
|
struct_.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +68,8 @@ impl<'a> Iterator for BookItems<'a> {
|
||||||
let cur = &self.items[self.current_index];
|
let cur = &self.items[self.current_index];
|
||||||
|
|
||||||
match *cur {
|
match *cur {
|
||||||
BookItem::Chapter(_, ref ch) | BookItem::Affix(ref ch) => {
|
BookItem::Chapter(_, ref ch) |
|
||||||
|
BookItem::Affix(ref ch) => {
|
||||||
self.stack.push((self.items, self.current_index));
|
self.stack.push((self.items, self.current_index));
|
||||||
self.items = &ch.sub_items[..];
|
self.items = &ch.sub_items[..];
|
||||||
self.current_index = 0;
|
self.current_index = 0;
|
||||||
|
|
143
src/book/mod.rs
143
src/book/mod.rs
|
@ -54,8 +54,10 @@ impl MDBook {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// In this example, `root_dir` will be the root directory of our book and is specified in function
|
/// In this example, `root_dir` will be the root directory of our book
|
||||||
/// of the current working directory by using a relative path instead of an absolute path.
|
/// and is specified in function of the current working directory
|
||||||
|
/// by using a relative path instead of an
|
||||||
|
/// absolute path.
|
||||||
///
|
///
|
||||||
/// Default directory paths:
|
/// Default directory paths:
|
||||||
///
|
///
|
||||||
|
@ -63,7 +65,8 @@ impl MDBook {
|
||||||
/// - output: `root/book`
|
/// - output: `root/book`
|
||||||
/// - theme: `root/theme`
|
/// - theme: `root/theme`
|
||||||
///
|
///
|
||||||
/// They can both be changed by using [`set_src()`](#method.set_src) and [`set_dest()`](#method.set_dest)
|
/// They can both be changed by using [`set_src()`](#method.set_src) and
|
||||||
|
/// [`set_dest()`](#method.set_dest)
|
||||||
|
|
||||||
pub fn new(root: &Path) -> MDBook {
|
pub fn new(root: &Path) -> MDBook {
|
||||||
|
|
||||||
|
@ -90,7 +93,8 @@ impl MDBook {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a flat depth-first iterator over the elements of the book, it returns an [BookItem enum](bookitem.html):
|
/// Returns a flat depth-first iterator over the elements of the book,
|
||||||
|
/// it returns an [BookItem enum](bookitem.html):
|
||||||
/// `(section: String, bookitem: &BookItem)`
|
/// `(section: String, bookitem: &BookItem)`
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
|
@ -126,7 +130,8 @@ impl MDBook {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `init()` creates some boilerplate files and directories to get you started with your book.
|
/// `init()` creates some boilerplate files and directories
|
||||||
|
/// to get you started with your book.
|
||||||
///
|
///
|
||||||
/// ```text
|
/// ```text
|
||||||
/// book-test/
|
/// book-test/
|
||||||
|
@ -136,7 +141,8 @@ impl MDBook {
|
||||||
/// └── SUMMARY.md
|
/// └── SUMMARY.md
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// It uses the paths given as source and output directories and adds a `SUMMARY.md` and a
|
/// It uses the paths given as source and output directories
|
||||||
|
/// and adds a `SUMMARY.md` and a
|
||||||
/// `chapter_1.md` to the source directory.
|
/// `chapter_1.md` to the source directory.
|
||||||
|
|
||||||
pub fn init(&mut self) -> Result<(), Box<Error>> {
|
pub fn init(&mut self) -> Result<(), Box<Error>> {
|
||||||
|
@ -152,12 +158,12 @@ impl MDBook {
|
||||||
|
|
||||||
if !self.dest.exists() {
|
if !self.dest.exists() {
|
||||||
debug!("[*]: {:?} does not exist, trying to create directory", self.dest);
|
debug!("[*]: {:?} does not exist, trying to create directory", self.dest);
|
||||||
try!(fs::create_dir_all(&self.dest));
|
fs::create_dir_all(&self.dest)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.src.exists() {
|
if !self.src.exists() {
|
||||||
debug!("[*]: {:?} does not exist, trying to create directory", self.src);
|
debug!("[*]: {:?} does not exist, trying to create directory", self.src);
|
||||||
try!(fs::create_dir_all(&self.src));
|
fs::create_dir_all(&self.src)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let summary = self.src.join("SUMMARY.md");
|
let summary = self.src.join("SUMMARY.md");
|
||||||
|
@ -167,18 +173,18 @@ impl MDBook {
|
||||||
// Summary does not exist, create it
|
// Summary does not exist, create it
|
||||||
|
|
||||||
debug!("[*]: {:?} does not exist, trying to create SUMMARY.md", self.src.join("SUMMARY.md"));
|
debug!("[*]: {:?} does not exist, trying to create SUMMARY.md", self.src.join("SUMMARY.md"));
|
||||||
let mut f = try!(File::create(&self.src.join("SUMMARY.md")));
|
let mut f = File::create(&self.src.join("SUMMARY.md"))?;
|
||||||
|
|
||||||
debug!("[*]: Writing to SUMMARY.md");
|
debug!("[*]: Writing to SUMMARY.md");
|
||||||
|
|
||||||
try!(writeln!(f, "# Summary"));
|
writeln!(f, "# Summary")?;
|
||||||
try!(writeln!(f, ""));
|
writeln!(f, "")?;
|
||||||
try!(writeln!(f, "- [Chapter 1](./chapter_1.md)"));
|
writeln!(f, "- [Chapter 1](./chapter_1.md)")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse SUMMARY.md, and create the missing item related file
|
// parse SUMMARY.md, and create the missing item related file
|
||||||
try!(self.parse_summary());
|
self.parse_summary()?;
|
||||||
|
|
||||||
debug!("[*]: constructing paths for missing files");
|
debug!("[*]: constructing paths for missing files");
|
||||||
for item in self.iter() {
|
for item in self.iter() {
|
||||||
|
@ -193,16 +199,15 @@ impl MDBook {
|
||||||
|
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
if !self.create_missing {
|
if !self.create_missing {
|
||||||
return Err(format!(
|
return Err(format!("'{}' referenced from SUMMARY.md does not exist.", path.to_string_lossy())
|
||||||
"'{}' referenced from SUMMARY.md does not exist.",
|
.into());
|
||||||
path.to_string_lossy()).into());
|
|
||||||
}
|
}
|
||||||
debug!("[*]: {:?} does not exist, trying to create file", path);
|
debug!("[*]: {:?} does not exist, trying to create file", path);
|
||||||
try!(::std::fs::create_dir_all(path.parent().unwrap()));
|
::std::fs::create_dir_all(path.parent().unwrap())?;
|
||||||
let mut f = try!(File::create(path));
|
let mut f = File::create(path)?;
|
||||||
|
|
||||||
// debug!("[*]: Writing to {:?}", path);
|
// debug!("[*]: Writing to {:?}", path);
|
||||||
try!(writeln!(f, "# {}", ch.name));
|
writeln!(f, "# {}", ch.name)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,17 +222,19 @@ impl MDBook {
|
||||||
if !gitignore.exists() {
|
if !gitignore.exists() {
|
||||||
// Gitignore does not exist, create it
|
// Gitignore does not exist, create it
|
||||||
|
|
||||||
// Because of `src/book/mdbook.rs#L37-L39`, `dest` will always start with `root`. If it
|
// Because of `src/book/mdbook.rs#L37-L39`,
|
||||||
// is not, `strip_prefix` will return an Error.
|
// `dest` will always start with `root`.
|
||||||
|
// If it is not, `strip_prefix` will return an Error.
|
||||||
if !self.get_dest().starts_with(&self.root) {
|
if !self.get_dest().starts_with(&self.root) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let relative = self.get_dest()
|
let relative = self.get_dest()
|
||||||
.strip_prefix(&self.root)
|
.strip_prefix(&self.root)
|
||||||
.expect("Destination is not relative to root.");
|
.expect("Destination is not relative to root.");
|
||||||
let relative = relative.to_str()
|
let relative = relative
|
||||||
.expect("Path could not be yielded into a string slice.");
|
.to_str()
|
||||||
|
.expect("Path could not be yielded into a string slice.");
|
||||||
|
|
||||||
debug!("[*]: {:?} does not exist, trying to create .gitignore", gitignore);
|
debug!("[*]: {:?} does not exist, trying to create .gitignore", gitignore);
|
||||||
|
|
||||||
|
@ -239,20 +246,21 @@ impl MDBook {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `build()` method is the one where everything happens. First it parses `SUMMARY.md` to
|
/// The `build()` method is the one where everything happens.
|
||||||
/// construct the book's structure in the form of a `Vec<BookItem>` and then calls `render()`
|
/// First it parses `SUMMARY.md` to construct the book's structure
|
||||||
|
/// in the form of a `Vec<BookItem>` and then calls `render()`
|
||||||
/// method of the current renderer.
|
/// method of the current renderer.
|
||||||
///
|
///
|
||||||
/// It is the renderer who generates all the output files.
|
/// It is the renderer who generates all the output files.
|
||||||
pub fn build(&mut self) -> Result<(), Box<Error>> {
|
pub fn build(&mut self) -> Result<(), Box<Error>> {
|
||||||
debug!("[fn]: build");
|
debug!("[fn]: build");
|
||||||
|
|
||||||
try!(self.init());
|
self.init()?;
|
||||||
|
|
||||||
// Clean output directory
|
// Clean output directory
|
||||||
try!(utils::fs::remove_dir_content(&self.dest));
|
utils::fs::remove_dir_content(&self.dest)?;
|
||||||
|
|
||||||
try!(self.renderer.render(&self));
|
self.renderer.render(&self)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -269,55 +277,54 @@ impl MDBook {
|
||||||
|
|
||||||
if !theme_dir.exists() {
|
if !theme_dir.exists() {
|
||||||
debug!("[*]: {:?} does not exist, trying to create directory", theme_dir);
|
debug!("[*]: {:?} does not exist, trying to create directory", theme_dir);
|
||||||
try!(fs::create_dir(&theme_dir));
|
fs::create_dir(&theme_dir)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// index.hbs
|
// index.hbs
|
||||||
let mut index = try!(File::create(&theme_dir.join("index.hbs")));
|
let mut index = File::create(&theme_dir.join("index.hbs"))?;
|
||||||
try!(index.write_all(theme::INDEX));
|
index.write_all(theme::INDEX)?;
|
||||||
|
|
||||||
// book.css
|
// book.css
|
||||||
let mut css = try!(File::create(&theme_dir.join("book.css")));
|
let mut css = File::create(&theme_dir.join("book.css"))?;
|
||||||
try!(css.write_all(theme::CSS));
|
css.write_all(theme::CSS)?;
|
||||||
|
|
||||||
// favicon.png
|
// favicon.png
|
||||||
let mut favicon = try!(File::create(&theme_dir.join("favicon.png")));
|
let mut favicon = File::create(&theme_dir.join("favicon.png"))?;
|
||||||
try!(favicon.write_all(theme::FAVICON));
|
favicon.write_all(theme::FAVICON)?;
|
||||||
|
|
||||||
// book.js
|
// book.js
|
||||||
let mut js = try!(File::create(&theme_dir.join("book.js")));
|
let mut js = File::create(&theme_dir.join("book.js"))?;
|
||||||
try!(js.write_all(theme::JS));
|
js.write_all(theme::JS)?;
|
||||||
|
|
||||||
// highlight.css
|
// highlight.css
|
||||||
let mut highlight_css = try!(File::create(&theme_dir.join("highlight.css")));
|
let mut highlight_css = File::create(&theme_dir.join("highlight.css"))?;
|
||||||
try!(highlight_css.write_all(theme::HIGHLIGHT_CSS));
|
highlight_css.write_all(theme::HIGHLIGHT_CSS)?;
|
||||||
|
|
||||||
// highlight.js
|
// highlight.js
|
||||||
let mut highlight_js = try!(File::create(&theme_dir.join("highlight.js")));
|
let mut highlight_js = File::create(&theme_dir.join("highlight.js"))?;
|
||||||
try!(highlight_js.write_all(theme::HIGHLIGHT_JS));
|
highlight_js.write_all(theme::HIGHLIGHT_JS)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_file<P: AsRef<Path>>(&self, filename: P, content: &[u8]) -> Result<(), Box<Error>> {
|
pub fn write_file<P: AsRef<Path>>(&self, filename: P, content: &[u8]) -> Result<(), Box<Error>> {
|
||||||
let path = self.get_dest().join(filename);
|
let path = self.get_dest().join(filename);
|
||||||
try!(utils::fs::create_file(&path).and_then(|mut file| {
|
utils::fs::create_file(&path)
|
||||||
file.write_all(content)
|
.and_then(|mut file| file.write_all(content))
|
||||||
}).map_err(|e| {
|
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Could not create {}: {}", path.display(), e)))?;
|
||||||
io::Error::new(io::ErrorKind::Other, format!("Could not create {}: {}", path.display(), e))
|
|
||||||
}));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the `book.json` file (if it exists) to extract the configuration parameters.
|
/// Parses the `book.json` file (if it exists) to extract
|
||||||
|
/// the configuration parameters.
|
||||||
/// The `book.json` file should be in the root directory of the book.
|
/// The `book.json` file should be in the root directory of the book.
|
||||||
/// The root directory is the one specified when creating a new `MDBook`
|
/// The root directory is the one specified when creating a new `MDBook`
|
||||||
|
|
||||||
pub fn read_config(mut self) -> Self {
|
pub fn read_config(mut self) -> Self {
|
||||||
|
|
||||||
let config = BookConfig::new(&self.root)
|
let config = BookConfig::new(&self.root)
|
||||||
.read_config(&self.root)
|
.read_config(&self.root)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
self.title = config.title;
|
self.title = config.title;
|
||||||
self.description = config.description;
|
self.description = config.description;
|
||||||
|
@ -330,8 +337,10 @@ impl MDBook {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// You can change the default renderer to another one by using this method. The only requirement
|
/// You can change the default renderer to another one
|
||||||
/// is for your renderer to implement the [Renderer trait](../../renderer/renderer/trait.Renderer.html)
|
/// by using this method. The only requirement
|
||||||
|
/// is for your renderer to implement the
|
||||||
|
/// [Renderer trait](../../renderer/renderer/trait.Renderer.html)
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// extern crate mdbook;
|
/// extern crate mdbook;
|
||||||
|
@ -343,12 +352,14 @@ impl MDBook {
|
||||||
/// let mut book = MDBook::new(Path::new("mybook"))
|
/// let mut book = MDBook::new(Path::new("mybook"))
|
||||||
/// .set_renderer(Box::new(HtmlHandlebars::new()));
|
/// .set_renderer(Box::new(HtmlHandlebars::new()));
|
||||||
///
|
///
|
||||||
/// // In this example we replace the default renderer by the default renderer...
|
/// // In this example we replace the default renderer
|
||||||
/// // Don't forget to put your renderer in a Box
|
/// // by the default renderer...
|
||||||
|
/// // Don't forget to put your renderer in a Box
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// **note:** Don't forget to put your renderer in a `Box` before passing it to `set_renderer()`
|
/// **note:** Don't forget to put your renderer in a `Box`
|
||||||
|
/// before passing it to `set_renderer()`
|
||||||
|
|
||||||
pub fn set_renderer(mut self, renderer: Box<Renderer>) -> Self {
|
pub fn set_renderer(mut self, renderer: Box<Renderer>) -> Self {
|
||||||
self.renderer = renderer;
|
self.renderer = renderer;
|
||||||
|
@ -357,7 +368,7 @@ impl MDBook {
|
||||||
|
|
||||||
pub fn test(&mut self) -> Result<(), Box<Error>> {
|
pub fn test(&mut self) -> Result<(), Box<Error>> {
|
||||||
// read in the chapters
|
// read in the chapters
|
||||||
try!(self.parse_summary());
|
self.parse_summary()?;
|
||||||
for item in self.iter() {
|
for item in self.iter() {
|
||||||
|
|
||||||
if let BookItem::Chapter(_, ref ch) = *item {
|
if let BookItem::Chapter(_, ref ch) = *item {
|
||||||
|
@ -367,17 +378,15 @@ impl MDBook {
|
||||||
|
|
||||||
println!("[*]: Testing file: {:?}", path);
|
println!("[*]: Testing file: {:?}", path);
|
||||||
|
|
||||||
let output_result = Command::new("rustdoc")
|
let output_result = Command::new("rustdoc").arg(&path).arg("--test").output();
|
||||||
.arg(&path)
|
let output = output_result?;
|
||||||
.arg("--test")
|
|
||||||
.output();
|
|
||||||
let output = try!(output_result);
|
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(Box::new(io::Error::new(ErrorKind::Other, format!(
|
return Err(Box::new(io::Error::new(ErrorKind::Other,
|
||||||
"{}\n{}",
|
format!("{}\n{}",
|
||||||
String::from_utf8_lossy(&output.stdout),
|
String::from_utf8_lossy(&output.stdout),
|
||||||
String::from_utf8_lossy(&output.stderr)))) as Box<Error>);
|
String::from_utf8_lossy(&output.stderr)))) as
|
||||||
|
Box<Error>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -480,7 +489,7 @@ impl MDBook {
|
||||||
// Construct book
|
// Construct book
|
||||||
fn parse_summary(&mut self) -> Result<(), Box<Error>> {
|
fn parse_summary(&mut self) -> Result<(), Box<Error>> {
|
||||||
// When append becomes stable, use self.content.append() ...
|
// When append becomes stable, use self.content.append() ...
|
||||||
self.content = try!(parse::construct_bookitems(&self.src.join("SUMMARY.md")));
|
self.content = parse::construct_bookitems(&self.src.join("SUMMARY.md"))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,8 @@ extern crate handlebars;
|
||||||
extern crate pulldown_cmark;
|
extern crate pulldown_cmark;
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
|
|
||||||
#[macro_use] extern crate log;
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
pub mod book;
|
pub mod book;
|
||||||
mod parse;
|
mod parse;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
|
|
|
@ -6,10 +6,10 @@ use book::bookitem::{BookItem, Chapter};
|
||||||
pub fn construct_bookitems(path: &PathBuf) -> Result<Vec<BookItem>> {
|
pub fn construct_bookitems(path: &PathBuf) -> Result<Vec<BookItem>> {
|
||||||
debug!("[fn]: construct_bookitems");
|
debug!("[fn]: construct_bookitems");
|
||||||
let mut summary = String::new();
|
let mut summary = String::new();
|
||||||
try!(try!(File::open(path)).read_to_string(&mut summary));
|
File::open(path)?.read_to_string(&mut summary)?;
|
||||||
|
|
||||||
debug!("[*]: Parse SUMMARY.md");
|
debug!("[*]: Parse SUMMARY.md");
|
||||||
let top_items = try!(parse_level(&mut summary.split('\n').collect(), 0, vec![0]));
|
let top_items = parse_level(&mut summary.split('\n').collect(), 0, vec![0])?;
|
||||||
debug!("[*]: Done parsing SUMMARY.md");
|
debug!("[*]: Done parsing SUMMARY.md");
|
||||||
Ok(top_items)
|
Ok(top_items)
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,10 @@ fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32
|
||||||
while !summary.is_empty() {
|
while !summary.is_empty() {
|
||||||
let item: BookItem;
|
let item: BookItem;
|
||||||
// Indentation level of the line to parse
|
// Indentation level of the line to parse
|
||||||
let level = try!(level(summary[0], 4));
|
let level = level(summary[0], 4)?;
|
||||||
|
|
||||||
// if level < current_level we remove the last digit of section, exit the current function,
|
// if level < current_level we remove the last digit of section,
|
||||||
|
// exit the current function,
|
||||||
// and return the parsed level to the calling function.
|
// and return the parsed level to the calling function.
|
||||||
if level < current_level {
|
if level < current_level {
|
||||||
break;
|
break;
|
||||||
|
@ -35,11 +36,13 @@ fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32
|
||||||
// Level can not be root level !!
|
// Level can not be root level !!
|
||||||
// Add a sub-number to section
|
// Add a sub-number to section
|
||||||
section.push(0);
|
section.push(0);
|
||||||
let last = items.pop().expect("There should be at least one item since this can't be the root level");
|
let last = items
|
||||||
|
.pop()
|
||||||
|
.expect("There should be at least one item since this can't be the root level");
|
||||||
|
|
||||||
if let BookItem::Chapter(ref s, ref ch) = last {
|
if let BookItem::Chapter(ref s, ref ch) = last {
|
||||||
let mut ch = ch.clone();
|
let mut ch = ch.clone();
|
||||||
ch.sub_items = try!(parse_level(summary, level, section.clone()));
|
ch.sub_items = parse_level(summary, level, section.clone())?;
|
||||||
items.push(BookItem::Chapter(s.clone(), ch));
|
items.push(BookItem::Chapter(s.clone(), ch));
|
||||||
|
|
||||||
// Remove the last number from the section, because we got back to our level..
|
// Remove the last number from the section, because we got back to our level..
|
||||||
|
@ -62,7 +65,8 @@ fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32
|
||||||
// Eliminate possible errors and set section to -1 after suffix
|
// Eliminate possible errors and set section to -1 after suffix
|
||||||
match parsed_item {
|
match parsed_item {
|
||||||
// error if level != 0 and BookItem is != Chapter
|
// error if level != 0 and BookItem is != Chapter
|
||||||
BookItem::Affix(_) | BookItem::Spacer if level > 0 => {
|
BookItem::Affix(_) |
|
||||||
|
BookItem::Spacer if level > 0 => {
|
||||||
return Err(Error::new(ErrorKind::Other,
|
return Err(Error::new(ErrorKind::Other,
|
||||||
"Your summary.md is messed up\n\n
|
"Your summary.md is messed up\n\n
|
||||||
\
|
\
|
||||||
|
@ -98,7 +102,9 @@ fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32
|
||||||
// Increment section
|
// Increment section
|
||||||
let len = section.len() - 1;
|
let len = section.len() - 1;
|
||||||
section[len] += 1;
|
section[len] += 1;
|
||||||
let s = section.iter().fold("".to_owned(), |s, i| s + &i.to_string() + ".");
|
let s = section
|
||||||
|
.iter()
|
||||||
|
.fold("".to_owned(), |s, i| s + &i.to_string() + ".");
|
||||||
BookItem::Chapter(s, ch)
|
BookItem::Chapter(s, ch)
|
||||||
},
|
},
|
||||||
_ => parsed_item,
|
_ => parsed_item,
|
||||||
|
|
|
@ -37,7 +37,8 @@ impl Renderer for HtmlHandlebars {
|
||||||
|
|
||||||
// Register template
|
// Register template
|
||||||
debug!("[*]: Register handlebars template");
|
debug!("[*]: Register handlebars template");
|
||||||
try!(handlebars.register_template_string("index", try!(String::from_utf8(theme.index))));
|
handlebars
|
||||||
|
.register_template_string("index", String::from_utf8(theme.index)?)?;
|
||||||
|
|
||||||
// Register helpers
|
// Register helpers
|
||||||
debug!("[*]: Register handlebars helpers");
|
debug!("[*]: Register handlebars helpers");
|
||||||
|
@ -45,7 +46,7 @@ impl Renderer for HtmlHandlebars {
|
||||||
handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
|
handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
|
||||||
handlebars.register_helper("next", Box::new(helpers::navigation::next));
|
handlebars.register_helper("next", Box::new(helpers::navigation::next));
|
||||||
|
|
||||||
let mut data = try!(make_data(book));
|
let mut data = make_data(book)?;
|
||||||
|
|
||||||
// Print version
|
// Print version
|
||||||
let mut print_content: String = String::new();
|
let mut print_content: String = String::new();
|
||||||
|
@ -69,11 +70,11 @@ impl Renderer for HtmlHandlebars {
|
||||||
let path = book.get_src().join(&ch.path);
|
let path = book.get_src().join(&ch.path);
|
||||||
|
|
||||||
debug!("[*]: Opening file: {:?}", path);
|
debug!("[*]: Opening file: {:?}", path);
|
||||||
let mut f = try!(File::open(&path));
|
let mut f = File::open(&path)?;
|
||||||
let mut content: String = String::new();
|
let mut content: String = String::new();
|
||||||
|
|
||||||
debug!("[*]: Reading file");
|
debug!("[*]: Reading file");
|
||||||
try!(f.read_to_string(&mut content));
|
f.read_to_string(&mut content)?;
|
||||||
|
|
||||||
// Parse for playpen links
|
// Parse for playpen links
|
||||||
if let Some(p) = path.parent() {
|
if let Some(p) = path.parent() {
|
||||||
|
@ -85,8 +86,10 @@ impl Renderer for HtmlHandlebars {
|
||||||
print_content.push_str(&content);
|
print_content.push_str(&content);
|
||||||
|
|
||||||
// Update the context with data for this file
|
// Update the context with data for this file
|
||||||
let path = ch.path.to_str().ok_or_else(||
|
let path =
|
||||||
io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
|
ch.path
|
||||||
|
.to_str()
|
||||||
|
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
|
||||||
data.insert("path".to_owned(), json!(path));
|
data.insert("path".to_owned(), json!(path));
|
||||||
data.insert("content".to_owned(), json!(content));
|
data.insert("content".to_owned(), json!(content));
|
||||||
data.insert("chapter_title".to_owned(), json!(ch.name));
|
data.insert("chapter_title".to_owned(), json!(ch.name));
|
||||||
|
@ -94,7 +97,7 @@ impl Renderer for HtmlHandlebars {
|
||||||
|
|
||||||
// Render the handlebars template with the data
|
// Render the handlebars template with the data
|
||||||
debug!("[*]: Render template");
|
debug!("[*]: Render template");
|
||||||
let rendered = try!(handlebars.render("index", &data));
|
let rendered = handlebars.render("index", &data)?;
|
||||||
|
|
||||||
let filename = Path::new(&ch.path).with_extension("html");
|
let filename = Path::new(&ch.path).with_extension("html");
|
||||||
|
|
||||||
|
@ -106,24 +109,26 @@ impl Renderer for HtmlHandlebars {
|
||||||
|
|
||||||
// Write to file
|
// Write to file
|
||||||
info!("[*] Creating {:?} ✓", filename.display());
|
info!("[*] Creating {:?} ✓", filename.display());
|
||||||
try!(book.write_file(filename, &rendered.into_bytes()));
|
book.write_file(filename, &rendered.into_bytes())?;
|
||||||
|
|
||||||
// Create an index.html from the first element in SUMMARY.md
|
// Create an index.html from the first element in SUMMARY.md
|
||||||
if index {
|
if index {
|
||||||
debug!("[*]: index.html");
|
debug!("[*]: index.html");
|
||||||
|
|
||||||
let mut content = String::new();
|
let mut content = String::new();
|
||||||
let _source = try!(File::open(book.get_dest().join(&ch.path.with_extension("html"))))
|
let _source = File::open(book.get_dest().join(&ch.path.with_extension("html")))?
|
||||||
.read_to_string(&mut content);
|
.read_to_string(&mut content);
|
||||||
|
|
||||||
// This could cause a problem when someone displays code containing <base href=...>
|
// This could cause a problem when someone displays
|
||||||
|
// code containing <base href=...>
|
||||||
// on the front page, however this case should be very very rare...
|
// on the front page, however this case should be very very rare...
|
||||||
content = content.lines()
|
content = content
|
||||||
|
.lines()
|
||||||
.filter(|line| !line.contains("<base href="))
|
.filter(|line| !line.contains("<base href="))
|
||||||
.collect::<Vec<&str>>()
|
.collect::<Vec<&str>>()
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
try!(book.write_file("index.html", content.as_bytes()));
|
book.write_file("index.html", content.as_bytes())?;
|
||||||
|
|
||||||
info!("[*] Creating index.html from {:?} ✓",
|
info!("[*] Creating index.html from {:?} ✓",
|
||||||
book.get_dest().join(&ch.path.with_extension("html")));
|
book.get_dest().join(&ch.path.with_extension("html")));
|
||||||
|
@ -145,7 +150,7 @@ impl Renderer for HtmlHandlebars {
|
||||||
// Render the handlebars template with the data
|
// Render the handlebars template with the data
|
||||||
debug!("[*]: Render template");
|
debug!("[*]: Render template");
|
||||||
|
|
||||||
let rendered = try!(handlebars.render("index", &data));
|
let rendered = handlebars.render("index", &data)?;
|
||||||
|
|
||||||
// do several kinds of post-processing
|
// do several kinds of post-processing
|
||||||
let rendered = build_header_links(rendered, "print.html");
|
let rendered = build_header_links(rendered, "print.html");
|
||||||
|
@ -153,29 +158,29 @@ impl Renderer for HtmlHandlebars {
|
||||||
let rendered = fix_code_blocks(rendered);
|
let rendered = fix_code_blocks(rendered);
|
||||||
let rendered = add_playpen_pre(rendered);
|
let rendered = add_playpen_pre(rendered);
|
||||||
|
|
||||||
try!(book.write_file(Path::new("print").with_extension("html"), &rendered.into_bytes()));
|
book.write_file(Path::new("print").with_extension("html"), &rendered.into_bytes())?;
|
||||||
info!("[*] Creating print.html ✓");
|
info!("[*] Creating print.html ✓");
|
||||||
|
|
||||||
// Copy static files (js, css, images, ...)
|
// Copy static files (js, css, images, ...)
|
||||||
|
|
||||||
debug!("[*] Copy static files");
|
debug!("[*] Copy static files");
|
||||||
try!(book.write_file("book.js", &theme.js));
|
book.write_file("book.js", &theme.js)?;
|
||||||
try!(book.write_file("book.css", &theme.css));
|
book.write_file("book.css", &theme.css)?;
|
||||||
try!(book.write_file("favicon.png", &theme.favicon));
|
book.write_file("favicon.png", &theme.favicon)?;
|
||||||
try!(book.write_file("jquery.js", &theme.jquery));
|
book.write_file("jquery.js", &theme.jquery)?;
|
||||||
try!(book.write_file("highlight.css", &theme.highlight_css));
|
book.write_file("highlight.css", &theme.highlight_css)?;
|
||||||
try!(book.write_file("tomorrow-night.css", &theme.tomorrow_night_css));
|
book.write_file("tomorrow-night.css", &theme.tomorrow_night_css)?;
|
||||||
try!(book.write_file("highlight.js", &theme.highlight_js));
|
book.write_file("highlight.js", &theme.highlight_js)?;
|
||||||
try!(book.write_file("_FontAwesome/css/font-awesome.css", theme::FONT_AWESOME));
|
book.write_file("_FontAwesome/css/font-awesome.css", theme::FONT_AWESOME)?;
|
||||||
try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.eot", theme::FONT_AWESOME_EOT));
|
book.write_file("_FontAwesome/fonts/fontawesome-webfont.eot", theme::FONT_AWESOME_EOT)?;
|
||||||
try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.svg", theme::FONT_AWESOME_SVG));
|
book.write_file("_FontAwesome/fonts/fontawesome-webfont.svg", theme::FONT_AWESOME_SVG)?;
|
||||||
try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.ttf", theme::FONT_AWESOME_TTF));
|
book.write_file("_FontAwesome/fonts/fontawesome-webfont.ttf", theme::FONT_AWESOME_TTF)?;
|
||||||
try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff", theme::FONT_AWESOME_WOFF));
|
book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff", theme::FONT_AWESOME_WOFF)?;
|
||||||
try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff2", theme::FONT_AWESOME_WOFF2));
|
book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff2", theme::FONT_AWESOME_WOFF2)?;
|
||||||
try!(book.write_file("_FontAwesome/fonts/FontAwesome.ttf", theme::FONT_AWESOME_TTF));
|
book.write_file("_FontAwesome/fonts/FontAwesome.ttf", theme::FONT_AWESOME_TTF)?;
|
||||||
|
|
||||||
// Copy all remaining files
|
// Copy all remaining files
|
||||||
try!(utils::fs::copy_files_except_ext(book.get_src(), book.get_dest(), true, &["md"]));
|
utils::fs::copy_files_except_ext(book.get_src(), book.get_dest(), true, &["md"])?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -207,15 +212,17 @@ fn make_data(book: &MDBook) -> Result<serde_json::Map<String, serde_json::Value>
|
||||||
match *item {
|
match *item {
|
||||||
BookItem::Affix(ref ch) => {
|
BookItem::Affix(ref ch) => {
|
||||||
chapter.insert("name".to_owned(), json!(ch.name));
|
chapter.insert("name".to_owned(), json!(ch.name));
|
||||||
let path = ch.path.to_str().ok_or_else(||
|
let path = ch.path
|
||||||
io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
|
.to_str()
|
||||||
|
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
|
||||||
chapter.insert("path".to_owned(), json!(path));
|
chapter.insert("path".to_owned(), json!(path));
|
||||||
},
|
},
|
||||||
BookItem::Chapter(ref s, ref ch) => {
|
BookItem::Chapter(ref s, ref ch) => {
|
||||||
chapter.insert("section".to_owned(), json!(s));
|
chapter.insert("section".to_owned(), json!(s));
|
||||||
chapter.insert("name".to_owned(), json!(ch.name));
|
chapter.insert("name".to_owned(), json!(ch.name));
|
||||||
let path = ch.path.to_str().ok_or_else(||
|
let path = ch.path
|
||||||
io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
|
.to_str()
|
||||||
|
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
|
||||||
chapter.insert("path".to_owned(), json!(path));
|
chapter.insert("path".to_owned(), json!(path));
|
||||||
},
|
},
|
||||||
BookItem::Spacer => {
|
BookItem::Spacer => {
|
||||||
|
@ -237,42 +244,55 @@ fn build_header_links(html: String, filename: &str) -> String {
|
||||||
let regex = Regex::new(r"<h(\d)>(.*?)</h\d>").unwrap();
|
let regex = Regex::new(r"<h(\d)>(.*?)</h\d>").unwrap();
|
||||||
let mut id_counter = HashMap::new();
|
let mut id_counter = HashMap::new();
|
||||||
|
|
||||||
regex.replace_all(&html, |caps: &Captures| {
|
regex
|
||||||
let level = &caps[1];
|
.replace_all(&html, |caps: &Captures| {
|
||||||
let text = &caps[2];
|
let level = &caps[1];
|
||||||
let mut id = text.to_string();
|
let text = &caps[2];
|
||||||
let repl_sub = vec!["<em>", "</em>", "<code>", "</code>",
|
let mut id = text.to_string();
|
||||||
"<strong>", "</strong>",
|
let repl_sub = vec!["<em>",
|
||||||
"<", ">", "&", "'", """];
|
"</em>",
|
||||||
for sub in repl_sub {
|
"<code>",
|
||||||
id = id.replace(sub, "");
|
"</code>",
|
||||||
}
|
"<strong>",
|
||||||
let id = id.chars().filter_map(|c| {
|
"</strong>",
|
||||||
if c.is_alphanumeric() || c == '-' || c == '_' {
|
"<",
|
||||||
if c.is_ascii() {
|
">",
|
||||||
Some(c.to_ascii_lowercase())
|
"&",
|
||||||
} else {
|
"'",
|
||||||
Some(c)
|
"""];
|
||||||
}
|
for sub in repl_sub {
|
||||||
} else if c.is_whitespace() && c.is_ascii() {
|
id = id.replace(sub, "");
|
||||||
Some('-')
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}).collect::<String>();
|
let id = id.chars()
|
||||||
|
.filter_map(|c| if c.is_alphanumeric() || c == '-' || c == '_' {
|
||||||
|
if c.is_ascii() {
|
||||||
|
Some(c.to_ascii_lowercase())
|
||||||
|
} else {
|
||||||
|
Some(c)
|
||||||
|
}
|
||||||
|
} else if c.is_whitespace() && c.is_ascii() {
|
||||||
|
Some('-')
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
let id_count = *id_counter.get(&id).unwrap_or(&0);
|
let id_count = *id_counter.get(&id).unwrap_or(&0);
|
||||||
id_counter.insert(id.clone(), id_count + 1);
|
id_counter.insert(id.clone(), id_count + 1);
|
||||||
|
|
||||||
let id = if id_count > 0 {
|
let id = if id_count > 0 {
|
||||||
format!("{}-{}", id, id_count)
|
format!("{}-{}", id, id_count)
|
||||||
} else {
|
} else {
|
||||||
id
|
id
|
||||||
};
|
};
|
||||||
|
|
||||||
format!("<a class=\"header\" href=\"{filename}#{id}\" id=\"{id}\"><h{level}>{text}</h{level}></a>",
|
format!("<a class=\"header\" href=\"{filename}#{id}\" id=\"{id}\"><h{level}>{text}</h{level}></a>",
|
||||||
level=level, id=id, text=text, filename=filename)
|
level = level,
|
||||||
}).into_owned()
|
id = id,
|
||||||
|
text = text,
|
||||||
|
filename = filename)
|
||||||
|
})
|
||||||
|
.into_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
// anchors to the same page (href="#anchor") do not work because of
|
// anchors to the same page (href="#anchor") do not work because of
|
||||||
|
@ -280,18 +300,24 @@ fn build_header_links(html: String, filename: &str) -> String {
|
||||||
// that in a very inelegant way
|
// that in a very inelegant way
|
||||||
fn fix_anchor_links(html: String, filename: &str) -> String {
|
fn fix_anchor_links(html: String, filename: &str) -> String {
|
||||||
let regex = Regex::new(r##"<a([^>]+)href="#([^"]+)"([^>]*)>"##).unwrap();
|
let regex = Regex::new(r##"<a([^>]+)href="#([^"]+)"([^>]*)>"##).unwrap();
|
||||||
regex.replace_all(&html, |caps: &Captures| {
|
regex
|
||||||
let before = &caps[1];
|
.replace_all(&html, |caps: &Captures| {
|
||||||
let anchor = &caps[2];
|
let before = &caps[1];
|
||||||
let after = &caps[3];
|
let anchor = &caps[2];
|
||||||
|
let after = &caps[3];
|
||||||
|
|
||||||
format!("<a{before}href=\"{filename}#{anchor}\"{after}>",
|
format!("<a{before}href=\"{filename}#{anchor}\"{after}>",
|
||||||
before=before, filename=filename, anchor=anchor, after=after)
|
before = before,
|
||||||
}).into_owned()
|
filename = filename,
|
||||||
|
anchor = anchor,
|
||||||
|
after = after)
|
||||||
|
})
|
||||||
|
.into_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// The rust book uses annotations for rustdoc to test code snippets, like the following:
|
// The rust book uses annotations for rustdoc to test code snippets,
|
||||||
|
// like the following:
|
||||||
// ```rust,should_panic
|
// ```rust,should_panic
|
||||||
// fn main() {
|
// fn main() {
|
||||||
// // Code here
|
// // Code here
|
||||||
|
@ -300,40 +326,48 @@ fn fix_anchor_links(html: String, filename: &str) -> String {
|
||||||
// This function replaces all commas by spaces in the code block classes
|
// This function replaces all commas by spaces in the code block classes
|
||||||
fn fix_code_blocks(html: String) -> String {
|
fn fix_code_blocks(html: String) -> String {
|
||||||
let regex = Regex::new(r##"<code([^>]+)class="([^"]+)"([^>]*)>"##).unwrap();
|
let regex = Regex::new(r##"<code([^>]+)class="([^"]+)"([^>]*)>"##).unwrap();
|
||||||
regex.replace_all(&html, |caps: &Captures| {
|
regex
|
||||||
let before = &caps[1];
|
.replace_all(&html, |caps: &Captures| {
|
||||||
let classes = &caps[2].replace(",", " ");
|
let before = &caps[1];
|
||||||
let after = &caps[3];
|
let classes = &caps[2].replace(",", " ");
|
||||||
|
let after = &caps[3];
|
||||||
|
|
||||||
format!("<code{before}class=\"{classes}\"{after}>", before=before, classes=classes, after=after)
|
format!("<code{before}class=\"{classes}\"{after}>", before = before, classes = classes, after = after)
|
||||||
}).into_owned()
|
})
|
||||||
|
.into_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_playpen_pre(html: String) -> String {
|
fn add_playpen_pre(html: String) -> String {
|
||||||
let regex = Regex::new(r##"((?s)<code[^>]?class="([^"]+)".*?>(.*?)</code>)"##).unwrap();
|
let regex = Regex::new(r##"((?s)<code[^>]?class="([^"]+)".*?>(.*?)</code>)"##).unwrap();
|
||||||
regex.replace_all(&html, |caps: &Captures| {
|
regex
|
||||||
let text = &caps[1];
|
.replace_all(&html, |caps: &Captures| {
|
||||||
let classes = &caps[2];
|
let text = &caps[1];
|
||||||
let code = &caps[3];
|
let classes = &caps[2];
|
||||||
|
let code = &caps[3];
|
||||||
|
|
||||||
if classes.contains("language-rust") && !classes.contains("ignore") {
|
if classes.contains("language-rust") && !classes.contains("ignore") {
|
||||||
// wrap the contents in an external pre block
|
// wrap the contents in an external pre block
|
||||||
|
|
||||||
if text.contains("fn main") || text.contains("quick_main!") {
|
if text.contains("fn main") || text.contains("quick_main!") {
|
||||||
format!("<pre class=\"playpen\">{}</pre>", text)
|
format!("<pre class=\"playpen\">{}</pre>", text)
|
||||||
} else {
|
} else {
|
||||||
// we need to inject our own main
|
// we need to inject our own main
|
||||||
let (attrs, code) = partition_source(code);
|
let (attrs, code) = partition_source(code);
|
||||||
format!("<pre class=\"playpen\"><code class=\"{}\"># #![allow(unused_variables)]
|
format!("<pre class=\"playpen\"><code class=\"{}\"># #![allow(unused_variables)]
|
||||||
{}#fn main() {{
|
{}#fn main() {{
|
||||||
{}
|
\
|
||||||
#}}</code></pre>", classes, attrs, code)
|
{}
|
||||||
|
#}}</code></pre>",
|
||||||
|
classes,
|
||||||
|
attrs,
|
||||||
|
code)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// not language-rust, so no-op
|
||||||
|
format!("{}", text)
|
||||||
}
|
}
|
||||||
} else {
|
})
|
||||||
// not language-rust, so no-op
|
.into_owned()
|
||||||
format!("{}", text)
|
|
||||||
}
|
|
||||||
}).into_owned()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn partition_source(s: &str) -> (String, String) {
|
fn partition_source(s: &str) -> (String, String) {
|
||||||
|
@ -343,8 +377,7 @@ fn partition_source(s: &str) -> (String, String) {
|
||||||
|
|
||||||
for line in s.lines() {
|
for line in s.lines() {
|
||||||
let trimline = line.trim();
|
let trimline = line.trim();
|
||||||
let header = trimline.chars().all(|c| c.is_whitespace()) ||
|
let header = trimline.chars().all(|c| c.is_whitespace()) || trimline.starts_with("#![");
|
||||||
trimline.starts_with("#![");
|
|
||||||
if !header || after_header {
|
if !header || after_header {
|
||||||
after_header = true;
|
after_header = true;
|
||||||
after.push_str(line);
|
after.push_str(line);
|
||||||
|
|
|
@ -14,9 +14,12 @@ pub fn previous(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(
|
||||||
// get value from context data
|
// get value from context data
|
||||||
// rc.get_path() is current json parent path, you should always use it like this
|
// rc.get_path() is current json parent path, you should always use it like this
|
||||||
// param is the key of value you want to display
|
// param is the key of value you want to display
|
||||||
let chapters = rc.context().navigate(rc.get_path(), &VecDeque::new(), "chapters").to_owned();
|
let chapters = rc.context()
|
||||||
|
.navigate(rc.get_path(), &VecDeque::new(), "chapters")
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
let current = rc.context().navigate(rc.get_path(), &VecDeque::new(), "path")
|
let current = rc.context()
|
||||||
|
.navigate(rc.get_path(), &VecDeque::new(), "path")
|
||||||
.to_string()
|
.to_string()
|
||||||
.replace("\"", "");
|
.replace("\"", "");
|
||||||
|
|
||||||
|
@ -84,7 +87,7 @@ pub fn previous(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(
|
||||||
match _h.template() {
|
match _h.template() {
|
||||||
Some(t) => {
|
Some(t) => {
|
||||||
*rc.context_mut() = updated_context;
|
*rc.context_mut() = updated_context;
|
||||||
try!(t.render(r, rc));
|
t.render(r, rc)?;
|
||||||
},
|
},
|
||||||
None => return Err(RenderError::new("Error with the handlebars template")),
|
None => return Err(RenderError::new("Error with the handlebars template")),
|
||||||
}
|
}
|
||||||
|
@ -115,9 +118,12 @@ pub fn next(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), R
|
||||||
// get value from context data
|
// get value from context data
|
||||||
// rc.get_path() is current json parent path, you should always use it like this
|
// rc.get_path() is current json parent path, you should always use it like this
|
||||||
// param is the key of value you want to display
|
// param is the key of value you want to display
|
||||||
let chapters = rc.context().navigate(rc.get_path(), &VecDeque::new(), "chapters").to_owned();
|
let chapters = rc.context()
|
||||||
|
.navigate(rc.get_path(), &VecDeque::new(), "chapters")
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
let current = rc.context().navigate(rc.get_path(), &VecDeque::new(), "path")
|
let current = rc.context()
|
||||||
|
.navigate(rc.get_path(), &VecDeque::new(), "path")
|
||||||
.to_string()
|
.to_string()
|
||||||
.replace("\"", "");
|
.replace("\"", "");
|
||||||
|
|
||||||
|
@ -181,7 +187,7 @@ pub fn next(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), R
|
||||||
match _h.template() {
|
match _h.template() {
|
||||||
Some(t) => {
|
Some(t) => {
|
||||||
*rc.context_mut() = updated_context;
|
*rc.context_mut() = updated_context;
|
||||||
try!(t.render(r, rc));
|
t.render(r, rc)?;
|
||||||
},
|
},
|
||||||
None => return Err(RenderError::new("Error with the handlebars template")),
|
None => return Err(RenderError::new("Error with the handlebars template")),
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,9 @@ use std::io::Read;
|
||||||
|
|
||||||
|
|
||||||
pub fn render_playpen(s: &str, path: &Path) -> String {
|
pub fn render_playpen(s: &str, path: &Path) -> String {
|
||||||
// When replacing one thing in a string by something with a different length, the indices
|
// When replacing one thing in a string by something with a different length,
|
||||||
// after that will not correspond, we therefore have to store the difference to correct this
|
// the indices after that will not correspond,
|
||||||
|
// we therefore have to store the difference to correct this
|
||||||
let mut previous_end_index = 0;
|
let mut previous_end_index = 0;
|
||||||
let mut replaced = String::new();
|
let mut replaced = String::new();
|
||||||
|
|
||||||
|
@ -35,13 +36,13 @@ pub fn render_playpen(s: &str, path: &Path) -> String {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let replacement = String::new() + "<pre><code class=\"language-rust\">" + &file_content +
|
let replacement = String::new() + "<pre><code class=\"language-rust\">" + &file_content + "</code></pre>";
|
||||||
"</code></pre>";
|
|
||||||
|
|
||||||
replaced.push_str(&s[previous_end_index..playpen.start_index]);
|
replaced.push_str(&s[previous_end_index..playpen.start_index]);
|
||||||
replaced.push_str(&replacement);
|
replaced.push_str(&replacement);
|
||||||
previous_end_index = playpen.end_index;
|
previous_end_index = playpen.end_index;
|
||||||
// println!("Playpen{{ {}, {}, {:?}, {} }}", playpen.start_index, playpen.end_index, playpen.rust_file,
|
// println!("Playpen{{ {}, {}, {:?}, {} }}", playpen.start_index,
|
||||||
|
// playpen.end_index, playpen.rust_file,
|
||||||
// playpen.editable);
|
// playpen.editable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,12 +101,12 @@ fn find_playpens(s: &str, base_path: &Path) -> Vec<Playpen> {
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
playpens.push(Playpen {
|
playpens.push(Playpen {
|
||||||
start_index: i,
|
start_index: i,
|
||||||
end_index: end_i,
|
end_index: end_i,
|
||||||
rust_file: base_path.join(PathBuf::from(params[0])),
|
rust_file: base_path.join(PathBuf::from(params[0])),
|
||||||
editable: editable,
|
editable: editable,
|
||||||
escaped: escaped,
|
escaped: escaped,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
playpens
|
playpens
|
||||||
|
@ -189,7 +190,11 @@ fn test_find_playpens_escaped_playpen() {
|
||||||
println!("\nOUTPUT: {:?}\n", find_playpens(s, Path::new("")));
|
println!("\nOUTPUT: {:?}\n", find_playpens(s, Path::new("")));
|
||||||
|
|
||||||
assert!(find_playpens(s, Path::new("")) ==
|
assert!(find_playpens(s, Path::new("")) ==
|
||||||
vec![
|
vec![Playpen {
|
||||||
Playpen{start_index: 39, end_index: 68, rust_file: PathBuf::from("file.rs"), editable: true, escaped: true},
|
start_index: 39,
|
||||||
]);
|
end_index: 68,
|
||||||
|
rust_file: PathBuf::from("file.rs"),
|
||||||
|
editable: true,
|
||||||
|
escaped: true,
|
||||||
|
}]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,14 @@ impl HelperDef for RenderToc {
|
||||||
// get value from context data
|
// get value from context data
|
||||||
// rc.get_path() is current json parent path, you should always use it like this
|
// rc.get_path() is current json parent path, you should always use it like this
|
||||||
// param is the key of value you want to display
|
// param is the key of value you want to display
|
||||||
let chapters = rc.context().navigate(rc.get_path(), &VecDeque::new(), "chapters").to_owned();
|
let chapters = rc.context()
|
||||||
let current = rc.context().navigate(rc.get_path(), &VecDeque::new(), "path").to_string().replace("\"", "");
|
.navigate(rc.get_path(), &VecDeque::new(), "chapters")
|
||||||
try!(rc.writer.write_all("<ul class=\"chapter\">".as_bytes()));
|
.to_owned();
|
||||||
|
let current = rc.context()
|
||||||
|
.navigate(rc.get_path(), &VecDeque::new(), "path")
|
||||||
|
.to_string()
|
||||||
|
.replace("\"", "");
|
||||||
|
rc.writer.write_all("<ul class=\"chapter\">".as_bytes())?;
|
||||||
|
|
||||||
// Decode json format
|
// Decode json format
|
||||||
let decoded: Vec<BTreeMap<String, String>> = serde_json::from_str(&chapters.to_string()).unwrap();
|
let decoded: Vec<BTreeMap<String, String>> = serde_json::from_str(&chapters.to_string()).unwrap();
|
||||||
|
@ -28,7 +33,8 @@ impl HelperDef for RenderToc {
|
||||||
|
|
||||||
// Spacer
|
// Spacer
|
||||||
if item.get("spacer").is_some() {
|
if item.get("spacer").is_some() {
|
||||||
try!(rc.writer.write_all("<li class=\"spacer\"></li>".as_bytes()));
|
rc.writer
|
||||||
|
.write_all("<li class=\"spacer\"></li>".as_bytes())?;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,48 +46,47 @@ impl HelperDef for RenderToc {
|
||||||
|
|
||||||
if level > current_level {
|
if level > current_level {
|
||||||
while level > current_level {
|
while level > current_level {
|
||||||
try!(rc.writer.write_all("<li>".as_bytes()));
|
rc.writer.write_all("<li>".as_bytes())?;
|
||||||
try!(rc.writer.write_all("<ul class=\"section\">".as_bytes()));
|
rc.writer.write_all("<ul class=\"section\">".as_bytes())?;
|
||||||
current_level += 1;
|
current_level += 1;
|
||||||
}
|
}
|
||||||
try!(rc.writer.write_all("<li>".as_bytes()));
|
rc.writer.write_all("<li>".as_bytes())?;
|
||||||
} else if level < current_level {
|
} else if level < current_level {
|
||||||
while level < current_level {
|
while level < current_level {
|
||||||
try!(rc.writer.write_all("</ul>".as_bytes()));
|
rc.writer.write_all("</ul>".as_bytes())?;
|
||||||
try!(rc.writer.write_all("</li>".as_bytes()));
|
rc.writer.write_all("</li>".as_bytes())?;
|
||||||
current_level -= 1;
|
current_level -= 1;
|
||||||
}
|
}
|
||||||
try!(rc.writer.write_all("<li>".as_bytes()));
|
rc.writer.write_all("<li>".as_bytes())?;
|
||||||
} else {
|
} else {
|
||||||
try!(rc.writer.write_all("<li".as_bytes()));
|
rc.writer.write_all("<li".as_bytes())?;
|
||||||
if item.get("section").is_none() {
|
if item.get("section").is_none() {
|
||||||
try!(rc.writer.write_all(" class=\"affix\"".as_bytes()));
|
rc.writer.write_all(" class=\"affix\"".as_bytes())?;
|
||||||
}
|
}
|
||||||
try!(rc.writer.write_all(">".as_bytes()));
|
rc.writer.write_all(">".as_bytes())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link
|
// Link
|
||||||
let path_exists = if let Some(path) = item.get("path") {
|
let path_exists = if let Some(path) = item.get("path") {
|
||||||
if !path.is_empty() {
|
if !path.is_empty() {
|
||||||
try!(rc.writer.write_all("<a href=\"".as_bytes()));
|
rc.writer.write_all("<a href=\"".as_bytes())?;
|
||||||
|
|
||||||
|
let tmp = Path::new(item.get("path").expect("Error: path should be Some(_)"))
|
||||||
|
.with_extension("html")
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
// Hack for windows who tends to use `\` as separator instead of `/`
|
||||||
|
.replace("\\", "/");
|
||||||
|
|
||||||
// Add link
|
// Add link
|
||||||
try!(rc.writer.write_all(Path::new(item.get("path")
|
rc.writer.write_all(tmp.as_bytes())?;
|
||||||
.expect("Error: path should be Some(_)"))
|
rc.writer.write_all("\"".as_bytes())?;
|
||||||
.with_extension("html")
|
|
||||||
.to_str()
|
|
||||||
.unwrap()
|
|
||||||
// Hack for windows who tends to use `\` as separator instead of `/`
|
|
||||||
.replace("\\", "/")
|
|
||||||
.as_bytes()));
|
|
||||||
|
|
||||||
try!(rc.writer.write_all("\"".as_bytes()));
|
|
||||||
|
|
||||||
if path == ¤t {
|
if path == ¤t {
|
||||||
try!(rc.writer.write_all(" class=\"active\"".as_bytes()));
|
rc.writer.write_all(" class=\"active\"".as_bytes())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
try!(rc.writer.write_all(">".as_bytes()));
|
rc.writer.write_all(">".as_bytes())?;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -92,47 +97,45 @@ impl HelperDef for RenderToc {
|
||||||
|
|
||||||
// Section does not necessarily exist
|
// Section does not necessarily exist
|
||||||
if let Some(section) = item.get("section") {
|
if let Some(section) = item.get("section") {
|
||||||
try!(rc.writer.write_all("<strong>".as_bytes()));
|
rc.writer.write_all("<strong>".as_bytes())?;
|
||||||
try!(rc.writer.write_all(section.as_bytes()));
|
rc.writer.write_all(section.as_bytes())?;
|
||||||
try!(rc.writer.write_all("</strong> ".as_bytes()));
|
rc.writer.write_all("</strong> ".as_bytes())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(name) = item.get("name") {
|
if let Some(name) = item.get("name") {
|
||||||
// Render only inline code blocks
|
// Render only inline code blocks
|
||||||
|
|
||||||
// filter all events that are not inline code blocks
|
// filter all events that are not inline code blocks
|
||||||
let parser = Parser::new(name).filter(|event| {
|
let parser = Parser::new(name).filter(|event| match *event {
|
||||||
match *event {
|
Event::Start(Tag::Code) |
|
||||||
Event::Start(Tag::Code) |
|
Event::End(Tag::Code) |
|
||||||
Event::End(Tag::Code) |
|
Event::InlineHtml(_) |
|
||||||
Event::InlineHtml(_) |
|
Event::Text(_) => true,
|
||||||
Event::Text(_) => true,
|
_ => false,
|
||||||
_ => false,
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// render markdown to html
|
// render markdown to html
|
||||||
let mut markdown_parsed_name = String::with_capacity(name.len() * 3 / 2);
|
let mut markdown_parsed_name = String::with_capacity(name.len() * 3 / 2);
|
||||||
html::push_html(&mut markdown_parsed_name, parser);
|
html::push_html(&mut markdown_parsed_name, parser);
|
||||||
|
|
||||||
// write to the handlebars template
|
// write to the handlebars template
|
||||||
try!(rc.writer.write_all(markdown_parsed_name.as_bytes()));
|
rc.writer.write_all(markdown_parsed_name.as_bytes())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if path_exists {
|
if path_exists {
|
||||||
try!(rc.writer.write_all("</a>".as_bytes()));
|
rc.writer.write_all("</a>".as_bytes())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
try!(rc.writer.write_all("</li>".as_bytes()));
|
rc.writer.write_all("</li>".as_bytes())?;
|
||||||
|
|
||||||
}
|
}
|
||||||
while current_level > 1 {
|
while current_level > 1 {
|
||||||
try!(rc.writer.write_all("</ul>".as_bytes()));
|
rc.writer.write_all("</ul>".as_bytes())?;
|
||||||
try!(rc.writer.write_all("</li>".as_bytes()));
|
rc.writer.write_all("</li>".as_bytes())?;
|
||||||
current_level -= 1;
|
current_level -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
try!(rc.writer.write_all("</ul>".as_bytes()));
|
rc.writer.write_all("</ul>".as_bytes())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,14 @@ pub static FONT_AWESOME_WOFF: &'static [u8] = include_bytes!("_FontAwesome/fonts
|
||||||
pub static FONT_AWESOME_WOFF2: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.woff2");
|
pub static FONT_AWESOME_WOFF2: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.woff2");
|
||||||
pub static FONT_AWESOME_OTF: &'static [u8] = include_bytes!("_FontAwesome/fonts/FontAwesome.otf");
|
pub static FONT_AWESOME_OTF: &'static [u8] = include_bytes!("_FontAwesome/fonts/FontAwesome.otf");
|
||||||
|
|
||||||
/// The `Theme` struct should be used instead of the static variables because the `new()` method
|
/// The `Theme` struct should be used instead of the static variables because
|
||||||
/// will look if the user has a theme directory in his source folder and use the users theme instead
|
/// the `new()` method
|
||||||
|
/// will look if the user has a theme directory in his source folder and use
|
||||||
|
/// the users theme instead
|
||||||
/// of the default.
|
/// of the default.
|
||||||
///
|
///
|
||||||
/// You should exceptionnaly use the static variables only if you need the default theme even if the
|
/// You should exceptionnaly use the static variables only if you need the
|
||||||
|
/// default theme even if the
|
||||||
/// user has specified another theme.
|
/// user has specified another theme.
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
pub index: Vec<u8>,
|
pub index: Vec<u8>,
|
||||||
|
|
|
@ -24,10 +24,11 @@ pub fn file_to_string(path: &Path) -> Result<String, Box<Error>> {
|
||||||
Ok(content)
|
Ok(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes a path and returns a path containing just enough `../` to point to the root of the given path.
|
/// 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
|
/// This is mostly interesting for a relative path to point back to the
|
||||||
/// path starts.
|
/// directory from where the path starts.
|
||||||
///
|
///
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// let mut path = Path::new("some/relative/path");
|
/// let mut path = Path::new("some/relative/path");
|
||||||
|
@ -41,9 +42,10 @@ pub fn file_to_string(path: &Path) -> Result<String, Box<Error>> {
|
||||||
/// "../../"
|
/// "../../"
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// **note:** it's not very fool-proof, if you find a situation where it doesn't return the correct
|
/// **note:** it's not very fool-proof, if you find a situation where
|
||||||
/// path. Consider [submitting a new issue](https://github.com/azerupi/mdBook/issues) or a
|
/// it doesn't return the correct path.
|
||||||
/// [pull-request](https://github.com/azerupi/mdBook/pulls) to improve it.
|
/// Consider [submitting a new issue](https://github.com/azerupi/mdBook/issues)
|
||||||
|
/// or a [pull-request](https://github.com/azerupi/mdBook/pulls) to improve it.
|
||||||
|
|
||||||
pub fn path_to_root(path: &Path) -> String {
|
pub fn path_to_root(path: &Path) -> String {
|
||||||
debug!("[fn]: path_to_root");
|
debug!("[fn]: path_to_root");
|
||||||
|
@ -66,8 +68,9 @@ pub fn path_to_root(path: &Path) -> String {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// This function creates a file and returns it. But before creating the file it checks every
|
/// This function creates a file and returns it. But before creating the file
|
||||||
/// directory in the path to see if it exists, and if it does not it will be created.
|
/// it checks every directory in the path to see if it exists,
|
||||||
|
/// and if it does not it will be created.
|
||||||
|
|
||||||
pub fn create_file(path: &Path) -> io::Result<File> {
|
pub fn create_file(path: &Path) -> io::Result<File> {
|
||||||
debug!("[fn]: create_file");
|
debug!("[fn]: create_file");
|
||||||
|
@ -76,7 +79,7 @@ pub fn create_file(path: &Path) -> io::Result<File> {
|
||||||
if let Some(p) = path.parent() {
|
if let Some(p) = path.parent() {
|
||||||
debug!("Parent directory is: {:?}", p);
|
debug!("Parent directory is: {:?}", p);
|
||||||
|
|
||||||
try!(fs::create_dir_all(p));
|
fs::create_dir_all(p)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("[*]: Create file: {:?}", path);
|
debug!("[*]: Create file: {:?}", path);
|
||||||
|
@ -86,13 +89,13 @@ pub fn create_file(path: &Path) -> io::Result<File> {
|
||||||
/// Removes all the content of a directory but not the directory itself
|
/// Removes all the content of a directory but not the directory itself
|
||||||
|
|
||||||
pub fn remove_dir_content(dir: &Path) -> Result<(), Box<Error>> {
|
pub fn remove_dir_content(dir: &Path) -> Result<(), Box<Error>> {
|
||||||
for item in try!(fs::read_dir(dir)) {
|
for item in fs::read_dir(dir)? {
|
||||||
if let Ok(item) = item {
|
if let Ok(item) = item {
|
||||||
let item = item.path();
|
let item = item.path();
|
||||||
if item.is_dir() {
|
if item.is_dir() {
|
||||||
try!(fs::remove_dir_all(item));
|
fs::remove_dir_all(item)?;
|
||||||
} else {
|
} else {
|
||||||
try!(fs::remove_file(item));
|
fs::remove_file(item)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,20 +104,21 @@ pub fn remove_dir_content(dir: &Path) -> Result<(), Box<Error>> {
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// Copies all files of a directory to another one except the files with the extensions given in the
|
/// Copies all files of a directory to another one except the files
|
||||||
/// `ext_blacklist` array
|
/// with the extensions given in the `ext_blacklist` array
|
||||||
|
|
||||||
pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blacklist: &[&str]) -> Result<(), Box<Error>> {
|
pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blacklist: &[&str])
|
||||||
|
-> Result<(), Box<Error>> {
|
||||||
debug!("[fn] copy_files_except_ext");
|
debug!("[fn] copy_files_except_ext");
|
||||||
// Check that from and to are different
|
// Check that from and to are different
|
||||||
if from == to {
|
if from == to {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
debug!("[*] Loop");
|
debug!("[*] Loop");
|
||||||
for entry in try!(fs::read_dir(from)) {
|
for entry in fs::read_dir(from)? {
|
||||||
let entry = try!(entry);
|
let entry = entry?;
|
||||||
debug!("[*] {:?}", entry.path());
|
debug!("[*] {:?}", entry.path());
|
||||||
let metadata = try!(entry.metadata());
|
let metadata = entry.metadata()?;
|
||||||
|
|
||||||
// If the entry is a dir and the recursive option is enabled, call itself
|
// If the entry is a dir and the recursive option is enabled, call itself
|
||||||
if metadata.is_dir() && recursive {
|
if metadata.is_dir() && recursive {
|
||||||
|
@ -125,13 +129,10 @@ pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blackl
|
||||||
|
|
||||||
// check if output dir already exists
|
// check if output dir already exists
|
||||||
if !to.join(entry.file_name()).exists() {
|
if !to.join(entry.file_name()).exists() {
|
||||||
try!(fs::create_dir(&to.join(entry.file_name())));
|
fs::create_dir(&to.join(entry.file_name()))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
try!(copy_files_except_ext(&from.join(entry.file_name()),
|
copy_files_except_ext(&from.join(entry.file_name()), &to.join(entry.file_name()), true, ext_blacklist)?;
|
||||||
&to.join(entry.file_name()),
|
|
||||||
true,
|
|
||||||
ext_blacklist));
|
|
||||||
} else if metadata.is_file() {
|
} else if metadata.is_file() {
|
||||||
|
|
||||||
// Check if it is in the blacklist
|
// Check if it is in the blacklist
|
||||||
|
@ -141,13 +142,22 @@ pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blackl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug!("[*] creating path for file: {:?}",
|
debug!("[*] creating path for file: {:?}",
|
||||||
&to.join(entry.path().file_name().expect("a file should have a file name...")));
|
&to.join(entry
|
||||||
|
.path()
|
||||||
|
.file_name()
|
||||||
|
.expect("a file should have a file name...")));
|
||||||
|
|
||||||
info!("[*] Copying file: {:?}\n to {:?}",
|
info!("[*] Copying file: {:?}\n to {:?}",
|
||||||
entry.path(),
|
entry.path(),
|
||||||
&to.join(entry.path().file_name().expect("a file should have a file name...")));
|
&to.join(entry
|
||||||
try!(fs::copy(entry.path(),
|
.path()
|
||||||
&to.join(entry.path().file_name().expect("a file should have a file name..."))));
|
.file_name()
|
||||||
|
.expect("a file should have a file name...")));
|
||||||
|
fs::copy(entry.path(),
|
||||||
|
&to.join(entry
|
||||||
|
.path()
|
||||||
|
.file_name()
|
||||||
|
.expect("a file should have a file name...")))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in a new issue