More precise time and show what changed

This commit is contained in:
Vincent Prouillet 2017-03-08 13:21:45 +09:00
parent e68af48eb6
commit 478e7054de
4 changed files with 148 additions and 33 deletions

10
.travis.yml Normal file
View file

@ -0,0 +1,10 @@
language: rust
cache: cargo
rust:
- nightly
- beta
- stable
notifications:
email: false

View file

@ -1,7 +1,7 @@
use std::env; use std::env;
use std::path::Path; use std::path::Path;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use std::time::Duration; use std::time::{Instant, Duration};
use std::thread; use std::thread;
use iron::{Iron, Request, IronResult, Response, status}; use iron::{Iron, Request, IronResult, Response, status};
@ -9,15 +9,25 @@ use mount::Mount;
use staticfile::Static; use staticfile::Static;
use notify::{Watcher, RecursiveMode, watcher}; use notify::{Watcher, RecursiveMode, watcher};
use ws::{WebSocket}; use ws::{WebSocket};
use gutenberg::Site; use gutenberg::Site;
use gutenberg::errors::{Result}; use gutenberg::errors::{Result};
const LIVE_RELOAD: &'static [u8; 37809] = include_bytes!("livereload.js");
use ::time_elapsed;
#[derive(Debug, PartialEq)]
enum ChangeKind {
Content,
Templates,
StaticFiles,
}
const LIVE_RELOAD: &'static str = include_str!("livereload.js");
fn livereload_handler(_: &mut Request) -> IronResult<Response> { fn livereload_handler(_: &mut Request) -> IronResult<Response> {
Ok(Response::with((status::Ok, String::from_utf8(LIVE_RELOAD.to_vec()).unwrap()))) Ok(Response::with((status::Ok, LIVE_RELOAD.to_string())))
} }
@ -37,7 +47,6 @@ pub fn serve(interface: &str, port: &str) -> Result<()> {
// 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.clone()).unwrap(); let _iron = Iron::new(mount).http(address.clone()).unwrap();
println!("Web server is available at http://{}", address); println!("Web server is available at http://{}", address);
println!("Press CTRL+C to stop");
// The websocket for livereload // The websocket for livereload
let ws_server = WebSocket::new(|_| { let ws_server = WebSocket::new(|_| {
@ -56,8 +65,9 @@ pub fn serve(interface: &str, port: &str) -> Result<()> {
watcher.watch("content/", RecursiveMode::Recursive).unwrap(); watcher.watch("content/", RecursiveMode::Recursive).unwrap();
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 = env::current_dir().unwrap(); let pwd = format!("{}", env::current_dir().unwrap().display());
println!("Listening for changes in {}/{{content, static, templates}}", pwd.display()); println!("Listening for changes in {}/{{content, static, templates}}", pwd);
println!("Press CTRL+C to stop");
use notify::DebouncedEvent::*; use notify::DebouncedEvent::*;
@ -65,27 +75,36 @@ pub fn serve(interface: &str, port: &str) -> Result<()> {
// See https://github.com/spf13/hugo/blob/master/commands/hugo.go // See https://github.com/spf13/hugo/blob/master/commands/hugo.go
// for a more complete version of that // for a more complete version of that
match rx.recv() { match rx.recv() {
Ok(event) => match event { Ok(event) => {
NoticeWrite(path) | match event {
NoticeRemove(path) | Create(path) |
Create(path) | Write(path) |
Write(path) | Remove(path) |
Remove(path) | Rename(_, path) => {
Rename(_, path) => { if is_temp_file(&path) {
if !is_temp_file(&path) { continue;
println!("Change detected in {}", path.display()); }
println!("Change detected, rebuilding site");
let what_changed = detect_change_kind(&pwd, &path);
match what_changed {
ChangeKind::Content => println!("Content changed {}", path.display()),
ChangeKind::Templates => println!("Template changed {}", path.display()),
ChangeKind::StaticFiles => println!("Static file changes detected {}", path.display()),
};
let start = Instant::now();
match site.rebuild() { match site.rebuild() {
Ok(_) => { Ok(_) => {
println!("Site rebuilt"); println!("Done in {:.1}s.", time_elapsed(start));
broadcaster.send(r#" broadcaster.send(r#"
{ {
"command": "reload", "command": "reload",
"path": "", "path": "",
"originalPath": "", "originalPath": "",
"liveCSS": true, "liveCSS": true,
"liveImg": true, "liveImg": true,
"protocol": ["http://livereload.com/protocols/official-7"] "protocol": ["http://livereload.com/protocols/official-7"]
}"#).unwrap(); }"#).unwrap();
}, },
Err(e) => { Err(e) => {
println!("Failed to build the site"); println!("Failed to build the site");
@ -95,9 +114,10 @@ pub fn serve(interface: &str, port: &str) -> Result<()> {
} }
} }
} }
} }
_ => {}
} }
_ => {}
}, },
Err(e) => println!("Watch error: {:?}", e), Err(e) => println!("Watch error: {:?}", e),
}; };
@ -116,10 +136,8 @@ fn is_temp_file(path: &Path) -> bool {
x if x.ends_with("jb_old___") => true, x if x.ends_with("jb_old___") => true,
x if x.ends_with("jb_tmp___") => true, x if x.ends_with("jb_tmp___") => true,
x if x.ends_with("jb_bak___") => true, x if x.ends_with("jb_bak___") => true,
// byword // vim
x if x.starts_with("sb-") => true, x if x.ends_with("~") => true,
// gnome
x if x.starts_with(".gooutputstream") => true,
_ => { _ => {
if let Some(filename) = path.file_stem() { if let Some(filename) = path.file_stem() {
// emacs // emacs
@ -129,6 +147,75 @@ fn is_temp_file(path: &Path) -> bool {
} }
} }
}, },
None => false, None => {
if path.ends_with(".DS_STORE") {
true
} else {
false
}
},
} }
} }
/// Detect what changed from the given path so we have an idea what needs
/// to be reloaded
fn detect_change_kind(pwd: &str, path: &Path) -> ChangeKind {
let path_str = format!("{}", path.display())
.replace(pwd, "")
.replace("\\", "/");
let change_kind = if path_str.starts_with("/templates") {
ChangeKind::Templates
} else if path_str.starts_with("/content") {
ChangeKind::Content
} else if path_str.starts_with("/static") {
ChangeKind::StaticFiles
} else {
panic!("Got a change in an unexpected path: {}", path_str);
};
change_kind
}
#[cfg(test)]
mod tests {
use std::path::Path;
use super::{is_temp_file, detect_change_kind, ChangeKind};
#[test]
fn test_can_recognize_temp_files() {
let testcases = vec![
Path::new("hello.swp"),
Path::new("hello.swx"),
Path::new(".DS_STORE"),
Path::new("hello.tmp"),
Path::new("hello.html.__jb_old___"),
Path::new("hello.html.__jb_tmp___"),
Path::new("hello.html.__jb_bak___"),
Path::new("hello.html~"),
Path::new("#hello.html"),
];
for t in testcases {
println!("{:?}", t.display());
assert!(is_temp_file(&t));
}
}
#[test]
fn test_can_detect_kind_of_changes() {
let testcases = vec![
(ChangeKind::Templates, "/home/vincent/site", Path::new("/home/vincent/site/templates/hello.html")),
(ChangeKind::StaticFiles, "/home/vincent/site", Path::new("/home/vincent/site/static/site.css")),
(ChangeKind::Content, "/home/vincent/site", Path::new("/home/vincent/site/content/posts/hello.md")),
];
for (expected, pwd, path) in testcases {
println!("{:?}", path.display());
assert_eq!(expected, detect_change_kind(&pwd, &path));
}
}
}

View file

@ -3,6 +3,7 @@ extern crate clap;
#[macro_use] #[macro_use]
extern crate error_chain; extern crate error_chain;
extern crate gutenberg; extern crate gutenberg;
extern crate chrono;
extern crate staticfile; extern crate staticfile;
extern crate iron; extern crate iron;
@ -12,10 +13,18 @@ extern crate ws;
use std::time::Instant; use std::time::Instant;
use chrono::Duration;
mod cmd; mod cmd;
// Print the time elapsed rounded to 1 decimal
fn time_elapsed(instant: Instant) -> f64 {
let duration_ms = Duration::from_std(instant.elapsed()).unwrap().num_milliseconds() as f64 / 1000.0;
(duration_ms * 10.0).round() / 10.0
}
fn main() { fn main() {
let matches = clap_app!(Gutenberg => let matches = clap_app!(Gutenberg =>
(version: crate_version!()) (version: crate_version!())
@ -52,8 +61,7 @@ fn main() {
let start = Instant::now(); let start = Instant::now();
match cmd::build() { match cmd::build() {
Ok(()) => { Ok(()) => {
let duration = start.elapsed(); println!("Site built in {:.1}s.", time_elapsed(start));
println!("Site built in {}s.", duration.as_secs());
}, },
Err(e) => { Err(e) => {
println!("Failed to build the site"); println!("Failed to build the site");

View file

@ -92,6 +92,16 @@ impl Site {
html html
} }
/// Reload the Tera templates
pub fn reload_templates(&mut self) -> Result<()> {
Ok(())
}
/// Copy the content of the `static` folder into the `public` folder
pub fn copy_static_files(&self) -> Result<()> {
Ok(())
}
/// Re-parse and re-generate the site /// Re-parse and re-generate the site
/// Very dumb for now, ideally it would only rebuild what changed /// Very dumb for now, ideally it would only rebuild what changed
pub fn rebuild(&mut self) -> Result<()> { pub fn rebuild(&mut self) -> Result<()> {