diff --git a/Cargo.toml b/Cargo.toml index ffcf0a9a..73f72a8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,11 +28,12 @@ glob = "0.2" log = "0.3" env_logger = "0.3" toml = { version = "0.2", features = ["serde"] } +open = "1.1" phf = "0.7" includedir = "0.2" # Watch feature -notify = { version = "2.5.5", optional = true } +notify = { version = "3.0", optional = true } time = { version = "0.1.34", optional = true } crossbeam = { version = "0.2.8", optional = true } diff --git a/README.md b/README.md index 3a553f0f..0fd2b41b 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ -mdBook is a utility to create modern online books from markdown files. +mdBook is a utility to create modern online books from Markdown files. **This project is still evolving.** See [#90](https://github.com/azerupi/mdBook/issues/90) @@ -29,7 +29,7 @@ See [#90](https://github.com/azerupi/mdBook/issues/90) ## What does it look like? -The [**Documentation**](http://azerupi.github.io/mdBook/) for mdBook has been written in markdown and is using mdBook to generate the online book-like website you can read. The documentation uses the latest version on github and showcases the available features. +The [**Documentation**](http://azerupi.github.io/mdBook/) for mdBook has been written in Markdown and is using mdBook to generate the online book-like website you can read. The documentation uses the latest version on GitHub and showcases the available features. ## Installation @@ -47,12 +47,12 @@ There are multiple ways to install mdBook. This will download and compile mdBook for you, the only thing left to do is to add the Cargo bin directory to your `PATH`. 3. **From Git** - The version published to Crates.io will ever so slightly be behind the version hosted here on Github. If you need the latest version you can build the git version of mdBook yourself. Cargo makes this ***super easy***! + The version published to crates.io will ever so slightly be behind the version hosted here on GitHub. If you need the latest version you can build the git version of mdBook yourself. Cargo makes this ***super easy***! ``` cargo install --git https://github.com/azerupi/mdBook.git ``` - Again, make sure to add the Cargo bin directory to your `PATH` + Again, make sure to add the Cargo bin directory to your `PATH`. 4. **For Contributions** If you want to contribute to mdBook you will have to clone the repository on your local machine: @@ -74,7 +74,7 @@ There are multiple ways to install mdBook. mdBook will primaraly be used as a command line tool, even though it exposes all its functionality as a Rust crate for integration in other projects. -Here are the main commands you will want to run, for a more exhaustive explanation, check out the [documentation](http://azerupi.github.io/mdBook/). +Here are the main commands you will want to run. For a more exhaustive explanation, check out the [documentation](http://azerupi.github.io/mdBook/). - `mdbook init` @@ -106,7 +106,7 @@ Here are the main commands you will want to run, for a more exhaustive explanati ### As a library -Aside from the command line interface, this crate can also be used as a library. This means that you could integrate it in an existing project, like a web-app for example. Since the command line interface is just a wrapper around the library functionality, when you use this crate as a library you have full access to all the functionality of the command line interface with and easy to use API and more! +Aside from the command line interface, this crate can also be used as a library. This means that you could integrate it in an existing project, like a web-app for example. Since the command line interface is just a wrapper around the library functionality, when you use this crate as a library you have full access to all the functionality of the command line interface with an easy to use API and more! See the [Documentation](http://azerupi.github.io/mdBook/lib/lib.html) and the [API docs](http://azerupi.github.io/mdBook/mdbook/index.html) for more information. @@ -123,4 +123,4 @@ You can pick any issue you want to work on. Usually it's a good idea to ask if s ## License -All the code is released under the ***Mozilla Public License v2.0***, for more information take a look at the [LICENSE](LICENSE) file +All the code is released under the ***Mozilla Public License v2.0***, for more information take a look at the [LICENSE](LICENSE) file. diff --git a/book-example/src/cli/build.md b/book-example/src/cli/build.md index 0d17e60d..0c296541 100644 --- a/book-example/src/cli/build.md +++ b/book-example/src/cli/build.md @@ -21,6 +21,15 @@ current working directory. mdbook build path/to/book ``` +#### --open + +When you use the `--open` (`-o`) option, mdbook will open the rendered book in +your default web browser after building it. + +#### --dest-dir + +The `--dest-dir` (`-d`) option allows you to change the output directory for your book. + ------------------- ***note:*** *make sure to run the build command in the root directory and not in the source directory* diff --git a/book-example/src/cli/init.md b/book-example/src/cli/init.md index a782369b..43d1ae02 100644 --- a/book-example/src/cli/init.md +++ b/book-example/src/cli/init.md @@ -22,7 +22,7 @@ configuration files, etc. - The `book` directory is where your book is rendered. All the output is ready to be uploaded to a server to be seen by your audience. -- The `SUMMARY.md` file is the most important file, it's the skeleton of your book and is discussed in more detail in another [chapter](../format/summary.html). +- The `SUMMARY.md` file is the most important file, it's the skeleton of your book and is discussed in more detail in another [chapter](format/summary.html). #### Tip & Trick: Hidden Feature When a `SUMMARY.md` file already exists, the `init` command will first parse it and generate the missing files according to the paths used in the `SUMMARY.md`. This allows you to think and create the whole structure of your book and then let mdBook generate it for you. diff --git a/book-example/src/cli/serve.md b/book-example/src/cli/serve.md index 2357dc89..e4578c99 100644 --- a/book-example/src/cli/serve.md +++ b/book-example/src/cli/serve.md @@ -26,8 +26,15 @@ mdbook server path/to/book -p 8000 -i 127.0.0.1 -a 192.168.1.100 If you were to want live reloading for this you would need to proxy the websocket calls through nginx as well from `192.168.1.100:` to `127.0.0.1:`. The `-w` flag allows for the websocket port to be configured. +#### --open + +When you use the `--open` (`-o`) option, mdbook will open the book in your +your default web browser after starting the server. + +#### --dest-dir + +The `--dest-dir` (`-d`) option allows you to change the output directory for your book. + ----- ***note:*** *the `serve` command has not gotten a lot of testing yet, there could be some rough edges. If you discover a problem, please report it [on Github](https://github.com/azerupi/mdBook/issues)* - -***note***: diff --git a/book-example/src/cli/watch.md b/book-example/src/cli/watch.md index ad130c78..8b110476 100644 --- a/book-example/src/cli/watch.md +++ b/book-example/src/cli/watch.md @@ -12,6 +12,14 @@ current working directory. mdbook watch path/to/book ``` +#### --open + +When you use the `--open` (`-o`) option, mdbook will open the rendered book in +your default web browser. + +#### --dest-dir + +The `--dest-dir` (`-d`) option allows you to change the output directory for your book. ----- diff --git a/book-example/src/format/theme/index-hbs.md b/book-example/src/format/theme/index-hbs.md index e509565a..2ffae757 100644 --- a/book-example/src/format/theme/index-hbs.md +++ b/book-example/src/format/theme/index-hbs.md @@ -20,8 +20,9 @@ Here is a list of the properties that are exposed: - ***language*** Language of the book in the form `en`. To use in \ for example. At the moment it is hardcoded. - ***title*** Title of the book, as specified in `book.toml` - - ***path*** Relative path to the original markdown file from the source directory + +- ***chapter_title*** Title of the current chapter, as listed in `SUMMARY.md` - ***content*** This is the rendered markdown. - ***path_to_root*** This is a path containing exclusively `../`'s that points to the root of the book from the current file. Since the original directory structure is maintained, it is useful to prepend relative links with this `path_to_root`. diff --git a/data/_html-template/_layouts/page.hbs b/data/assets/_html-template/_layouts/page.hbs similarity index 100% rename from data/_html-template/_layouts/page.hbs rename to data/assets/_html-template/_layouts/page.hbs diff --git a/data/_html-template/_stylus/book.styl b/data/assets/_html-template/_stylus/book.styl similarity index 100% rename from data/_html-template/_stylus/book.styl rename to data/assets/_html-template/_stylus/book.styl diff --git a/data/_html-template/_stylus/general.styl b/data/assets/_html-template/_stylus/general.styl similarity index 100% rename from data/_html-template/_stylus/general.styl rename to data/assets/_html-template/_stylus/general.styl diff --git a/data/_html-template/_stylus/highlightjs-light.styl b/data/assets/_html-template/_stylus/highlightjs-light.styl similarity index 100% rename from data/_html-template/_stylus/highlightjs-light.styl rename to data/assets/_html-template/_stylus/highlightjs-light.styl diff --git a/data/_html-template/_stylus/highlightjs-tomorrow-night.styl b/data/assets/_html-template/_stylus/highlightjs-tomorrow-night.styl similarity index 100% rename from data/_html-template/_stylus/highlightjs-tomorrow-night.styl rename to data/assets/_html-template/_stylus/highlightjs-tomorrow-night.styl diff --git a/data/_html-template/_stylus/highlightjs.styl b/data/assets/_html-template/_stylus/highlightjs.styl similarity index 100% rename from data/_html-template/_stylus/highlightjs.styl rename to data/assets/_html-template/_stylus/highlightjs.styl diff --git a/data/_html-template/_stylus/menu.styl b/data/assets/_html-template/_stylus/menu.styl similarity index 100% rename from data/_html-template/_stylus/menu.styl rename to data/assets/_html-template/_stylus/menu.styl diff --git a/data/_html-template/_stylus/nav-icons.styl b/data/assets/_html-template/_stylus/nav-icons.styl similarity index 100% rename from data/_html-template/_stylus/nav-icons.styl rename to data/assets/_html-template/_stylus/nav-icons.styl diff --git a/data/_html-template/_stylus/page.styl b/data/assets/_html-template/_stylus/page.styl similarity index 100% rename from data/_html-template/_stylus/page.styl rename to data/assets/_html-template/_stylus/page.styl diff --git a/data/_html-template/_stylus/print.styl b/data/assets/_html-template/_stylus/print.styl similarity index 100% rename from data/_html-template/_stylus/print.styl rename to data/assets/_html-template/_stylus/print.styl diff --git a/data/_html-template/_stylus/sidebar.styl b/data/assets/_html-template/_stylus/sidebar.styl similarity index 100% rename from data/_html-template/_stylus/sidebar.styl rename to data/assets/_html-template/_stylus/sidebar.styl diff --git a/data/_html-template/_stylus/theme-popup.styl b/data/assets/_html-template/_stylus/theme-popup.styl similarity index 100% rename from data/_html-template/_stylus/theme-popup.styl rename to data/assets/_html-template/_stylus/theme-popup.styl diff --git a/data/_html-template/_stylus/themes/base.styl b/data/assets/_html-template/_stylus/themes/base.styl similarity index 100% rename from data/_html-template/_stylus/themes/base.styl rename to data/assets/_html-template/_stylus/themes/base.styl diff --git a/data/_html-template/_stylus/themes/coal.styl b/data/assets/_html-template/_stylus/themes/coal.styl similarity index 100% rename from data/_html-template/_stylus/themes/coal.styl rename to data/assets/_html-template/_stylus/themes/coal.styl diff --git a/data/_html-template/_stylus/themes/index.styl b/data/assets/_html-template/_stylus/themes/index.styl similarity index 100% rename from data/_html-template/_stylus/themes/index.styl rename to data/assets/_html-template/_stylus/themes/index.styl diff --git a/data/_html-template/_stylus/themes/light.styl b/data/assets/_html-template/_stylus/themes/light.styl similarity index 100% rename from data/_html-template/_stylus/themes/light.styl rename to data/assets/_html-template/_stylus/themes/light.styl diff --git a/data/_html-template/_stylus/themes/navy.styl b/data/assets/_html-template/_stylus/themes/navy.styl similarity index 100% rename from data/_html-template/_stylus/themes/navy.styl rename to data/assets/_html-template/_stylus/themes/navy.styl diff --git a/data/_html-template/_stylus/themes/rust.styl b/data/assets/_html-template/_stylus/themes/rust.styl similarity index 100% rename from data/_html-template/_stylus/themes/rust.styl rename to data/assets/_html-template/_stylus/themes/rust.styl diff --git a/data/_html-template/_stylus/variables.styl b/data/assets/_html-template/_stylus/variables.styl similarity index 100% rename from data/_html-template/_stylus/variables.styl rename to data/assets/_html-template/_stylus/variables.styl diff --git a/data/_html-template/css/book.css b/data/assets/_html-template/css/book.css similarity index 100% rename from data/_html-template/css/book.css rename to data/assets/_html-template/css/book.css diff --git a/data/_html-template/css/font-awesome.min.css b/data/assets/_html-template/css/font-awesome.min.css similarity index 100% rename from data/_html-template/css/font-awesome.min.css rename to data/assets/_html-template/css/font-awesome.min.css diff --git a/data/_html-template/fonts/FontAwesome.otf b/data/assets/_html-template/fonts/FontAwesome.otf similarity index 100% rename from data/_html-template/fonts/FontAwesome.otf rename to data/assets/_html-template/fonts/FontAwesome.otf diff --git a/data/_html-template/fonts/fontawesome-webfont.eot b/data/assets/_html-template/fonts/fontawesome-webfont.eot similarity index 100% rename from data/_html-template/fonts/fontawesome-webfont.eot rename to data/assets/_html-template/fonts/fontawesome-webfont.eot diff --git a/data/_html-template/fonts/fontawesome-webfont.svg b/data/assets/_html-template/fonts/fontawesome-webfont.svg similarity index 100% rename from data/_html-template/fonts/fontawesome-webfont.svg rename to data/assets/_html-template/fonts/fontawesome-webfont.svg diff --git a/data/_html-template/fonts/fontawesome-webfont.ttf b/data/assets/_html-template/fonts/fontawesome-webfont.ttf similarity index 100% rename from data/_html-template/fonts/fontawesome-webfont.ttf rename to data/assets/_html-template/fonts/fontawesome-webfont.ttf diff --git a/data/_html-template/fonts/fontawesome-webfont.woff b/data/assets/_html-template/fonts/fontawesome-webfont.woff similarity index 100% rename from data/_html-template/fonts/fontawesome-webfont.woff rename to data/assets/_html-template/fonts/fontawesome-webfont.woff diff --git a/data/_html-template/fonts/fontawesome-webfont.woff2 b/data/assets/_html-template/fonts/fontawesome-webfont.woff2 similarity index 100% rename from data/_html-template/fonts/fontawesome-webfont.woff2 rename to data/assets/_html-template/fonts/fontawesome-webfont.woff2 diff --git a/data/_html-template/images/favicon.png b/data/assets/_html-template/images/favicon.png similarity index 100% rename from data/_html-template/images/favicon.png rename to data/assets/_html-template/images/favicon.png diff --git a/data/_html-template/js/book.js b/data/assets/_html-template/js/book.js similarity index 100% rename from data/_html-template/js/book.js rename to data/assets/_html-template/js/book.js diff --git a/data/_html-template/js/highlight.js b/data/assets/_html-template/js/highlight.js similarity index 100% rename from data/_html-template/js/highlight.js rename to data/assets/_html-template/js/highlight.js diff --git a/data/_html-template/js/jquery-2.1.4.min.js b/data/assets/_html-template/js/jquery-2.1.4.min.js similarity index 100% rename from data/_html-template/js/jquery-2.1.4.min.js rename to data/assets/_html-template/js/jquery-2.1.4.min.js diff --git a/data/book-init/book.toml b/data/book-init/book.toml new file mode 100644 index 00000000..628d6842 --- /dev/null +++ b/data/book-init/book.toml @@ -0,0 +1,2 @@ +title = "An Empty Canvas" +author = "The Author" diff --git a/data/book-init/src/SUMMARY.md b/data/book-init/src/SUMMARY.md new file mode 100644 index 00000000..d3f3e163 --- /dev/null +++ b/data/book-init/src/SUMMARY.md @@ -0,0 +1,8 @@ +# Title + +[Introduction](introduction.md) + +- [First Chapter](first-chapter.md) +- [Second Chapter]() + +[Glossary](glossary.md) diff --git a/data/book-init/src/first-chapter.md b/data/book-init/src/first-chapter.md new file mode 100644 index 00000000..639f365b --- /dev/null +++ b/data/book-init/src/first-chapter.md @@ -0,0 +1,8 @@ +# First Chapter + +> first cicada – +> for a quick song sighted +> on the post +> +> *Issa, 1816* + diff --git a/data/book-init/src/glossary.md b/data/book-init/src/glossary.md new file mode 100644 index 00000000..ea8297e7 --- /dev/null +++ b/data/book-init/src/glossary.md @@ -0,0 +1,7 @@ +# Glossary + +**ci·ca·da** (sĭ-kā′də, -kä′-) *n. pl.* ci·ca·das or ci·ca·dae (-dē′) + +Any of various insects chiefly of the family Cicadidae, having a broad head, +membranous wings, and in the male a pair of resonating organs that produce a +characteristic high-pitched, droning sound. diff --git a/data/book-init/src/introduction.md b/data/book-init/src/introduction.md new file mode 100644 index 00000000..e10b99d0 --- /dev/null +++ b/data/book-init/src/introduction.md @@ -0,0 +1 @@ +# Introduction diff --git a/src/bin/mdbook.rs b/src/bin/mdbook.rs index 02498434..ebed6a71 100644 --- a/src/bin/mdbook.rs +++ b/src/bin/mdbook.rs @@ -5,6 +5,7 @@ extern crate clap; #[macro_use] extern crate log; extern crate env_logger; +extern crate open; // Dependencies for the Watch feature #[cfg(feature = "watch")] @@ -23,7 +24,9 @@ extern crate staticfile; extern crate ws; use std::env; +use std::fs; use std::error::Error; +use std::ffi::OsStr; use std::io::{self, Write, ErrorKind}; use std::path::{Path, PathBuf}; use std::process::Command; @@ -34,6 +37,8 @@ use clap::{App, ArgMatches, SubCommand, AppSettings}; #[cfg(feature = "watch")] use notify::Watcher; #[cfg(feature = "watch")] +use std::time::Duration; +#[cfg(feature = "watch")] use std::sync::mpsc::channel; use mdbook::MDBook; @@ -57,22 +62,28 @@ fn main() { .subcommand(SubCommand::with_name("init") .about("Create boilerplate structure and files in the directory") // the {n} denotes a newline which will properly aligned in all help messages - .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when ommitted)'") + .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'") .arg_from_usage("--copy-assets 'Copies the default assets (css, layout template, etc.) into your project folder'") .arg_from_usage("--force 'skip confirmation prompts'")) .subcommand(SubCommand::with_name("build") .about("Build the book from the markdown files") - .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when ommitted)'")) + .arg_from_usage("-o, --open 'Open the compiled book in a web browser'") + .arg_from_usage("-d, --dest-dir=[dest-dir] 'The output directory for your book{n}(Defaults to ./book when omitted)'") + .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'")) .subcommand(SubCommand::with_name("watch") .about("Watch the files for changes") - .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when ommitted)'")) + .arg_from_usage("-o, --open 'Open the compiled book in a web browser'") + .arg_from_usage("-d, --dest-dir=[dest-dir] 'The output directory for your book{n}(Defaults to ./book when omitted)'") + .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'")) .subcommand(SubCommand::with_name("serve") .about("Serve the book at http://localhost:3000. Rebuild and reload on change.") - .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when ommitted)'") + .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when omitted)'") + .arg_from_usage("-d, --dest-dir=[dest-dir] 'The output directory for your book{n}(Defaults to ./book when omitted)'") .arg_from_usage("-p, --port=[port] 'Use another port{n}(Defaults to 3000)'") .arg_from_usage("-w, --websocket-port=[ws-port] 'Use another port for the websocket connection (livereload){n}(Defaults to 3001)'") .arg_from_usage("-i, --interface=[interface] 'Interface to listen on{n}(Defaults to localhost)'") - .arg_from_usage("-a, --address=[address] 'Address that the browser can reach the websocket server from{n}(Defaults to the interface addres)'")) + .arg_from_usage("-a, --address=[address] 'Address that the browser can reach the websocket server from{n}(Defaults to the interface addres)'") + .arg_from_usage("-o, --open 'Open the compiled book in a web browser'")) .subcommand(SubCommand::with_name("test") .about("Test that code samples compile")) .get_matches(); @@ -106,10 +117,36 @@ fn confirm() -> bool { } } -// Init command implementation +/// Init command implementation +/// +/// It creates some boilerplate files and directories to get you started with your book. +/// +/// ```text +/// thebook/ +/// ├── book.toml +/// └── src +/// ├── SUMMARY.md +/// ├── first-chapter.md +/// ├── glossary.md +/// └── introduction.md +/// ``` +/// +/// It copies the embedded starter book as stored in data/book-init. fn init(args: &ArgMatches) -> Result<(), Box> { + debug!("[fn]: init"); let book_dir = get_book_dir(args); + + if !book_dir.exists() { + fs::create_dir_all(&book_dir).unwrap(); + info!("{:?} created", &book_dir); + } + + try!(utils::fs::copy_data("data/book-init/*", + "data/book-init/", + vec![], + &book_dir)); + let mut book_project = MDBook::new(&book_dir); book_project.read_config(); @@ -134,8 +171,8 @@ fn init(args: &ArgMatches) -> Result<(), Box> { } // Copy the assets - try!(utils::fs::copy_data("data/**/*", - "data/", + try!(utils::fs::copy_data("data/assets/**/*", + "data/assets/", vec![], &book_project.get_project_root().join("assets"))); @@ -154,6 +191,7 @@ fn init(args: &ArgMatches) -> Result<(), Box> { println!("\nAll done, no errors..."); + debug!("[*]: init done"); Ok(()) } @@ -161,9 +199,18 @@ fn init(args: &ArgMatches) -> Result<(), Box> { fn build(args: &ArgMatches) -> Result<(), Box> { let book_dir = get_book_dir(args); + let mut dest_base: Option = None; + if let Some(p) = args.value_of("dest-dir") { + dest_base = Some(PathBuf::from(p)); + } + // TODO select render format intent when we acutally have different renderers let renderer = HtmlHandlebars::new(); - try!(renderer.build(&book_dir)); + let book_project: MDBook = try!(renderer.build(&book_dir, &dest_base)); + + if args.is_present("open") { + open(book_project.get_dest_base().join("index.html")); + } Ok(()) } @@ -172,22 +219,28 @@ fn build(args: &ArgMatches) -> Result<(), Box> { #[cfg(feature = "watch")] fn watch(args: &ArgMatches) -> Result<(), Box> { let book_dir = get_book_dir(args); - let mut book = MDBook::new(&book_dir); - book.read_config(); - // |event, book| - trigger_on_change(&mut book, |event, _| { - if let Some(path) = event.path { - println!("File changed: {:?}\nBuilding book...\n", path); + let mut dest_base: Option = None; + if let Some(p) = args.value_of("dest-dir") { + dest_base = Some(PathBuf::from(p)); + } - // TODO select render format intent when we acutally have different renderers - let renderer = HtmlHandlebars::new(); - match renderer.build(&book_dir) { - Err(e) => println!("Error while building: {:?}", e), - _ => {}, - } - println!(""); + let renderer = HtmlHandlebars::new(); + let mut book_project: MDBook = try!(renderer.build(&book_dir, &dest_base)); + + if args.is_present("open") { + open(book_project.get_dest_base().join("index.html")); + } + + trigger_on_change(&mut book_project, |path, _| { + println!("File changed: {:?}\nBuilding book...\n", path); + // TODO select render format intent when we acutally have different renderers + let renderer = HtmlHandlebars::new(); + match renderer.build(&book_dir, &dest_base) { + Err(e) => println!("Error while building: {:?}", e), + _ => {}, } + println!(""); }); println!("watch"); @@ -200,8 +253,20 @@ fn serve(args: &ArgMatches) -> Result<(), Box> { const RELOAD_COMMAND: &'static str = "reload"; let book_dir = get_book_dir(args); + + let mut dest_base: Option = None; + if let Some(p) = args.value_of("dest-dir") { + dest_base = Some(PathBuf::from(p)); + } + let mut book = MDBook::new(&book_dir); + book.read_config(); + + if let Some(p) = dest_base { + book.set_dest_base(&p); + } + book.parse_books(); book.link_translations(); @@ -209,6 +274,7 @@ fn serve(args: &ArgMatches) -> Result<(), Box> { let ws_port = args.value_of("ws-port").unwrap_or("3001"); let interface = args.value_of("interface").unwrap_or("localhost"); let public_address = args.value_of("address").unwrap_or(interface); + let open_browser = args.is_present("open"); let address = format!("{}:{}", interface, port); let ws_address = format!("{}:{}", interface, ws_port); @@ -250,16 +316,18 @@ fn serve(args: &ArgMatches) -> Result<(), Box> { println!("\nServing on {}", address); - trigger_on_change(&mut book, move |event, book| { - if let Some(path) = event.path { - println!("File changed: {:?}\nBuilding book...\n", path); - let renderer = HtmlHandlebars::new(); - match renderer.render(&book) { - Err(e) => println!("Error while building: {:?}", e), - _ => broadcaster.send(RELOAD_COMMAND).unwrap(), - } - println!(""); + if open_browser { + open(format!("http://{}", address)); + } + + trigger_on_change(&mut book, move |path, book| { + println!("File changed: {:?}\nBuilding book...\n", path); + let renderer = HtmlHandlebars::new(); + match renderer.render(&book) { + Err(e) => println!("Error while building: {:?}", e), + _ => broadcaster.send(RELOAD_COMMAND).unwrap(), } + println!(""); }); Ok(()) @@ -319,61 +387,64 @@ fn get_book_dir(args: &ArgMatches) -> PathBuf { } } +fn open>(path: P) { + if let Err(e) = open::that(path) { + println!("Error opening web browser: {}", e); + } +} + // Calls the closure when a book source file is changed. This is blocking! #[cfg(feature = "watch")] fn trigger_on_change(book: &mut MDBook, closure: F) -> () - where F: Fn(notify::Event, &mut MDBook) -> () + where F: Fn(&Path, &mut MDBook) -> () { + use notify::RecursiveMode::*; + use notify::DebouncedEvent::*; + // Create a channel to receive the events. let (tx, rx) = channel(); - let w: Result = notify::Watcher::new(tx); - - match w { - Ok(mut watcher) => { - // Add the source directory to the watcher - if let Err(e) = watcher.watch(book.get_src_base()) { - println!("Error while watching {:?}:\n {:?}", book.get_src_base(), e); - ::std::process::exit(0); - }; - - // Add the book.toml or book.json file to the watcher if it exists, - // because it's not located in the source directory - - if let Err(_) = watcher.watch(book.get_project_root().join("book.toml")) { - // do nothing if book.toml is not found - } - - if let Err(_) = watcher.watch(book.get_project_root().join("book.json")) { - // do nothing if book.json is not found - } - - let mut previous_time = time::get_time(); - - println!("\nListening for changes...\n"); - - loop { - match rx.recv() { - Ok(event) => { - // Skip the event if an event has already been issued in the last second - let time = time::get_time(); - if time - previous_time < time::Duration::seconds(1) { - continue; - } else { - previous_time = time; - } - - closure(event, book); - }, - Err(e) => { - println!("An error occured: {:?}", e); - }, - } - } - }, + let mut watcher = match notify::watcher(tx, Duration::from_secs(1)) { + Ok(w) => w, Err(e) => { println!("Error while trying to watch the files:\n\n\t{:?}", e); - ::std::process::exit(0); - }, + ::std::process::exit(2); + } + }; + + // Add the source directory to the watcher + if let Err(e) = watcher.watch(book.get_src_base(), Recursive) { + println!("Error while watching {:?}:\n {:?}", book.get_src_base(), e); + ::std::process::exit(2); + }; + + // Add the book.{json,toml} file to the watcher if it exists, because it's not + // located in the source directory + if let Err(_) = watcher.watch(book.get_project_root().join("book.json"), NonRecursive) { + // do nothing if book.json is not found + } + if let Err(_) = watcher.watch(book.get_project_root().join("book.toml"), NonRecursive) { + // do nothing if book.toml is not found + } + + println!("\nListening for changes...\n"); + + loop { + match rx.recv() { + 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); + }, + } } } diff --git a/src/book/book.rs b/src/book/book.rs index 2405c0e1..5722e6a6 100644 --- a/src/book/book.rs +++ b/src/book/book.rs @@ -1,6 +1,4 @@ -use std::fs::File; -use std::path::{Path, PathBuf}; - +use std::path::PathBuf; use book::bookconfig::BookConfig; use book::toc::{TocItem, TocContent}; diff --git a/src/book/bookconfig.rs b/src/book/bookconfig.rs index 42f417ca..633631d7 100644 --- a/src/book/bookconfig.rs +++ b/src/book/bookconfig.rs @@ -1,13 +1,8 @@ extern crate toml; -use std::process::exit; -use std::fs::File; -use std::io::Read; use std::ffi::OsStr; use std::path::{Path, PathBuf}; use std::collections::BTreeMap; -use std::str::FromStr; -use serde_json; use utils; diff --git a/src/book/chapter.rs b/src/book/chapter.rs index 4f8f34c5..17b77592 100644 --- a/src/book/chapter.rs +++ b/src/book/chapter.rs @@ -4,10 +4,9 @@ extern crate toml; use regex::Regex; use std::ffi::OsStr; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::fs::File; -use std::error::Error; -use std::io::{self, Read}; +use std::io::Read; use std::collections::BTreeMap; use utils; @@ -130,7 +129,7 @@ impl Chapter { debug!("[*] Creating: {:?}", src_path); match create_with_str(src_path, &format!("# {}", self.title)) { Ok(_) => { return Ok(self); }, - Err(e) => { + Err(_) => { return Err(format!("Could not create: {:?}", src_path)); }, } @@ -140,8 +139,12 @@ impl Chapter { match File::open(src_path) { Err(e) => { return Err(format!("Read error: {:?}", e)); }, Ok(mut f) => { - // TODO try! here and return error - f.read_to_string(&mut text); + match f.read_to_string(&mut text) { + Ok(_) => {}, + Err(e) => { + return Err(format!("Error: {:#?}", e)); + }, + } self.content = Some(utils::strip_toml_header(&text)); } } diff --git a/src/book/mod.rs b/src/book/mod.rs index 9108fee0..c62f74c7 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -9,8 +9,6 @@ pub mod toc; pub mod chapter; pub use self::book::Book; -use renderer::{Renderer, HtmlHandlebars}; - use self::chapter::TranslationLink; use self::toc::{TocItem, TocContent}; use utils; @@ -19,9 +17,6 @@ use std::env; use std::process::exit; use std::ffi::OsStr; use std::path::{Path, PathBuf}; -use std::fs::{self, File}; -use std::io::Read; -use std::error::Error; use std::collections::{HashMap, BTreeMap}; #[derive(Debug, Clone)] @@ -142,34 +137,6 @@ impl MDBook { MDBook::default().set_project_root(project_root).clone() } - /// `init()` creates some boilerplate files and directories to get you started with your book. - /// - /// ```text - /// book-example/ - /// ├── book - /// └── src - /// ├── chapter_1.md - /// └── SUMMARY.md - /// ``` - /// - /// 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> { - - debug!("[fn]: init"); - - if !self.project_root.exists() { - fs::create_dir_all(&self.project_root).unwrap(); - info!("{:?} created", &self.project_root); - } - - // Read book.toml if exists and populate .translations - self.read_config(); - - debug!("[*]: init done"); - Ok(()) - } - /// Parses the `book.toml` file (if it exists) to extract the configuration parameters. /// The `book.toml` file should be in the root directory of the book project. /// The project root directory is the one specified when creating a new `MDBook` @@ -186,7 +153,6 @@ impl MDBook { /// In this example, `project_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. pub fn read_config(&mut self) -> &mut Self { - debug!("[fn]: read_config"); // exit(2) is a clear indication for the user that something is wrong @@ -194,7 +160,7 @@ impl MDBook { // Read book.toml or book.json if exists to a BTreeMap - if Path::new(self.project_root.join("book.toml").as_os_str()).exists() { + if self.project_root.join("book.toml").exists() { debug!("[*]: Reading config"); let text = match utils::fs::file_to_string(&self.project_root.join("book.toml")) { @@ -213,7 +179,7 @@ impl MDBook { } } - } else if Path::new(self.project_root.join("book.json").as_os_str()).exists() { + } else if self.project_root.join("book.json").exists() { debug!("[*]: Reading config"); let text = match utils::fs::file_to_string(&self.project_root.join("book.json")) { @@ -444,6 +410,8 @@ impl MDBook { /// prepare a Vec of default links to point to the index.html of each translation pub fn translation_index_links(&self) -> Option> { + debug!("[fn] translation_index_links()"); + let mut default_links: Vec = vec![]; let mut keys = self.translations.keys() @@ -578,6 +546,22 @@ impl MDBook { } else { self.src_base = path.to_owned(); } + + let a = self.translations.clone(); + let keys = a.keys(); + let is_multilang: bool = keys.clone().count() > 1; + + for key in keys { + if let Some(mut book) = self.translations.get_mut(key) { + if is_multilang { + book.config.src = self.src_base.join(key); + book.config.is_multilang = is_multilang; + } else { + book.config.src = self.src_base.to_owned(); + } + } + } + self } @@ -591,6 +575,22 @@ impl MDBook { } else { self.dest_base = path.to_owned(); } + + let a = self.translations.clone(); + let keys = a.keys(); + let is_multilang: bool = keys.clone().count() > 1; + + for key in keys { + if let Some(mut book) = self.translations.get_mut(key) { + if is_multilang { + book.config.dest = self.dest_base.join(key); + book.config.is_multilang = is_multilang; + } else { + book.config.dest = self.dest_base.to_owned(); + } + } + } + self } diff --git a/src/lib.rs b/src/lib.rs index 4edabaec..0c3e3a56 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,7 @@ //! fn main() { //! let path = PathBuf::from("my-book"); // Path to the book project's root //! let renderer = HtmlHandlebars::new(); -//! try!(renderer.build(&path)); // Build the book +//! try!(renderer.build(&path, &None)); // Build the book //! } //! ``` //! diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index 275ac856..f65ba064 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -4,15 +4,12 @@ use book::{MDBook, Book}; use book::chapter::{Chapter, TranslationLink}; use book::toc::{TocItem, TocContent}; use utils; -use FILES; use std::process::exit; -use std::path::{Path, PathBuf}; -use std::ffi::OsStr; -use std::fs::{self, File}; +use std::path::PathBuf; +use std::fs; use std::error::Error; -use std::io::{self, Read, Write}; -use std::collections::BTreeMap; +use std::io::{self, Write}; use handlebars::Handlebars; @@ -30,13 +27,17 @@ impl HtmlHandlebars { impl Renderer for HtmlHandlebars { /// Prepares the project and calls `render()`. - fn build(&self, project_root: &PathBuf) -> Result<(), Box> { + fn build(&self, project_root: &PathBuf, dest_base: &Option) -> Result> { debug!("[fn]: build"); let mut book_project = MDBook::new(&project_root); book_project.read_config(); + if let Some(p) = dest_base.clone() { + book_project.set_dest_base(&p); + } + if !book_project.get_src_base().exists() { println!("Source folder doesn't exist: {:?}", book_project.get_src_base()); exit(2); @@ -55,11 +56,12 @@ impl Renderer for HtmlHandlebars { } } - Ok(()) + Ok(book_project) } /// Renders the chapters and copies static assets. fn render(&self, book_project: &MDBook) -> Result<(), Box> { + debug!("[fn]: render"); debug!("[*]: Check if book's base output folder exists"); if let Err(_) = fs::create_dir_all(&book_project.get_dest_base()) { @@ -82,11 +84,15 @@ impl Renderer for HtmlHandlebars { let c = a.join("_*"); let exclude_glob = c.to_str().unwrap(); - // anyone wants to catch errors? - utils::fs::copy_files(include_glob, - base, - vec![exclude_glob], - &book_project.get_dest_base()); + // Ignoring all errors. Should try to see which types are worth returning. + + match utils::fs::copy_files(include_glob, + base, + vec![exclude_glob], + &book_project.get_dest_base()) { + Ok(_) => {}, + Err(_) => {}, + } } // Copy template's static assets @@ -118,20 +124,23 @@ impl Renderer for HtmlHandlebars { // ) // } - // anyone wants to catch errors? - utils::fs::copy_files(include_glob, - base, - vec![exclude_glob], - &book_project.get_dest_base()); + // Ignoring all errors. Should try to see which types are worth returning. + match utils::fs::copy_files(include_glob, + base, + vec![exclude_glob], + &book_project.get_dest_base()) { + Ok(_) => {}, + Err(_) => {}, + } } else { - try!(utils::fs::copy_data("data/_html-template/**/*", - "data/_html-template/", - vec!["data/_html-template/_*"], + try!(utils::fs::copy_data("data/assets/_html-template/**/*", + "data/assets/_html-template/", + vec!["data/assets/_html-template/_*"], &book_project.get_dest_base())); } - debug!("[fn]: render"); + debug!("[*]: start rendering"); let mut handlebars = Handlebars::new(); let translation_indexes = book_project.translation_index_links(); @@ -159,7 +168,7 @@ impl Renderer for HtmlHandlebars { let s = if let Some(p) = first_path_that_exists(&search_paths) { try!(utils::fs::file_to_string(&p)) } else { - try!(utils::fs::get_data_file("data/_html-template/_layouts/page.hbs")) + try!(utils::fs::get_data_file("data/assets/_html-template/_layouts/page.hbs")) }; // Register template @@ -194,7 +203,7 @@ impl Renderer for HtmlHandlebars { } // Render the chapters of each book - for (key, book) in &book_project.translations { + for (_, book) in &book_project.translations { // Check if book's dest directory exists diff --git a/src/renderer/html_handlebars/helpers/customcss.rs b/src/renderer/html_handlebars/helpers/customcss.rs index cfd937b9..6c1a1f4c 100644 --- a/src/renderer/html_handlebars/helpers/customcss.rs +++ b/src/renderer/html_handlebars/helpers/customcss.rs @@ -1,5 +1,4 @@ -use std::path::Path; -use std::collections::{VecDeque, BTreeMap}; +use std::collections::VecDeque; use serde_json; use handlebars::{Handlebars, HelperDef, RenderError, RenderContext, Helper, Context}; diff --git a/src/renderer/html_handlebars/helpers/translations.rs b/src/renderer/html_handlebars/helpers/translations.rs index 6d009d91..4e603808 100644 --- a/src/renderer/html_handlebars/helpers/translations.rs +++ b/src/renderer/html_handlebars/helpers/translations.rs @@ -1,4 +1,3 @@ -use std::path::Path; use std::collections::{VecDeque, BTreeMap}; use serde_json; diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 1054c887..09b4faa4 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -6,20 +6,27 @@ use book::MDBook; use std::error::Error; use std::path::PathBuf; +// TODO refactor dest_base out of the .build() call. It's only here b/c to +// influence the build output with the --dest-dir CLI arg. It is a good +// thing though that .build() encapsulates the steps to prepare the MDBook +// struct for .render(). Maybe give it the CLI args and process them within +// .build(). + pub trait Renderer { /// When the output format is determined (by a CLI argument for example), /// call `.build()` of the selected Renderer implementation. /// /// Constructs an `MDBook` struct given the path of the book project, - /// preparing the project and calling `render()`, doing what is necessary - /// for the particular output format. + /// optionally using a custom output folder (such as when given with + /// `--dest-dir` CLI argument). It prepares the project and calls + /// `render()`, doing what is necessary for the particular output format. /// /// This involves parsing config options from `book.toml` and parsing the /// `SUMMARY.md` of each translation to a nested `Vec`. /// /// Finally it calls `render()` to process the chapters and static assets. - fn build(&self, project_root: &PathBuf) -> Result<(), Box>; + fn build(&self, project_root: &PathBuf, dest_base: &Option) -> Result>; /// Responsible for rendering the chapters and copying static assets. fn render(&self, book_project: &MDBook) -> Result<(), Box>; diff --git a/src/tests/bookconfig_test.rs b/src/tests/bookconfig_test.rs index 35a2c180..e47aa767 100644 --- a/src/tests/bookconfig_test.rs +++ b/src/tests/bookconfig_test.rs @@ -1,7 +1,6 @@ -#[cfg(test)] +#![cfg(test)] use std::path::PathBuf; - use book::bookconfig::BookConfig; #[test] diff --git a/src/tests/chapter_test.rs b/src/tests/chapter_test.rs index 1a99a734..e1bd421e 100644 --- a/src/tests/chapter_test.rs +++ b/src/tests/chapter_test.rs @@ -1,7 +1,6 @@ -#[cfg(test)] +#![cfg(test)] use std::path::PathBuf; - use book::bookconfig::Author; use book::chapter::Chapter; @@ -11,7 +10,12 @@ fn it_parses_when_exists() { let path = PathBuf::from("at-the-mountains-of-madness.md"); let mut result = Chapter::new("Mountains".to_string(), path.clone()); - result.parse_or_create_using(&src_path); + match result.parse_or_create_using(&src_path) { + Ok(_) => {}, + Err(e) => { + println!("Error: {:#?}", e); + } + } let mut expected = Chapter::new("Mountains".to_string(), path.clone()); diff --git a/src/tests/fs_test.rs b/src/tests/fs_test.rs index a1874358..2b11eb1c 100644 --- a/src/tests/fs_test.rs +++ b/src/tests/fs_test.rs @@ -1,11 +1,10 @@ -#[cfg(test)] +#![cfg(test)] extern crate tempdir; use std; use std::fs::{self, File}; use std::io::Read; -use std::path::Path; use utils; use utils::fs::copy_files_except_ext; @@ -15,18 +14,21 @@ fn it_copies_data_file() { let dest_base = std::env::temp_dir().join("in tangles of old alleys"); let dest_path = dest_base.join("book.css"); - utils::fs::copy_data_file("data/_html-template/css/books.css", &dest_path); + match utils::fs::copy_data_file("data/assets/_html-template/css/books.css", &dest_path) { + Ok(_) => {}, + Err(e) => { println!("{:#?}", e); } + } let mut file = match File::open(&dest_path) { Ok(f) => f, - Err(e) => { + Err(_) => { println!("Failed to open {:?}", dest_path); return; }, }; let mut content = String::new(); - if let Err(e) = file.read_to_string(&mut content) { + if let Err(_) = file.read_to_string(&mut content) { println!("Failed to read {:?}", dest_path); return; } @@ -34,7 +36,10 @@ fn it_copies_data_file() { assert!(content.as_str().contains("Open Sans")); if dest_base.exists() { - fs::remove_dir_all(dest_base); + match fs::remove_dir_all(dest_base) { + Ok(_) => {}, + Err(e) => { println!("{:#?}", e); }, + } } } @@ -42,9 +47,9 @@ fn it_copies_data_file() { fn it_copies_data_by_pattern() { let dest_base = std::env::temp_dir().join("near the quays"); - if let Err(e) = utils::fs::copy_data("data/_html-template/**/*", - "data/_html-template/", - vec!["data/_html-template/_*"], + if let Err(e) = utils::fs::copy_data("data/assets/_html-template/**/*", + "data/assets/_html-template/", + vec!["data/assets/_html-template/_*"], &dest_base) { println!("Error: {:#?}", e); return; @@ -54,7 +59,10 @@ fn it_copies_data_by_pattern() { assert!(!dest_base.join("_layouts").exists()); if dest_base.exists() { - fs::remove_dir_all(dest_base); + match fs::remove_dir_all(dest_base) { + Ok(_) => {}, + Err(e) => { println!("{:#?}", e); }, + } } } @@ -72,14 +80,20 @@ fn it_doesnt_delete_toplevel_dotfiles() { Ok(_) => {}, } - utils::fs::clean_output_dir(&dest_base); + match utils::fs::clean_output_dir(&dest_base) { + Ok(_) => {}, + Err(e) => { println!("{:#?}", e); } + } assert!(dest_base.exists()); assert!(dest_base.join(".dotfile").exists()); assert!(!dest_base.join("door.html").exists()); if dest_base.exists() { - fs::remove_dir_all(dest_base); + match fs::remove_dir_all(dest_base) { + Ok(_) => {}, + Err(e) => { println!("{:#?}", e); }, + } } } diff --git a/src/tests/hbs_renderer_multilang.rs b/src/tests/hbs_renderer_multilang.rs index cdd7744e..c79abdcc 100644 --- a/src/tests/hbs_renderer_multilang.rs +++ b/src/tests/hbs_renderer_multilang.rs @@ -1,4 +1,4 @@ -#[cfg(test)] +#![cfg(test)] use std::path::{Path, PathBuf}; @@ -11,7 +11,7 @@ fn it_renders_multilanguage_book() { let path = PathBuf::from(".").join("src").join("tests").join("book-wonderland-multilang"); let renderer = HtmlHandlebars::new(); - if let Err(e) = renderer.build(&path) { + if let Err(e) = renderer.build(&path, &None) { panic!("{:#?}", e); } @@ -20,8 +20,8 @@ fn it_renders_multilanguage_book() { proj.parse_books(); let mut book_path: &Path = proj.translations.get("en").unwrap().config.get_dest(); - let mut chapter_path: PathBuf = PathBuf::from("".to_string()); - let mut s: String = String::new(); + let mut chapter_path: PathBuf; + let mut s: String; // Test if index.html in the project dest folder is the main book's first chapter diff --git a/src/tests/hbs_renderer_test.rs b/src/tests/hbs_renderer_test.rs index 1252e995..ea3105ce 100644 --- a/src/tests/hbs_renderer_test.rs +++ b/src/tests/hbs_renderer_test.rs @@ -1,4 +1,4 @@ -#[cfg(test)] +#![cfg(test)] use std::path::{Path, PathBuf}; @@ -11,7 +11,7 @@ fn it_renders_html_from_minimal_book() { let path = PathBuf::from(".").join("src").join("tests").join("book-minimal"); let renderer = HtmlHandlebars::new(); - if let Err(e) = renderer.build(&path) { + if let Err(e) = renderer.build(&path, &None) { println!("{:#?}", e); } @@ -20,8 +20,8 @@ fn it_renders_html_from_minimal_book() { proj.parse_books(); let book_path: &Path = proj.translations.get("en").unwrap().config.get_dest(); - let mut chapter_path: PathBuf = PathBuf::from("".to_string()); - let mut s: String = String::new(); + let mut chapter_path: PathBuf; + let mut s: String; // Test if "Library of Babel" was rendered @@ -54,7 +54,7 @@ fn it_copies_local_assets_when_found() { let path = PathBuf::from(".").join("src").join("tests").join("book-minimal-with-assets"); let renderer = HtmlHandlebars::new(); - if let Err(e) = renderer.build(&path) { + if let Err(e) = renderer.build(&path, &None) { println!("{:#?}", e); } @@ -64,13 +64,11 @@ fn it_copies_local_assets_when_found() { let book_path: &Path = proj.translations.get("en").unwrap().config.get_dest(); - let mut chapter_path: PathBuf = PathBuf::from("".to_string()); - let mut s: String = String::new(); // Test if "Library of Babel" was rendered - chapter_path = book_path.join("fictions").join("babel").with_extension("html"); - s = utils::fs::file_to_string(&chapter_path).unwrap(); + let chapter_path = book_path.join("fictions").join("babel").with_extension("html"); + let s = utils::fs::file_to_string(&chapter_path).unwrap(); assert!(s.contains("The Library of Babel")); assert_eq!(book_path.join("css").join("book.css").exists(), true); diff --git a/src/tests/mdbook_test.rs b/src/tests/mdbook_test.rs index 18e2be31..8388dc26 100644 --- a/src/tests/mdbook_test.rs +++ b/src/tests/mdbook_test.rs @@ -1,18 +1,16 @@ -#[cfg(test)] +#![cfg(test)] extern crate toml; use std::process::exit; use std::path::PathBuf; -use serde_json; - use utils; use book::MDBook; use book::book::Book; use book::bookconfig::{BookConfig, Author, Language}; use book::chapter::Chapter; -use book::toc::{TocItem, TocContent}; +use book::toc::TocItem; #[test] fn it_parses_simple_json_config() { diff --git a/src/tests/summary_test.rs b/src/tests/summary_test.rs index 8042fbf3..16d49eb2 100644 --- a/src/tests/summary_test.rs +++ b/src/tests/summary_test.rs @@ -1,6 +1,4 @@ -#[cfg(test)] - -use std::path::PathBuf; +#![cfg(test)] use parse::summary::parse_level; diff --git a/src/tests/toc_test.rs b/src/tests/toc_test.rs index d4372fab..4382dbd8 100644 --- a/src/tests/toc_test.rs +++ b/src/tests/toc_test.rs @@ -1,6 +1,5 @@ -#[cfg(test)] +#![cfg(test)] -use book::chapter::Chapter; use book::toc::{TocItem, TocContent, flat_toc, toc_node_count_id}; use parse::summary::parse_level; diff --git a/src/tests/utils_test.rs b/src/tests/utils_test.rs index 76ef7447..fdf9da53 100644 --- a/src/tests/utils_test.rs +++ b/src/tests/utils_test.rs @@ -1,9 +1,6 @@ -#[cfg(test)] - -use std::collections::BTreeMap; +#![cfg(test)] use serde_json; - use utils::*; #[test] diff --git a/src/utils/fs.rs b/src/utils/fs.rs index d07002a9..db8f8910 100644 --- a/src/utils/fs.rs +++ b/src/utils/fs.rs @@ -61,17 +61,17 @@ pub fn copy_data_file(src_path: &str, dest_path: &Path) -> Result<(), Box /// `include_base` will be removed from the source path. This way the path /// relative to the `dest_path` can be controlled. /// -/// The following will copy all files under "data/_html-template/", excluding -/// folders that start with "_", take the "data/_html-template/" part off the +/// The following will copy all files under "data/assets/_html-template/", excluding +/// folders that start with "_", take the "data/assets/_html-template/" part off the /// source path, and write the entries to "assets" folder. /// -/// I.e. "data/_html-template/css/book.css" will be written to +/// I.e. "data/assets/_html-template/css/book.css" will be written to /// "assets/css/book.css". /// /// ```ignore -/// utils::fs::copy_data("data/_html-template/**/*", -/// "data/_html-template/", -/// vec!["data/_html-template/_*"], +/// utils::fs::copy_data("data/assets/_html-template/**/*", +/// "data/assets/_html-template/", +/// vec!["data/assets/_html-template/_*"], /// &Path::new("assets")); /// ``` pub fn copy_data(include_glob: &str, @@ -221,7 +221,7 @@ 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. -pub fn create_file(path: &Path) -> Result> { +pub fn create_file(path: &Path) -> io::Result { debug!("[fn]: create_file"); // Construct path @@ -232,15 +232,7 @@ pub fn create_file(path: &Path) -> Result> { } debug!("[*]: Create file: {:?}", path); - let f = match File::create(path) { - Ok(f) => f, - Err(e) => { - debug!("File::create: {}", e); - return Err(Box::new(io::Error::new(io::ErrorKind::Other, format!("{}", e)))); - }, - }; - - Ok(f) + File::create(path) } /// A cleaning operation intended to be used on the output directory of a book diff --git a/src/utils/mod.rs b/src/utils/mod.rs index f3c17b55..7603d369 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -4,7 +4,6 @@ extern crate toml; use regex::Regex; use std::str::FromStr; -use std::error::Error; use std::collections::BTreeMap; use serde_json;