1 use std
::path
::PathBuf
;
3 use std
::io
::{Read, Result, Error, ErrorKind}
;
4 use book
::bookitem
::{BookItem, Chapter}
;
6 pub fn construct_bookitems(path
: &PathBuf
) -> Result
<Vec
<BookItem
>> {
7 debug
!("[fn]: construct_bookitems");
8 let mut summary
= String
::new();
9 File
::open(path
)?
.read_to_string(&mut summary
)?
;
11 debug
!("[*]: Parse SUMMARY.md");
12 let top_items
= parse_level(&mut summary
.split('
\n'
).collect(), 0, vec
![0])?
;
13 debug
!("[*]: Done parsing SUMMARY.md");
17 fn parse_level(summary
: &mut Vec
<&str>, current_level
: i32, mut section
: Vec
<i32>) -> Result
<Vec
<BookItem
>> {
18 debug
!("[fn]: parse_level");
19 let mut items
: Vec
<BookItem
> = vec
![];
21 // Construct the book recursively
22 while !summary
.is_empty() {
24 // Indentation level of the line to parse
25 let level
= level(summary
[0], 4)?
;
27 // if level < current_level we remove the last digit of section,
28 // exit the current function,
29 // and return the parsed level to the calling function.
30 if level
< current_level
{
34 // if level > current_level we call ourselves to go one level deeper
35 if level
> current_level
{
36 // Level can not be root level !!
37 // Add a sub-number to section
41 .expect("There should be at least one item since this can't be the root level");
43 if let BookItem
::Chapter(ref s
, ref ch
) = last
{
44 let mut ch
= ch
.clone();
45 ch
.sub_items
= parse_level(summary
, level
, section
.clone())?
;
46 items
.push(BookItem
::Chapter(s
.clone(), ch
));
48 // Remove the last number from the section, because we got back to our level..
52 return Err(Error
::new(ErrorKind
::Other
,
53 "Your summary.md is messed up\n\n
55 Suffix and Spacer elements can only exist on the root level.\n
57 Prefix elements can only exist before any chapter and there can be \
58 no chapters after suffix elements."));
62 // level and current_level are the same, parse the line
63 item
= if let Some(parsed_item
) = parse_line(summary
[0]) {
65 // Eliminate possible errors and set section to -1 after suffix
67 // error if level != 0 and BookItem is != Chapter
69 BookItem
::Spacer
if level
> 0 => {
70 return Err(Error
::new(ErrorKind
::Other
,
71 "Your summary.md is messed up\n\n
73 Prefix, Suffix and Spacer elements can only exist on the \
76 elements can only exist before any chapter and there can be \
77 no chapters after suffix elements."))
80 // error if BookItem == Chapter and section == -1
81 BookItem
::Chapter(_
, _
) if section
[0] == -1 => {
82 return Err(Error
::new(ErrorKind
::Other
,
83 "Your summary.md is messed up\n\n
85 Prefix, Suffix and Spacer elements can only exist on the \
88 elements can only exist before any chapter and there can be \
89 no chapters after suffix elements."))
92 // Set section = -1 after suffix
93 BookItem
::Affix(_
) if section
[0] > 0 => {
101 BookItem
::Chapter(_
, ch
) => {
103 let len
= section
.len() - 1;
107 .fold("".to_owned(), |s
, i
| s
+ &i
.to_string() + ".");
108 BookItem
::Chapter(s
, ch
)
114 // If parse_line does not return Some(_) continue...
123 debug
!("[*]: Level: {:?}", items
);
128 fn level(line
: &str, spaces_in_tab
: i32) -> Result
<i32> {
129 debug
!("[fn]: level");
133 for ch
in line
.chars() {
139 if spaces
>= spaces_in_tab
{
145 // If there are spaces left, there is an indentation error
147 debug
!("[SUMMARY.md]:");
148 debug
!("\t[line]: {}", line
);
149 debug
!("[*]: There is an indentation error on this line. Indentation should be {} spaces", spaces_in_tab
);
150 return Err(Error
::new(ErrorKind
::Other
, format
!("Indentation error on line:\n\n{}", line
)));
157 fn parse_line(l
: &str) -> Option
<BookItem
> {
158 debug
!("[fn]: parse_line");
160 // Remove leading and trailing spaces or tabs
161 let line
= l
.trim_matches(|c
: char| c
== ' '
|| c
== '
\t'
);
163 // Spacers are "------"
164 if line
.starts_with("--") {
165 debug
!("[*]: Line is spacer");
166 return Some(BookItem
::Spacer
);
169 if let Some(c
) = line
.chars().nth(0) {
173 debug
!("[*]: Line is list element");
175 if let Some((name
, path
)) = read_link(line
) {
176 return Some(BookItem
::Chapter("0".to_owned(), Chapter
::new(name
, path
)));
183 debug
!("[*]: Line is a link element");
185 if let Some((name
, path
)) = read_link(line
) {
186 return Some(BookItem
::Affix(Chapter
::new(name
, path
)));
198 fn read_link(line
: &str) -> Option
<(String
, PathBuf
)> {
199 let mut start_delimitor
;
200 let mut end_delimitor
;
202 // In the future, support for list item that is not a link
203 // Not sure if I should error on line I can't parse or just ignore them...
204 if let Some(i
) = line
.find('
['
) {
207 debug
!("[*]: '[' not found, this line is not a link. Ignoring...");
211 if let Some(i
) = line
[start_delimitor
..].find("](") {
212 end_delimitor
= start_delimitor
+ i
;
214 debug
!("[*]: '](' not found, this line is not a link. Ignoring...");
218 let name
= line
[start_delimitor
+ 1..end_delimitor
].to_owned();
220 start_delimitor
= end_delimitor
+ 1;
221 if let Some(i
) = line
[start_delimitor
..].find('
)'
) {
222 end_delimitor
= start_delimitor
+ i
;
224 debug
!("[*]: ')' not found, this line is not a link. Ignoring...");
228 let path
= PathBuf
::from(line
[start_delimitor
+ 1..end_delimitor
].to_owned());