More precise time and show what changed
This commit is contained in:
parent
e68af48eb6
commit
478e7054de
10
.travis.yml
Normal file
10
.travis.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
language: rust
|
||||||
|
cache: cargo
|
||||||
|
|
||||||
|
rust:
|
||||||
|
- nightly
|
||||||
|
- beta
|
||||||
|
- stable
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email: false
|
149
src/cmd/serve.rs
149
src/cmd/serve.rs
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -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");
|
||||||
|
|
10
src/site.rs
10
src/site.rs
|
@ -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<()> {
|
||||||
|
|
Loading…
Reference in a new issue