mirror of
https://github.com/rust-lang/mdBook
synced 2024-12-13 14:22:35 +00:00
Rudimentary: Parse SUMMARY.md, support for nested levels. Only list items: "- [name](path)" or "* [name](path)" #2
This commit is contained in:
parent
4fe0bc2de5
commit
7fa5b06ccb
8 changed files with 187 additions and 14 deletions
|
@ -3,6 +3,7 @@ use std::path::PathBuf;
|
|||
pub struct BookConfig {
|
||||
dest: PathBuf,
|
||||
src: PathBuf,
|
||||
indent_spaces: i32,
|
||||
multilingual: bool,
|
||||
}
|
||||
|
||||
|
@ -12,6 +13,7 @@ impl BookConfig {
|
|||
BookConfig {
|
||||
dest: PathBuf::from("book"),
|
||||
src: PathBuf::from("src"),
|
||||
indent_spaces: 4,
|
||||
multilingual: false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,41 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
pub struct BookItem {
|
||||
name: String,
|
||||
path: PathBuf,
|
||||
sub_items: Vec<BookItem>,
|
||||
pub name: String,
|
||||
pub path: PathBuf,
|
||||
pub sub_items: Vec<BookItem>,
|
||||
spacer: bool,
|
||||
}
|
||||
|
||||
pub enum ItemType {
|
||||
Pre,
|
||||
Chapter,
|
||||
Post
|
||||
}
|
||||
|
||||
impl BookItem {
|
||||
|
||||
fn new(name: String, path: PathBuf) -> Self {
|
||||
pub fn new(name: String, path: PathBuf) -> Self {
|
||||
|
||||
BookItem {
|
||||
name: name,
|
||||
path: path,
|
||||
sub_items: vec![],
|
||||
spacer: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spacer() -> Self {
|
||||
BookItem {
|
||||
name: String::from("SPACER"),
|
||||
path: PathBuf::new(),
|
||||
sub_items: vec![],
|
||||
spacer: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn push(&mut self, item: BookItem) {
|
||||
self.sub_items.push(item);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::path::PathBuf;
|
||||
use std::fs::{self, File, metadata};
|
||||
use std::io::Write;
|
||||
use std::io::Error;
|
||||
use std::io::{Write, Result};
|
||||
|
||||
use book::bookconfig::BookConfig;
|
||||
use book::bookitem::BookItem;
|
||||
use parse;
|
||||
|
||||
pub struct MDBook {
|
||||
title: String,
|
||||
|
@ -37,7 +37,7 @@ impl MDBook {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn init(&self) -> Result<(), Error> {
|
||||
pub fn init(&self) -> Result<()> {
|
||||
|
||||
let dest = self.config.dest();
|
||||
let src = self.config.src();
|
||||
|
@ -67,11 +67,11 @@ impl MDBook {
|
|||
Err(_) => {
|
||||
// There is a very high chance that the error is due to the fact that
|
||||
// the directory / file does not exist
|
||||
Result::Ok(File::create(&src.join("SUMMARY.md")).unwrap())
|
||||
Ok(File::create(&src.join("SUMMARY.md")).unwrap())
|
||||
},
|
||||
Ok(_) => {
|
||||
/* If there is no error, the directory / file does exist */
|
||||
Result::Err("SUMMARY.md does already exist")
|
||||
Err("SUMMARY.md does already exist")
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -87,9 +87,9 @@ impl MDBook {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn build(&self) -> Result<(), Error> {
|
||||
|
||||
pub fn build(&mut self) -> Result<()> {
|
||||
|
||||
try!(self.parse_summary());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -116,4 +116,23 @@ impl MDBook {
|
|||
self
|
||||
}
|
||||
|
||||
|
||||
// Construct book
|
||||
fn parse_summary(&mut self) -> Result<()> {
|
||||
|
||||
// When append becomes stale, use self.content.append() ...
|
||||
let book_items = try!(parse::construct_bookitems(&self.config.src().join("SUMMARY.md")));
|
||||
|
||||
for item in book_items {
|
||||
self.content.push(item)
|
||||
}
|
||||
|
||||
// Debug
|
||||
for item in &self.content {
|
||||
println!("name: \"{}\" path: {:?}", item.name, item.path);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
pub mod mdbook;
|
||||
pub mod bookitem;
|
||||
mod bookconfig;
|
||||
mod bookitem;
|
||||
|
||||
|
||||
use self::bookconfig::BookConfig;
|
||||
use self::bookitem::BookItem;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
mod book;
|
||||
mod parse;
|
||||
|
||||
pub use book::mdbook::MDBook;
|
||||
use book::bookitem::BookItem;
|
||||
|
|
|
@ -143,8 +143,13 @@ fn build(args: Vec<String>) {
|
|||
println!("{}", usage);
|
||||
}
|
||||
|
||||
let dir = std::env::current_dir().unwrap();
|
||||
let book = MDBook::new(&dir);
|
||||
let dir = if args.len() <= 2 {
|
||||
std::env::current_dir().unwrap()
|
||||
} else {
|
||||
std::env::current_dir().unwrap().join(&args[2])
|
||||
};
|
||||
|
||||
let mut book = MDBook::new(&dir);
|
||||
|
||||
if let Err(e) = book.build() {
|
||||
println!("Error: {}", e);
|
||||
|
|
3
src/parse/mod.rs
Normal file
3
src/parse/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub use self::summary::construct_bookitems;
|
||||
|
||||
pub mod summary;
|
121
src/parse/summary.rs
Normal file
121
src/parse/summary.rs
Normal file
|
@ -0,0 +1,121 @@
|
|||
use std::path::PathBuf;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Result, Error, ErrorKind};
|
||||
use book::bookitem::BookItem;
|
||||
|
||||
/*
|
||||
pub enum LineType {
|
||||
Blank,
|
||||
Header,
|
||||
Link(String, PathBuf), // Name, Path
|
||||
ListItem(String, PathBuf, i32), // Name, Path, Level
|
||||
Other,
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn construct_bookitems(path: &PathBuf) -> Result<Vec<BookItem>> {
|
||||
let mut summary = String::new();
|
||||
try!(try!(File::open(path)).read_to_string(&mut summary));
|
||||
|
||||
let top_items = try!(parse_level(&mut summary.split('\n').collect(), 0));
|
||||
|
||||
|
||||
|
||||
Ok(top_items)
|
||||
}
|
||||
|
||||
fn parse_level(summary: &mut Vec<&str>, current_level: i32) -> Result<Vec<BookItem>> {
|
||||
|
||||
let mut items: Vec<BookItem> = vec![];
|
||||
|
||||
loop {
|
||||
if summary.len() <= 0 { break }
|
||||
|
||||
let level = try!(level(summary[0], 4));
|
||||
|
||||
if current_level > level { break }
|
||||
else if current_level < level {
|
||||
items.last_mut().unwrap().sub_items = try!(parse_level(summary, level))
|
||||
}
|
||||
else {
|
||||
// Do the thing
|
||||
if let Some(item) = parse_line(summary[0].clone()) {
|
||||
items.push(item);
|
||||
}
|
||||
summary.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(items)
|
||||
}
|
||||
|
||||
fn level(line: &str, spaces_in_tab: i32) -> Result<i32> {
|
||||
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 {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("There is an indentation error on line:\n\n{}", line)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Ok(level)
|
||||
}
|
||||
|
||||
|
||||
fn parse_line(line: &str) -> Option<BookItem> {
|
||||
let mut name;
|
||||
let mut path;
|
||||
// Remove leading and trailing spaces or tabs
|
||||
line.trim_matches(|c: char| { c == ' ' || c == '\t' });
|
||||
|
||||
if let Some(c) = line.chars().nth(0) {
|
||||
match c {
|
||||
// List item
|
||||
'-' | '*' => {
|
||||
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 { return None }
|
||||
|
||||
if let Some(i) = line[start_delimitor..].find("](") {
|
||||
end_delimitor = start_delimitor +i;
|
||||
}
|
||||
else { 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 { return None }
|
||||
|
||||
path = PathBuf::from(line[start_delimitor + 1 .. end_delimitor].to_string());
|
||||
|
||||
return Some(BookItem::new(name, path))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
Loading…
Reference in a new issue