mirror of
https://github.com/rust-lang/mdBook
synced 2024-11-15 09:17:06 +00:00
Add a rustfmt config and run rustfmt on the code base
This commit is contained in:
parent
6bac41caa8
commit
ad0794a0bd
13 changed files with 556 additions and 371 deletions
15
rustfmt.toml
Normal file
15
rustfmt.toml
Normal file
|
@ -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...
|
||||
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),
|
||||
#[cfg(feature = "watch")]
|
||||
("watch", Some(sub_matches)) => watch(sub_matches),
|
||||
("test", Some(sub_matches)) => test(sub_matches),
|
||||
(_, _) => unreachable!()
|
||||
(_, _) => unreachable!(),
|
||||
};
|
||||
|
||||
if let Err(e) = res {
|
||||
|
@ -77,7 +77,7 @@ fn confirm() -> bool {
|
|||
io::stdin().read_line(&mut s).ok();
|
||||
match &*s.trim() {
|
||||
"Y" | "y" | "yes" | "Yes" => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,66 +151,68 @@ fn watch(args: &ArgMatches) -> Result<(), Box<Error>> {
|
|||
let book = MDBook::new(&book_dir).read_config();
|
||||
|
||||
// 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 {
|
||||
Ok(mut watcher) => {
|
||||
match w {
|
||||
Ok(mut watcher) => {
|
||||
|
||||
// Add the source directory to the watcher
|
||||
if let Err(e) = watcher.watch(book.get_src()) {
|
||||
println!("Error while watching {:?}:\n {:?}", book.get_src(), e);
|
||||
::std::process::exit(0);
|
||||
};
|
||||
// Add the source directory to the watcher
|
||||
if let Err(e) = watcher.watch(book.get_src()) {
|
||||
println!("Error while watching {:?}:\n {:?}", book.get_src(), e);
|
||||
::std::process::exit(0);
|
||||
};
|
||||
|
||||
// Add the book.json file to the watcher if it exists, because it's not
|
||||
// located in the source directory
|
||||
if let Err(_) = watcher.watch(book_dir.join("book.json")) {
|
||||
// do nothing if book.json is not found
|
||||
}
|
||||
// Add the book.json file to the watcher if it exists, because it's not
|
||||
// located in the source directory
|
||||
if let Err(_) = watcher.watch(book_dir.join("book.json")) {
|
||||
// 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| {
|
||||
loop {
|
||||
match rx.recv() {
|
||||
Ok(event) => {
|
||||
crossbeam::scope(|scope| {
|
||||
loop {
|
||||
match rx.recv() {
|
||||
Ok(event) => {
|
||||
|
||||
// Skip the event if an event has already been issued in the last second
|
||||
if time::get_time().sec - previous_time < 1 { continue }
|
||||
// Skip the event if an event has already been issued in the last second
|
||||
if time::get_time().sec - previous_time < 1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(path) = event.path {
|
||||
// Trigger the build process in a new thread (to keep receiving events)
|
||||
scope.spawn(move || {
|
||||
println!("File changed: {:?}\nBuilding book...\n", path);
|
||||
match build(args) {
|
||||
Err(e) => println!("Error while building: {:?}", e),
|
||||
_ => {}
|
||||
}
|
||||
println!("");
|
||||
});
|
||||
if let Some(path) = event.path {
|
||||
// Trigger the build process in a new thread (to keep receiving events)
|
||||
scope.spawn(move || {
|
||||
println!("File changed: {:?}\nBuilding book...\n", path);
|
||||
match build(args) {
|
||||
Err(e) => println!("Error while building: {:?}", e),
|
||||
_ => {},
|
||||
}
|
||||
println!("");
|
||||
});
|
||||
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("An error occured: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("An error occured: {:?}", e);
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Error while trying to watch the files:\n\n\t{:?}", e);
|
||||
::std::process::exit(0);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Error while trying to watch the files:\n\n\t{:?}", e);
|
||||
::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...
|
||||
let p = Path::new(dir);
|
||||
if p.is_relative() {
|
||||
env::current_dir().unwrap().join(dir)
|
||||
env::current_dir().unwrap().join(dir)
|
||||
} else {
|
||||
p.to_path_buf()
|
||||
p.to_path_buf()
|
||||
}
|
||||
} else {
|
||||
env::current_dir().unwrap()
|
||||
|
|
|
@ -26,7 +26,7 @@ impl BookConfig {
|
|||
root: root.to_owned(),
|
||||
dest: PathBuf::from("book"),
|
||||
src: PathBuf::from("src"),
|
||||
indent_spaces: 4, // indentation used for SUMMARY.md
|
||||
indent_spaces: 4, // indentation used for SUMMARY.md
|
||||
multilingual: false,
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ impl BookConfig {
|
|||
Ok(f) => f,
|
||||
Err(_) => {
|
||||
debug!("[*]: Failed to open {:?}", root.join("book.json"));
|
||||
return self
|
||||
return self;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -49,7 +49,9 @@ impl BookConfig {
|
|||
|
||||
// Just return if an error occured.
|
||||
// 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
|
||||
if let Ok(config) = Json::from_str(&data) {
|
||||
|
@ -57,9 +59,15 @@ impl BookConfig {
|
|||
|
||||
debug!("[*]: Extracting data from config");
|
||||
// Title, author, description
|
||||
if let Some(a) = config.find_path(&["title"]) { self.title = 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("\"", "") }
|
||||
if let Some(a) = config.find_path(&["title"]) {
|
||||
self.title = 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
|
||||
if let Some(a) = config.find_path(&["dest"]) {
|
||||
|
@ -71,7 +79,9 @@ impl BookConfig {
|
|||
let dest = self.get_root().join(&dest).to_owned();
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ pub struct BookItems<'a> {
|
|||
|
||||
|
||||
impl Chapter {
|
||||
|
||||
pub fn new(name: String, path: PathBuf) -> Self {
|
||||
|
||||
Chapter {
|
||||
|
@ -40,13 +39,14 @@ impl Chapter {
|
|||
|
||||
|
||||
impl ToJson for Chapter {
|
||||
|
||||
fn to_json(&self) -> Json {
|
||||
let mut m: BTreeMap<String, Json> = BTreeMap::new();
|
||||
m.insert("name".to_owned(), self.name.to_json());
|
||||
m.insert("path".to_owned(),self.path.to_str()
|
||||
.expect("Json conversion failed for path").to_json()
|
||||
);
|
||||
m.insert("path".to_owned(),
|
||||
self.path
|
||||
.to_str()
|
||||
.expect("Json conversion failed for path")
|
||||
.to_json());
|
||||
m.to_json()
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ impl<'a> Iterator for BookItems<'a> {
|
|||
Some((parent_items, parent_idx)) => {
|
||||
self.items = parent_items;
|
||||
self.current_index = parent_idx + 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
} else {
|
||||
let cur = self.items.get(self.current_index).unwrap();
|
||||
|
@ -79,10 +79,10 @@ impl<'a> Iterator for BookItems<'a> {
|
|||
},
|
||||
BookItem::Spacer => {
|
||||
self.current_index += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return Some(cur)
|
||||
return Some(cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ pub struct MDBook {
|
|||
}
|
||||
|
||||
impl MDBook {
|
||||
|
||||
/// Create a new `MDBook` struct with root directory `root`
|
||||
///
|
||||
/// - The default source directory is set to `root/src`
|
||||
|
@ -148,11 +147,11 @@ impl MDBook {
|
|||
try!(::std::fs::create_dir_all(path.parent().unwrap()));
|
||||
let mut f = try!(File::create(path));
|
||||
|
||||
//debug!("[*]: Writing to {:?}", path);
|
||||
// debug!("[*]: Writing to {:?}", path);
|
||||
try!(writeln!(f, "# {}", ch.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,20 +171,19 @@ impl MDBook {
|
|||
return;
|
||||
}
|
||||
|
||||
let relative = self.get_dest().strip_prefix(self.get_root())
|
||||
.expect("Destination is not relative to root.");
|
||||
let relative = self.get_dest()
|
||||
.strip_prefix(self.get_root())
|
||||
.expect("Destination is not relative to root.");
|
||||
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);
|
||||
|
||||
let mut f = File::create(&gitignore)
|
||||
.expect("Could not create file.");
|
||||
let mut f = File::create(&gitignore).expect("Could not create file.");
|
||||
|
||||
debug!("[*]: Writing to .gitignore");
|
||||
|
||||
writeln!(f, "{}", relative)
|
||||
.expect("Could not write to file.");
|
||||
writeln!(f, "{}", relative).expect("Could not write to file.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,9 +308,9 @@ impl MDBook {
|
|||
println!("[*]: Testing file: {:?}", path);
|
||||
|
||||
let output_result = Command::new("rustdoc")
|
||||
.arg(&path)
|
||||
.arg("--test")
|
||||
.output();
|
||||
.arg(&path)
|
||||
.arg("--test")
|
||||
.output();
|
||||
let output = try!(output_result);
|
||||
|
||||
if !output.status.success() {
|
||||
|
@ -322,8 +320,8 @@ impl MDBook {
|
|||
String::from_utf8_lossy(&output.stderr)))) as Box<Error>);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -337,11 +335,13 @@ impl MDBook {
|
|||
|
||||
// Handle absolute and relative paths
|
||||
match dest.is_absolute() {
|
||||
true => { self.config.set_dest(dest); },
|
||||
true => {
|
||||
self.config.set_dest(dest);
|
||||
},
|
||||
false => {
|
||||
let dest = self.config.get_root().join(dest).to_owned();
|
||||
self.config.set_dest(&dest);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
self
|
||||
|
@ -355,11 +355,13 @@ impl MDBook {
|
|||
|
||||
// Handle absolute and relative paths
|
||||
match src.is_absolute() {
|
||||
true => { self.config.set_src(src); },
|
||||
true => {
|
||||
self.config.set_src(src);
|
||||
},
|
||||
false => {
|
||||
let src = self.config.get_root().join(src).to_owned();
|
||||
self.config.set_src(&src);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
self
|
||||
|
@ -402,5 +404,4 @@ impl MDBook {
|
|||
self.content = try!(parse::construct_bookitems(&self.config.get_src().join("SUMMARY.md")));
|
||||
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,
|
||||
// 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 {
|
||||
|
@ -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..
|
||||
section.pop();
|
||||
continue
|
||||
continue;
|
||||
} else {
|
||||
return Err(Error::new( ErrorKind::Other, 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."
|
||||
)))
|
||||
return Err(Error::new(ErrorKind::Other,
|
||||
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.")));
|
||||
};
|
||||
|
||||
} else {
|
||||
|
@ -59,26 +63,32 @@ fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32
|
|||
match parsed_item {
|
||||
// error if level != 0 and BookItem is != Chapter
|
||||
BookItem::Affix(_) | BookItem::Spacer if level > 0 => {
|
||||
return Err(Error::new( ErrorKind::Other, 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."
|
||||
)))
|
||||
return Err(Error::new(ErrorKind::Other,
|
||||
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.")))
|
||||
},
|
||||
|
||||
// error if BookItem == Chapter and section == -1
|
||||
BookItem::Chapter(_, _) if section[0] == -1 => {
|
||||
return Err(Error::new( ErrorKind::Other, 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."
|
||||
)))
|
||||
return Err(Error::new(ErrorKind::Other,
|
||||
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.")))
|
||||
},
|
||||
|
||||
// Set section = -1 after suffix
|
||||
BookItem::Affix(_) if section[0] > 0 => {
|
||||
section[0] = -1;
|
||||
}
|
||||
},
|
||||
|
||||
_ => {},
|
||||
}
|
||||
|
@ -86,12 +96,12 @@ fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32
|
|||
match parsed_item {
|
||||
BookItem::Chapter(_, ch) => {
|
||||
// Increment section
|
||||
let len = section.len() -1;
|
||||
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
|
||||
},
|
||||
_ => parsed_item,
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -131,11 +141,7 @@ fn level(line: &str, spaces_in_tab: i32) -> Result<i32> {
|
|||
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)
|
||||
)
|
||||
)
|
||||
return Err(Error::new(ErrorKind::Other, format!("Indentation error on line:\n\n{}", line)));
|
||||
}
|
||||
|
||||
Ok(level)
|
||||
|
@ -146,12 +152,12 @@ fn parse_line(l: &str) -> Option<BookItem> {
|
|||
debug!("[fn]: parse_line");
|
||||
|
||||
// Remove leading and trailing spaces or tabs
|
||||
let line = l.trim_matches(|c: char| { c == ' ' || c == '\t' });
|
||||
let line = l.trim_matches(|c: char| c == ' ' || c == '\t');
|
||||
|
||||
// Spacers are "------"
|
||||
if line.starts_with("--") {
|
||||
debug!("[*]: Line is spacer");
|
||||
return Some(BookItem::Spacer)
|
||||
return Some(BookItem::Spacer);
|
||||
}
|
||||
|
||||
if let Some(c) = line.chars().nth(0) {
|
||||
|
@ -161,18 +167,22 @@ fn parse_line(l: &str) -> Option<BookItem> {
|
|||
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 }
|
||||
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 }
|
||||
}
|
||||
_ => {}
|
||||
return Some(BookItem::Affix(Chapter::new(name, path)));
|
||||
} 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
|
||||
// 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 {
|
||||
if let Some(i) = line.find('[') {
|
||||
start_delimitor = i;
|
||||
} else {
|
||||
debug!("[*]: '[' not found, this line is not a link. Ignoring...");
|
||||
return None
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(i) = line[start_delimitor..].find("](") {
|
||||
end_delimitor = start_delimitor +i;
|
||||
}
|
||||
else {
|
||||
end_delimitor = start_delimitor + i;
|
||||
} else {
|
||||
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;
|
||||
if let Some(i) = line[start_delimitor..].find(')') {
|
||||
end_delimitor = start_delimitor + i;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
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))
|
||||
}
|
||||
|
|
|
@ -51,7 +51,8 @@ impl Renderer for HtmlHandlebars {
|
|||
// Check if dest directory exists
|
||||
debug!("[*]: Check if destination directory exists");
|
||||
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
|
||||
|
@ -83,8 +84,13 @@ impl Renderer for HtmlHandlebars {
|
|||
// Remove content from previous file and render content for this one
|
||||
data.remove("path");
|
||||
match ch.path.to_str() {
|
||||
Some(p) => { 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"))),
|
||||
Some(p) => {
|
||||
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 content = String::new();
|
||||
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=...>
|
||||
// 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()));
|
||||
|
||||
output!(
|
||||
"[*] Creating index.html from {:?} ✓",
|
||||
book.get_dest().join(&ch.path.with_extension("html"))
|
||||
);
|
||||
output!("[*] Creating index.html from {:?} ✓",
|
||||
book.get_dest().join(&ch.path.with_extension("html")));
|
||||
index = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,72 +166,112 @@ impl Renderer for HtmlHandlebars {
|
|||
|
||||
debug!("[*] Copy static files");
|
||||
// JavaScript
|
||||
let mut js_file = if let Ok(f) = File::create(book.get_dest().join("book.js")) { f } else {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create book.js")))
|
||||
let mut js_file = if let Ok(f) = File::create(book.get_dest().join("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));
|
||||
|
||||
// Css
|
||||
let mut css_file = if let Ok(f) = File::create(book.get_dest().join("book.css")) { f } else {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create book.css")))
|
||||
let mut css_file = if let Ok(f) = File::create(book.get_dest().join("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));
|
||||
|
||||
// Favicon
|
||||
let mut favicon_file = if let Ok(f) = File::create(book.get_dest().join("favicon.png")) { f } else {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create favicon.png")))
|
||||
let mut favicon_file = if let Ok(f) = File::create(book.get_dest().join("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));
|
||||
|
||||
// JQuery local fallback
|
||||
let mut jquery = if let Ok(f) = File::create(book.get_dest().join("jquery.js")) { f } else {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create jquery.js")))
|
||||
let mut jquery = if let Ok(f) = File::create(book.get_dest().join("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));
|
||||
|
||||
// syntax highlighting
|
||||
let mut highlight_css = if let Ok(f) = File::create(book.get_dest().join("highlight.css")) { f } else {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create highlight.css")))
|
||||
let mut highlight_css = if let Ok(f) = File::create(book.get_dest().join("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));
|
||||
|
||||
let mut tomorrow_night_css = if let Ok(f) = File::create(book.get_dest().join("tomorrow-night.css")) { f } else {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create tomorrow-night.css")))
|
||||
let mut tomorrow_night_css = if let Ok(f) = File::create(book.get_dest().join("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));
|
||||
|
||||
let mut highlight_js = if let Ok(f) = File::create(book.get_dest().join("highlight.js")) { f } else {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create highlight.js")))
|
||||
let mut highlight_js = if let Ok(f) = File::create(book.get_dest().join("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));
|
||||
|
||||
// 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 {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create font-awesome.css")))
|
||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest()
|
||||
.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));
|
||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest().join("_FontAwesome/fonts/fontawesome-webfont.eot")) { f } else {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.eot")))
|
||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest()
|
||||
.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));
|
||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest().join("_FontAwesome/fonts/fontawesome-webfont.svg")) { f } else {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.svg")))
|
||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest()
|
||||
.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));
|
||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest().join("_FontAwesome/fonts/fontawesome-webfont.ttf")) { f } else {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.ttf")))
|
||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest()
|
||||
.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));
|
||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest().join("_FontAwesome/fonts/fontawesome-webfont.woff")) { f } else {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.woff")))
|
||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest()
|
||||
.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));
|
||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest().join("_FontAwesome/fonts/fontawesome-webfont.woff2")) { f } else {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create fontawesome-webfont.woff2")))
|
||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest()
|
||||
.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));
|
||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest().join("_FontAwesome/fonts/FontAwesome.ttf")) { f } else {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "Could not create FontAwesome.ttf")))
|
||||
let mut font_awesome = if let Ok(f) = utils::create_file(&book.get_dest()
|
||||
.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));
|
||||
|
||||
|
@ -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");
|
||||
|
||||
let mut data = BTreeMap::new();
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("language".to_owned(), "en".to_json());
|
||||
data.insert("title".to_owned(), book.get_title().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) => {
|
||||
chapter.insert("name".to_owned(), ch.name.to_json());
|
||||
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"))),
|
||||
}
|
||||
},
|
||||
|
@ -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("name".to_owned(), ch.name.to_json());
|
||||
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"))),
|
||||
}
|
||||
},
|
||||
BookItem::Spacer => {
|
||||
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 current = c.navigate(rc.get_path(), "path")
|
||||
.to_string()
|
||||
.replace("\"", "");
|
||||
.to_string()
|
||||
.replace("\"", "");
|
||||
|
||||
|
||||
debug!("[*]: Decode chapters from JSON");
|
||||
// Decode json format
|
||||
let decoded: Vec<BTreeMap<String, String>> = match json::decode(&chapters.to_string()) {
|
||||
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;
|
||||
|
||||
|
@ -41,7 +41,7 @@ pub fn previous(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext
|
|||
if path == ¤t {
|
||||
|
||||
debug!("[*]: Found current chapter");
|
||||
if let Some(previous) = previous{
|
||||
if let Some(previous) = previous {
|
||||
|
||||
debug!("[*]: Creating BTreeMap to inject in context");
|
||||
// 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 => {
|
||||
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
|
||||
|
@ -67,11 +67,19 @@ pub fn previous(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext
|
|||
debug!("[*]: Inserting link: {:?}", path);
|
||||
|
||||
match path.to_str() {
|
||||
Some(p) => { previous_chapter.insert("link".to_owned(), p.to_json()); },
|
||||
None => return Err(RenderError{ desc: "Link could not be converted to str".to_owned() })
|
||||
Some(p) => {
|
||||
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");
|
||||
|
@ -84,14 +92,13 @@ pub fn previous(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext
|
|||
Some(t) => {
|
||||
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;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
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 current = c.navigate(rc.get_path(), "path")
|
||||
.to_string()
|
||||
.replace("\"", "");
|
||||
.to_string()
|
||||
.replace("\"", "");
|
||||
|
||||
debug!("[*]: Decode chapters from JSON");
|
||||
// Decode json format
|
||||
let decoded: Vec<BTreeMap<String, String>> = match json::decode(&chapters.to_string()) {
|
||||
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;
|
||||
|
||||
|
@ -140,7 +147,7 @@ pub fn next(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext) ->
|
|||
|
||||
let previous_path = match previous.get("path") {
|
||||
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 {
|
||||
|
@ -154,8 +161,10 @@ pub fn next(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext) ->
|
|||
Some(n) => {
|
||||
debug!("[*]: Inserting title: {}", n);
|
||||
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);
|
||||
|
||||
match link.to_str() {
|
||||
Some(l) => { next_chapter.insert("link".to_owned(), l.to_json()); },
|
||||
None => return Err(RenderError{ desc: "Link could not converted to str".to_owned() })
|
||||
Some(l) => {
|
||||
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");
|
||||
|
@ -178,10 +189,10 @@ pub fn next(c: &Context, _h: &Helper, r: &Handlebars, rc: &mut RenderContext) ->
|
|||
Some(t) => {
|
||||
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) {
|
||||
|
||||
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]);
|
||||
previous_end_index = playpen.end_index;
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the file exists
|
||||
if !playpen.rust_file.exists() || !playpen.rust_file.is_file() {
|
||||
output!("[-] No file exists for {{{{#playpen }}}}\n {}", playpen.rust_file.to_str().unwrap());
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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();
|
||||
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(&replacement);
|
||||
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..]);
|
||||
|
@ -45,7 +53,7 @@ pub fn render_playpen(s: &str, path: &Path) -> String {
|
|||
}
|
||||
|
||||
#[derive(PartialOrd, PartialEq, Debug)]
|
||||
struct Playpen{
|
||||
struct Playpen {
|
||||
start_index: usize,
|
||||
end_index: usize,
|
||||
rust_file: PathBuf,
|
||||
|
@ -61,38 +69,50 @@ fn find_playpens(s: &str, base_path: &Path) -> Vec<Playpen> {
|
|||
let mut escaped = false;
|
||||
|
||||
if i > 0 {
|
||||
if let Some(c) = s[i-1..].chars().nth(0) {
|
||||
if c == '\\' { escaped = true }
|
||||
if let Some(c) = s[i - 1..].chars().nth(0) {
|
||||
if c == '\\' {
|
||||
escaped = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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());
|
||||
|
||||
// If there is nothing between "{{#playpen" and "}}" skip
|
||||
if end_i-2 - (i+10) < 1 { continue }
|
||||
if s[i+10..end_i-2].trim().len() == 0 { continue }
|
||||
if end_i - 2 - (i + 10) < 1 {
|
||||
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
|
||||
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;
|
||||
|
||||
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(
|
||||
Playpen{
|
||||
start_index: i,
|
||||
end_index: end_i,
|
||||
rust_file: base_path.join(PathBuf::from(params[0])),
|
||||
editable: editable,
|
||||
escaped: escaped
|
||||
}
|
||||
)
|
||||
playpens.push(Playpen {
|
||||
start_index: i,
|
||||
end_index: end_i,
|
||||
rust_file: base_path.join(PathBuf::from(params[0])),
|
||||
editable: editable,
|
||||
escaped: escaped,
|
||||
})
|
||||
}
|
||||
|
||||
playpens
|
||||
|
@ -101,8 +121,7 @@ fn find_playpens(s: &str, base_path: &Path) -> Vec<Playpen> {
|
|||
|
||||
|
||||
|
||||
//
|
||||
//---------------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------------
|
||||
// Tests
|
||||
//
|
||||
|
||||
|
@ -130,10 +149,21 @@ fn test_find_playpens_simple_playpen() {
|
|||
|
||||
println!("\nOUTPUT: {:?}\n", find_playpens(s, Path::new("")));
|
||||
|
||||
assert!(find_playpens(s, Path::new("")) == vec![
|
||||
Playpen{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}
|
||||
]);
|
||||
assert!(find_playpens(s, Path::new("")) ==
|
||||
vec![Playpen {
|
||||
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]
|
||||
|
@ -142,10 +172,21 @@ fn test_find_playpens_complex_playpen() {
|
|||
|
||||
println!("\nOUTPUT: {:?}\n", find_playpens(s, Path::new("dir")));
|
||||
|
||||
assert!(find_playpens(s, Path::new("dir")) == vec![
|
||||
Playpen{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}
|
||||
]);
|
||||
assert!(find_playpens(s, Path::new("dir")) ==
|
||||
vec![Playpen {
|
||||
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]
|
||||
|
@ -154,7 +195,8 @@ fn test_find_playpens_escaped_playpen() {
|
|||
|
||||
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},
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -14,117 +14,119 @@ use self::pulldown_cmark::{Parser, html, Event, Tag};
|
|||
pub struct 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
|
||||
// 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
|
||||
let chapters = c.navigate(rc.get_path(), "chapters");
|
||||
let current = c.navigate(rc.get_path(), "path").to_string().replace("\"", "");
|
||||
try!(rc.writer.write("<ul class=\"chapter\">".as_bytes()));
|
||||
// get value from context data
|
||||
// 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
|
||||
let chapters = c.navigate(rc.get_path(), "chapters");
|
||||
let current = c.navigate(rc.get_path(), "path").to_string().replace("\"", "");
|
||||
try!(rc.writer.write("<ul class=\"chapter\">".as_bytes()));
|
||||
|
||||
// Decode json format
|
||||
let decoded: Vec<BTreeMap<String,String>> = json::decode(&chapters.to_string()).unwrap();
|
||||
// Decode json format
|
||||
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
|
||||
if let Some(_) = item.get("spacer") {
|
||||
try!(rc.writer.write("<li class=\"spacer\"></li>".as_bytes()));
|
||||
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;
|
||||
// Spacer
|
||||
if let Some(_) = item.get("spacer") {
|
||||
try!(rc.writer.write("<li class=\"spacer\"></li>".as_bytes()));
|
||||
continue;
|
||||
}
|
||||
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 path_exists = if let Some(path) = item.get("path") {
|
||||
if !path.is_empty() {
|
||||
try!(rc.writer.write("<a href=\"".as_bytes()));
|
||||
let level = if let Some(s) = item.get("section") {
|
||||
s.len() / 2
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
// 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()));
|
||||
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()));
|
||||
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 {
|
||||
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(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()));
|
||||
if let Some(name) = item.get("name") {
|
||||
// Render only inline code blocks
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if let Some(name) = item.get("name") {
|
||||
// Render only inline code blocks
|
||||
|
||||
// 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(())
|
||||
}
|
||||
|
||||
try!(rc.writer.write("</ul>".as_bytes()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use std::error::Error;
|
||||
|
||||
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
|
||||
if !src.exists() || !src.is_dir() {
|
||||
return theme
|
||||
return theme;
|
||||
}
|
||||
|
||||
let src = src.join("theme");
|
||||
// If src does exist, check if there is a theme directory in it
|
||||
if !src.exists() || !src.is_dir() {
|
||||
return theme
|
||||
return theme;
|
||||
}
|
||||
|
||||
// Check for individual files if they exist
|
||||
|
@ -73,7 +73,7 @@ impl Theme {
|
|||
// book.js
|
||||
if let Ok(mut f) = File::open(&src.join("book.js")) {
|
||||
theme.js.clear();
|
||||
let _ = f.read_to_end(&mut theme.js);
|
||||
let _ = f.read_to_end(&mut theme.js);
|
||||
}
|
||||
|
||||
// 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");
|
||||
// Remove filename and add "../" for every directory
|
||||
|
||||
path.to_path_buf().parent().expect("")
|
||||
.components().fold(String::new(), |mut s, c| {
|
||||
path.to_path_buf()
|
||||
.parent()
|
||||
.expect("")
|
||||
.components()
|
||||
.fold(String::new(), |mut s, c| {
|
||||
match c {
|
||||
Component::Normal(_) => s.push_str("../"),
|
||||
_ => {
|
||||
debug!("[*]: Other path component... {:?}", c);
|
||||
}
|
||||
},
|
||||
}
|
||||
s
|
||||
})
|
||||
|
@ -64,7 +67,7 @@ pub fn create_file(path: &Path) -> Result<File, Box<Error>> {
|
|||
Ok(f) => f,
|
||||
Err(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)) {
|
||||
if let Ok(item) = item {
|
||||
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(())
|
||||
|
@ -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>> {
|
||||
debug!("[fn] copy_files_except_ext");
|
||||
// Check that from and to are different
|
||||
if from == to { return Ok(()) }
|
||||
if from == to {
|
||||
return Ok(());
|
||||
}
|
||||
debug!("[*] Loop");
|
||||
for entry in try!(fs::read_dir(from)) {
|
||||
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 metadata.is_dir() && recursive {
|
||||
if entry.path() == to.to_path_buf() { continue }
|
||||
if entry.path() == to.to_path_buf() {
|
||||
continue;
|
||||
}
|
||||
debug!("[*] is dir");
|
||||
|
||||
// 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!(copy_files_except_ext(
|
||||
&from.join(entry.file_name()),
|
||||
&to.join(entry.file_name()),
|
||||
true,
|
||||
ext_blacklist
|
||||
));
|
||||
try!(copy_files_except_ext(&from.join(entry.file_name()),
|
||||
&to.join(entry.file_name()),
|
||||
true,
|
||||
ext_blacklist));
|
||||
} else if metadata.is_file() {
|
||||
|
||||
// Check if it is in the blacklist
|
||||
if let Some(ext) = entry.path().extension() {
|
||||
if ext_blacklist.contains(&ext.to_str().unwrap()) { continue }
|
||||
debug!("[*] creating path for file: {:?}", &to.join(entry.path().file_name().expect("a file should have a file name...")));
|
||||
if ext_blacklist.contains(&ext.to_str().unwrap()) {
|
||||
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...")));
|
||||
try!(fs::copy(entry.path(), &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...")));
|
||||
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
|
||||
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.md")) { panic!("Could not create file.md") }
|
||||
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") }
|
||||
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.md")) {
|
||||
panic!("Could not create file.md")
|
||||
}
|
||||
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
|
||||
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/sub_dir_exists")) { panic!("Could not create output/sub_dir_exists") }
|
||||
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/sub_dir_exists")) {
|
||||
panic!("Could not create output/sub_dir_exists")
|
||||
}
|
||||
|
||||
match copy_files_except_ext(&tmp.path(), &tmp.path().join("output"), true, &["md"]) {
|
||||
Err(e) => panic!("Error while executing the function:\n{:?}", e),
|
||||
|
@ -186,11 +219,21 @@ mod tests {
|
|||
}
|
||||
|
||||
// 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.md")).exists() { 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") }
|
||||
if !(&tmp.path().join("output/file.txt")).exists() {
|
||||
panic!("output/file.txt should exist")
|
||||
}
|
||||
if (&tmp.path().join("output/file.md")).exists() {
|
||||
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 a new issue