Add a rustfmt config and run rustfmt on the code base
This commit is contained in:
parent
6bac41caa8
commit
ad0794a0bd
|
@ -0,0 +1,15 @@
|
||||||
|
write_mode = "Overwrite"
|
||||||
|
|
||||||
|
max_width = 120
|
||||||
|
ideal_width = 120
|
||||||
|
fn_call_width = 100
|
||||||
|
|
||||||
|
fn_args_density = "Compressed"
|
||||||
|
|
||||||
|
enum_trailing_comma = true
|
||||||
|
match_block_trailing_comma = true
|
||||||
|
struct_trailing_comma = "Always"
|
||||||
|
wrap_comments = true
|
||||||
|
|
||||||
|
report_todo = "Always"
|
||||||
|
report_fixme = "Always"
|
|
@ -56,12 +56,12 @@ fn main() {
|
||||||
|
|
||||||
// Check which subcomamnd the user ran...
|
// Check which subcomamnd the user ran...
|
||||||
let res = match matches.subcommand() {
|
let res = match matches.subcommand() {
|
||||||
("init", Some(sub_matches)) => init(sub_matches),
|
("init", Some(sub_matches)) => init(sub_matches),
|
||||||
("build", Some(sub_matches)) => build(sub_matches),
|
("build", Some(sub_matches)) => build(sub_matches),
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
("watch", Some(sub_matches)) => watch(sub_matches),
|
("watch", Some(sub_matches)) => watch(sub_matches),
|
||||||
("test", Some(sub_matches)) => test(sub_matches),
|
("test", Some(sub_matches)) => test(sub_matches),
|
||||||
(_, _) => unreachable!()
|
(_, _) => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
|
@ -77,7 +77,7 @@ fn confirm() -> bool {
|
||||||
io::stdin().read_line(&mut s).ok();
|
io::stdin().read_line(&mut s).ok();
|
||||||
match &*s.trim() {
|
match &*s.trim() {
|
||||||
"Y" | "y" | "yes" | "Yes" => true,
|
"Y" | "y" | "yes" | "Yes" => true,
|
||||||
_ => false
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
if confirm() {
|
if confirm() {
|
||||||
book.create_gitignore();
|
book.create_gitignore();
|
||||||
println!("\n.gitignore created.");
|
println!("\n.gitignore created.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("\nAll done, no errors...");
|
println!("\nAll done, no errors...");
|
||||||
|
@ -151,66 +151,68 @@ fn watch(args: &ArgMatches) -> Result<(), Box<Error>> {
|
||||||
let book = MDBook::new(&book_dir).read_config();
|
let book = MDBook::new(&book_dir).read_config();
|
||||||
|
|
||||||
// Create a channel to receive the events.
|
// Create a channel to receive the events.
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
|
|
||||||
let w: Result<notify::RecommendedWatcher, notify::Error> = notify::Watcher::new(tx);
|
let w: Result<notify::RecommendedWatcher, notify::Error> = notify::Watcher::new(tx);
|
||||||
|
|
||||||
match w {
|
match w {
|
||||||
Ok(mut watcher) => {
|
Ok(mut watcher) => {
|
||||||
|
|
||||||
// Add the source directory to the watcher
|
// Add the source directory to the watcher
|
||||||
if let Err(e) = watcher.watch(book.get_src()) {
|
if let Err(e) = watcher.watch(book.get_src()) {
|
||||||
println!("Error while watching {:?}:\n {:?}", book.get_src(), e);
|
println!("Error while watching {:?}:\n {:?}", book.get_src(), e);
|
||||||
::std::process::exit(0);
|
::std::process::exit(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add the book.json file to the watcher if it exists, because it's not
|
// Add the book.json file to the watcher if it exists, because it's not
|
||||||
// located in the source directory
|
// located in the source directory
|
||||||
if let Err(_) = watcher.watch(book_dir.join("book.json")) {
|
if let Err(_) = watcher.watch(book_dir.join("book.json")) {
|
||||||
// do nothing if book.json is not found
|
// do nothing if book.json is not found
|
||||||
}
|
}
|
||||||
|
|
||||||
let previous_time = time::get_time().sec;
|
let previous_time = time::get_time().sec;
|
||||||
|
|
||||||
crossbeam::scope(|scope| {
|
crossbeam::scope(|scope| {
|
||||||
loop {
|
loop {
|
||||||
match rx.recv() {
|
match rx.recv() {
|
||||||
Ok(event) => {
|
Ok(event) => {
|
||||||
|
|
||||||
// Skip the event if an event has already been issued in the last second
|
// Skip the event if an event has already been issued in the last second
|
||||||
if time::get_time().sec - previous_time < 1 { continue }
|
if time::get_time().sec - previous_time < 1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(path) = event.path {
|
if let Some(path) = event.path {
|
||||||
// Trigger the build process in a new thread (to keep receiving events)
|
// Trigger the build process in a new thread (to keep receiving events)
|
||||||
scope.spawn(move || {
|
scope.spawn(move || {
|
||||||
println!("File changed: {:?}\nBuilding book...\n", path);
|
println!("File changed: {:?}\nBuilding book...\n", path);
|
||||||
match build(args) {
|
match build(args) {
|
||||||
Err(e) => println!("Error while building: {:?}", e),
|
Err(e) => println!("Error while building: {:?}", e),
|
||||||
_ => {}
|
_ => {},
|
||||||
}
|
}
|
||||||
println!("");
|
println!("");
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("An error occured: {:?}", e);
|
println!("An error occured: {:?}", e);
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error while trying to watch the files:\n\n\t{:?}", e);
|
println!("Error while trying to watch the files:\n\n\t{:?}", e);
|
||||||
::std::process::exit(0);
|
::std::process::exit(0);
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -230,9 +232,9 @@ fn get_book_dir(args: &ArgMatches) -> PathBuf {
|
||||||
// Check if path is relative from current dir, or absolute...
|
// Check if path is relative from current dir, or absolute...
|
||||||
let p = Path::new(dir);
|
let p = Path::new(dir);
|
||||||
if p.is_relative() {
|
if p.is_relative() {
|
||||||
env::current_dir().unwrap().join(dir)
|
env::current_dir().unwrap().join(dir)
|
||||||
} else {
|
} else {
|
||||||
p.to_path_buf()
|
p.to_path_buf()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
env::current_dir().unwrap()
|
env::current_dir().unwrap()
|
||||||
|
|
|
@ -26,7 +26,7 @@ impl BookConfig {
|
||||||
root: root.to_owned(),
|
root: root.to_owned(),
|
||||||
dest: PathBuf::from("book"),
|
dest: PathBuf::from("book"),
|
||||||
src: PathBuf::from("src"),
|
src: PathBuf::from("src"),
|
||||||
indent_spaces: 4, // indentation used for SUMMARY.md
|
indent_spaces: 4, // indentation used for SUMMARY.md
|
||||||
multilingual: false,
|
multilingual: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ impl BookConfig {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
debug!("[*]: Failed to open {:?}", root.join("book.json"));
|
debug!("[*]: Failed to open {:?}", root.join("book.json"));
|
||||||
return self
|
return self;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -49,7 +49,9 @@ impl BookConfig {
|
||||||
|
|
||||||
// Just return if an error occured.
|
// Just return if an error occured.
|
||||||
// I would like to propagate the error, but I have to return `&self`
|
// I would like to propagate the error, but I have to return `&self`
|
||||||
if let Err(_) = config_file.read_to_string(&mut data) { return self }
|
if let Err(_) = config_file.read_to_string(&mut data) {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
// Convert to JSON
|
// Convert to JSON
|
||||||
if let Ok(config) = Json::from_str(&data) {
|
if let Ok(config) = Json::from_str(&data) {
|
||||||
|
@ -57,9 +59,15 @@ impl BookConfig {
|
||||||
|
|
||||||
debug!("[*]: Extracting data from config");
|
debug!("[*]: Extracting data from config");
|
||||||
// Title, author, description
|
// Title, author, description
|
||||||
if let Some(a) = config.find_path(&["title"]) { self.title = a.to_string().replace("\"", "") }
|
if let Some(a) = config.find_path(&["title"]) {
|
||||||
if let Some(a) = config.find_path(&["author"]) { self.author = a.to_string().replace("\"", "") }
|
self.title = a.to_string().replace("\"", "")
|
||||||
if let Some(a) = config.find_path(&["description"]) { self.description = a.to_string().replace("\"", "") }
|
}
|
||||||
|
if let Some(a) = config.find_path(&["author"]) {
|
||||||
|
self.author = a.to_string().replace("\"", "")
|
||||||
|
}
|
||||||
|
if let Some(a) = config.find_path(&["description"]) {
|
||||||
|
self.description = a.to_string().replace("\"", "")
|
||||||
|
}
|
||||||
|
|
||||||
// Destination
|
// Destination
|
||||||
if let Some(a) = config.find_path(&["dest"]) {
|
if let Some(a) = config.find_path(&["dest"]) {
|
||||||
|
@ -71,7 +79,9 @@ impl BookConfig {
|
||||||
let dest = self.get_root().join(&dest).to_owned();
|
let dest = self.get_root().join(&dest).to_owned();
|
||||||
self.set_dest(&dest);
|
self.set_dest(&dest);
|
||||||
},
|
},
|
||||||
false => { self.set_dest(&dest); },
|
false => {
|
||||||
|
self.set_dest(&dest);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,5 +115,4 @@ impl BookConfig {
|
||||||
self.src = src.to_owned();
|
self.src = src.to_owned();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ pub struct BookItems<'a> {
|
||||||
|
|
||||||
|
|
||||||
impl Chapter {
|
impl Chapter {
|
||||||
|
|
||||||
pub fn new(name: String, path: PathBuf) -> Self {
|
pub fn new(name: String, path: PathBuf) -> Self {
|
||||||
|
|
||||||
Chapter {
|
Chapter {
|
||||||
|
@ -40,13 +39,14 @@ impl Chapter {
|
||||||
|
|
||||||
|
|
||||||
impl ToJson for Chapter {
|
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_owned(), self.name.to_json());
|
m.insert("name".to_owned(), self.name.to_json());
|
||||||
m.insert("path".to_owned(),self.path.to_str()
|
m.insert("path".to_owned(),
|
||||||
.expect("Json conversion failed for path").to_json()
|
self.path
|
||||||
);
|
.to_str()
|
||||||
|
.expect("Json conversion failed for path")
|
||||||
|
.to_json());
|
||||||
m.to_json()
|
m.to_json()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ impl<'a> Iterator for BookItems<'a> {
|
||||||
Some((parent_items, parent_idx)) => {
|
Some((parent_items, parent_idx)) => {
|
||||||
self.items = parent_items;
|
self.items = parent_items;
|
||||||
self.current_index = parent_idx + 1;
|
self.current_index = parent_idx + 1;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let cur = self.items.get(self.current_index).unwrap();
|
let cur = self.items.get(self.current_index).unwrap();
|
||||||
|
@ -79,10 +79,10 @@ impl<'a> Iterator for BookItems<'a> {
|
||||||
},
|
},
|
||||||
BookItem::Spacer => {
|
BookItem::Spacer => {
|
||||||
self.current_index += 1;
|
self.current_index += 1;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return Some(cur)
|
return Some(cur);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ pub struct MDBook {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MDBook {
|
impl MDBook {
|
||||||
|
|
||||||
/// Create a new `MDBook` struct with root directory `root`
|
/// Create a new `MDBook` struct with root directory `root`
|
||||||
///
|
///
|
||||||
/// - The default source directory is set to `root/src`
|
/// - The default source directory is set to `root/src`
|
||||||
|
@ -148,11 +147,11 @@ impl MDBook {
|
||||||
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, "# {}", ch.name));
|
try!(writeln!(f, "# {}", ch.name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,20 +171,19 @@ impl MDBook {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let relative = self.get_dest().strip_prefix(self.get_root())
|
let relative = self.get_dest()
|
||||||
.expect("Destination is not relative to root.");
|
.strip_prefix(self.get_root())
|
||||||
|
.expect("Destination is not relative to root.");
|
||||||
let relative = relative.to_str()
|
let relative = relative.to_str()
|
||||||
.expect("Path could not be yielded into a string slice.");
|
.expect("Path could not be yielded into a string slice.");
|
||||||
|
|
||||||
debug!("[*]: {:?} does not exist, trying to create .gitignore", gitignore);
|
debug!("[*]: {:?} does not exist, trying to create .gitignore", gitignore);
|
||||||
|
|
||||||
let mut f = File::create(&gitignore)
|
let mut f = File::create(&gitignore).expect("Could not create file.");
|
||||||
.expect("Could not create file.");
|
|
||||||
|
|
||||||
debug!("[*]: Writing to .gitignore");
|
debug!("[*]: Writing to .gitignore");
|
||||||
|
|
||||||
writeln!(f, "{}", relative)
|
writeln!(f, "{}", relative).expect("Could not write to file.");
|
||||||
.expect("Could not write to file.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,9 +308,9 @@ impl MDBook {
|
||||||
println!("[*]: Testing file: {:?}", path);
|
println!("[*]: Testing file: {:?}", path);
|
||||||
|
|
||||||
let output_result = Command::new("rustdoc")
|
let output_result = Command::new("rustdoc")
|
||||||
.arg(&path)
|
.arg(&path)
|
||||||
.arg("--test")
|
.arg("--test")
|
||||||
.output();
|
.output();
|
||||||
let output = try!(output_result);
|
let output = try!(output_result);
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
|
@ -322,8 +320,8 @@ impl MDBook {
|
||||||
String::from_utf8_lossy(&output.stderr)))) as Box<Error>);
|
String::from_utf8_lossy(&output.stderr)))) as Box<Error>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
_ => {}
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -337,11 +335,13 @@ impl MDBook {
|
||||||
|
|
||||||
// Handle absolute and relative paths
|
// Handle absolute and relative paths
|
||||||
match dest.is_absolute() {
|
match dest.is_absolute() {
|
||||||
true => { self.config.set_dest(dest); },
|
true => {
|
||||||
|
self.config.set_dest(dest);
|
||||||
|
},
|
||||||
false => {
|
false => {
|
||||||
let dest = self.config.get_root().join(dest).to_owned();
|
let dest = self.config.get_root().join(dest).to_owned();
|
||||||
self.config.set_dest(&dest);
|
self.config.set_dest(&dest);
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
|
@ -355,11 +355,13 @@ impl MDBook {
|
||||||
|
|
||||||
// Handle absolute and relative paths
|
// Handle absolute and relative paths
|
||||||
match src.is_absolute() {
|
match src.is_absolute() {
|
||||||
true => { self.config.set_src(src); },
|
true => {
|
||||||
|
self.config.set_src(src);
|
||||||
|
},
|
||||||
false => {
|
false => {
|
||||||
let src = self.config.get_root().join(src).to_owned();
|
let src = self.config.get_root().join(src).to_owned();
|
||||||
self.config.set_src(&src);
|
self.config.set_src(&src);
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
|
@ -391,7 +393,7 @@ impl MDBook {
|
||||||
self.config.description = description.to_owned();
|
self.config.description = description.to_owned();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_description(&self) -> &str {
|
pub fn get_description(&self) -> &str {
|
||||||
&self.config.description
|
&self.config.description
|
||||||
}
|
}
|
||||||
|
@ -402,5 +404,4 @@ impl MDBook {
|
||||||
self.content = try!(parse::construct_bookitems(&self.config.get_src().join("SUMMARY.md")));
|
self.content = try!(parse::construct_bookitems(&self.config.get_src().join("SUMMARY.md")));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,9 @@ fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32
|
||||||
|
|
||||||
// if level < current_level we remove the last digit of section, exit the current function,
|
// if level < current_level we remove the last digit of section, exit the current function,
|
||||||
// and return the parsed level to the calling function.
|
// and return the parsed level to the calling function.
|
||||||
if level < current_level { break }
|
if level < current_level {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// if level > current_level we call ourselves to go one level deeper
|
// if level > current_level we call ourselves to go one level deeper
|
||||||
if level > current_level {
|
if level > current_level {
|
||||||
|
@ -42,13 +44,15 @@ fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32
|
||||||
|
|
||||||
// Remove the last number from the section, because we got back to our level..
|
// Remove the last number from the section, because we got back to our level..
|
||||||
section.pop();
|
section.pop();
|
||||||
continue
|
continue;
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::new( ErrorKind::Other, format!(
|
return Err(Error::new(ErrorKind::Other,
|
||||||
"Your summary.md is messed up\n\n
|
format!("Your summary.md is messed up\n\n
|
||||||
Prefix, Suffix and Spacer elements can only exist on the root level.\n
|
Prefix, \
|
||||||
Prefix elements can only exist before any chapter and there can be no chapters after suffix elements."
|
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 {
|
} else {
|
||||||
|
@ -59,26 +63,32 @@ fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32
|
||||||
match parsed_item {
|
match parsed_item {
|
||||||
// error if level != 0 and BookItem is != Chapter
|
// error if level != 0 and BookItem is != Chapter
|
||||||
BookItem::Affix(_) | BookItem::Spacer if level > 0 => {
|
BookItem::Affix(_) | BookItem::Spacer if level > 0 => {
|
||||||
return Err(Error::new( ErrorKind::Other, format!(
|
return Err(Error::new(ErrorKind::Other,
|
||||||
"Your summary.md is messed up\n\n
|
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."
|
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
|
// error if BookItem == Chapter and section == -1
|
||||||
BookItem::Chapter(_, _) if section[0] == -1 => {
|
BookItem::Chapter(_, _) if section[0] == -1 => {
|
||||||
return Err(Error::new( ErrorKind::Other, format!(
|
return Err(Error::new(ErrorKind::Other,
|
||||||
"Your summary.md is messed up\n\n
|
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."
|
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
|
// Set section = -1 after suffix
|
||||||
BookItem::Affix(_) if section[0] > 0 => {
|
BookItem::Affix(_) if section[0] > 0 => {
|
||||||
section[0] = -1;
|
section[0] = -1;
|
||||||
}
|
},
|
||||||
|
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
@ -86,12 +96,12 @@ fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32
|
||||||
match parsed_item {
|
match parsed_item {
|
||||||
BookItem::Chapter(_, ch) => {
|
BookItem::Chapter(_, ch) => {
|
||||||
// Increment section
|
// Increment section
|
||||||
let len = section.len() -1;
|
let len = section.len() - 1;
|
||||||
section[len] += 1;
|
section[len] += 1;
|
||||||
let s = section.iter().fold("".to_owned(), |s, i| s + &i.to_string() + ".");
|
let s = section.iter().fold("".to_owned(), |s, i| s + &i.to_string() + ".");
|
||||||
BookItem::Chapter(s, ch)
|
BookItem::Chapter(s, ch)
|
||||||
}
|
},
|
||||||
_ => parsed_item
|
_ => parsed_item,
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -131,11 +141,7 @@ fn level(line: &str, spaces_in_tab: i32) -> Result<i32> {
|
||||||
debug!("[SUMMARY.md]:");
|
debug!("[SUMMARY.md]:");
|
||||||
debug!("\t[line]: {}", line);
|
debug!("\t[line]: {}", line);
|
||||||
debug!("[*]: There is an indentation error on this line. Indentation should be {} spaces", spaces_in_tab);
|
debug!("[*]: There is an indentation error on this line. Indentation should be {} spaces", spaces_in_tab);
|
||||||
return Err(Error::new(
|
return Err(Error::new(ErrorKind::Other, format!("Indentation error on line:\n\n{}", line)));
|
||||||
ErrorKind::Other,
|
|
||||||
format!("Indentation error on line:\n\n{}", line)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(level)
|
Ok(level)
|
||||||
|
@ -146,12 +152,12 @@ fn parse_line(l: &str) -> Option<BookItem> {
|
||||||
debug!("[fn]: parse_line");
|
debug!("[fn]: parse_line");
|
||||||
|
|
||||||
// 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 "------"
|
// Spacers are "------"
|
||||||
if line.starts_with("--") {
|
if line.starts_with("--") {
|
||||||
debug!("[*]: Line is spacer");
|
debug!("[*]: Line is spacer");
|
||||||
return Some(BookItem::Spacer)
|
return Some(BookItem::Spacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(c) = line.chars().nth(0) {
|
if let Some(c) = line.chars().nth(0) {
|
||||||
|
@ -161,18 +167,22 @@ fn parse_line(l: &str) -> Option<BookItem> {
|
||||||
debug!("[*]: Line is list element");
|
debug!("[*]: Line is list element");
|
||||||
|
|
||||||
if let Some((name, path)) = read_link(line) {
|
if let Some((name, path)) = read_link(line) {
|
||||||
return Some(BookItem::Chapter("0".to_owned(), Chapter::new(name, path)))
|
return Some(BookItem::Chapter("0".to_owned(), Chapter::new(name, path)));
|
||||||
} else { return None }
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// Non-list element
|
// Non-list element
|
||||||
'[' => {
|
'[' => {
|
||||||
debug!("[*]: Line is a link element");
|
debug!("[*]: Line is a link element");
|
||||||
|
|
||||||
if let Some((name, path)) = read_link(line) {
|
if let Some((name, path)) = read_link(line) {
|
||||||
return Some(BookItem::Affix(Chapter::new(name, path)))
|
return Some(BookItem::Affix(Chapter::new(name, path)));
|
||||||
} else { return None }
|
} else {
|
||||||
}
|
return None;
|
||||||
_ => {}
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,32 +195,31 @@ fn read_link(line: &str) -> Option<(String, PathBuf)> {
|
||||||
|
|
||||||
// In the future, support for list item that is not a link
|
// 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...
|
// 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; }
|
if let Some(i) = line.find('[') {
|
||||||
else {
|
start_delimitor = i;
|
||||||
|
} else {
|
||||||
debug!("[*]: '[' not found, this line is not a link. Ignoring...");
|
debug!("[*]: '[' not found, this line is not a link. Ignoring...");
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(i) = line[start_delimitor..].find("](") {
|
if let Some(i) = line[start_delimitor..].find("](") {
|
||||||
end_delimitor = start_delimitor +i;
|
end_delimitor = start_delimitor + i;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
debug!("[*]: '](' not found, this line is not a link. Ignoring...");
|
debug!("[*]: '](' not found, this line is not a link. Ignoring...");
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = line[start_delimitor + 1 .. end_delimitor].to_owned();
|
let name = line[start_delimitor + 1..end_delimitor].to_owned();
|
||||||
|
|
||||||
start_delimitor = end_delimitor + 1;
|
start_delimitor = end_delimitor + 1;
|
||||||
if let Some(i) = line[start_delimitor..].find(')') {
|
if let Some(i) = line[start_delimitor..].find(')') {
|
||||||
end_delimitor = start_delimitor + i;
|
end_delimitor = start_delimitor + i;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
debug!("[*]: ')' not found, this line is not a link. Ignoring...");
|
debug!("[*]: ')' not found, this line is not a link. Ignoring...");
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = PathBuf::from(line[start_delimitor + 1 .. end_delimitor].to_owned());
|
let path = PathBuf::from(line[start_delimitor + 1..end_delimitor].to_owned());
|
||||||
|
|
||||||
Some((name, path))
|
Some((name, path))
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,8 @@ impl Renderer for HtmlHandlebars {
|
||||||
// Check if dest directory exists
|
// Check if dest directory exists
|
||||||
debug!("[*]: Check if destination directory exists");
|
debug!("[*]: Check if destination directory exists");
|
||||||
if let Err(_) = fs::create_dir_all(book.get_dest()) {
|
if let Err(_) = fs::create_dir_all(book.get_dest()) {
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Unexpected error when constructing destination path")))
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other,
|
||||||
|
"Unexpected error when constructing destination path")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render a file for every entry in the book
|
// Render a file for every entry in the book
|
||||||
|
@ -83,8 +84,13 @@ impl Renderer for HtmlHandlebars {
|
||||||
// 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 ch.path.to_str() {
|
match ch.path.to_str() {
|
||||||
Some(p) => { data.insert("path".to_owned(), p.to_json()); },
|
Some(p) => {
|
||||||
None => return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))),
|
data.insert("path".to_owned(), p.to_json());
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other,
|
||||||
|
"Could not convert path to str")))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -114,23 +120,24 @@ impl Renderer for HtmlHandlebars {
|
||||||
let mut index_file = try!(File::create(book.get_dest().join("index.html")));
|
let mut index_file = try!(File::create(book.get_dest().join("index.html")));
|
||||||
let mut content = String::new();
|
let mut content = String::new();
|
||||||
let _source = try!(File::open(book.get_dest().join(&ch.path.with_extension("html"))))
|
let _source = try!(File::open(book.get_dest().join(&ch.path.with_extension("html"))))
|
||||||
.read_to_string(&mut content);
|
.read_to_string(&mut content);
|
||||||
|
|
||||||
// This could cause a problem when someone displays code containing <base href=...>
|
// This could cause a problem when someone displays code containing <base href=...>
|
||||||
// on the front page, however this case should be very very rare...
|
// on the front page, however this case should be very very rare...
|
||||||
content = content.lines().filter(|line| !line.contains("<base href=")).collect::<Vec<&str>>().join("\n");
|
content = content.lines()
|
||||||
|
.filter(|line| !line.contains("<base href="))
|
||||||
|
.collect::<Vec<&str>>()
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
try!(index_file.write_all(content.as_bytes()));
|
try!(index_file.write_all(content.as_bytes()));
|
||||||
|
|
||||||
output!(
|
output!("[*] Creating index.html from {:?} ✓",
|
||||||
"[*] Creating index.html from {:?} ✓",
|
book.get_dest().join(&ch.path.with_extension("html")));
|
||||||
book.get_dest().join(&ch.path.with_extension("html"))
|
|
||||||
);
|
|
||||||
index = false;
|
index = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
_ => {}
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,72 +166,112 @@ impl Renderer for HtmlHandlebars {
|
||||||
|
|
||||||
debug!("[*] Copy static files");
|
debug!("[*] Copy static files");
|
||||||
// JavaScript
|
// JavaScript
|
||||||
let mut js_file = if let Ok(f) = File::create(book.get_dest().join("book.js")) { f } else {
|
let mut js_file = if let Ok(f) = File::create(book.get_dest().join("book.js")) {
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create book.js")))
|
f
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create book.js")));
|
||||||
};
|
};
|
||||||
try!(js_file.write_all(&theme.js));
|
try!(js_file.write_all(&theme.js));
|
||||||
|
|
||||||
// Css
|
// Css
|
||||||
let mut css_file = if let Ok(f) = File::create(book.get_dest().join("book.css")) { f } else {
|
let mut css_file = if let Ok(f) = File::create(book.get_dest().join("book.css")) {
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create book.css")))
|
f
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create book.css")));
|
||||||
};
|
};
|
||||||
try!(css_file.write_all(&theme.css));
|
try!(css_file.write_all(&theme.css));
|
||||||
|
|
||||||
// Favicon
|
// Favicon
|
||||||
let mut favicon_file = if let Ok(f) = File::create(book.get_dest().join("favicon.png")) { f } else {
|
let mut favicon_file = if let Ok(f) = File::create(book.get_dest().join("favicon.png")) {
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create favicon.png")))
|
f
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create favicon.png")));
|
||||||
};
|
};
|
||||||
try!(favicon_file.write_all(&theme.favicon));
|
try!(favicon_file.write_all(&theme.favicon));
|
||||||
|
|
||||||
// JQuery local fallback
|
// JQuery local fallback
|
||||||
let mut jquery = if let Ok(f) = File::create(book.get_dest().join("jquery.js")) { f } else {
|
let mut jquery = if let Ok(f) = File::create(book.get_dest().join("jquery.js")) {
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create jquery.js")))
|
f
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create jquery.js")));
|
||||||
};
|
};
|
||||||
try!(jquery.write_all(&theme.jquery));
|
try!(jquery.write_all(&theme.jquery));
|
||||||
|
|
||||||
// syntax highlighting
|
// syntax highlighting
|
||||||
let mut highlight_css = if let Ok(f) = File::create(book.get_dest().join("highlight.css")) { f } else {
|
let mut highlight_css = if let Ok(f) = File::create(book.get_dest().join("highlight.css")) {
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create highlight.css")))
|
f
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create highlight.css")));
|
||||||
};
|
};
|
||||||
try!(highlight_css.write_all(&theme.highlight_css));
|
try!(highlight_css.write_all(&theme.highlight_css));
|
||||||
|
|
||||||
let mut tomorrow_night_css = if let Ok(f) = File::create(book.get_dest().join("tomorrow-night.css")) { f } else {
|
let mut tomorrow_night_css = if let Ok(f) = File::create(book.get_dest().join("tomorrow-night.css")) {
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create tomorrow-night.css")))
|
f
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create tomorrow-night.css")));
|
||||||
};
|
};
|
||||||
try!(tomorrow_night_css.write_all(&theme.tomorrow_night_css));
|
try!(tomorrow_night_css.write_all(&theme.tomorrow_night_css));
|
||||||
|
|
||||||
let mut highlight_js = if let Ok(f) = File::create(book.get_dest().join("highlight.js")) { f } else {
|
let mut highlight_js = if let Ok(f) = File::create(book.get_dest().join("highlight.js")) {
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create highlight.js")))
|
f
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create highlight.js")));
|
||||||
};
|
};
|
||||||
try!(highlight_js.write_all(&theme.highlight_js));
|
try!(highlight_js.write_all(&theme.highlight_js));
|
||||||
|
|
||||||
// Font Awesome local fallback
|
// Font Awesome local fallback
|
||||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest().join("_FontAwesome/css/font-awesome.css")) { f } else {
|
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest()
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create font-awesome.css")))
|
.join("_FontAwesome/css/font-awesome.css")) {
|
||||||
|
f
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create font-awesome.css")));
|
||||||
};
|
};
|
||||||
try!(font_awesome.write_all(theme::FONT_AWESOME));
|
try!(font_awesome.write_all(theme::FONT_AWESOME));
|
||||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest().join("_FontAwesome/fonts/fontawesome-webfont.eot")) { f } else {
|
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest()
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.eot")))
|
.join("_FontAwesome/fonts/fontawesome-webfon\
|
||||||
|
t.eot")) {
|
||||||
|
f
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.eot")));
|
||||||
};
|
};
|
||||||
try!(font_awesome.write_all(theme::FONT_AWESOME_EOT));
|
try!(font_awesome.write_all(theme::FONT_AWESOME_EOT));
|
||||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest().join("_FontAwesome/fonts/fontawesome-webfont.svg")) { f } else {
|
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest()
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.svg")))
|
.join("_FontAwesome/fonts/fontawesome-webfon\
|
||||||
|
t.svg")) {
|
||||||
|
f
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.svg")));
|
||||||
};
|
};
|
||||||
try!(font_awesome.write_all(theme::FONT_AWESOME_SVG));
|
try!(font_awesome.write_all(theme::FONT_AWESOME_SVG));
|
||||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest().join("_FontAwesome/fonts/fontawesome-webfont.ttf")) { f } else {
|
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest()
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.ttf")))
|
.join("_FontAwesome/fonts/fontawesome-webfon\
|
||||||
|
t.ttf")) {
|
||||||
|
f
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.ttf")));
|
||||||
};
|
};
|
||||||
try!(font_awesome.write_all(theme::FONT_AWESOME_TTF));
|
try!(font_awesome.write_all(theme::FONT_AWESOME_TTF));
|
||||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest().join("_FontAwesome/fonts/fontawesome-webfont.woff")) { f } else {
|
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest()
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.woff")))
|
.join("_FontAwesome/fonts/fontawesome-webfon\
|
||||||
|
t.woff")) {
|
||||||
|
f
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.woff")));
|
||||||
};
|
};
|
||||||
try!(font_awesome.write_all(theme::FONT_AWESOME_WOFF));
|
try!(font_awesome.write_all(theme::FONT_AWESOME_WOFF));
|
||||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest().join("_FontAwesome/fonts/fontawesome-webfont.woff2")) { f } else {
|
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest()
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.woff2")))
|
.join("_FontAwesome/fonts/fontawesome-webfon\
|
||||||
|
t.woff2")) {
|
||||||
|
f
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.woff2")));
|
||||||
};
|
};
|
||||||
try!(font_awesome.write_all(theme::FONT_AWESOME_WOFF2));
|
try!(font_awesome.write_all(theme::FONT_AWESOME_WOFF2));
|
||||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest().join("_FontAwesome/fonts/FontAwesome.ttf")) { f } else {
|
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest()
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create FontAwesome.ttf")))
|
.join("_FontAwesome/fonts/FontAwesome.ttf")) {
|
||||||
|
f
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create FontAwesome.ttf")));
|
||||||
};
|
};
|
||||||
try!(font_awesome.write_all(theme::FONT_AWESOME_TTF));
|
try!(font_awesome.write_all(theme::FONT_AWESOME_TTF));
|
||||||
|
|
||||||
|
@ -235,10 +282,10 @@ impl Renderer for HtmlHandlebars {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_data(book: &MDBook) -> Result<BTreeMap<String,Json>, Box<Error>> {
|
fn make_data(book: &MDBook) -> Result<BTreeMap<String, Json>, Box<Error>> {
|
||||||
debug!("[fn]: make_data");
|
debug!("[fn]: make_data");
|
||||||
|
|
||||||
let mut data = BTreeMap::new();
|
let mut data = BTreeMap::new();
|
||||||
data.insert("language".to_owned(), "en".to_json());
|
data.insert("language".to_owned(), "en".to_json());
|
||||||
data.insert("title".to_owned(), book.get_title().to_json());
|
data.insert("title".to_owned(), book.get_title().to_json());
|
||||||
data.insert("description".to_owned(), book.get_description().to_json());
|
data.insert("description".to_owned(), book.get_description().to_json());
|
||||||
|
@ -254,7 +301,9 @@ fn make_data(book: &MDBook) -> Result<BTreeMap<String,Json>, Box<Error>> {
|
||||||
BookItem::Affix(ref ch) => {
|
BookItem::Affix(ref ch) => {
|
||||||
chapter.insert("name".to_owned(), ch.name.to_json());
|
chapter.insert("name".to_owned(), ch.name.to_json());
|
||||||
match ch.path.to_str() {
|
match ch.path.to_str() {
|
||||||
Some(p) => { chapter.insert("path".to_owned(), p.to_json()); },
|
Some(p) => {
|
||||||
|
chapter.insert("path".to_owned(), 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"))),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -262,13 +311,15 @@ fn make_data(book: &MDBook) -> Result<BTreeMap<String,Json>, Box<Error>> {
|
||||||
chapter.insert("section".to_owned(), s.to_json());
|
chapter.insert("section".to_owned(), s.to_json());
|
||||||
chapter.insert("name".to_owned(), ch.name.to_json());
|
chapter.insert("name".to_owned(), ch.name.to_json());
|
||||||
match ch.path.to_str() {
|
match ch.path.to_str() {
|
||||||
Some(p) => { chapter.insert("path".to_owned(), p.to_json()); },
|
Some(p) => {
|
||||||
|
chapter.insert("path".to_owned(), 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"))),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BookItem::Spacer => {
|
BookItem::Spacer => {
|
||||||
chapter.insert("spacer".to_owned(), "_spacer_".to_json());
|
chapter.insert("spacer".to_owned(), "_spacer_".to_json());
|
||||||
}
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,15 +19,15 @@ pub fn previous(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext
|
||||||
let chapters = c.navigate(rc.get_path(), "chapters");
|
let chapters = c.navigate(rc.get_path(), "chapters");
|
||||||
|
|
||||||
let current = c.navigate(rc.get_path(), "path")
|
let current = c.navigate(rc.get_path(), "path")
|
||||||
.to_string()
|
.to_string()
|
||||||
.replace("\"", "");
|
.replace("\"", "");
|
||||||
|
|
||||||
|
|
||||||
debug!("[*]: Decode chapters from JSON");
|
debug!("[*]: Decode chapters from JSON");
|
||||||
// Decode json format
|
// Decode json format
|
||||||
let decoded: Vec<BTreeMap<String, String>> = match json::decode(&chapters.to_string()) {
|
let decoded: Vec<BTreeMap<String, String>> = match json::decode(&chapters.to_string()) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => return Err(RenderError{ desc: "Could not decode the JSON data".to_owned()}),
|
Err(_) => return Err(RenderError { desc: "Could not decode the JSON data".to_owned() }),
|
||||||
};
|
};
|
||||||
let mut previous: Option<BTreeMap<String, String>> = None;
|
let mut previous: Option<BTreeMap<String, String>> = None;
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ pub fn previous(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext
|
||||||
if path == ¤t {
|
if path == ¤t {
|
||||||
|
|
||||||
debug!("[*]: Found current chapter");
|
debug!("[*]: Found current chapter");
|
||||||
if let Some(previous) = previous{
|
if let Some(previous) = previous {
|
||||||
|
|
||||||
debug!("[*]: Creating BTreeMap to inject in context");
|
debug!("[*]: Creating BTreeMap to inject in context");
|
||||||
// Create new BTreeMap to extend the context: 'title' and 'link'
|
// Create new BTreeMap to extend the context: 'title' and 'link'
|
||||||
|
@ -55,8 +55,8 @@ pub fn previous(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
debug!("[*]: No title found for chapter");
|
debug!("[*]: No title found for chapter");
|
||||||
return Err(RenderError{ desc: "No title found for chapter in JSON data".to_owned() })
|
return Err(RenderError { desc: "No title found for chapter in JSON data".to_owned() });
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Chapter link
|
// Chapter link
|
||||||
|
@ -67,11 +67,19 @@ pub fn previous(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext
|
||||||
debug!("[*]: Inserting link: {:?}", path);
|
debug!("[*]: Inserting link: {:?}", path);
|
||||||
|
|
||||||
match path.to_str() {
|
match path.to_str() {
|
||||||
Some(p) => { previous_chapter.insert("link".to_owned(), p.to_json()); },
|
Some(p) => {
|
||||||
None => return Err(RenderError{ desc: "Link could not be converted to str".to_owned() })
|
previous_chapter.insert("link".to_owned(), p.to_json());
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
return Err(RenderError {
|
||||||
|
desc: "Link could not be converted to str".to_owned(),
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => return Err(RenderError{ desc: "No path found for chapter in JSON data".to_owned() })
|
None => {
|
||||||
|
return Err(RenderError { desc: "No path found for chapter in JSON data".to_owned() })
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("[*]: Inject in context");
|
debug!("[*]: Inject in context");
|
||||||
|
@ -84,14 +92,13 @@ pub fn previous(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext
|
||||||
Some(t) => {
|
Some(t) => {
|
||||||
try!(t.render(&updated_context, r, rc));
|
try!(t.render(&updated_context, r, rc));
|
||||||
},
|
},
|
||||||
None => return Err(RenderError{ desc: "Error with the handlebars template".to_owned() })
|
None => return Err(RenderError { desc: "Error with the handlebars template".to_owned() }),
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
previous = Some(item.clone());
|
previous = Some(item.clone());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -117,14 +124,14 @@ pub fn next(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext) ->
|
||||||
let chapters = c.navigate(rc.get_path(), "chapters");
|
let chapters = c.navigate(rc.get_path(), "chapters");
|
||||||
|
|
||||||
let current = c.navigate(rc.get_path(), "path")
|
let current = c.navigate(rc.get_path(), "path")
|
||||||
.to_string()
|
.to_string()
|
||||||
.replace("\"", "");
|
.replace("\"", "");
|
||||||
|
|
||||||
debug!("[*]: Decode chapters from JSON");
|
debug!("[*]: Decode chapters from JSON");
|
||||||
// Decode json format
|
// Decode json format
|
||||||
let decoded: Vec<BTreeMap<String, String>> = match json::decode(&chapters.to_string()) {
|
let decoded: Vec<BTreeMap<String, String>> = match json::decode(&chapters.to_string()) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => return Err(RenderError{ desc: "Could not decode the JSON data".to_owned() }),
|
Err(_) => return Err(RenderError { desc: "Could not decode the JSON data".to_owned() }),
|
||||||
};
|
};
|
||||||
let mut previous: Option<BTreeMap<String, String>> = None;
|
let mut previous: Option<BTreeMap<String, String>> = None;
|
||||||
|
|
||||||
|
@ -140,7 +147,7 @@ pub fn next(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext) ->
|
||||||
|
|
||||||
let previous_path = match previous.get("path") {
|
let previous_path = match previous.get("path") {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => return Err(RenderError{ desc: "No path found for chapter in JSON data".to_owned() })
|
None => return Err(RenderError { desc: "No path found for chapter in JSON data".to_owned() }),
|
||||||
};
|
};
|
||||||
|
|
||||||
if previous_path == ¤t {
|
if previous_path == ¤t {
|
||||||
|
@ -154,8 +161,10 @@ pub fn next(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext) ->
|
||||||
Some(n) => {
|
Some(n) => {
|
||||||
debug!("[*]: Inserting title: {}", n);
|
debug!("[*]: Inserting title: {}", n);
|
||||||
next_chapter.insert("title".to_owned(), n.to_json());
|
next_chapter.insert("title".to_owned(), n.to_json());
|
||||||
}
|
},
|
||||||
None => return Err(RenderError{ desc: "No title found for chapter in JSON data".to_owned() })
|
None => {
|
||||||
|
return Err(RenderError { desc: "No title found for chapter in JSON data".to_owned() })
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -163,8 +172,10 @@ pub fn next(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext) ->
|
||||||
debug!("[*]: Inserting link: {:?}", link);
|
debug!("[*]: Inserting link: {:?}", link);
|
||||||
|
|
||||||
match link.to_str() {
|
match link.to_str() {
|
||||||
Some(l) => { next_chapter.insert("link".to_owned(), l.to_json()); },
|
Some(l) => {
|
||||||
None => return Err(RenderError{ desc: "Link could not converted to str".to_owned() })
|
next_chapter.insert("link".to_owned(), l.to_json());
|
||||||
|
},
|
||||||
|
None => return Err(RenderError { desc: "Link could not converted to str".to_owned() }),
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("[*]: Inject in context");
|
debug!("[*]: Inject in context");
|
||||||
|
@ -178,10 +189,10 @@ pub fn next(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext) ->
|
||||||
Some(t) => {
|
Some(t) => {
|
||||||
try!(t.render(&updated_context, r, rc));
|
try!(t.render(&updated_context, r, rc));
|
||||||
},
|
},
|
||||||
None => return Err(RenderError{ desc: "Error with the handlebars template".to_owned() })
|
None => return Err(RenderError { desc: "Error with the handlebars template".to_owned() }),
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,29 +14,37 @@ pub fn render_playpen(s: &str, path: &Path) -> String {
|
||||||
for playpen in find_playpens(s, path) {
|
for playpen in find_playpens(s, path) {
|
||||||
|
|
||||||
if playpen.escaped {
|
if playpen.escaped {
|
||||||
replaced.push_str(&s[previous_end_index..playpen.start_index-1]);
|
replaced.push_str(&s[previous_end_index..playpen.start_index - 1]);
|
||||||
replaced.push_str(&s[playpen.start_index..playpen.end_index]);
|
replaced.push_str(&s[playpen.start_index..playpen.end_index]);
|
||||||
previous_end_index = playpen.end_index;
|
previous_end_index = playpen.end_index;
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the file exists
|
// Check if the file exists
|
||||||
if !playpen.rust_file.exists() || !playpen.rust_file.is_file() {
|
if !playpen.rust_file.exists() || !playpen.rust_file.is_file() {
|
||||||
output!("[-] No file exists for {{{{#playpen }}}}\n {}", playpen.rust_file.to_str().unwrap());
|
output!("[-] No file exists for {{{{#playpen }}}}\n {}", playpen.rust_file.to_str().unwrap());
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open file & read file
|
// Open file & read file
|
||||||
let mut file = if let Ok(f) = File::open(&playpen.rust_file) { f } else { continue };
|
let mut file = if let Ok(f) = File::open(&playpen.rust_file) {
|
||||||
|
f
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let mut file_content = String::new();
|
let mut file_content = String::new();
|
||||||
if let Err(_) = file.read_to_string(&mut file_content) { continue };
|
if let Err(_) = file.read_to_string(&mut file_content) {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
let replacement = String::new() + "<pre class=\"playpen\"><code class=\"language-rust\">" + &file_content + "</code></pre>";
|
let replacement = String::new() + "<pre class=\"playpen\"><code class=\"language-rust\">" + &file_content +
|
||||||
|
"</code></pre>";
|
||||||
|
|
||||||
replaced.push_str(&s[previous_end_index..playpen.start_index]);
|
replaced.push_str(&s[previous_end_index..playpen.start_index]);
|
||||||
replaced.push_str(&replacement);
|
replaced.push_str(&replacement);
|
||||||
previous_end_index = playpen.end_index;
|
previous_end_index = playpen.end_index;
|
||||||
//println!("Playpen{{ {}, {}, {:?}, {} }}", playpen.start_index, playpen.end_index, playpen.rust_file, playpen.editable);
|
// println!("Playpen{{ {}, {}, {:?}, {} }}", playpen.start_index, playpen.end_index, playpen.rust_file,
|
||||||
|
// playpen.editable);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaced.push_str(&s[previous_end_index..]);
|
replaced.push_str(&s[previous_end_index..]);
|
||||||
|
@ -45,7 +53,7 @@ pub fn render_playpen(s: &str, path: &Path) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialOrd, PartialEq, Debug)]
|
#[derive(PartialOrd, PartialEq, Debug)]
|
||||||
struct Playpen{
|
struct Playpen {
|
||||||
start_index: usize,
|
start_index: usize,
|
||||||
end_index: usize,
|
end_index: usize,
|
||||||
rust_file: PathBuf,
|
rust_file: PathBuf,
|
||||||
|
@ -61,38 +69,50 @@ fn find_playpens(s: &str, base_path: &Path) -> Vec<Playpen> {
|
||||||
let mut escaped = false;
|
let mut escaped = false;
|
||||||
|
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
if let Some(c) = s[i-1..].chars().nth(0) {
|
if let Some(c) = s[i - 1..].chars().nth(0) {
|
||||||
if c == '\\' { escaped = true }
|
if c == '\\' {
|
||||||
|
escaped = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// DON'T forget the "+ i" else you have an index out of bounds error !!
|
// DON'T forget the "+ i" else you have an index out of bounds error !!
|
||||||
let end_i = if let Some(n) = s[i..].find("}}") { n } else { continue } + i + 2;
|
let end_i = if let Some(n) = s[i..].find("}}") {
|
||||||
|
n
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
} + i + 2;
|
||||||
|
|
||||||
debug!("s[{}..{}] = {}", i, end_i, s[i..end_i].to_string());
|
debug!("s[{}..{}] = {}", i, end_i, s[i..end_i].to_string());
|
||||||
|
|
||||||
// If there is nothing between "{{#playpen" and "}}" skip
|
// If there is nothing between "{{#playpen" and "}}" skip
|
||||||
if end_i-2 - (i+10) < 1 { continue }
|
if end_i - 2 - (i + 10) < 1 {
|
||||||
if s[i+10..end_i-2].trim().len() == 0 { continue }
|
continue;
|
||||||
|
}
|
||||||
|
if s[i + 10..end_i - 2].trim().len() == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
debug!("{}", s[i+10..end_i-2].to_string());
|
debug!("{}", s[i + 10..end_i - 2].to_string());
|
||||||
|
|
||||||
// Split on whitespaces
|
// Split on whitespaces
|
||||||
let params: Vec<&str> = s[i+10..end_i-2].split_whitespace().collect();
|
let params: Vec<&str> = s[i + 10..end_i - 2].split_whitespace().collect();
|
||||||
let mut editable = false;
|
let mut editable = false;
|
||||||
|
|
||||||
if params.len() > 1 {
|
if params.len() > 1 {
|
||||||
editable = if let Some(_) = params[1].find("editable") {true} else {false};
|
editable = if let Some(_) = params[1].find("editable") {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
playpens.push(
|
playpens.push(Playpen {
|
||||||
Playpen{
|
start_index: i,
|
||||||
start_index: i,
|
end_index: end_i,
|
||||||
end_index: end_i,
|
rust_file: base_path.join(PathBuf::from(params[0])),
|
||||||
rust_file: base_path.join(PathBuf::from(params[0])),
|
editable: editable,
|
||||||
editable: editable,
|
escaped: escaped,
|
||||||
escaped: escaped
|
})
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
playpens
|
playpens
|
||||||
|
@ -101,8 +121,7 @@ fn find_playpens(s: &str, base_path: &Path) -> Vec<Playpen> {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
// ---------------------------------------------------------------------------------
|
||||||
//---------------------------------------------------------------------------------
|
|
||||||
// Tests
|
// Tests
|
||||||
//
|
//
|
||||||
|
|
||||||
|
@ -130,10 +149,21 @@ fn test_find_playpens_simple_playpen() {
|
||||||
|
|
||||||
println!("\nOUTPUT: {:?}\n", find_playpens(s, Path::new("")));
|
println!("\nOUTPUT: {:?}\n", find_playpens(s, Path::new("")));
|
||||||
|
|
||||||
assert!(find_playpens(s, Path::new("")) == vec![
|
assert!(find_playpens(s, Path::new("")) ==
|
||||||
Playpen{start_index: 22, end_index: 42, rust_file: PathBuf::from("file.rs"), editable: false, escaped: false},
|
vec![Playpen {
|
||||||
Playpen{start_index: 47, end_index: 68, rust_file: PathBuf::from("test.rs"), editable: false, escaped: false}
|
start_index: 22,
|
||||||
]);
|
end_index: 42,
|
||||||
|
rust_file: PathBuf::from("file.rs"),
|
||||||
|
editable: false,
|
||||||
|
escaped: false,
|
||||||
|
},
|
||||||
|
Playpen {
|
||||||
|
start_index: 47,
|
||||||
|
end_index: 68,
|
||||||
|
rust_file: PathBuf::from("test.rs"),
|
||||||
|
editable: false,
|
||||||
|
escaped: false,
|
||||||
|
}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -142,10 +172,21 @@ fn test_find_playpens_complex_playpen() {
|
||||||
|
|
||||||
println!("\nOUTPUT: {:?}\n", find_playpens(s, Path::new("dir")));
|
println!("\nOUTPUT: {:?}\n", find_playpens(s, Path::new("dir")));
|
||||||
|
|
||||||
assert!(find_playpens(s, Path::new("dir")) == vec![
|
assert!(find_playpens(s, Path::new("dir")) ==
|
||||||
Playpen{start_index: 22, end_index: 51, rust_file: PathBuf::from("dir/file.rs"), editable: true, escaped: false},
|
vec![Playpen {
|
||||||
Playpen{start_index: 56, end_index: 86, rust_file: PathBuf::from("dir/test.rs"), editable: true, escaped: false}
|
start_index: 22,
|
||||||
]);
|
end_index: 51,
|
||||||
|
rust_file: PathBuf::from("dir/file.rs"),
|
||||||
|
editable: true,
|
||||||
|
escaped: false,
|
||||||
|
},
|
||||||
|
Playpen {
|
||||||
|
start_index: 56,
|
||||||
|
end_index: 86,
|
||||||
|
rust_file: PathBuf::from("dir/test.rs"),
|
||||||
|
editable: true,
|
||||||
|
escaped: false,
|
||||||
|
}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -154,7 +195,8 @@ fn test_find_playpens_escaped_playpen() {
|
||||||
|
|
||||||
println!("\nOUTPUT: {:?}\n", find_playpens(s, Path::new("")));
|
println!("\nOUTPUT: {:?}\n", find_playpens(s, Path::new("")));
|
||||||
|
|
||||||
assert!(find_playpens(s, Path::new("")) == vec![
|
assert!(find_playpens(s, Path::new("")) ==
|
||||||
|
vec![
|
||||||
Playpen{start_index: 39, end_index: 68, rust_file: PathBuf::from("file.rs"), editable: true, escaped: true},
|
Playpen{start_index: 39, end_index: 68, rust_file: PathBuf::from("file.rs"), editable: true, escaped: true},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,117 +14,119 @@ use self::pulldown_cmark::{Parser, html, Event, Tag};
|
||||||
pub struct RenderToc;
|
pub struct RenderToc;
|
||||||
|
|
||||||
impl HelperDef for RenderToc {
|
impl HelperDef for RenderToc {
|
||||||
fn call(&self, c: &Context, _h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
|
fn call(&self, c: &Context, _h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
|
||||||
|
|
||||||
// get value from context data
|
// get value from context data
|
||||||
// rc.get_path() is current json parent path, you should always use it like this
|
// rc.get_path() is current json parent path, you should always use it like this
|
||||||
// param is the key of value you want to display
|
// param is the key of value you want to display
|
||||||
let chapters = c.navigate(rc.get_path(), "chapters");
|
let chapters = c.navigate(rc.get_path(), "chapters");
|
||||||
let current = c.navigate(rc.get_path(), "path").to_string().replace("\"", "");
|
let current = c.navigate(rc.get_path(), "path").to_string().replace("\"", "");
|
||||||
try!(rc.writer.write("<ul class=\"chapter\">".as_bytes()));
|
try!(rc.writer.write("<ul class=\"chapter\">".as_bytes()));
|
||||||
|
|
||||||
// Decode json format
|
// Decode json format
|
||||||
let decoded: Vec<BTreeMap<String,String>> = json::decode(&chapters.to_string()).unwrap();
|
let decoded: Vec<BTreeMap<String, String>> = json::decode(&chapters.to_string()).unwrap();
|
||||||
|
|
||||||
let mut current_level = 1;
|
let mut current_level = 1;
|
||||||
|
|
||||||
for item in decoded {
|
for item in decoded {
|
||||||
|
|
||||||
// Spacer
|
// Spacer
|
||||||
if let Some(_) = item.get("spacer") {
|
if let Some(_) = item.get("spacer") {
|
||||||
try!(rc.writer.write("<li class=\"spacer\"></li>".as_bytes()));
|
try!(rc.writer.write("<li class=\"spacer\"></li>".as_bytes()));
|
||||||
continue
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
let level = if let Some(s) = item.get("section") { s.len() / 2 } else { 1 };
|
|
||||||
|
|
||||||
if level > current_level {
|
|
||||||
try!(rc.writer.write("<li>".as_bytes()));
|
|
||||||
try!(rc.writer.write("<ul class=\"section\">".as_bytes()));
|
|
||||||
try!(rc.writer.write("<li>".as_bytes()));
|
|
||||||
} else if level < current_level {
|
|
||||||
while level < current_level {
|
|
||||||
try!(rc.writer.write("</ul>".as_bytes()));
|
|
||||||
try!(rc.writer.write("</li>".as_bytes()));
|
|
||||||
current_level = current_level - 1;
|
|
||||||
}
|
}
|
||||||
try!(rc.writer.write("<li>".as_bytes()));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
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
|
let level = if let Some(s) = item.get("section") {
|
||||||
let path_exists = if let Some(path) = item.get("path") {
|
s.len() / 2
|
||||||
if !path.is_empty() {
|
} else {
|
||||||
try!(rc.writer.write("<a href=\"".as_bytes()));
|
1
|
||||||
|
};
|
||||||
|
|
||||||
// Add link
|
if level > current_level {
|
||||||
try!(rc.writer.write(
|
try!(rc.writer.write("<li>".as_bytes()));
|
||||||
Path::new(
|
try!(rc.writer.write("<ul class=\"section\">".as_bytes()));
|
||||||
item.get("path")
|
try!(rc.writer.write("<li>".as_bytes()));
|
||||||
.expect("Error: path should be Some(_)")
|
} else if level < current_level {
|
||||||
).with_extension("html")
|
while level < current_level {
|
||||||
.to_str().unwrap().as_bytes()
|
try!(rc.writer.write("</ul>".as_bytes()));
|
||||||
));
|
try!(rc.writer.write("</li>".as_bytes()));
|
||||||
|
current_level = current_level - 1;
|
||||||
try!(rc.writer.write("\"".as_bytes()));
|
}
|
||||||
|
try!(rc.writer.write("<li>".as_bytes()));
|
||||||
if path == ¤t {
|
} else {
|
||||||
try!(rc.writer.write(" class=\"active\"".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()));
|
try!(rc.writer.write(">".as_bytes()));
|
||||||
true
|
}
|
||||||
|
|
||||||
|
// Link
|
||||||
|
let path_exists = if let Some(path) = item.get("path") {
|
||||||
|
if !path.is_empty() {
|
||||||
|
try!(rc.writer.write("<a href=\"".as_bytes()));
|
||||||
|
|
||||||
|
// Add link
|
||||||
|
try!(rc.writer.write(Path::new(item.get("path")
|
||||||
|
.expect("Error: path should be Some(_)"))
|
||||||
|
.with_extension("html")
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.as_bytes()));
|
||||||
|
|
||||||
|
try!(rc.writer.write("\"".as_bytes()));
|
||||||
|
|
||||||
|
if path == ¤t {
|
||||||
|
try!(rc.writer.write(" class=\"active\"".as_bytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(rc.writer.write(">".as_bytes()));
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Section does not necessarily exist
|
||||||
|
if let Some(section) = item.get("section") {
|
||||||
|
try!(rc.writer.write("<strong>".as_bytes()));
|
||||||
|
try!(rc.writer.write(section.as_bytes()));
|
||||||
|
try!(rc.writer.write("</strong> ".as_bytes()));
|
||||||
}
|
}
|
||||||
}else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
// Section does not necessarily exist
|
if let Some(name) = item.get("name") {
|
||||||
if let Some(section) = item.get("section") {
|
// Render only inline code blocks
|
||||||
try!(rc.writer.write("<strong>".as_bytes()));
|
|
||||||
try!(rc.writer.write(section.as_bytes()));
|
// filter all events that are not inline code blocks
|
||||||
try!(rc.writer.write("</strong> ".as_bytes()));
|
let parser = Parser::new(&name).filter(|event| {
|
||||||
|
match event {
|
||||||
|
&Event::Start(Tag::Code) | &Event::End(Tag::Code) => true,
|
||||||
|
&Event::InlineHtml(_) => true,
|
||||||
|
&Event::Text(_) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// render markdown to html
|
||||||
|
let mut markdown_parsed_name = String::with_capacity(name.len() * 3 / 2);
|
||||||
|
html::push_html(&mut markdown_parsed_name, parser);
|
||||||
|
|
||||||
|
// write to the handlebars template
|
||||||
|
try!(rc.writer.write(markdown_parsed_name.as_bytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if path_exists {
|
||||||
|
try!(rc.writer.write("</a>".as_bytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(rc.writer.write("</li>".as_bytes()));
|
||||||
|
|
||||||
|
current_level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(name) = item.get("name") {
|
try!(rc.writer.write("</ul>".as_bytes()));
|
||||||
// Render only inline code blocks
|
Ok(())
|
||||||
|
|
||||||
// filter all events that are not inline code blocks
|
|
||||||
let parser = Parser::new(&name).filter(|event|{
|
|
||||||
match event {
|
|
||||||
&Event::Start(Tag::Code) | &Event::End(Tag::Code) => true,
|
|
||||||
&Event::InlineHtml(_) => true,
|
|
||||||
&Event::Text(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// render markdown to html
|
|
||||||
let mut markdown_parsed_name = String::with_capacity(name.len() * 3 / 2);
|
|
||||||
html::push_html(&mut markdown_parsed_name, parser);
|
|
||||||
|
|
||||||
// write to the handlebars template
|
|
||||||
try!(rc.writer.write(markdown_parsed_name.as_bytes()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if path_exists {
|
|
||||||
try!(rc.writer.write("</a>".as_bytes()));
|
|
||||||
}
|
|
||||||
|
|
||||||
try!(rc.writer.write("</li>".as_bytes()));
|
|
||||||
|
|
||||||
current_level = level;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try!(rc.writer.write("</ul>".as_bytes()));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
pub trait Renderer {
|
pub trait Renderer {
|
||||||
fn render(&self, book: &::book::MDBook) -> Result<(), Box<Error>>;
|
fn render(&self, book: &::book::MDBook) -> Result<(), Box<Error>>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,13 +53,13 @@ impl Theme {
|
||||||
|
|
||||||
// Check if the given path exists
|
// Check if the given path exists
|
||||||
if !src.exists() || !src.is_dir() {
|
if !src.exists() || !src.is_dir() {
|
||||||
return theme
|
return theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = src.join("theme");
|
let src = src.join("theme");
|
||||||
// If src does exist, check if there is a theme directory in it
|
// If src does exist, check if there is a theme directory in it
|
||||||
if !src.exists() || !src.is_dir() {
|
if !src.exists() || !src.is_dir() {
|
||||||
return theme
|
return theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for individual files if they exist
|
// Check for individual files if they exist
|
||||||
|
@ -73,7 +73,7 @@ impl Theme {
|
||||||
// book.js
|
// book.js
|
||||||
if let Ok(mut f) = File::open(&src.join("book.js")) {
|
if let Ok(mut f) = File::open(&src.join("book.js")) {
|
||||||
theme.js.clear();
|
theme.js.clear();
|
||||||
let _ = f.read_to_end(&mut theme.js);
|
let _ = f.read_to_end(&mut theme.js);
|
||||||
}
|
}
|
||||||
|
|
||||||
// book.css
|
// book.css
|
||||||
|
|
105
src/utils/mod.rs
105
src/utils/mod.rs
|
@ -32,13 +32,16 @@ pub fn path_to_root(path: &Path) -> String {
|
||||||
debug!("[fn]: path_to_root");
|
debug!("[fn]: path_to_root");
|
||||||
// Remove filename and add "../" for every directory
|
// Remove filename and add "../" for every directory
|
||||||
|
|
||||||
path.to_path_buf().parent().expect("")
|
path.to_path_buf()
|
||||||
.components().fold(String::new(), |mut s, c| {
|
.parent()
|
||||||
|
.expect("")
|
||||||
|
.components()
|
||||||
|
.fold(String::new(), |mut s, c| {
|
||||||
match c {
|
match c {
|
||||||
Component::Normal(_) => s.push_str("../"),
|
Component::Normal(_) => s.push_str("../"),
|
||||||
_ => {
|
_ => {
|
||||||
debug!("[*]: Other path component... {:?}", c);
|
debug!("[*]: Other path component... {:?}", c);
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
s
|
s
|
||||||
})
|
})
|
||||||
|
@ -64,7 +67,7 @@ pub fn create_file(path: &Path) -> Result<File, Box<Error>> {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!("File::create: {}", e);
|
debug!("File::create: {}", e);
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, format!("{}", e))))
|
return Err(Box::new(io::Error::new(io::ErrorKind::Other, format!("{}", e))));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,7 +80,11 @@ pub fn remove_dir_content(dir: &Path) -> Result<(), Box<Error>> {
|
||||||
for item in try!(fs::read_dir(dir)) {
|
for item in try!(fs::read_dir(dir)) {
|
||||||
if let Ok(item) = item {
|
if let Ok(item) = item {
|
||||||
let item = item.path();
|
let item = item.path();
|
||||||
if item.is_dir() { try!(fs::remove_dir_all(item)); } else { try!(fs::remove_file(item)); }
|
if item.is_dir() {
|
||||||
|
try!(fs::remove_dir_all(item));
|
||||||
|
} else {
|
||||||
|
try!(fs::remove_file(item));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -91,7 +98,9 @@ pub fn remove_dir_content(dir: &Path) -> Result<(), Box<Error>> {
|
||||||
pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blacklist: &[&str]) -> Result<(), Box<Error>> {
|
pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blacklist: &[&str]) -> Result<(), Box<Error>> {
|
||||||
debug!("[fn] copy_files_except_ext");
|
debug!("[fn] copy_files_except_ext");
|
||||||
// Check that from and to are different
|
// Check that from and to are different
|
||||||
if from == to { return Ok(()) }
|
if from == to {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
debug!("[*] Loop");
|
debug!("[*] Loop");
|
||||||
for entry in try!(fs::read_dir(from)) {
|
for entry in try!(fs::read_dir(from)) {
|
||||||
let entry = try!(entry);
|
let entry = try!(entry);
|
||||||
|
@ -100,7 +109,9 @@ pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blackl
|
||||||
|
|
||||||
// If the entry is a dir and the recursive option is enabled, call itself
|
// If the entry is a dir and the recursive option is enabled, call itself
|
||||||
if metadata.is_dir() && recursive {
|
if metadata.is_dir() && recursive {
|
||||||
if entry.path() == to.to_path_buf() { continue }
|
if entry.path() == to.to_path_buf() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
debug!("[*] is dir");
|
debug!("[*] is dir");
|
||||||
|
|
||||||
// check if output dir already exists
|
// check if output dir already exists
|
||||||
|
@ -108,21 +119,25 @@ pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blackl
|
||||||
try!(fs::create_dir(&to.join(entry.file_name())));
|
try!(fs::create_dir(&to.join(entry.file_name())));
|
||||||
}
|
}
|
||||||
|
|
||||||
try!(copy_files_except_ext(
|
try!(copy_files_except_ext(&from.join(entry.file_name()),
|
||||||
&from.join(entry.file_name()),
|
&to.join(entry.file_name()),
|
||||||
&to.join(entry.file_name()),
|
true,
|
||||||
true,
|
ext_blacklist));
|
||||||
ext_blacklist
|
|
||||||
));
|
|
||||||
} else if metadata.is_file() {
|
} else if metadata.is_file() {
|
||||||
|
|
||||||
// Check if it is in the blacklist
|
// Check if it is in the blacklist
|
||||||
if let Some(ext) = entry.path().extension() {
|
if let Some(ext) = entry.path().extension() {
|
||||||
if ext_blacklist.contains(&ext.to_str().unwrap()) { continue }
|
if ext_blacklist.contains(&ext.to_str().unwrap()) {
|
||||||
debug!("[*] creating path for file: {:?}", &to.join(entry.path().file_name().expect("a file should have a file name...")));
|
continue;
|
||||||
|
}
|
||||||
|
debug!("[*] creating path for file: {:?}",
|
||||||
|
&to.join(entry.path().file_name().expect("a file should have a file name...")));
|
||||||
|
|
||||||
output!("[*] copying file: {:?}\n to {:?}", entry.path(), &to.join(entry.path().file_name().expect("a file should have a file name...")));
|
output!("[*] copying file: {:?}\n to {:?}",
|
||||||
try!(fs::copy(entry.path(), &to.join(entry.path().file_name().expect("a file should have a file name..."))));
|
entry.path(),
|
||||||
|
&to.join(entry.path().file_name().expect("a file should have a file name...")));
|
||||||
|
try!(fs::copy(entry.path(),
|
||||||
|
&to.join(entry.path().file_name().expect("a file should have a file name..."))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,17 +183,35 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a couple of files
|
// Create a couple of files
|
||||||
if let Err(_) = fs::File::create(&tmp.path().join("file.txt")) { panic!("Could not create file.txt") }
|
if let Err(_) = fs::File::create(&tmp.path().join("file.txt")) {
|
||||||
if let Err(_) = fs::File::create(&tmp.path().join("file.md")) { panic!("Could not create file.md") }
|
panic!("Could not create file.txt")
|
||||||
if let Err(_) = fs::File::create(&tmp.path().join("file.png")) { panic!("Could not create file.png") }
|
}
|
||||||
if let Err(_) = fs::create_dir(&tmp.path().join("sub_dir")) { panic!("Could not create sub_dir") }
|
if let Err(_) = fs::File::create(&tmp.path().join("file.md")) {
|
||||||
if let Err(_) = fs::File::create(&tmp.path().join("sub_dir/file.png")) { panic!("Could not create sub_dir/file.png") }
|
panic!("Could not create file.md")
|
||||||
if let Err(_) = fs::create_dir(&tmp.path().join("sub_dir_exists")) { panic!("Could not create sub_dir_exists") }
|
}
|
||||||
if let Err(_) = fs::File::create(&tmp.path().join("sub_dir_exists/file.txt")) { panic!("Could not create sub_dir_exists/file.txt") }
|
if let Err(_) = fs::File::create(&tmp.path().join("file.png")) {
|
||||||
|
panic!("Could not create file.png")
|
||||||
|
}
|
||||||
|
if let Err(_) = fs::create_dir(&tmp.path().join("sub_dir")) {
|
||||||
|
panic!("Could not create sub_dir")
|
||||||
|
}
|
||||||
|
if let Err(_) = fs::File::create(&tmp.path().join("sub_dir/file.png")) {
|
||||||
|
panic!("Could not create sub_dir/file.png")
|
||||||
|
}
|
||||||
|
if let Err(_) = fs::create_dir(&tmp.path().join("sub_dir_exists")) {
|
||||||
|
panic!("Could not create sub_dir_exists")
|
||||||
|
}
|
||||||
|
if let Err(_) = fs::File::create(&tmp.path().join("sub_dir_exists/file.txt")) {
|
||||||
|
panic!("Could not create sub_dir_exists/file.txt")
|
||||||
|
}
|
||||||
|
|
||||||
// Create output dir
|
// Create output dir
|
||||||
if let Err(_) = fs::create_dir(&tmp.path().join("output")) { panic!("Could not create output") }
|
if let Err(_) = fs::create_dir(&tmp.path().join("output")) {
|
||||||
if let Err(_) = fs::create_dir(&tmp.path().join("output/sub_dir_exists")) { panic!("Could not create output/sub_dir_exists") }
|
panic!("Could not create output")
|
||||||
|
}
|
||||||
|
if let Err(_) = fs::create_dir(&tmp.path().join("output/sub_dir_exists")) {
|
||||||
|
panic!("Could not create output/sub_dir_exists")
|
||||||
|
}
|
||||||
|
|
||||||
match copy_files_except_ext(&tmp.path(), &tmp.path().join("output"), true, &["md"]) {
|
match copy_files_except_ext(&tmp.path(), &tmp.path().join("output"), true, &["md"]) {
|
||||||
Err(e) => panic!("Error while executing the function:\n{:?}", e),
|
Err(e) => panic!("Error while executing the function:\n{:?}", e),
|
||||||
|
@ -186,11 +219,21 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the correct files where created
|
// Check if the correct files where created
|
||||||
if !(&tmp.path().join("output/file.txt")).exists() { panic!("output/file.txt should exist") }
|
if !(&tmp.path().join("output/file.txt")).exists() {
|
||||||
if (&tmp.path().join("output/file.md")).exists() { panic!("output/file.md should not exist") }
|
panic!("output/file.txt should exist")
|
||||||
if !(&tmp.path().join("output/file.png")).exists() { panic!("output/file.png should exist") }
|
}
|
||||||
if !(&tmp.path().join("output/sub_dir/file.png")).exists() { panic!("output/sub_dir/file.png should exist") }
|
if (&tmp.path().join("output/file.md")).exists() {
|
||||||
if !(&tmp.path().join("output/sub_dir_exists/file.txt")).exists() { panic!("output/sub_dir/file.png should exist") }
|
panic!("output/file.md should not exist")
|
||||||
|
}
|
||||||
|
if !(&tmp.path().join("output/file.png")).exists() {
|
||||||
|
panic!("output/file.png should exist")
|
||||||
|
}
|
||||||
|
if !(&tmp.path().join("output/sub_dir/file.png")).exists() {
|
||||||
|
panic!("output/sub_dir/file.png should exist")
|
||||||
|
}
|
||||||
|
if !(&tmp.path().join("output/sub_dir_exists/file.txt")).exists() {
|
||||||
|
panic!("output/sub_dir/file.png should exist")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue