Merge pull request #288 from budziq/rustfmt_update
Made changes with rustfmt including `use_try_shorthand`
This commit is contained in:
commit
f038dcb404
3
build.rs
3
build.rs
|
@ -20,7 +20,8 @@ fn main() {
|
|||
.arg(format!("{}", theme_dir.to_str().unwrap()))
|
||||
.arg("--use")
|
||||
.arg("nib")
|
||||
.status().unwrap()
|
||||
.status()
|
||||
.unwrap()
|
||||
.success() {
|
||||
panic!("Stylus encoutered an error");
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ enum_trailing_comma = true
|
|||
match_block_trailing_comma = true
|
||||
struct_trailing_comma = "Always"
|
||||
wrap_comments = true
|
||||
use_try_shorthand = true
|
||||
|
||||
report_todo = "Always"
|
||||
report_fixme = "Always"
|
||||
|
|
|
@ -121,7 +121,7 @@ fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
|
|||
let mut book = MDBook::new(&book_dir);
|
||||
|
||||
// Call the function that does the initialization
|
||||
try!(book.init());
|
||||
book.init()?;
|
||||
|
||||
// If flag `--theme` is present, copy theme to src
|
||||
if args.is_present("theme") {
|
||||
|
@ -142,7 +142,7 @@ fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
|
|||
}
|
||||
|
||||
// Call the function that copies the theme
|
||||
try!(book.copy_theme());
|
||||
book.copy_theme()?;
|
||||
println!("\nTheme copied.");
|
||||
|
||||
}
|
||||
|
@ -172,14 +172,14 @@ fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
|
|||
|
||||
let mut book = match args.value_of("dest-dir") {
|
||||
Some(dest_dir) => book.set_dest(Path::new(dest_dir)),
|
||||
None => book
|
||||
None => book,
|
||||
};
|
||||
|
||||
if args.is_present("no-create") {
|
||||
book.create_missing = false;
|
||||
}
|
||||
|
||||
try!(book.build());
|
||||
book.build()?;
|
||||
|
||||
if args.is_present("open") {
|
||||
open(book.get_dest().join("index.html"));
|
||||
|
@ -197,11 +197,11 @@ fn watch(args: &ArgMatches) -> Result<(), Box<Error>> {
|
|||
|
||||
let mut book = match args.value_of("dest-dir") {
|
||||
Some(dest_dir) => book.set_dest(Path::new(dest_dir)),
|
||||
None => book
|
||||
None => book,
|
||||
};
|
||||
|
||||
if args.is_present("open") {
|
||||
try!(book.build());
|
||||
book.build()?;
|
||||
open(book.get_dest().join("index.html"));
|
||||
}
|
||||
|
||||
|
@ -227,7 +227,7 @@ fn serve(args: &ArgMatches) -> Result<(), Box<Error>> {
|
|||
|
||||
let mut book = match args.value_of("dest-dir") {
|
||||
Some(dest_dir) => book.set_dest(Path::new(dest_dir)),
|
||||
None => book
|
||||
None => book,
|
||||
};
|
||||
|
||||
let port = args.value_of("port").unwrap_or("3000");
|
||||
|
@ -253,25 +253,22 @@ fn serve(args: &ArgMatches) -> Result<(), Box<Error>> {
|
|||
socket.close();
|
||||
}}
|
||||
</script>
|
||||
"#, public_address, ws_port, RELOAD_COMMAND).to_owned());
|
||||
"#,
|
||||
public_address,
|
||||
ws_port,
|
||||
RELOAD_COMMAND));
|
||||
|
||||
try!(book.build());
|
||||
book.build()?;
|
||||
|
||||
let staticfile = staticfile::Static::new(book.get_dest());
|
||||
let iron = iron::Iron::new(staticfile);
|
||||
let _iron = iron.http(&*address).unwrap();
|
||||
|
||||
let ws_server = ws::WebSocket::new(|_| {
|
||||
|_| {
|
||||
Ok(())
|
||||
}
|
||||
}).unwrap();
|
||||
let ws_server = ws::WebSocket::new(|_| |_| Ok(())).unwrap();
|
||||
|
||||
let broadcaster = ws_server.broadcaster();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
ws_server.listen(&*ws_address).unwrap();
|
||||
});
|
||||
std::thread::spawn(move || { ws_server.listen(&*ws_address).unwrap(); });
|
||||
|
||||
println!("\nServing on {}", address);
|
||||
|
||||
|
@ -296,7 +293,7 @@ fn test(args: &ArgMatches) -> Result<(), Box<Error>> {
|
|||
let book_dir = get_book_dir(args);
|
||||
let mut book = MDBook::new(&book_dir).read_config();
|
||||
|
||||
try!(book.test());
|
||||
book.test()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -339,7 +336,7 @@ fn trigger_on_change<F>(book: &mut MDBook, closure: F) -> ()
|
|||
Err(e) => {
|
||||
println!("Error while trying to watch the files:\n\n\t{:?}", e);
|
||||
::std::process::exit(0);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Add the source directory to the watcher
|
||||
|
@ -350,10 +347,14 @@ fn trigger_on_change<F>(book: &mut MDBook, closure: F) -> ()
|
|||
|
||||
// Add the book.{json,toml} file to the watcher if it exists, because it's not
|
||||
// located in the source directory
|
||||
if watcher.watch(book.get_root().join("book.json"), NonRecursive).is_err() {
|
||||
if watcher
|
||||
.watch(book.get_root().join("book.json"), NonRecursive)
|
||||
.is_err() {
|
||||
// do nothing if book.json is not found
|
||||
}
|
||||
if watcher.watch(book.get_root().join("book.toml"), NonRecursive).is_err() {
|
||||
if watcher
|
||||
.watch(book.get_root().join("book.toml"), NonRecursive)
|
||||
.is_err() {
|
||||
// do nothing if book.toml is not found
|
||||
}
|
||||
|
||||
|
@ -361,16 +362,18 @@ fn trigger_on_change<F>(book: &mut MDBook, closure: F) -> ()
|
|||
|
||||
loop {
|
||||
match rx.recv() {
|
||||
Ok(event) => match event {
|
||||
NoticeWrite(path) |
|
||||
NoticeRemove(path) |
|
||||
Create(path) |
|
||||
Write(path) |
|
||||
Remove(path) |
|
||||
Rename(_, path) => {
|
||||
closure(&path, book);
|
||||
Ok(event) => {
|
||||
match event {
|
||||
NoticeWrite(path) |
|
||||
NoticeRemove(path) |
|
||||
Create(path) |
|
||||
Write(path) |
|
||||
Remove(path) |
|
||||
Rename(_, path) => {
|
||||
closure(&path, book);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("An error occured: {:?}", e);
|
||||
|
|
|
@ -53,7 +53,7 @@ impl BookConfig {
|
|||
Err(_) => {
|
||||
error!("[*]: Failed to open {:?}", &path);
|
||||
exit(2);
|
||||
}
|
||||
},
|
||||
};
|
||||
if f.read_to_string(&mut data).is_err() {
|
||||
error!("[*]: Failed to read {:?}", &path);
|
||||
|
@ -85,11 +85,11 @@ impl BookConfig {
|
|||
|
||||
pub fn parse_from_toml_string(&mut self, data: &str) -> &mut Self {
|
||||
let config = match toml::from_str(data) {
|
||||
Ok(x) => {x},
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
error!("[*]: Toml parse errors in book.toml: {:?}", e);
|
||||
exit(2);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
self.parse_from_btreemap(&config);
|
||||
|
@ -97,7 +97,8 @@ impl BookConfig {
|
|||
self
|
||||
}
|
||||
|
||||
/// Parses the string to JSON and converts it to BTreeMap<String, toml::Value>.
|
||||
/// Parses the string to JSON and converts it
|
||||
/// to BTreeMap<String, toml::Value>.
|
||||
pub fn parse_from_json_string(&mut self, data: &str) -> &mut Self {
|
||||
|
||||
let c: serde_json::Value = match serde_json::from_str(data) {
|
||||
|
@ -105,7 +106,7 @@ impl BookConfig {
|
|||
Err(e) => {
|
||||
error!("[*]: JSON parse errors in book.json: {:?}", e);
|
||||
exit(2);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let config = json_object_to_btreemap(c.as_object().unwrap());
|
||||
|
@ -205,10 +206,7 @@ pub fn json_object_to_btreemap(json: &serde_json::Map<String, serde_json::Value>
|
|||
let mut config: BTreeMap<String, toml::Value> = BTreeMap::new();
|
||||
|
||||
for (key, value) in json.iter() {
|
||||
config.insert(
|
||||
String::from_str(key).unwrap(),
|
||||
json_value_to_toml_value(value.to_owned())
|
||||
);
|
||||
config.insert(String::from_str(key).unwrap(), json_value_to_toml_value(value.to_owned()));
|
||||
}
|
||||
|
||||
config
|
||||
|
@ -223,10 +221,10 @@ pub fn json_value_to_toml_value(json: serde_json::Value) -> toml::Value {
|
|||
serde_json::Value::Number(x) => toml::Value::Float(x.as_f64().unwrap()),
|
||||
serde_json::Value::String(x) => toml::Value::String(x),
|
||||
serde_json::Value::Array(x) => {
|
||||
toml::Value::Array(x.iter().map(|v| json_value_to_toml_value(v.to_owned())).collect())
|
||||
},
|
||||
serde_json::Value::Object(x) => {
|
||||
toml::Value::Table(json_object_to_btreemap(&x))
|
||||
toml::Value::Array(x.iter()
|
||||
.map(|v| json_value_to_toml_value(v.to_owned()))
|
||||
.collect())
|
||||
},
|
||||
serde_json::Value::Object(x) => toml::Value::Table(json_object_to_btreemap(&x)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,10 +37,12 @@ impl Chapter {
|
|||
|
||||
|
||||
impl Serialize for Chapter {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||
let mut struct_ = try!(serializer.serialize_struct("Chapter", 2));
|
||||
try!(struct_.serialize_field("name", &self.name));
|
||||
try!(struct_.serialize_field("path", &self.path));
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer
|
||||
{
|
||||
let mut struct_ = serializer.serialize_struct("Chapter", 2)?;
|
||||
struct_.serialize_field("name", &self.name)?;
|
||||
struct_.serialize_field("path", &self.path)?;
|
||||
struct_.end()
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +68,8 @@ impl<'a> Iterator for BookItems<'a> {
|
|||
let cur = &self.items[self.current_index];
|
||||
|
||||
match *cur {
|
||||
BookItem::Chapter(_, ref ch) | BookItem::Affix(ref ch) => {
|
||||
BookItem::Chapter(_, ref ch) |
|
||||
BookItem::Affix(ref ch) => {
|
||||
self.stack.push((self.items, self.current_index));
|
||||
self.items = &ch.sub_items[..];
|
||||
self.current_index = 0;
|
||||
|
|
143
src/book/mod.rs
143
src/book/mod.rs
|
@ -54,8 +54,10 @@ impl MDBook {
|
|||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// In this example, `root_dir` will be the root directory of our book and is specified in function
|
||||
/// of the current working directory by using a relative path instead of an absolute path.
|
||||
/// In this example, `root_dir` will be the root directory of our book
|
||||
/// and is specified in function of the current working directory
|
||||
/// by using a relative path instead of an
|
||||
/// absolute path.
|
||||
///
|
||||
/// Default directory paths:
|
||||
///
|
||||
|
@ -63,7 +65,8 @@ impl MDBook {
|
|||
/// - output: `root/book`
|
||||
/// - theme: `root/theme`
|
||||
///
|
||||
/// They can both be changed by using [`set_src()`](#method.set_src) and [`set_dest()`](#method.set_dest)
|
||||
/// They can both be changed by using [`set_src()`](#method.set_src) and
|
||||
/// [`set_dest()`](#method.set_dest)
|
||||
|
||||
pub fn new(root: &Path) -> MDBook {
|
||||
|
||||
|
@ -90,7 +93,8 @@ impl MDBook {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns a flat depth-first iterator over the elements of the book, it returns an [BookItem enum](bookitem.html):
|
||||
/// Returns a flat depth-first iterator over the elements of the book,
|
||||
/// it returns an [BookItem enum](bookitem.html):
|
||||
/// `(section: String, bookitem: &BookItem)`
|
||||
///
|
||||
/// ```no_run
|
||||
|
@ -126,7 +130,8 @@ impl MDBook {
|
|||
}
|
||||
}
|
||||
|
||||
/// `init()` creates some boilerplate files and directories to get you started with your book.
|
||||
/// `init()` creates some boilerplate files and directories
|
||||
/// to get you started with your book.
|
||||
///
|
||||
/// ```text
|
||||
/// book-test/
|
||||
|
@ -136,7 +141,8 @@ impl MDBook {
|
|||
/// └── SUMMARY.md
|
||||
/// ```
|
||||
///
|
||||
/// It uses the paths given as source and output directories and adds a `SUMMARY.md` and a
|
||||
/// It uses the paths given as source and output directories
|
||||
/// and adds a `SUMMARY.md` and a
|
||||
/// `chapter_1.md` to the source directory.
|
||||
|
||||
pub fn init(&mut self) -> Result<(), Box<Error>> {
|
||||
|
@ -152,12 +158,12 @@ impl MDBook {
|
|||
|
||||
if !self.dest.exists() {
|
||||
debug!("[*]: {:?} does not exist, trying to create directory", self.dest);
|
||||
try!(fs::create_dir_all(&self.dest));
|
||||
fs::create_dir_all(&self.dest)?;
|
||||
}
|
||||
|
||||
if !self.src.exists() {
|
||||
debug!("[*]: {:?} does not exist, trying to create directory", self.src);
|
||||
try!(fs::create_dir_all(&self.src));
|
||||
fs::create_dir_all(&self.src)?;
|
||||
}
|
||||
|
||||
let summary = self.src.join("SUMMARY.md");
|
||||
|
@ -167,18 +173,18 @@ impl MDBook {
|
|||
// Summary does not exist, create it
|
||||
|
||||
debug!("[*]: {:?} does not exist, trying to create SUMMARY.md", self.src.join("SUMMARY.md"));
|
||||
let mut f = try!(File::create(&self.src.join("SUMMARY.md")));
|
||||
let mut f = File::create(&self.src.join("SUMMARY.md"))?;
|
||||
|
||||
debug!("[*]: Writing to SUMMARY.md");
|
||||
|
||||
try!(writeln!(f, "# Summary"));
|
||||
try!(writeln!(f, ""));
|
||||
try!(writeln!(f, "- [Chapter 1](./chapter_1.md)"));
|
||||
writeln!(f, "# Summary")?;
|
||||
writeln!(f, "")?;
|
||||
writeln!(f, "- [Chapter 1](./chapter_1.md)")?;
|
||||
}
|
||||
}
|
||||
|
||||
// parse SUMMARY.md, and create the missing item related file
|
||||
try!(self.parse_summary());
|
||||
self.parse_summary()?;
|
||||
|
||||
debug!("[*]: constructing paths for missing files");
|
||||
for item in self.iter() {
|
||||
|
@ -193,16 +199,15 @@ impl MDBook {
|
|||
|
||||
if !path.exists() {
|
||||
if !self.create_missing {
|
||||
return Err(format!(
|
||||
"'{}' referenced from SUMMARY.md does not exist.",
|
||||
path.to_string_lossy()).into());
|
||||
return Err(format!("'{}' referenced from SUMMARY.md does not exist.", path.to_string_lossy())
|
||||
.into());
|
||||
}
|
||||
debug!("[*]: {:?} does not exist, trying to create file", path);
|
||||
try!(::std::fs::create_dir_all(path.parent().unwrap()));
|
||||
let mut f = try!(File::create(path));
|
||||
::std::fs::create_dir_all(path.parent().unwrap())?;
|
||||
let mut f = File::create(path)?;
|
||||
|
||||
// debug!("[*]: Writing to {:?}", path);
|
||||
try!(writeln!(f, "# {}", ch.name));
|
||||
writeln!(f, "# {}", ch.name)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -217,17 +222,19 @@ impl MDBook {
|
|||
if !gitignore.exists() {
|
||||
// Gitignore does not exist, create it
|
||||
|
||||
// Because of `src/book/mdbook.rs#L37-L39`, `dest` will always start with `root`. If it
|
||||
// is not, `strip_prefix` will return an Error.
|
||||
// Because of `src/book/mdbook.rs#L37-L39`,
|
||||
// `dest` will always start with `root`.
|
||||
// If it is not, `strip_prefix` will return an Error.
|
||||
if !self.get_dest().starts_with(&self.root) {
|
||||
return;
|
||||
}
|
||||
|
||||
let relative = self.get_dest()
|
||||
.strip_prefix(&self.root)
|
||||
.expect("Destination is not relative to root.");
|
||||
let relative = relative.to_str()
|
||||
.expect("Path could not be yielded into a string slice.");
|
||||
.strip_prefix(&self.root)
|
||||
.expect("Destination is not relative to root.");
|
||||
let relative = relative
|
||||
.to_str()
|
||||
.expect("Path could not be yielded into a string slice.");
|
||||
|
||||
debug!("[*]: {:?} does not exist, trying to create .gitignore", gitignore);
|
||||
|
||||
|
@ -239,20 +246,21 @@ impl MDBook {
|
|||
}
|
||||
}
|
||||
|
||||
/// The `build()` method is the one where everything happens. First it parses `SUMMARY.md` to
|
||||
/// construct the book's structure in the form of a `Vec<BookItem>` and then calls `render()`
|
||||
/// The `build()` method is the one where everything happens.
|
||||
/// First it parses `SUMMARY.md` to construct the book's structure
|
||||
/// in the form of a `Vec<BookItem>` and then calls `render()`
|
||||
/// method of the current renderer.
|
||||
///
|
||||
/// It is the renderer who generates all the output files.
|
||||
pub fn build(&mut self) -> Result<(), Box<Error>> {
|
||||
debug!("[fn]: build");
|
||||
|
||||
try!(self.init());
|
||||
self.init()?;
|
||||
|
||||
// Clean output directory
|
||||
try!(utils::fs::remove_dir_content(&self.dest));
|
||||
utils::fs::remove_dir_content(&self.dest)?;
|
||||
|
||||
try!(self.renderer.render(&self));
|
||||
self.renderer.render(&self)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -269,55 +277,54 @@ impl MDBook {
|
|||
|
||||
if !theme_dir.exists() {
|
||||
debug!("[*]: {:?} does not exist, trying to create directory", theme_dir);
|
||||
try!(fs::create_dir(&theme_dir));
|
||||
fs::create_dir(&theme_dir)?;
|
||||
}
|
||||
|
||||
// index.hbs
|
||||
let mut index = try!(File::create(&theme_dir.join("index.hbs")));
|
||||
try!(index.write_all(theme::INDEX));
|
||||
let mut index = File::create(&theme_dir.join("index.hbs"))?;
|
||||
index.write_all(theme::INDEX)?;
|
||||
|
||||
// book.css
|
||||
let mut css = try!(File::create(&theme_dir.join("book.css")));
|
||||
try!(css.write_all(theme::CSS));
|
||||
let mut css = File::create(&theme_dir.join("book.css"))?;
|
||||
css.write_all(theme::CSS)?;
|
||||
|
||||
// favicon.png
|
||||
let mut favicon = try!(File::create(&theme_dir.join("favicon.png")));
|
||||
try!(favicon.write_all(theme::FAVICON));
|
||||
let mut favicon = File::create(&theme_dir.join("favicon.png"))?;
|
||||
favicon.write_all(theme::FAVICON)?;
|
||||
|
||||
// book.js
|
||||
let mut js = try!(File::create(&theme_dir.join("book.js")));
|
||||
try!(js.write_all(theme::JS));
|
||||
let mut js = File::create(&theme_dir.join("book.js"))?;
|
||||
js.write_all(theme::JS)?;
|
||||
|
||||
// highlight.css
|
||||
let mut highlight_css = try!(File::create(&theme_dir.join("highlight.css")));
|
||||
try!(highlight_css.write_all(theme::HIGHLIGHT_CSS));
|
||||
let mut highlight_css = File::create(&theme_dir.join("highlight.css"))?;
|
||||
highlight_css.write_all(theme::HIGHLIGHT_CSS)?;
|
||||
|
||||
// highlight.js
|
||||
let mut highlight_js = try!(File::create(&theme_dir.join("highlight.js")));
|
||||
try!(highlight_js.write_all(theme::HIGHLIGHT_JS));
|
||||
let mut highlight_js = File::create(&theme_dir.join("highlight.js"))?;
|
||||
highlight_js.write_all(theme::HIGHLIGHT_JS)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_file<P: AsRef<Path>>(&self, filename: P, content: &[u8]) -> Result<(), Box<Error>> {
|
||||
let path = self.get_dest().join(filename);
|
||||
try!(utils::fs::create_file(&path).and_then(|mut file| {
|
||||
file.write_all(content)
|
||||
}).map_err(|e| {
|
||||
io::Error::new(io::ErrorKind::Other, format!("Could not create {}: {}", path.display(), e))
|
||||
}));
|
||||
utils::fs::create_file(&path)
|
||||
.and_then(|mut file| file.write_all(content))
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Could not create {}: {}", path.display(), e)))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the `book.json` file (if it exists) to extract the configuration parameters.
|
||||
/// Parses the `book.json` file (if it exists) to extract
|
||||
/// the configuration parameters.
|
||||
/// The `book.json` file should be in the root directory of the book.
|
||||
/// The root directory is the one specified when creating a new `MDBook`
|
||||
|
||||
pub fn read_config(mut self) -> Self {
|
||||
|
||||
let config = BookConfig::new(&self.root)
|
||||
.read_config(&self.root)
|
||||
.to_owned();
|
||||
.read_config(&self.root)
|
||||
.to_owned();
|
||||
|
||||
self.title = config.title;
|
||||
self.description = config.description;
|
||||
|
@ -330,8 +337,10 @@ impl MDBook {
|
|||
self
|
||||
}
|
||||
|
||||
/// You can change the default renderer to another one by using this method. The only requirement
|
||||
/// is for your renderer to implement the [Renderer trait](../../renderer/renderer/trait.Renderer.html)
|
||||
/// You can change the default renderer to another one
|
||||
/// by using this method. The only requirement
|
||||
/// is for your renderer to implement the
|
||||
/// [Renderer trait](../../renderer/renderer/trait.Renderer.html)
|
||||
///
|
||||
/// ```no_run
|
||||
/// extern crate mdbook;
|
||||
|
@ -343,12 +352,14 @@ impl MDBook {
|
|||
/// let mut book = MDBook::new(Path::new("mybook"))
|
||||
/// .set_renderer(Box::new(HtmlHandlebars::new()));
|
||||
///
|
||||
/// // In this example we replace the default renderer by the default renderer...
|
||||
/// // Don't forget to put your renderer in a Box
|
||||
/// // In this example we replace the default renderer
|
||||
/// // by the default renderer...
|
||||
/// // Don't forget to put your renderer in a Box
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// **note:** Don't forget to put your renderer in a `Box` before passing it to `set_renderer()`
|
||||
/// **note:** Don't forget to put your renderer in a `Box`
|
||||
/// before passing it to `set_renderer()`
|
||||
|
||||
pub fn set_renderer(mut self, renderer: Box<Renderer>) -> Self {
|
||||
self.renderer = renderer;
|
||||
|
@ -357,7 +368,7 @@ impl MDBook {
|
|||
|
||||
pub fn test(&mut self) -> Result<(), Box<Error>> {
|
||||
// read in the chapters
|
||||
try!(self.parse_summary());
|
||||
self.parse_summary()?;
|
||||
for item in self.iter() {
|
||||
|
||||
if let BookItem::Chapter(_, ref ch) = *item {
|
||||
|
@ -367,17 +378,15 @@ impl MDBook {
|
|||
|
||||
println!("[*]: Testing file: {:?}", path);
|
||||
|
||||
let output_result = Command::new("rustdoc")
|
||||
.arg(&path)
|
||||
.arg("--test")
|
||||
.output();
|
||||
let output = try!(output_result);
|
||||
let output_result = Command::new("rustdoc").arg(&path).arg("--test").output();
|
||||
let output = output_result?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(Box::new(io::Error::new(ErrorKind::Other, format!(
|
||||
"{}\n{}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)))) as Box<Error>);
|
||||
return Err(Box::new(io::Error::new(ErrorKind::Other,
|
||||
format!("{}\n{}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)))) as
|
||||
Box<Error>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -480,7 +489,7 @@ impl MDBook {
|
|||
// Construct book
|
||||
fn parse_summary(&mut self) -> Result<(), Box<Error>> {
|
||||
// When append becomes stable, use self.content.append() ...
|
||||
self.content = try!(parse::construct_bookitems(&self.src.join("SUMMARY.md")));
|
||||
self.content = parse::construct_bookitems(&self.src.join("SUMMARY.md"))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,8 @@ extern crate handlebars;
|
|||
extern crate pulldown_cmark;
|
||||
extern crate regex;
|
||||
|
||||
#[macro_use] extern crate log;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
pub mod book;
|
||||
mod parse;
|
||||
pub mod renderer;
|
||||
|
|
|
@ -6,10 +6,10 @@ use book::bookitem::{BookItem, Chapter};
|
|||
pub fn construct_bookitems(path: &PathBuf) -> Result<Vec<BookItem>> {
|
||||
debug!("[fn]: construct_bookitems");
|
||||
let mut summary = String::new();
|
||||
try!(try!(File::open(path)).read_to_string(&mut summary));
|
||||
File::open(path)?.read_to_string(&mut summary)?;
|
||||
|
||||
debug!("[*]: Parse SUMMARY.md");
|
||||
let top_items = try!(parse_level(&mut summary.split('\n').collect(), 0, vec![0]));
|
||||
let top_items = parse_level(&mut summary.split('\n').collect(), 0, vec![0])?;
|
||||
debug!("[*]: Done parsing SUMMARY.md");
|
||||
Ok(top_items)
|
||||
}
|
||||
|
@ -22,9 +22,10 @@ fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32
|
|||
while !summary.is_empty() {
|
||||
let item: BookItem;
|
||||
// Indentation level of the line to parse
|
||||
let level = try!(level(summary[0], 4));
|
||||
let level = level(summary[0], 4)?;
|
||||
|
||||
// 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.
|
||||
if level < current_level {
|
||||
break;
|
||||
|
@ -35,11 +36,13 @@ fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32
|
|||
// Level can not be root level !!
|
||||
// Add a sub-number to section
|
||||
section.push(0);
|
||||
let last = items.pop().expect("There should be at least one item since this can't be the root level");
|
||||
let last = items
|
||||
.pop()
|
||||
.expect("There should be at least one item since this can't be the root level");
|
||||
|
||||
if let BookItem::Chapter(ref s, ref ch) = last {
|
||||
let mut ch = ch.clone();
|
||||
ch.sub_items = try!(parse_level(summary, level, section.clone()));
|
||||
ch.sub_items = parse_level(summary, level, section.clone())?;
|
||||
items.push(BookItem::Chapter(s.clone(), ch));
|
||||
|
||||
// Remove the last number from the section, because we got back to our level..
|
||||
|
@ -62,7 +65,8 @@ fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32
|
|||
// Eliminate possible errors and set section to -1 after suffix
|
||||
match parsed_item {
|
||||
// error if level != 0 and BookItem is != Chapter
|
||||
BookItem::Affix(_) | BookItem::Spacer if level > 0 => {
|
||||
BookItem::Affix(_) |
|
||||
BookItem::Spacer if level > 0 => {
|
||||
return Err(Error::new(ErrorKind::Other,
|
||||
"Your summary.md is messed up\n\n
|
||||
\
|
||||
|
@ -98,7 +102,9 @@ fn parse_level(summary: &mut Vec<&str>, current_level: i32, mut section: Vec<i32
|
|||
// Increment section
|
||||
let len = 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)
|
||||
},
|
||||
_ => parsed_item,
|
||||
|
|
|
@ -37,7 +37,8 @@ impl Renderer for HtmlHandlebars {
|
|||
|
||||
// Register template
|
||||
debug!("[*]: Register handlebars template");
|
||||
try!(handlebars.register_template_string("index", try!(String::from_utf8(theme.index))));
|
||||
handlebars
|
||||
.register_template_string("index", String::from_utf8(theme.index)?)?;
|
||||
|
||||
// Register helpers
|
||||
debug!("[*]: Register handlebars helpers");
|
||||
|
@ -45,7 +46,7 @@ impl Renderer for HtmlHandlebars {
|
|||
handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
|
||||
handlebars.register_helper("next", Box::new(helpers::navigation::next));
|
||||
|
||||
let mut data = try!(make_data(book));
|
||||
let mut data = make_data(book)?;
|
||||
|
||||
// Print version
|
||||
let mut print_content: String = String::new();
|
||||
|
@ -69,11 +70,11 @@ impl Renderer for HtmlHandlebars {
|
|||
let path = book.get_src().join(&ch.path);
|
||||
|
||||
debug!("[*]: Opening file: {:?}", path);
|
||||
let mut f = try!(File::open(&path));
|
||||
let mut f = File::open(&path)?;
|
||||
let mut content: String = String::new();
|
||||
|
||||
debug!("[*]: Reading file");
|
||||
try!(f.read_to_string(&mut content));
|
||||
f.read_to_string(&mut content)?;
|
||||
|
||||
// Parse for playpen links
|
||||
if let Some(p) = path.parent() {
|
||||
|
@ -85,8 +86,10 @@ impl Renderer for HtmlHandlebars {
|
|||
print_content.push_str(&content);
|
||||
|
||||
// Update the context with data for this file
|
||||
let path = ch.path.to_str().ok_or_else(||
|
||||
io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
|
||||
let path =
|
||||
ch.path
|
||||
.to_str()
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
|
||||
data.insert("path".to_owned(), json!(path));
|
||||
data.insert("content".to_owned(), json!(content));
|
||||
data.insert("chapter_title".to_owned(), json!(ch.name));
|
||||
|
@ -94,7 +97,7 @@ impl Renderer for HtmlHandlebars {
|
|||
|
||||
// Render the handlebars template with the data
|
||||
debug!("[*]: Render template");
|
||||
let rendered = try!(handlebars.render("index", &data));
|
||||
let rendered = handlebars.render("index", &data)?;
|
||||
|
||||
let filename = Path::new(&ch.path).with_extension("html");
|
||||
|
||||
|
@ -106,24 +109,26 @@ impl Renderer for HtmlHandlebars {
|
|||
|
||||
// Write to file
|
||||
info!("[*] Creating {:?} ✓", filename.display());
|
||||
try!(book.write_file(filename, &rendered.into_bytes()));
|
||||
book.write_file(filename, &rendered.into_bytes())?;
|
||||
|
||||
// Create an index.html from the first element in SUMMARY.md
|
||||
if index {
|
||||
debug!("[*]: index.html");
|
||||
|
||||
let mut content = String::new();
|
||||
let _source = try!(File::open(book.get_dest().join(&ch.path.with_extension("html"))))
|
||||
let _source = File::open(book.get_dest().join(&ch.path.with_extension("html")))?
|
||||
.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...
|
||||
content = content.lines()
|
||||
content = content
|
||||
.lines()
|
||||
.filter(|line| !line.contains("<base href="))
|
||||
.collect::<Vec<&str>>()
|
||||
.join("\n");
|
||||
|
||||
try!(book.write_file("index.html", content.as_bytes()));
|
||||
book.write_file("index.html", content.as_bytes())?;
|
||||
|
||||
info!("[*] Creating index.html from {:?} ✓",
|
||||
book.get_dest().join(&ch.path.with_extension("html")));
|
||||
|
@ -145,7 +150,7 @@ impl Renderer for HtmlHandlebars {
|
|||
// Render the handlebars template with the data
|
||||
debug!("[*]: Render template");
|
||||
|
||||
let rendered = try!(handlebars.render("index", &data));
|
||||
let rendered = handlebars.render("index", &data)?;
|
||||
|
||||
// do several kinds of post-processing
|
||||
let rendered = build_header_links(rendered, "print.html");
|
||||
|
@ -153,29 +158,29 @@ impl Renderer for HtmlHandlebars {
|
|||
let rendered = fix_code_blocks(rendered);
|
||||
let rendered = add_playpen_pre(rendered);
|
||||
|
||||
try!(book.write_file(Path::new("print").with_extension("html"), &rendered.into_bytes()));
|
||||
book.write_file(Path::new("print").with_extension("html"), &rendered.into_bytes())?;
|
||||
info!("[*] Creating print.html ✓");
|
||||
|
||||
// Copy static files (js, css, images, ...)
|
||||
|
||||
debug!("[*] Copy static files");
|
||||
try!(book.write_file("book.js", &theme.js));
|
||||
try!(book.write_file("book.css", &theme.css));
|
||||
try!(book.write_file("favicon.png", &theme.favicon));
|
||||
try!(book.write_file("jquery.js", &theme.jquery));
|
||||
try!(book.write_file("highlight.css", &theme.highlight_css));
|
||||
try!(book.write_file("tomorrow-night.css", &theme.tomorrow_night_css));
|
||||
try!(book.write_file("highlight.js", &theme.highlight_js));
|
||||
try!(book.write_file("_FontAwesome/css/font-awesome.css", theme::FONT_AWESOME));
|
||||
try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.eot", theme::FONT_AWESOME_EOT));
|
||||
try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.svg", theme::FONT_AWESOME_SVG));
|
||||
try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.ttf", theme::FONT_AWESOME_TTF));
|
||||
try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff", theme::FONT_AWESOME_WOFF));
|
||||
try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff2", theme::FONT_AWESOME_WOFF2));
|
||||
try!(book.write_file("_FontAwesome/fonts/FontAwesome.ttf", theme::FONT_AWESOME_TTF));
|
||||
book.write_file("book.js", &theme.js)?;
|
||||
book.write_file("book.css", &theme.css)?;
|
||||
book.write_file("favicon.png", &theme.favicon)?;
|
||||
book.write_file("jquery.js", &theme.jquery)?;
|
||||
book.write_file("highlight.css", &theme.highlight_css)?;
|
||||
book.write_file("tomorrow-night.css", &theme.tomorrow_night_css)?;
|
||||
book.write_file("highlight.js", &theme.highlight_js)?;
|
||||
book.write_file("_FontAwesome/css/font-awesome.css", theme::FONT_AWESOME)?;
|
||||
book.write_file("_FontAwesome/fonts/fontawesome-webfont.eot", theme::FONT_AWESOME_EOT)?;
|
||||
book.write_file("_FontAwesome/fonts/fontawesome-webfont.svg", theme::FONT_AWESOME_SVG)?;
|
||||
book.write_file("_FontAwesome/fonts/fontawesome-webfont.ttf", theme::FONT_AWESOME_TTF)?;
|
||||
book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff", theme::FONT_AWESOME_WOFF)?;
|
||||
book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff2", theme::FONT_AWESOME_WOFF2)?;
|
||||
book.write_file("_FontAwesome/fonts/FontAwesome.ttf", theme::FONT_AWESOME_TTF)?;
|
||||
|
||||
// Copy all remaining files
|
||||
try!(utils::fs::copy_files_except_ext(book.get_src(), book.get_dest(), true, &["md"]));
|
||||
utils::fs::copy_files_except_ext(book.get_src(), book.get_dest(), true, &["md"])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -207,15 +212,17 @@ fn make_data(book: &MDBook) -> Result<serde_json::Map<String, serde_json::Value>
|
|||
match *item {
|
||||
BookItem::Affix(ref ch) => {
|
||||
chapter.insert("name".to_owned(), json!(ch.name));
|
||||
let path = ch.path.to_str().ok_or_else(||
|
||||
io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
|
||||
let path = ch.path
|
||||
.to_str()
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
|
||||
chapter.insert("path".to_owned(), json!(path));
|
||||
},
|
||||
BookItem::Chapter(ref s, ref ch) => {
|
||||
chapter.insert("section".to_owned(), json!(s));
|
||||
chapter.insert("name".to_owned(), json!(ch.name));
|
||||
let path = ch.path.to_str().ok_or_else(||
|
||||
io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
|
||||
let path = ch.path
|
||||
.to_str()
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
|
||||
chapter.insert("path".to_owned(), json!(path));
|
||||
},
|
||||
BookItem::Spacer => {
|
||||
|
@ -237,42 +244,55 @@ fn build_header_links(html: String, filename: &str) -> String {
|
|||
let regex = Regex::new(r"<h(\d)>(.*?)</h\d>").unwrap();
|
||||
let mut id_counter = HashMap::new();
|
||||
|
||||
regex.replace_all(&html, |caps: &Captures| {
|
||||
let level = &caps[1];
|
||||
let text = &caps[2];
|
||||
let mut id = text.to_string();
|
||||
let repl_sub = vec!["<em>", "</em>", "<code>", "</code>",
|
||||
"<strong>", "</strong>",
|
||||
"<", ">", "&", "'", """];
|
||||
for sub in repl_sub {
|
||||
id = id.replace(sub, "");
|
||||
}
|
||||
let id = id.chars().filter_map(|c| {
|
||||
if c.is_alphanumeric() || c == '-' || c == '_' {
|
||||
if c.is_ascii() {
|
||||
Some(c.to_ascii_lowercase())
|
||||
} else {
|
||||
Some(c)
|
||||
}
|
||||
} else if c.is_whitespace() && c.is_ascii() {
|
||||
Some('-')
|
||||
} else {
|
||||
None
|
||||
regex
|
||||
.replace_all(&html, |caps: &Captures| {
|
||||
let level = &caps[1];
|
||||
let text = &caps[2];
|
||||
let mut id = text.to_string();
|
||||
let repl_sub = vec!["<em>",
|
||||
"</em>",
|
||||
"<code>",
|
||||
"</code>",
|
||||
"<strong>",
|
||||
"</strong>",
|
||||
"<",
|
||||
">",
|
||||
"&",
|
||||
"'",
|
||||
"""];
|
||||
for sub in repl_sub {
|
||||
id = id.replace(sub, "");
|
||||
}
|
||||
}).collect::<String>();
|
||||
let id = id.chars()
|
||||
.filter_map(|c| if c.is_alphanumeric() || c == '-' || c == '_' {
|
||||
if c.is_ascii() {
|
||||
Some(c.to_ascii_lowercase())
|
||||
} else {
|
||||
Some(c)
|
||||
}
|
||||
} else if c.is_whitespace() && c.is_ascii() {
|
||||
Some('-')
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.collect::<String>();
|
||||
|
||||
let id_count = *id_counter.get(&id).unwrap_or(&0);
|
||||
id_counter.insert(id.clone(), id_count + 1);
|
||||
let id_count = *id_counter.get(&id).unwrap_or(&0);
|
||||
id_counter.insert(id.clone(), id_count + 1);
|
||||
|
||||
let id = if id_count > 0 {
|
||||
format!("{}-{}", id, id_count)
|
||||
} else {
|
||||
id
|
||||
};
|
||||
let id = if id_count > 0 {
|
||||
format!("{}-{}", id, id_count)
|
||||
} else {
|
||||
id
|
||||
};
|
||||
|
||||
format!("<a class=\"header\" href=\"{filename}#{id}\" id=\"{id}\"><h{level}>{text}</h{level}></a>",
|
||||
level=level, id=id, text=text, filename=filename)
|
||||
}).into_owned()
|
||||
format!("<a class=\"header\" href=\"{filename}#{id}\" id=\"{id}\"><h{level}>{text}</h{level}></a>",
|
||||
level = level,
|
||||
id = id,
|
||||
text = text,
|
||||
filename = filename)
|
||||
})
|
||||
.into_owned()
|
||||
}
|
||||
|
||||
// anchors to the same page (href="#anchor") do not work because of
|
||||
|
@ -280,18 +300,24 @@ fn build_header_links(html: String, filename: &str) -> String {
|
|||
// that in a very inelegant way
|
||||
fn fix_anchor_links(html: String, filename: &str) -> String {
|
||||
let regex = Regex::new(r##"<a([^>]+)href="#([^"]+)"([^>]*)>"##).unwrap();
|
||||
regex.replace_all(&html, |caps: &Captures| {
|
||||
let before = &caps[1];
|
||||
let anchor = &caps[2];
|
||||
let after = &caps[3];
|
||||
regex
|
||||
.replace_all(&html, |caps: &Captures| {
|
||||
let before = &caps[1];
|
||||
let anchor = &caps[2];
|
||||
let after = &caps[3];
|
||||
|
||||
format!("<a{before}href=\"{filename}#{anchor}\"{after}>",
|
||||
before=before, filename=filename, anchor=anchor, after=after)
|
||||
}).into_owned()
|
||||
format!("<a{before}href=\"{filename}#{anchor}\"{after}>",
|
||||
before = before,
|
||||
filename = filename,
|
||||
anchor = anchor,
|
||||
after = after)
|
||||
})
|
||||
.into_owned()
|
||||
}
|
||||
|
||||
|
||||
// The rust book uses annotations for rustdoc to test code snippets, like the following:
|
||||
// The rust book uses annotations for rustdoc to test code snippets,
|
||||
// like the following:
|
||||
// ```rust,should_panic
|
||||
// fn main() {
|
||||
// // Code here
|
||||
|
@ -300,40 +326,48 @@ fn fix_anchor_links(html: String, filename: &str) -> String {
|
|||
// This function replaces all commas by spaces in the code block classes
|
||||
fn fix_code_blocks(html: String) -> String {
|
||||
let regex = Regex::new(r##"<code([^>]+)class="([^"]+)"([^>]*)>"##).unwrap();
|
||||
regex.replace_all(&html, |caps: &Captures| {
|
||||
let before = &caps[1];
|
||||
let classes = &caps[2].replace(",", " ");
|
||||
let after = &caps[3];
|
||||
regex
|
||||
.replace_all(&html, |caps: &Captures| {
|
||||
let before = &caps[1];
|
||||
let classes = &caps[2].replace(",", " ");
|
||||
let after = &caps[3];
|
||||
|
||||
format!("<code{before}class=\"{classes}\"{after}>", before=before, classes=classes, after=after)
|
||||
}).into_owned()
|
||||
format!("<code{before}class=\"{classes}\"{after}>", before = before, classes = classes, after = after)
|
||||
})
|
||||
.into_owned()
|
||||
}
|
||||
|
||||
fn add_playpen_pre(html: String) -> String {
|
||||
let regex = Regex::new(r##"((?s)<code[^>]?class="([^"]+)".*?>(.*?)</code>)"##).unwrap();
|
||||
regex.replace_all(&html, |caps: &Captures| {
|
||||
let text = &caps[1];
|
||||
let classes = &caps[2];
|
||||
let code = &caps[3];
|
||||
regex
|
||||
.replace_all(&html, |caps: &Captures| {
|
||||
let text = &caps[1];
|
||||
let classes = &caps[2];
|
||||
let code = &caps[3];
|
||||
|
||||
if classes.contains("language-rust") && !classes.contains("ignore") {
|
||||
// wrap the contents in an external pre block
|
||||
if classes.contains("language-rust") && !classes.contains("ignore") {
|
||||
// wrap the contents in an external pre block
|
||||
|
||||
if text.contains("fn main") || text.contains("quick_main!") {
|
||||
format!("<pre class=\"playpen\">{}</pre>", text)
|
||||
} else {
|
||||
// we need to inject our own main
|
||||
let (attrs, code) = partition_source(code);
|
||||
format!("<pre class=\"playpen\"><code class=\"{}\"># #![allow(unused_variables)]
|
||||
if text.contains("fn main") || text.contains("quick_main!") {
|
||||
format!("<pre class=\"playpen\">{}</pre>", text)
|
||||
} else {
|
||||
// we need to inject our own main
|
||||
let (attrs, code) = partition_source(code);
|
||||
format!("<pre class=\"playpen\"><code class=\"{}\"># #![allow(unused_variables)]
|
||||
{}#fn main() {{
|
||||
{}
|
||||
#}}</code></pre>", classes, attrs, code)
|
||||
\
|
||||
{}
|
||||
#}}</code></pre>",
|
||||
classes,
|
||||
attrs,
|
||||
code)
|
||||
}
|
||||
} else {
|
||||
// not language-rust, so no-op
|
||||
format!("{}", text)
|
||||
}
|
||||
} else {
|
||||
// not language-rust, so no-op
|
||||
format!("{}", text)
|
||||
}
|
||||
}).into_owned()
|
||||
})
|
||||
.into_owned()
|
||||
}
|
||||
|
||||
fn partition_source(s: &str) -> (String, String) {
|
||||
|
@ -343,8 +377,7 @@ fn partition_source(s: &str) -> (String, String) {
|
|||
|
||||
for line in s.lines() {
|
||||
let trimline = line.trim();
|
||||
let header = trimline.chars().all(|c| c.is_whitespace()) ||
|
||||
trimline.starts_with("#![");
|
||||
let header = trimline.chars().all(|c| c.is_whitespace()) || trimline.starts_with("#![");
|
||||
if !header || after_header {
|
||||
after_header = true;
|
||||
after.push_str(line);
|
||||
|
|
|
@ -14,9 +14,12 @@ pub fn previous(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(
|
|||
// 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 = rc.context().navigate(rc.get_path(), &VecDeque::new(), "chapters").to_owned();
|
||||
let chapters = rc.context()
|
||||
.navigate(rc.get_path(), &VecDeque::new(), "chapters")
|
||||
.to_owned();
|
||||
|
||||
let current = rc.context().navigate(rc.get_path(), &VecDeque::new(), "path")
|
||||
let current = rc.context()
|
||||
.navigate(rc.get_path(), &VecDeque::new(), "path")
|
||||
.to_string()
|
||||
.replace("\"", "");
|
||||
|
||||
|
@ -84,7 +87,7 @@ pub fn previous(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(
|
|||
match _h.template() {
|
||||
Some(t) => {
|
||||
*rc.context_mut() = updated_context;
|
||||
try!(t.render(r, rc));
|
||||
t.render(r, rc)?;
|
||||
},
|
||||
None => return Err(RenderError::new("Error with the handlebars template")),
|
||||
}
|
||||
|
@ -115,9 +118,12 @@ pub fn next(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), R
|
|||
// 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 = rc.context().navigate(rc.get_path(), &VecDeque::new(), "chapters").to_owned();
|
||||
let chapters = rc.context()
|
||||
.navigate(rc.get_path(), &VecDeque::new(), "chapters")
|
||||
.to_owned();
|
||||
|
||||
let current = rc.context().navigate(rc.get_path(), &VecDeque::new(), "path")
|
||||
let current = rc.context()
|
||||
.navigate(rc.get_path(), &VecDeque::new(), "path")
|
||||
.to_string()
|
||||
.replace("\"", "");
|
||||
|
||||
|
@ -181,7 +187,7 @@ pub fn next(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), R
|
|||
match _h.template() {
|
||||
Some(t) => {
|
||||
*rc.context_mut() = updated_context;
|
||||
try!(t.render(r, rc));
|
||||
t.render(r, rc)?;
|
||||
},
|
||||
None => return Err(RenderError::new("Error with the handlebars template")),
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@ use std::io::Read;
|
|||
|
||||
|
||||
pub fn render_playpen(s: &str, path: &Path) -> String {
|
||||
// When replacing one thing in a string by something with a different length, the indices
|
||||
// after that will not correspond, we therefore have to store the difference to correct this
|
||||
// When replacing one thing in a string by something with a different length,
|
||||
// the indices after that will not correspond,
|
||||
// we therefore have to store the difference to correct this
|
||||
let mut previous_end_index = 0;
|
||||
let mut replaced = String::new();
|
||||
|
||||
|
@ -35,13 +36,13 @@ pub fn render_playpen(s: &str, path: &Path) -> String {
|
|||
continue;
|
||||
};
|
||||
|
||||
let replacement = String::new() + "<pre><code class=\"language-rust\">" + &file_content +
|
||||
"</code></pre>";
|
||||
let replacement = String::new() + "<pre><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,
|
||||
// println!("Playpen{{ {}, {}, {:?}, {} }}", playpen.start_index,
|
||||
// playpen.end_index, playpen.rust_file,
|
||||
// playpen.editable);
|
||||
}
|
||||
|
||||
|
@ -100,12 +101,12 @@ fn find_playpens(s: &str, base_path: &Path) -> Vec<Playpen> {
|
|||
.unwrap_or(false);
|
||||
|
||||
playpens.push(Playpen {
|
||||
start_index: i,
|
||||
end_index: end_i,
|
||||
rust_file: base_path.join(PathBuf::from(params[0])),
|
||||
editable: editable,
|
||||
escaped: escaped,
|
||||
})
|
||||
start_index: i,
|
||||
end_index: end_i,
|
||||
rust_file: base_path.join(PathBuf::from(params[0])),
|
||||
editable: editable,
|
||||
escaped: escaped,
|
||||
})
|
||||
}
|
||||
|
||||
playpens
|
||||
|
@ -189,7 +190,11 @@ fn test_find_playpens_escaped_playpen() {
|
|||
println!("\nOUTPUT: {:?}\n", find_playpens(s, Path::new("")));
|
||||
|
||||
assert!(find_playpens(s, Path::new("")) ==
|
||||
vec![
|
||||
Playpen{start_index: 39, end_index: 68, rust_file: PathBuf::from("file.rs"), editable: true, escaped: true},
|
||||
]);
|
||||
vec![Playpen {
|
||||
start_index: 39,
|
||||
end_index: 68,
|
||||
rust_file: PathBuf::from("file.rs"),
|
||||
editable: true,
|
||||
escaped: true,
|
||||
}]);
|
||||
}
|
||||
|
|
|
@ -15,9 +15,14 @@ impl HelperDef for RenderToc {
|
|||
// 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 = rc.context().navigate(rc.get_path(), &VecDeque::new(), "chapters").to_owned();
|
||||
let current = rc.context().navigate(rc.get_path(), &VecDeque::new(), "path").to_string().replace("\"", "");
|
||||
try!(rc.writer.write_all("<ul class=\"chapter\">".as_bytes()));
|
||||
let chapters = rc.context()
|
||||
.navigate(rc.get_path(), &VecDeque::new(), "chapters")
|
||||
.to_owned();
|
||||
let current = rc.context()
|
||||
.navigate(rc.get_path(), &VecDeque::new(), "path")
|
||||
.to_string()
|
||||
.replace("\"", "");
|
||||
rc.writer.write_all("<ul class=\"chapter\">".as_bytes())?;
|
||||
|
||||
// Decode json format
|
||||
let decoded: Vec<BTreeMap<String, String>> = serde_json::from_str(&chapters.to_string()).unwrap();
|
||||
|
@ -28,7 +33,8 @@ impl HelperDef for RenderToc {
|
|||
|
||||
// Spacer
|
||||
if item.get("spacer").is_some() {
|
||||
try!(rc.writer.write_all("<li class=\"spacer\"></li>".as_bytes()));
|
||||
rc.writer
|
||||
.write_all("<li class=\"spacer\"></li>".as_bytes())?;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -40,48 +46,47 @@ impl HelperDef for RenderToc {
|
|||
|
||||
if level > current_level {
|
||||
while level > current_level {
|
||||
try!(rc.writer.write_all("<li>".as_bytes()));
|
||||
try!(rc.writer.write_all("<ul class=\"section\">".as_bytes()));
|
||||
rc.writer.write_all("<li>".as_bytes())?;
|
||||
rc.writer.write_all("<ul class=\"section\">".as_bytes())?;
|
||||
current_level += 1;
|
||||
}
|
||||
try!(rc.writer.write_all("<li>".as_bytes()));
|
||||
rc.writer.write_all("<li>".as_bytes())?;
|
||||
} else if level < current_level {
|
||||
while level < current_level {
|
||||
try!(rc.writer.write_all("</ul>".as_bytes()));
|
||||
try!(rc.writer.write_all("</li>".as_bytes()));
|
||||
rc.writer.write_all("</ul>".as_bytes())?;
|
||||
rc.writer.write_all("</li>".as_bytes())?;
|
||||
current_level -= 1;
|
||||
}
|
||||
try!(rc.writer.write_all("<li>".as_bytes()));
|
||||
rc.writer.write_all("<li>".as_bytes())?;
|
||||
} else {
|
||||
try!(rc.writer.write_all("<li".as_bytes()));
|
||||
rc.writer.write_all("<li".as_bytes())?;
|
||||
if item.get("section").is_none() {
|
||||
try!(rc.writer.write_all(" class=\"affix\"".as_bytes()));
|
||||
rc.writer.write_all(" class=\"affix\"".as_bytes())?;
|
||||
}
|
||||
try!(rc.writer.write_all(">".as_bytes()));
|
||||
rc.writer.write_all(">".as_bytes())?;
|
||||
}
|
||||
|
||||
// Link
|
||||
let path_exists = if let Some(path) = item.get("path") {
|
||||
if !path.is_empty() {
|
||||
try!(rc.writer.write_all("<a href=\"".as_bytes()));
|
||||
rc.writer.write_all("<a href=\"".as_bytes())?;
|
||||
|
||||
let tmp = Path::new(item.get("path").expect("Error: path should be Some(_)"))
|
||||
.with_extension("html")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
// Hack for windows who tends to use `\` as separator instead of `/`
|
||||
.replace("\\", "/");
|
||||
|
||||
// Add link
|
||||
try!(rc.writer.write_all(Path::new(item.get("path")
|
||||
.expect("Error: path should be Some(_)"))
|
||||
.with_extension("html")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
// Hack for windows who tends to use `\` as separator instead of `/`
|
||||
.replace("\\", "/")
|
||||
.as_bytes()));
|
||||
|
||||
try!(rc.writer.write_all("\"".as_bytes()));
|
||||
rc.writer.write_all(tmp.as_bytes())?;
|
||||
rc.writer.write_all("\"".as_bytes())?;
|
||||
|
||||
if path == ¤t {
|
||||
try!(rc.writer.write_all(" class=\"active\"".as_bytes()));
|
||||
rc.writer.write_all(" class=\"active\"".as_bytes())?;
|
||||
}
|
||||
|
||||
try!(rc.writer.write_all(">".as_bytes()));
|
||||
rc.writer.write_all(">".as_bytes())?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -92,47 +97,45 @@ impl HelperDef for RenderToc {
|
|||
|
||||
// Section does not necessarily exist
|
||||
if let Some(section) = item.get("section") {
|
||||
try!(rc.writer.write_all("<strong>".as_bytes()));
|
||||
try!(rc.writer.write_all(section.as_bytes()));
|
||||
try!(rc.writer.write_all("</strong> ".as_bytes()));
|
||||
rc.writer.write_all("<strong>".as_bytes())?;
|
||||
rc.writer.write_all(section.as_bytes())?;
|
||||
rc.writer.write_all("</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) |
|
||||
Event::InlineHtml(_) |
|
||||
Event::Text(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
let parser = Parser::new(name).filter(|event| match *event {
|
||||
Event::Start(Tag::Code) |
|
||||
Event::End(Tag::Code) |
|
||||
Event::InlineHtml(_) |
|
||||
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_all(markdown_parsed_name.as_bytes()));
|
||||
rc.writer.write_all(markdown_parsed_name.as_bytes())?;
|
||||
}
|
||||
|
||||
if path_exists {
|
||||
try!(rc.writer.write_all("</a>".as_bytes()));
|
||||
rc.writer.write_all("</a>".as_bytes())?;
|
||||
}
|
||||
|
||||
try!(rc.writer.write_all("</li>".as_bytes()));
|
||||
rc.writer.write_all("</li>".as_bytes())?;
|
||||
|
||||
}
|
||||
while current_level > 1 {
|
||||
try!(rc.writer.write_all("</ul>".as_bytes()));
|
||||
try!(rc.writer.write_all("</li>".as_bytes()));
|
||||
rc.writer.write_all("</ul>".as_bytes())?;
|
||||
rc.writer.write_all("</li>".as_bytes())?;
|
||||
current_level -= 1;
|
||||
}
|
||||
|
||||
try!(rc.writer.write_all("</ul>".as_bytes()));
|
||||
rc.writer.write_all("</ul>".as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,14 @@ pub static FONT_AWESOME_WOFF: &'static [u8] = include_bytes!("_FontAwesome/fonts
|
|||
pub static FONT_AWESOME_WOFF2: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.woff2");
|
||||
pub static FONT_AWESOME_OTF: &'static [u8] = include_bytes!("_FontAwesome/fonts/FontAwesome.otf");
|
||||
|
||||
/// The `Theme` struct should be used instead of the static variables because the `new()` method
|
||||
/// will look if the user has a theme directory in his source folder and use the users theme instead
|
||||
/// The `Theme` struct should be used instead of the static variables because
|
||||
/// the `new()` method
|
||||
/// will look if the user has a theme directory in his source folder and use
|
||||
/// the users theme instead
|
||||
/// of the default.
|
||||
///
|
||||
/// You should exceptionnaly use the static variables only if you need the default theme even if the
|
||||
/// You should exceptionnaly use the static variables only if you need the
|
||||
/// default theme even if the
|
||||
/// user has specified another theme.
|
||||
pub struct Theme {
|
||||
pub index: Vec<u8>,
|
||||
|
|
|
@ -24,10 +24,11 @@ pub fn file_to_string(path: &Path) -> Result<String, Box<Error>> {
|
|||
Ok(content)
|
||||
}
|
||||
|
||||
/// Takes a path and returns a path containing just enough `../` to point to the root of the given path.
|
||||
/// Takes a path and returns a path containing just enough `../` to point to
|
||||
/// the root of the given path.
|
||||
///
|
||||
/// This is mostly interesting for a relative path to point back to the directory from where the
|
||||
/// path starts.
|
||||
/// This is mostly interesting for a relative path to point back to the
|
||||
/// directory from where the path starts.
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut path = Path::new("some/relative/path");
|
||||
|
@ -41,9 +42,10 @@ pub fn file_to_string(path: &Path) -> Result<String, Box<Error>> {
|
|||
/// "../../"
|
||||
/// ```
|
||||
///
|
||||
/// **note:** it's not very fool-proof, if you find a situation where it doesn't return the correct
|
||||
/// path. Consider [submitting a new issue](https://github.com/azerupi/mdBook/issues) or a
|
||||
/// [pull-request](https://github.com/azerupi/mdBook/pulls) to improve it.
|
||||
/// **note:** it's not very fool-proof, if you find a situation where
|
||||
/// it doesn't return the correct path.
|
||||
/// Consider [submitting a new issue](https://github.com/azerupi/mdBook/issues)
|
||||
/// or a [pull-request](https://github.com/azerupi/mdBook/pulls) to improve it.
|
||||
|
||||
pub fn path_to_root(path: &Path) -> String {
|
||||
debug!("[fn]: path_to_root");
|
||||
|
@ -66,8 +68,9 @@ pub fn path_to_root(path: &Path) -> String {
|
|||
|
||||
|
||||
|
||||
/// This function creates a file and returns it. But before creating the file it checks every
|
||||
/// directory in the path to see if it exists, and if it does not it will be created.
|
||||
/// This function creates a file and returns it. But before creating the file
|
||||
/// it checks every directory in the path to see if it exists,
|
||||
/// and if it does not it will be created.
|
||||
|
||||
pub fn create_file(path: &Path) -> io::Result<File> {
|
||||
debug!("[fn]: create_file");
|
||||
|
@ -76,7 +79,7 @@ pub fn create_file(path: &Path) -> io::Result<File> {
|
|||
if let Some(p) = path.parent() {
|
||||
debug!("Parent directory is: {:?}", p);
|
||||
|
||||
try!(fs::create_dir_all(p));
|
||||
fs::create_dir_all(p)?;
|
||||
}
|
||||
|
||||
debug!("[*]: Create file: {:?}", path);
|
||||
|
@ -86,13 +89,13 @@ pub fn create_file(path: &Path) -> io::Result<File> {
|
|||
/// Removes all the content of a directory but not the directory itself
|
||||
|
||||
pub fn remove_dir_content(dir: &Path) -> Result<(), Box<Error>> {
|
||||
for item in try!(fs::read_dir(dir)) {
|
||||
for item in fs::read_dir(dir)? {
|
||||
if let Ok(item) = item {
|
||||
let item = item.path();
|
||||
if item.is_dir() {
|
||||
try!(fs::remove_dir_all(item));
|
||||
fs::remove_dir_all(item)?;
|
||||
} else {
|
||||
try!(fs::remove_file(item));
|
||||
fs::remove_file(item)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,20 +104,21 @@ pub fn remove_dir_content(dir: &Path) -> Result<(), Box<Error>> {
|
|||
|
||||
///
|
||||
///
|
||||
/// Copies all files of a directory to another one except the files with the extensions given in the
|
||||
/// `ext_blacklist` array
|
||||
/// Copies all files of a directory to another one except the files
|
||||
/// with the extensions given in the `ext_blacklist` array
|
||||
|
||||
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");
|
||||
// Check that from and to are different
|
||||
if from == to {
|
||||
return Ok(());
|
||||
}
|
||||
debug!("[*] Loop");
|
||||
for entry in try!(fs::read_dir(from)) {
|
||||
let entry = try!(entry);
|
||||
for entry in fs::read_dir(from)? {
|
||||
let entry = entry?;
|
||||
debug!("[*] {:?}", entry.path());
|
||||
let metadata = try!(entry.metadata());
|
||||
let metadata = entry.metadata()?;
|
||||
|
||||
// If the entry is a dir and the recursive option is enabled, call itself
|
||||
if metadata.is_dir() && recursive {
|
||||
|
@ -125,13 +129,10 @@ pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blackl
|
|||
|
||||
// check if output dir already exists
|
||||
if !to.join(entry.file_name()).exists() {
|
||||
try!(fs::create_dir(&to.join(entry.file_name())));
|
||||
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));
|
||||
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
|
||||
|
@ -141,13 +142,22 @@ pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blackl
|
|||
}
|
||||
}
|
||||
debug!("[*] creating path for file: {:?}",
|
||||
&to.join(entry.path().file_name().expect("a file should have a file name...")));
|
||||
&to.join(entry
|
||||
.path()
|
||||
.file_name()
|
||||
.expect("a file should have a file name...")));
|
||||
|
||||
info!("[*] 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..."))));
|
||||
&to.join(entry
|
||||
.path()
|
||||
.file_name()
|
||||
.expect("a file should have a file name...")));
|
||||
fs::copy(entry.path(),
|
||||
&to.join(entry
|
||||
.path()
|
||||
.file_name()
|
||||
.expect("a file should have a file name...")))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in New Issue