From ce6dbd6736d85b9ecac5d68f826334bb58ca4e2e Mon Sep 17 00:00:00 2001 From: Michael Bryan Date: Mon, 21 Aug 2017 10:19:23 +0800 Subject: [PATCH] Deleted previous bookitem.md and moved loader contents across --- src/{loader => book}/book.rs | 20 ++- src/book/mod.rs | 4 +- src/{loader => book}/summary.rs | 0 src/lib.rs | 4 +- src/loader/mod.rs | 50 ------- src/parse/mod.rs | 3 - src/parse/summary.rs | 231 -------------------------------- 7 files changed, 23 insertions(+), 289 deletions(-) rename src/{loader => book}/book.rs (93%) rename src/{loader => book}/summary.rs (100%) delete mode 100644 src/loader/mod.rs delete mode 100644 src/parse/mod.rs delete mode 100644 src/parse/summary.rs diff --git a/src/loader/book.rs b/src/book/book.rs similarity index 93% rename from src/loader/book.rs rename to src/book/book.rs index 803c4020..6bbb47bf 100644 --- a/src/loader/book.rs +++ b/src/book/book.rs @@ -3,10 +3,28 @@ use std::collections::VecDeque; use std::fs::File; use std::io::Read; -use loader::summary::{Summary, Link, SummaryItem, SectionNumber}; +use super::summary::{Summary, Link, SummaryItem, SectionNumber}; use errors::*; +/// Load a book into memory from its `src/` directory. +pub fn load_book>(src_dir: P) -> Result { + 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. /// /// For the moment a book is just a collection of `BookItems`. diff --git a/src/book/mod.rs b/src/book/mod.rs index e8896e0a..35a5cfb7 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -1,6 +1,8 @@ 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::fs::{self, File}; diff --git a/src/loader/summary.rs b/src/book/summary.rs similarity index 100% rename from src/loader/summary.rs rename to src/book/summary.rs diff --git a/src/lib.rs b/src/lib.rs index 97af9a4f..ec6e76b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,17 +90,15 @@ extern crate tempdir; #[cfg(test)] extern crate tempdir; -mod parse; mod preprocess; pub mod book; pub mod config; pub mod renderer; pub mod theme; pub mod utils; -pub mod loader; pub use book::MDBook; -pub use book::BookItem; +pub use book::Book; pub use renderer::Renderer; /// The error types used through out this crate. diff --git a/src/loader/mod.rs b/src/loader/mod.rs deleted file mode 100644 index 0209497f..00000000 --- a/src/loader/mod.rs +++ /dev/null @@ -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>(src_dir: P) -> Result { - 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) -} diff --git a/src/parse/mod.rs b/src/parse/mod.rs deleted file mode 100644 index c8c8aab7..00000000 --- a/src/parse/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub use self::summary::construct_bookitems; - -pub mod summary; diff --git a/src/parse/summary.rs b/src/parse/summary.rs deleted file mode 100644 index cc8452d4..00000000 --- a/src/parse/summary.rs +++ /dev/null @@ -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> { - 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) -> Result> { - debug!("[fn]: parse_level"); - let mut items: Vec = 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 { - 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 { - 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)) -}