Deleted previous bookitem.md and moved loader contents across

This commit is contained in:
Michael Bryan 2017-08-21 10:19:23 +08:00
parent 02d1d9317d
commit ce6dbd6736
7 changed files with 23 additions and 289 deletions

View File

@ -3,10 +3,28 @@ use std::collections::VecDeque;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use loader::summary::{Summary, Link, SummaryItem, SectionNumber}; use super::summary::{Summary, Link, SummaryItem, SectionNumber};
use errors::*; use errors::*;
/// Load a book into memory from its `src/` directory.
pub fn load_book<P: AsRef<Path>>(src_dir: P) -> Result<Book> {
let src_dir = src_dir.as_ref();
let summary_md = src_dir.join("SUMMARY.md");
let mut summary_content = String::new();
File::open(summary_md)
.chain_err(|| "Couldn't open SUMMARY.md")?
.read_to_string(&mut summary_content)?;
let summary = parse_summary(&summary_content).chain_err(
|| "Summary parsing failed",
)?;
load_book_from_disk(&summary, src_dir)
}
/// A dumb tree structure representing a book. /// A dumb tree structure representing a book.
/// ///
/// For the moment a book is just a collection of `BookItems`. /// For the moment a book is just a collection of `BookItems`.

View File

@ -1,6 +1,8 @@
pub mod bookitem; pub mod bookitem;
pub mod book;
pub mod summary;
pub use self::bookitem::{BookItem, BookItems}; use self::book::{parse_book, Book, BookItem, BookItems};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::fs::{self, File}; use std::fs::{self, File};

View File

@ -90,17 +90,15 @@ extern crate tempdir;
#[cfg(test)] #[cfg(test)]
extern crate tempdir; extern crate tempdir;
mod parse;
mod preprocess; mod preprocess;
pub mod book; pub mod book;
pub mod config; pub mod config;
pub mod renderer; pub mod renderer;
pub mod theme; pub mod theme;
pub mod utils; pub mod utils;
pub mod loader;
pub use book::MDBook; pub use book::MDBook;
pub use book::BookItem; pub use book::Book;
pub use renderer::Renderer; pub use renderer::Renderer;
/// The error types used through out this crate. /// The error types used through out this crate.

View File

@ -1,50 +0,0 @@
//! Functionality for loading the internal book representation from disk.
//!
//! The typical use case is to create a `Loader` pointing at the correct
//! source directory then call the `load()` method. Internally this will
//! search for the `SUMMARY.md` file, parse it, then use the parsed
//! `Summary` to construct an in-memory representation of the entire book.
//!
//! # Examples
//!
//! ```rust,no_run
//! # fn run() -> mdbook::errors::Result<()> {
//! use mdbook::loader::load_book;
//! let book = load_book("./src/")?;
//! # Ok(())
//! # }
//! # fn main() { run().unwrap() }
//! ```
#![deny(missing_docs)]
use std::path::Path;
use std::fs::File;
use std::io::Read;
use errors::*;
mod summary;
mod book;
pub use self::book::{Book, BookItems, BookItem, Chapter};
pub use self::summary::SectionNumber;
use self::book::load_book_from_disk;
use self::summary::parse_summary;
/// Load a book into memory from its `src/` directory.
pub fn load_book<P: AsRef<Path>>(src_dir: P) -> Result<Book> {
let src_dir = src_dir.as_ref();
let summary_md = src_dir.join("SUMMARY.md");
let mut summary_content = String::new();
File::open(summary_md)
.chain_err(|| "Couldn't open SUMMARY.md")?
.read_to_string(&mut summary_content)?;
let summary = parse_summary(&summary_content).chain_err(
|| "Summary parsing failed",
)?;
load_book_from_disk(&summary, src_dir)
}

View File

@ -1,3 +0,0 @@
pub use self::summary::construct_bookitems;
pub mod summary;

View File

@ -1,231 +0,0 @@
use std::path::PathBuf;
use std::fs::File;
use std::io::{Read, Result, Error, ErrorKind};
use book::bookitem::{BookItem, Chapter};
pub fn construct_bookitems(path: &PathBuf) -> Result<Vec<BookItem>> {
debug!("[fn]: construct_bookitems");
let mut summary = String::new();
File::open(path)?.read_to_string(&mut summary)?;
debug!("[*]: Parse SUMMARY.md");
let top_items = parse_level(&mut summary.split('\n').collect(), 0, vec![0])?;
debug!("[*]: Done parsing SUMMARY.md");
Ok(top_items)
}
fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32>) -> Result<Vec<BookItem>> {
debug!("[fn]: parse_level");
let mut items: Vec<BookItem> = vec![];
// Construct the book recursively
while !summary.is_empty() {
let item: BookItem;
// Indentation level of the line to parse
let level = level(summary[0], 4)?;
// if level < current_level we remove the last digit of section,
// exit the current function,
// and return the parsed level to the calling function.
if level < current_level {
break;
}
// 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(0);
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 {
let mut ch = ch.clone();
ch.sub_items = 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,
"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,
"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,
"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)
}
fn level(line: &str, spaces_in_tab: i32) -> Result<i32> {
debug!("[fn]: level");
let mut spaces = 0;
let mut level = 0;
for ch in line.chars() {
match ch {
' ' => spaces += 1,
'\t' => level += 1,
_ => break,
}
if spaces >= spaces_in_tab {
level += 1;
spaces = 0;
}
}
// If there are spaces left, there is an indentation error
if spaces > 0 {
debug!("[SUMMARY.md]:");
debug!("\t[line]: {}", line);
debug!("[*]: There is an indentation error on this line. Indentation should be {} spaces", spaces_in_tab);
return Err(Error::new(ErrorKind::Other, format!("Indentation error on line:\n\n{}", line)));
}
Ok(level)
}
fn parse_line(l: &str) -> Option<BookItem> {
debug!("[fn]: parse_line");
// Remove leading and trailing spaces or tabs
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) {
match c {
// List item
'-' | '*' => {
debug!("[*]: Line is list element");
if let Some((name, path)) = read_link(line) {
return Some(BookItem::Chapter("0".to_owned(), Chapter::new(name, path)));
} else {
return None;
}
},
// Non-list element
'[' => {
debug!("[*]: Line is a link element");
if let Some((name, path)) = read_link(line) {
return Some(BookItem::Affix(Chapter::new(name, path)));
} else {
return 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_owned();
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_owned());
Some((name, path))
}