Big refactoring, now using enum for different book items (Chapter, Affix, Spacer, ...) Closes #9
This commit is contained in:
parent
6962731474
commit
a050d9c4ad
|
@ -11,3 +11,5 @@
|
||||||
- [index.hbs](format/theme/index-hbs.md)
|
- [index.hbs](format/theme/index-hbs.md)
|
||||||
- [Syntax highlighting](format/theme/syntax-highlighting.md)
|
- [Syntax highlighting](format/theme/syntax-highlighting.md)
|
||||||
- [Rust Library](lib/lib.md)
|
- [Rust Library](lib/lib.md)
|
||||||
|
-----------
|
||||||
|
[Contributors](misc/contributors.md)
|
||||||
|
|
|
@ -5,11 +5,17 @@ use std::path::PathBuf;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BookItem {
|
pub enum BookItem {
|
||||||
|
Chapter(String, Chapter), // String = section
|
||||||
|
Affix(Chapter),
|
||||||
|
Spacer,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Chapter {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub sub_items: Vec<BookItem>,
|
pub sub_items: Vec<BookItem>,
|
||||||
spacer: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -20,30 +26,21 @@ pub struct BookItems<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl BookItem {
|
impl Chapter {
|
||||||
|
|
||||||
pub fn new(name: String, path: PathBuf) -> Self {
|
pub fn new(name: String, path: PathBuf) -> Self {
|
||||||
|
|
||||||
BookItem {
|
Chapter {
|
||||||
name: name,
|
name: name,
|
||||||
path: path,
|
path: path,
|
||||||
sub_items: vec![],
|
sub_items: vec![],
|
||||||
spacer: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _spacer() -> Self {
|
|
||||||
BookItem {
|
|
||||||
name: String::from("SPACER"),
|
|
||||||
path: PathBuf::new(),
|
|
||||||
sub_items: vec![],
|
|
||||||
spacer: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl ToJson for BookItem {
|
impl ToJson for Chapter {
|
||||||
|
|
||||||
fn to_json(&self) -> Json {
|
fn to_json(&self) -> Json {
|
||||||
let mut m: BTreeMap<String, Json> = BTreeMap::new();
|
let mut m: BTreeMap<String, Json> = BTreeMap::new();
|
||||||
m.insert("name".to_string(), self.name.to_json());
|
m.insert("name".to_string(), self.name.to_json());
|
||||||
|
@ -59,9 +56,9 @@ impl ToJson for BookItem {
|
||||||
// Shamelessly copied from Rustbook
|
// Shamelessly copied from Rustbook
|
||||||
// (https://github.com/rust-lang/rust/blob/master/src/rustbook/book.rs)
|
// (https://github.com/rust-lang/rust/blob/master/src/rustbook/book.rs)
|
||||||
impl<'a> Iterator for BookItems<'a> {
|
impl<'a> Iterator for BookItems<'a> {
|
||||||
type Item = (String, &'a BookItem);
|
type Item = &'a BookItem;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<(String, &'a BookItem)> {
|
fn next(&mut self) -> Option<&'a BookItem> {
|
||||||
loop {
|
loop {
|
||||||
if self.current_index >= self.items.len() {
|
if self.current_index >= self.items.len() {
|
||||||
match self.stack.pop() {
|
match self.stack.pop() {
|
||||||
|
@ -74,18 +71,18 @@ impl<'a> Iterator for BookItems<'a> {
|
||||||
} else {
|
} else {
|
||||||
let cur = self.items.get(self.current_index).unwrap();
|
let cur = self.items.get(self.current_index).unwrap();
|
||||||
|
|
||||||
let mut section = "".to_string();
|
match cur {
|
||||||
for &(_, idx) in &self.stack {
|
&BookItem::Chapter(_, ref ch) | &BookItem::Affix(ref ch) => {
|
||||||
section.push_str(&(idx + 1).to_string()[..]);
|
self.stack.push((self.items, self.current_index));
|
||||||
section.push('.');
|
self.items = &ch.sub_items[..];
|
||||||
|
self.current_index = 0;
|
||||||
|
},
|
||||||
|
&BookItem::Spacer => {
|
||||||
|
self.current_index += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
section.push_str(&(self.current_index + 1).to_string()[..]);
|
|
||||||
section.push('.');
|
|
||||||
|
|
||||||
self.stack.push((self.items, self.current_index));
|
return Some(cur)
|
||||||
self.items = &cur.sub_items[..];
|
|
||||||
self.current_index = 0;
|
|
||||||
return Some((section, cur))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,21 +126,29 @@ impl MDBook {
|
||||||
// parse SUMMARY.md, and create the missing item related file
|
// parse SUMMARY.md, and create the missing item related file
|
||||||
try!(self.parse_summary());
|
try!(self.parse_summary());
|
||||||
|
|
||||||
for (_, item) in self.iter() {
|
debug!("[*]: constructing paths for missing files");
|
||||||
if item.path != PathBuf::new() {
|
for item in self.iter() {
|
||||||
let path = self.config.get_src().join(&item.path);
|
debug!("[*]: item: {:?}", item);
|
||||||
|
match item {
|
||||||
|
&BookItem::Spacer => continue,
|
||||||
|
&BookItem::Chapter(_, ref ch) | &BookItem::Affix(ref ch) => {
|
||||||
|
if ch.path != PathBuf::new() {
|
||||||
|
let path = self.config.get_src().join(&ch.path);
|
||||||
|
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
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()));
|
try!(::std::fs::create_dir_all(path.parent().unwrap()));
|
||||||
let mut f = try!(File::create(path));
|
let mut f = try!(File::create(path));
|
||||||
|
|
||||||
debug!("[*]: Writing to {:?}", path);
|
//debug!("[*]: Writing to {:?}", path);
|
||||||
try!(writeln!(f, "# {}", item.name));
|
try!(writeln!(f, "# {}", ch.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("[*]: init done");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,14 +308,8 @@ 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() ...
|
||||||
let book_items = try!(parse::construct_bookitems(&self.config.get_src().join("SUMMARY.md")));
|
self.content = try!(parse::construct_bookitems(&self.config.get_src().join("SUMMARY.md")));
|
||||||
|
|
||||||
for item in book_items {
|
|
||||||
self.content.push(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Result, Error, ErrorKind};
|
use std::io::{Read, Result, Error, ErrorKind};
|
||||||
use book::bookitem::BookItem;
|
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");
|
||||||
|
@ -9,36 +9,106 @@ pub fn construct_bookitems(path: &PathBuf) -> Result<Vec<BookItem>> {
|
||||||
try!(try!(File::open(path)).read_to_string(&mut summary));
|
try!(try!(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));
|
let top_items = try!(parse_level(&mut summary.split('\n').collect(), 0, vec![0]));
|
||||||
|
debug!("[*]: Done parsing SUMMARY.md");
|
||||||
Ok(top_items)
|
Ok(top_items)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_level(summary: &mut Vec<&str>, current_level: i32) -> Result<Vec<BookItem>> {
|
fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32>) -> Result<Vec<BookItem>> {
|
||||||
debug!("[fn]: parse_level");
|
debug!("[fn]: parse_level");
|
||||||
let mut items: Vec<BookItem> = vec![];
|
let mut items: Vec<BookItem> = vec![];
|
||||||
|
|
||||||
loop {
|
// Construct the book recursively
|
||||||
if summary.len() <= 0 { break }
|
while summary.len() > 0 {
|
||||||
|
let item: BookItem;
|
||||||
|
// Indentation level of the line to parse
|
||||||
let level = try!(level(summary[0], 4));
|
let level = try!(level(summary[0], 4));
|
||||||
|
|
||||||
if current_level > level { break }
|
// if level < current_level we remove the last digit of section, exit the current function,
|
||||||
else if current_level < level {
|
// and return the parsed level to the calling function.
|
||||||
items.last_mut().unwrap().sub_items = try!(parse_level(summary, level))
|
if level < current_level { break }
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Do the thing
|
|
||||||
if let Some(item) = parse_line(summary[0].clone()) {
|
|
||||||
items.push(item);
|
|
||||||
}
|
|
||||||
summary.remove(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// if level > current_level we call ourselves to go one level deeper
|
||||||
|
if level > current_level {
|
||||||
|
// Level can not be root level !!
|
||||||
|
// Add a sub-number to section
|
||||||
|
section.push(1);
|
||||||
|
let last = items.pop().expect("There should be at least one item since this can't be the root level");
|
||||||
|
|
||||||
|
item = if let BookItem::Chapter(ref s, ref ch) = last {
|
||||||
|
let mut ch = ch.clone();
|
||||||
|
ch.sub_items = try!(parse_level(summary, level, section.clone()));
|
||||||
|
items.push(BookItem::Chapter(s.clone(), ch));
|
||||||
|
|
||||||
|
// Remove the last number from the section, because we got back to our level..
|
||||||
|
section.pop();
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return Err(Error::new( ErrorKind::Other, format!(
|
||||||
|
"Your summary.md is messed up\n\n
|
||||||
|
Prefix, Suffix and Spacer elements can only exist on the root level.\n
|
||||||
|
Prefix elements can only exist before any chapter and there can be no chapters after suffix elements."
|
||||||
|
)))
|
||||||
|
};
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// level and current_level are the same, parse the line
|
||||||
|
item = if let Some(parsed_item) = parse_line(summary[0]) {
|
||||||
|
|
||||||
|
// Eliminate possible errors and set section to -1 after suffix
|
||||||
|
match parsed_item {
|
||||||
|
// error if level != 0 and BookItem is != Chapter
|
||||||
|
BookItem::Affix(_) | BookItem::Spacer if level > 0 => {
|
||||||
|
return Err(Error::new( ErrorKind::Other, format!(
|
||||||
|
"Your summary.md is messed up\n\n
|
||||||
|
Prefix, Suffix and Spacer elements can only exist on the root level.\n
|
||||||
|
Prefix elements can only exist before any chapter and there can be no chapters after suffix elements."
|
||||||
|
)))
|
||||||
|
},
|
||||||
|
|
||||||
|
// error if BookItem == Chapter and section == -1
|
||||||
|
BookItem::Chapter(_, _) if section[0] == -1 => {
|
||||||
|
return Err(Error::new( ErrorKind::Other, format!(
|
||||||
|
"Your summary.md is messed up\n\n
|
||||||
|
Prefix, Suffix and Spacer elements can only exist on the root level.\n
|
||||||
|
Prefix elements can only exist before any chapter and there can be no chapters after suffix elements."
|
||||||
|
)))
|
||||||
|
},
|
||||||
|
|
||||||
|
// Set section = -1 after suffix
|
||||||
|
BookItem::Affix(_) if section[0] > 0 => {
|
||||||
|
section[0] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
match parsed_item {
|
||||||
|
BookItem::Chapter(_, ch) => {
|
||||||
|
// Increment section
|
||||||
|
let len = section.len() -1;
|
||||||
|
section[len] += 1;
|
||||||
|
let s = section.iter().fold("".to_owned(), |s, i| s + &i.to_string() + ".");
|
||||||
|
BookItem::Chapter(s, ch)
|
||||||
|
}
|
||||||
|
_ => parsed_item
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// If parse_line does not return Some(_) continue...
|
||||||
|
summary.remove(0);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.remove(0);
|
||||||
|
items.push(item)
|
||||||
|
}
|
||||||
|
debug!("[*]: Level: {:?}", items);
|
||||||
Ok(items)
|
Ok(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn level(line: &str, spaces_in_tab: i32) -> Result<i32> {
|
fn level(line: &str, spaces_in_tab: i32) -> Result<i32> {
|
||||||
debug!("[fn]: level");
|
debug!("[fn]: level");
|
||||||
let mut spaces = 0;
|
let mut spaces = 0;
|
||||||
|
@ -74,49 +144,33 @@ fn level(line: &str, spaces_in_tab: i32) -> Result<i32> {
|
||||||
|
|
||||||
fn parse_line(l: &str) -> Option<BookItem> {
|
fn parse_line(l: &str) -> Option<BookItem> {
|
||||||
debug!("[fn]: parse_line");
|
debug!("[fn]: parse_line");
|
||||||
let mut name;
|
|
||||||
let mut path;
|
|
||||||
// Remove leading and trailing spaces or tabs
|
// Remove leading and trailing spaces or tabs
|
||||||
let line = l.trim_matches(|c: char| { c == ' ' || c == '\t' });
|
let line = l.trim_matches(|c: char| { c == ' ' || c == '\t' });
|
||||||
|
|
||||||
|
// Spacers are "------"
|
||||||
|
if line.starts_with("--") {
|
||||||
|
debug!("[*]: Line is spacer");
|
||||||
|
return Some(BookItem::Spacer)
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(c) = line.chars().nth(0) {
|
if let Some(c) = line.chars().nth(0) {
|
||||||
match c {
|
match c {
|
||||||
// List item
|
// List item
|
||||||
'-' | '*' => {
|
'-' | '*' => {
|
||||||
debug!("[*]: Line is list element");
|
debug!("[*]: Line is list element");
|
||||||
let mut start_delimitor;
|
|
||||||
let mut end_delimitor;
|
|
||||||
|
|
||||||
// In the future, support for list item that is not a link
|
if let Some((name, path)) = read_link(line) {
|
||||||
// Not sure if I should error on line I can't parse or just ignore them...
|
return Some(BookItem::Chapter("0".to_owned(), Chapter::new(name, path)))
|
||||||
if let Some(i) = line.find('[') { start_delimitor = i; }
|
} else { return None }
|
||||||
else {
|
},
|
||||||
debug!("[*]: '[' not found, this line is not a link. Ignoring...");
|
// Non-list element
|
||||||
return None
|
'[' => {
|
||||||
}
|
debug!("[*]: Line is a link element");
|
||||||
|
|
||||||
if let Some(i) = line[start_delimitor..].find("](") {
|
if let Some((name, path)) = read_link(line) {
|
||||||
end_delimitor = start_delimitor +i;
|
return Some(BookItem::Affix(Chapter::new(name, path)))
|
||||||
}
|
} else { return None }
|
||||||
else {
|
|
||||||
debug!("[*]: '](' not found, this line is not a link. Ignoring...");
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
name = line[start_delimitor + 1 .. end_delimitor].to_string();
|
|
||||||
|
|
||||||
start_delimitor = end_delimitor + 1;
|
|
||||||
if let Some(i) = line[start_delimitor..].find(')') {
|
|
||||||
end_delimitor = start_delimitor + i;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
debug!("[*]: ')' not found, this line is not a link. Ignoring...");
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
path = PathBuf::from(line[start_delimitor + 1 .. end_delimitor].to_string());
|
|
||||||
|
|
||||||
return Some(BookItem::new(name, path))
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -124,3 +178,39 @@ fn parse_line(l: &str) -> Option<BookItem> {
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_link(line: &str) -> Option<(String, PathBuf)> {
|
||||||
|
let mut start_delimitor;
|
||||||
|
let mut end_delimitor;
|
||||||
|
|
||||||
|
// In the future, support for list item that is not a link
|
||||||
|
// Not sure if I should error on line I can't parse or just ignore them...
|
||||||
|
if let Some(i) = line.find('[') { start_delimitor = i; }
|
||||||
|
else {
|
||||||
|
debug!("[*]: '[' not found, this line is not a link. Ignoring...");
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(i) = line[start_delimitor..].find("](") {
|
||||||
|
end_delimitor = start_delimitor +i;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
debug!("[*]: '](' not found, this line is not a link. Ignoring...");
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = line[start_delimitor + 1 .. end_delimitor].to_string();
|
||||||
|
|
||||||
|
start_delimitor = end_delimitor + 1;
|
||||||
|
if let Some(i) = line[start_delimitor..].find(')') {
|
||||||
|
end_delimitor = start_delimitor + i;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
debug!("[*]: ')' not found, this line is not a link. Ignoring...");
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = PathBuf::from(line[start_delimitor + 1 .. end_delimitor].to_string());
|
||||||
|
|
||||||
|
Some((name, path))
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ extern crate pulldown_cmark;
|
||||||
use renderer::html_handlebars::helpers;
|
use renderer::html_handlebars::helpers;
|
||||||
use renderer::Renderer;
|
use renderer::Renderer;
|
||||||
use book::MDBook;
|
use book::MDBook;
|
||||||
|
use book::bookitem::BookItem;
|
||||||
use {utils, theme};
|
use {utils, theme};
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
@ -57,64 +58,69 @@ impl Renderer for HtmlHandlebars {
|
||||||
|
|
||||||
// Render a file for every entry in the book
|
// Render a file for every entry in the book
|
||||||
let mut index = true;
|
let mut index = true;
|
||||||
for (_, item) in book.iter() {
|
for item in book.iter() {
|
||||||
|
|
||||||
if item.path != PathBuf::new() {
|
match item {
|
||||||
|
&BookItem::Chapter(_, ref ch) | &BookItem::Affix(ref ch) => {
|
||||||
|
if ch.path != PathBuf::new() {
|
||||||
|
|
||||||
let path = book.get_src().join(&item.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 = try!(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));
|
try!(f.read_to_string(&mut content));
|
||||||
|
|
||||||
// Render markdown using the pulldown-cmark crate
|
// Render markdown using the pulldown-cmark crate
|
||||||
content = render_html(&content);
|
content = render_html(&content);
|
||||||
print_content.push_str(&content);
|
print_content.push_str(&content);
|
||||||
|
|
||||||
// Remove content from previous file and render content for this one
|
// Remove content from previous file and render content for this one
|
||||||
data.remove("path");
|
data.remove("path");
|
||||||
match item.path.to_str() {
|
match ch.path.to_str() {
|
||||||
Some(p) => { data.insert("path".to_string(), p.to_json()); },
|
Some(p) => { data.insert("path".to_string(), p.to_json()); },
|
||||||
None => return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))),
|
None => return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Remove content from previous file and render content for this one
|
// Remove content from previous file and render content for this one
|
||||||
data.remove("content");
|
data.remove("content");
|
||||||
data.insert("content".to_string(), content.to_json());
|
data.insert("content".to_string(), content.to_json());
|
||||||
|
|
||||||
// Remove path to root from previous file and render content for this one
|
// Remove path to root from previous file and render content for this one
|
||||||
data.remove("path_to_root");
|
data.remove("path_to_root");
|
||||||
data.insert("path_to_root".to_string(), utils::path_to_root(&item.path).to_json());
|
data.insert("path_to_root".to_string(), utils::path_to_root(&ch.path).to_json());
|
||||||
|
|
||||||
// Rendere the handlebars template with the data
|
// Rendere the handlebars template with the data
|
||||||
debug!("[*]: Render template");
|
debug!("[*]: Render template");
|
||||||
let rendered = try!(handlebars.render("index", &data));
|
let rendered = try!(handlebars.render("index", &data));
|
||||||
|
|
||||||
debug!("[*]: Create file {:?}", &book.get_dest().join(&item.path).with_extension("html"));
|
debug!("[*]: Create file {:?}", &book.get_dest().join(&ch.path).with_extension("html"));
|
||||||
// Write to file
|
// Write to file
|
||||||
let mut file = try!(utils::create_file(&book.get_dest().join(&item.path).with_extension("html")));
|
let mut file = try!(utils::create_file(&book.get_dest().join(&ch.path).with_extension("html")));
|
||||||
output!("[*] Creating {:?} ✓", &book.get_dest().join(&item.path).with_extension("html"));
|
output!("[*] Creating {:?} ✓", &book.get_dest().join(&ch.path).with_extension("html"));
|
||||||
|
|
||||||
try!(file.write_all(&rendered.into_bytes()));
|
try!(file.write_all(&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");
|
||||||
try!(fs::copy(
|
try!(fs::copy(
|
||||||
book.get_dest().join(&item.path.with_extension("html")),
|
book.get_dest().join(&ch.path.with_extension("html")),
|
||||||
book.get_dest().join("index.html")
|
book.get_dest().join("index.html")
|
||||||
));
|
));
|
||||||
|
|
||||||
output!(
|
output!(
|
||||||
"[*] Creating index.html from {:?} ✓",
|
"[*] Creating index.html from {:?} ✓",
|
||||||
book.get_dest().join(&item.path.with_extension("html"))
|
book.get_dest().join(&ch.path.with_extension("html"))
|
||||||
);
|
);
|
||||||
index = false;
|
index = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,13 +175,30 @@ fn make_data(book: &MDBook) -> Result<BTreeMap<String,Json>, Box<Error>> {
|
||||||
|
|
||||||
let mut chapters = vec![];
|
let mut chapters = vec![];
|
||||||
|
|
||||||
for (section, item) in book.iter() {
|
for item in book.iter() {
|
||||||
|
// Create the data to inject in the template
|
||||||
let mut chapter = BTreeMap::new();
|
let mut chapter = BTreeMap::new();
|
||||||
chapter.insert("section".to_string(), section.to_json());
|
|
||||||
chapter.insert("name".to_string(), item.name.to_json());
|
match item {
|
||||||
match item.path.to_str() {
|
&BookItem::Affix(ref ch) => {
|
||||||
Some(p) => { chapter.insert("path".to_string(), p.to_json()); },
|
chapter.insert("name".to_string(), ch.name.to_json());
|
||||||
None => return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))),
|
match ch.path.to_str() {
|
||||||
|
Some(p) => { chapter.insert("path".to_string(), p.to_json()); },
|
||||||
|
None => return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
&BookItem::Chapter(ref s, ref ch) => {
|
||||||
|
chapter.insert("section".to_string(), s.to_json());
|
||||||
|
chapter.insert("name".to_string(), ch.name.to_json());
|
||||||
|
match ch.path.to_str() {
|
||||||
|
Some(p) => { chapter.insert("path".to_string(), p.to_json()); },
|
||||||
|
None => return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
&BookItem::Spacer => {
|
||||||
|
chapter.insert("spacer".to_string(), "_spacer_".to_json());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chapters.push(chapter);
|
chapters.push(chapter);
|
||||||
|
|
|
@ -28,7 +28,14 @@ impl HelperDef for RenderToc {
|
||||||
|
|
||||||
for item in decoded {
|
for item in decoded {
|
||||||
|
|
||||||
let level = item.get("section").expect("Error: section should be Some(_)").len() / 2;
|
// Spacer
|
||||||
|
if let Some(_) = item.get("spacer") {
|
||||||
|
try!(rc.writer.write("<li class=\"spacer\"></li>".as_bytes()));
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let level = if let Some(s) = item.get("section") { s.len() / 2 } else { 1 };
|
||||||
|
|
||||||
if level > current_level {
|
if level > current_level {
|
||||||
try!(rc.writer.write("<li>".as_bytes()));
|
try!(rc.writer.write("<li>".as_bytes()));
|
||||||
try!(rc.writer.write("<ul class=\"section\">".as_bytes()));
|
try!(rc.writer.write("<ul class=\"section\">".as_bytes()));
|
||||||
|
@ -42,7 +49,11 @@ impl HelperDef for RenderToc {
|
||||||
try!(rc.writer.write("<li>".as_bytes()));
|
try!(rc.writer.write("<li>".as_bytes()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try!(rc.writer.write("<li>".as_bytes()));
|
try!(rc.writer.write("<li".as_bytes()));
|
||||||
|
if let None = item.get("section") {
|
||||||
|
try!(rc.writer.write(" class=\"affix\"".as_bytes()));
|
||||||
|
}
|
||||||
|
try!(rc.writer.write(">".as_bytes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link
|
// Link
|
||||||
|
@ -74,10 +85,16 @@ impl HelperDef for RenderToc {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
try!(rc.writer.write("<strong>".as_bytes()));
|
// Section does not necessarily exist
|
||||||
try!(rc.writer.write(item.get("section").expect("Error: section should be Some(_)").as_bytes()));
|
if let Some(section) = item.get("section") {
|
||||||
try!(rc.writer.write("</strong> ".as_bytes()));
|
try!(rc.writer.write("<strong>".as_bytes()));
|
||||||
try!(rc.writer.write(item.get("name").expect("Error: name should be Some(_)").as_bytes()));
|
try!(rc.writer.write(section.as_bytes()));
|
||||||
|
try!(rc.writer.write("</strong> ".as_bytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(name) = item.get("name") {
|
||||||
|
try!(rc.writer.write(name.as_bytes()));
|
||||||
|
}
|
||||||
|
|
||||||
if path_exists {
|
if path_exists {
|
||||||
try!(rc.writer.write("</a>".as_bytes()));
|
try!(rc.writer.write("</a>".as_bytes()));
|
||||||
|
|
|
@ -102,6 +102,17 @@ html, body {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chapter .affix {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.chapter .spacer {
|
||||||
|
width: 100%;
|
||||||
|
height: 3px;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
margin: 10px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.menu-bar {
|
.menu-bar {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
|
|
Loading…
Reference in New Issue