@@ -19,6 +19,7 @@ dependencies = [ | |||||
"syntect 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | "syntect 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", | "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"tera 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", | "tera 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"term-painter 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"toml 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | "toml 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", | "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"ws 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", | "ws 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -820,6 +821,23 @@ dependencies = [ | |||||
"url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
[[package]] | |||||
name = "term" | |||||
version = "0.4.5" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | |||||
name = "term-painter" | |||||
version = "0.2.3" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | [[package]] | ||||
name = "term_size" | name = "term_size" | ||||
version = "0.2.3" | version = "0.2.3" | ||||
@@ -1131,6 +1149,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
"checksum syntect 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6728e7e9bbd971751d17d39b0e38e3558c10b9fb32125441bb17c434a2754e7c" | "checksum syntect 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6728e7e9bbd971751d17d39b0e38e3558c10b9fb32125441bb17c434a2754e7c" | ||||
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" | "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" | ||||
"checksum tera 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6f2ff83a1773a0482ddc961d0030b514f1848f592ae9612afb241e5eb455df75" | "checksum tera 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6f2ff83a1773a0482ddc961d0030b514f1848f592ae9612afb241e5eb455df75" | ||||
"checksum term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d168af3930b369cfe245132550579d47dfd873d69470755a19c2c6568dbbd989" | |||||
"checksum term-painter 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ab900bf2f05175932b13d4fc12f8ff09ef777715b04998791ab2c930841e496b" | |||||
"checksum term_size 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "07b6c1ac5b3fffd75073276bca1ceed01f67a28537097a2a9539e116e50fb21a" | "checksum term_size 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "07b6c1ac5b3fffd75073276bca1ceed01f67a28537097a2a9539e116e50fb21a" | ||||
"checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a" | "checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a" | ||||
"checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7" | "checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7" | ||||
@@ -29,6 +29,7 @@ slug = "0.1" | |||||
syntect = "1" | syntect = "1" | ||||
chrono = "0.3" | chrono = "0.3" | ||||
toml = { version = "0.3", default-features = false, features = ["serde"]} | toml = { version = "0.3", default-features = false, features = ["serde"]} | ||||
term-painter = "0.2" | |||||
# Below is for the serve cmd | # Below is for the serve cmd | ||||
staticfile = "0.4" | staticfile = "0.4" | ||||
@@ -23,9 +23,9 @@ environment: | |||||
install: | install: | ||||
- ps: >- | - ps: >- | ||||
If ($Env:TARGET -eq 'x86_64-pc-windows-gnu') { | If ($Env:TARGET -eq 'x86_64-pc-windows-gnu') { | ||||
$Env:PATH += ';C:\msys64\mingw64\bin' | |||||
$Env:PATH += ';C:\msys64\mingw64\bin;C:\Program Files\Git\mingw64\bin' | |||||
} ElseIf ($Env:TARGET -eq 'i686-pc-windows-gnu') { | } ElseIf ($Env:TARGET -eq 'i686-pc-windows-gnu') { | ||||
$Env:PATH += ';C:\msys64\mingw32\bin' | |||||
$Env:PATH += ';C:\msys64\mingw32\bin;C:\Program Files\Git\mingw64\bin' | |||||
} | } | ||||
- curl -sSf -o rustup-init.exe https://win.rustup.rs/ | - curl -sSf -o rustup-init.exe https://win.rustup.rs/ | ||||
- rustup-init.exe -y --default-host %TARGET% --default-toolchain %RUST_VERSION% | - rustup-init.exe -y --default-host %TARGET% --default-toolchain %RUST_VERSION% | ||||
@@ -14,7 +14,7 @@ use gutenberg::{Site, populate_previous_and_next_pages}; | |||||
fn bench_loading_test_site(b: &mut test::Bencher) { | fn bench_loading_test_site(b: &mut test::Bencher) { | ||||
let mut path = env::current_dir().unwrap().to_path_buf(); | let mut path = env::current_dir().unwrap().to_path_buf(); | ||||
path.push("test_site"); | path.push("test_site"); | ||||
let mut site = Site::new(&path).unwrap(); | |||||
let mut site = Site::new(&path, "config.toml").unwrap(); | |||||
b.iter(|| site.load().unwrap()); | b.iter(|| site.load().unwrap()); | ||||
@@ -25,7 +25,7 @@ fn bench_loading_test_site(b: &mut test::Bencher) { | |||||
fn bench_building_test_site(b: &mut test::Bencher) { | fn bench_building_test_site(b: &mut test::Bencher) { | ||||
let mut path = env::current_dir().unwrap().to_path_buf(); | let mut path = env::current_dir().unwrap().to_path_buf(); | ||||
path.push("test_site"); | path.push("test_site"); | ||||
let mut site = Site::new(&path).unwrap(); | |||||
let mut site = Site::new(&path, "config.toml").unwrap(); | |||||
site.load().unwrap(); | site.load().unwrap(); | ||||
let tmp_dir = TempDir::new("example").expect("create temp dir"); | let tmp_dir = TempDir::new("example").expect("create temp dir"); | ||||
let public = &tmp_dir.path().join("public"); | let public = &tmp_dir.path().join("public"); | ||||
@@ -39,9 +39,9 @@ fn bench_building_test_site(b: &mut test::Bencher) { | |||||
fn bench_populate_previous_and_next_pages(b: &mut test::Bencher) { | fn bench_populate_previous_and_next_pages(b: &mut test::Bencher) { | ||||
let mut path = env::current_dir().unwrap().to_path_buf(); | let mut path = env::current_dir().unwrap().to_path_buf(); | ||||
path.push("test_site"); | path.push("test_site"); | ||||
let mut site = Site::new(&path).unwrap(); | |||||
let mut site = Site::new(&path, "config.toml").unwrap(); | |||||
site.load().unwrap(); | site.load().unwrap(); | ||||
let mut pages = site.pages.values().map(|p| p.clone()).collect::<Vec<_>>(); | |||||
let mut pages = site.pages.values().cloned().collect::<Vec<_>>(); | |||||
pages.sort_by(|a, b| a.partial_cmp(b).unwrap()); | pages.sort_by(|a, b| a.partial_cmp(b).unwrap()); | ||||
b.iter(|| populate_previous_and_next_pages(pages.as_slice(), false)); | b.iter(|| populate_previous_and_next_pages(pages.as_slice(), false)); | ||||
@@ -7,5 +7,6 @@ use gutenberg::Site; | |||||
pub fn build(config_file: &str) -> Result<()> { | pub fn build(config_file: &str) -> Result<()> { | ||||
let mut site = Site::new(env::current_dir().unwrap(), config_file)?; | let mut site = Site::new(env::current_dir().unwrap(), config_file)?; | ||||
site.load()?; | site.load()?; | ||||
println!("-> Creating {} pages and {} sections", site.pages.len(), site.sections.len()); | |||||
site.build() | site.build() | ||||
} | } |
@@ -14,7 +14,8 @@ use gutenberg::Site; | |||||
use gutenberg::errors::{Result}; | use gutenberg::errors::{Result}; | ||||
use ::report_elapsed_time; | |||||
use ::{report_elapsed_time, unravel_errors}; | |||||
use console; | |||||
#[derive(Debug, PartialEq)] | #[derive(Debug, PartialEq)] | ||||
@@ -46,22 +47,16 @@ fn rebuild_done_handling(broadcaster: &Sender, res: Result<()>, reload_path: &st | |||||
}}"#, reload_path) | }}"#, reload_path) | ||||
).unwrap(); | ).unwrap(); | ||||
}, | }, | ||||
Err(e) => { | |||||
println!("Failed to build the site"); | |||||
println!("Error: {}", e); | |||||
for e in e.iter().skip(1) { | |||||
println!("Reason: {}", e) | |||||
} | |||||
} | |||||
Err(e) => unravel_errors("Failed to build the site", &e, false) | |||||
} | } | ||||
} | } | ||||
// Most of it taken from mdbook | // Most of it taken from mdbook | ||||
pub fn serve(interface: &str, port: &str, config_file: &str) -> Result<()> { | pub fn serve(interface: &str, port: &str, config_file: &str) -> Result<()> { | ||||
println!("Building site..."); | |||||
let start = Instant::now(); | let start = Instant::now(); | ||||
let mut site = Site::new(env::current_dir().unwrap(), config_file)?; | let mut site = Site::new(env::current_dir().unwrap(), config_file)?; | ||||
let address = format!("{}:{}", interface, port); | let address = format!("{}:{}", interface, port); | ||||
// Override the base url so links work in localhost | // Override the base url so links work in localhost | ||||
site.config.base_url = if site.config.base_url.ends_with('/') { | site.config.base_url = if site.config.base_url.ends_with('/') { | ||||
@@ -72,6 +67,7 @@ pub fn serve(interface: &str, port: &str, config_file: &str) -> Result<()> { | |||||
site.load()?; | site.load()?; | ||||
site.enable_live_reload(); | site.enable_live_reload(); | ||||
println!("-> Creating {} pages and {} sections", site.pages.len(), site.sections.len()); | |||||
site.build()?; | site.build()?; | ||||
report_elapsed_time(start); | report_elapsed_time(start); | ||||
@@ -84,7 +80,6 @@ pub fn serve(interface: &str, port: &str, config_file: &str) -> Result<()> { | |||||
// Starts with a _ to not trigger the unused lint | // Starts with a _ to not trigger the unused lint | ||||
// we need to assign to a variable otherwise it will block | // we need to assign to a variable otherwise it will block | ||||
let _iron = Iron::new(mount).http(address.as_str()).unwrap(); | let _iron = Iron::new(mount).http(address.as_str()).unwrap(); | ||||
println!("Web server is available at http://{}", address); | |||||
// The websocket for livereload | // The websocket for livereload | ||||
let ws_server = WebSocket::new(|_| { | let ws_server = WebSocket::new(|_| { | ||||
@@ -104,8 +99,10 @@ pub fn serve(interface: &str, port: &str, config_file: &str) -> Result<()> { | |||||
watcher.watch("static/", RecursiveMode::Recursive).unwrap(); | watcher.watch("static/", RecursiveMode::Recursive).unwrap(); | ||||
watcher.watch("templates/", RecursiveMode::Recursive).unwrap(); | watcher.watch("templates/", RecursiveMode::Recursive).unwrap(); | ||||
let pwd = format!("{}", env::current_dir().unwrap().display()); | let pwd = format!("{}", env::current_dir().unwrap().display()); | ||||
println!("Listening for changes in {}/{{content, static, templates}}", pwd); | println!("Listening for changes in {}/{{content, static, templates}}", pwd); | ||||
println!("Press CTRL+C to stop\n"); | |||||
println!("Web server is available at http://{}", address); | |||||
println!("Press Ctrl+C to stop\n"); | |||||
use notify::DebouncedEvent::*; | use notify::DebouncedEvent::*; | ||||
@@ -127,17 +124,17 @@ pub fn serve(interface: &str, port: &str, config_file: &str) -> Result<()> { | |||||
let start = Instant::now(); | let start = Instant::now(); | ||||
match detect_change_kind(&pwd, &path) { | match detect_change_kind(&pwd, &path) { | ||||
(ChangeKind::Content, _) => { | (ChangeKind::Content, _) => { | ||||
println!("-> Content changed {}", path.display()); | |||||
console::info(&format!("-> Content changed {}", path.display())); | |||||
// Force refresh | // Force refresh | ||||
rebuild_done_handling(&broadcaster, site.rebuild_after_content_change(&path), "/x.js"); | rebuild_done_handling(&broadcaster, site.rebuild_after_content_change(&path), "/x.js"); | ||||
}, | }, | ||||
(ChangeKind::Templates, _) => { | (ChangeKind::Templates, _) => { | ||||
println!("-> Template changed {}", path.display()); | |||||
console::info(&format!("-> Template changed {}", path.display())); | |||||
// Force refresh | // Force refresh | ||||
rebuild_done_handling(&broadcaster, site.rebuild_after_template_change(), "/x.js"); | rebuild_done_handling(&broadcaster, site.rebuild_after_template_change(), "/x.js"); | ||||
}, | }, | ||||
(ChangeKind::StaticFiles, p) => { | (ChangeKind::StaticFiles, p) => { | ||||
println!("-> Static file changes detected {}", path.display()); | |||||
console::info(&format!("-> Static file changes detected {}", path.display())); | |||||
rebuild_done_handling(&broadcaster, site.copy_static_directory(), &p); | rebuild_done_handling(&broadcaster, site.copy_static_directory(), &p); | ||||
}, | }, | ||||
}; | }; | ||||
@@ -146,14 +143,14 @@ pub fn serve(interface: &str, port: &str, config_file: &str) -> Result<()> { | |||||
_ => {} | _ => {} | ||||
} | } | ||||
}, | }, | ||||
Err(e) => println!("Watch error: {:?}", e), | |||||
Err(e) => console::error(&format!("Watch error: {:?}", e)), | |||||
}; | }; | ||||
} | } | ||||
} | } | ||||
/// Returns whether the path we received corresponds to a temp file create | |||||
/// by an editor | |||||
/// Returns whether the path we received corresponds to a temp file created | |||||
/// by an editor or the OS | |||||
fn is_temp_file(path: &Path) -> bool { | fn is_temp_file(path: &Path) -> bool { | ||||
let ext = path.extension(); | let ext = path.extension(); | ||||
match ext { | match ext { | ||||
@@ -194,7 +191,7 @@ fn detect_change_kind(pwd: &str, path: &Path) -> (ChangeKind, String) { | |||||
} else if path_str.starts_with("/static") { | } else if path_str.starts_with("/static") { | ||||
ChangeKind::StaticFiles | ChangeKind::StaticFiles | ||||
} else { | } else { | ||||
panic!("Got a change in an unexpected path: {}", path_str); | |||||
unreachable!("Got a change in an unexpected path: {}", path_str); | |||||
}; | }; | ||||
(change_kind, path_str) | (change_kind, path_str) | ||||
@@ -221,7 +218,6 @@ mod tests { | |||||
]; | ]; | ||||
for t in testcases { | for t in testcases { | ||||
println!("{:?}", t.display()); | |||||
assert!(is_temp_file(&t)); | assert!(is_temp_file(&t)); | ||||
} | } | ||||
} | } | ||||
@@ -244,7 +240,6 @@ mod tests { | |||||
]; | ]; | ||||
for (expected, pwd, path) in testcases { | for (expected, pwd, path) in testcases { | ||||
println!("{:?}", path.display()); | |||||
assert_eq!(expected, detect_change_kind(&pwd, &path)); | assert_eq!(expected, detect_change_kind(&pwd, &path)); | ||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,15 @@ | |||||
use term_painter::ToStyle; | |||||
use term_painter::Color::*; | |||||
pub fn info(message: &str) { | |||||
println!("{}", NotSet.bold().paint(message)); | |||||
} | |||||
pub fn success(message: &str) { | |||||
println!("{}", Green.bold().paint(message)); | |||||
} | |||||
pub fn error(message: &str) { | |||||
println!("{}", Red.bold().paint(message)); | |||||
} |
@@ -4,6 +4,7 @@ extern crate clap; | |||||
extern crate error_chain; | extern crate error_chain; | ||||
extern crate gutenberg; | extern crate gutenberg; | ||||
extern crate chrono; | extern crate chrono; | ||||
extern crate term_painter; | |||||
extern crate staticfile; | extern crate staticfile; | ||||
extern crate iron; | extern crate iron; | ||||
@@ -13,23 +14,38 @@ extern crate ws; | |||||
use std::time::Instant; | use std::time::Instant; | ||||
use chrono::Duration; | use chrono::Duration; | ||||
use gutenberg::errors::Error; | |||||
mod cmd; | mod cmd; | ||||
mod console; | |||||
// Print the time elapsed rounded to 1 decimal | |||||
/// Print the time elapsed rounded to 1 decimal | |||||
fn report_elapsed_time(instant: Instant) { | fn report_elapsed_time(instant: Instant) { | ||||
let duration_ms = Duration::from_std(instant.elapsed()).unwrap().num_milliseconds() as f64; | let duration_ms = Duration::from_std(instant.elapsed()).unwrap().num_milliseconds() as f64; | ||||
if duration_ms < 1000.0 { | if duration_ms < 1000.0 { | ||||
println!("Done in {}ms.\n", duration_ms); | |||||
console::success(&format!("Done in {}ms.\n", duration_ms)); | |||||
} else { | } else { | ||||
let duration_sec = duration_ms / 1000.0; | let duration_sec = duration_ms / 1000.0; | ||||
println!("Done in {:.1}s.\n", ((duration_sec * 10.0).round() / 10.0)); | |||||
console::success(&format!("Done in {:.1}s.\n", ((duration_sec * 10.0).round() / 10.0))); | |||||
} | } | ||||
} | } | ||||
////Display an error message, the actual error and then exits if requested | |||||
fn unravel_errors(message: &str, error: &Error, exit: bool) { | |||||
console::error(message); | |||||
console::error(&format!("Error: {}", error)); | |||||
for e in error.iter().skip(1) { | |||||
console::error(&format!("Reason: {}", e)); | |||||
} | |||||
if exit { | |||||
::std::process::exit(1); | |||||
} | |||||
} | |||||
fn main() { | fn main() { | ||||
let matches = clap_app!(Gutenberg => | let matches = clap_app!(Gutenberg => | ||||
@@ -57,44 +73,25 @@ fn main() { | |||||
match matches.subcommand() { | match matches.subcommand() { | ||||
("init", Some(matches)) => { | ("init", Some(matches)) => { | ||||
match cmd::create_new_project(matches.value_of("name").unwrap()) { | match cmd::create_new_project(matches.value_of("name").unwrap()) { | ||||
Ok(()) => { | |||||
println!("Project created"); | |||||
}, | |||||
Err(e) => { | |||||
println!("Error: {}", e); | |||||
::std::process::exit(1); | |||||
}, | |||||
Ok(()) => console::success("Project created"), | |||||
Err(e) => unravel_errors("Failed to create the project", &e, true), | |||||
}; | }; | ||||
}, | }, | ||||
("build", Some(_)) => { | ("build", Some(_)) => { | ||||
println!("Building site"); | |||||
console::info("Building site..."); | |||||
let start = Instant::now(); | let start = Instant::now(); | ||||
match cmd::build(&config_file) { | |||||
Ok(()) => { | |||||
report_elapsed_time(start); | |||||
}, | |||||
Err(e) => { | |||||
println!("Failed to build the site"); | |||||
println!("Error: {}", e); | |||||
for e in e.iter().skip(1) { | |||||
println!("Reason: {}", e) | |||||
} | |||||
::std::process::exit(1); | |||||
}, | |||||
match cmd::build(config_file) { | |||||
Ok(()) => report_elapsed_time(start), | |||||
Err(e) => unravel_errors("Failed to build the site", &e, true), | |||||
}; | }; | ||||
}, | }, | ||||
("serve", Some(matches)) => { | ("serve", Some(matches)) => { | ||||
let interface = matches.value_of("interface").unwrap_or("127.0.0.1"); | let interface = matches.value_of("interface").unwrap_or("127.0.0.1"); | ||||
let port = matches.value_of("port").unwrap_or("1111"); | let port = matches.value_of("port").unwrap_or("1111"); | ||||
match cmd::serve(interface, port, &config_file) { | |||||
console::info("Building site..."); | |||||
match cmd::serve(interface, port, config_file) { | |||||
Ok(()) => (), | Ok(()) => (), | ||||
Err(e) => { | |||||
println!("Error: {}", e); | |||||
for e in e.iter().skip(1) { | |||||
println!("Reason: {}", e) | |||||
} | |||||
::std::process::exit(1); | |||||
}, | |||||
Err(e) => unravel_errors("Failed to build the site", &e, true), | |||||
}; | }; | ||||
}, | }, | ||||
_ => unreachable!(), | _ => unreachable!(), | ||||
@@ -206,7 +206,7 @@ impl Page { | |||||
context.add("page", self); | context.add("page", self); | ||||
tera.render(&tpl_name, &context) | tera.render(&tpl_name, &context) | ||||
.chain_err(|| format!("Failed to render page '{}'", self.file_name)) | |||||
.chain_err(|| format!("Failed to render page '{}'", self.file_path.display())) | |||||
} | } | ||||
} | } | ||||