You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

134 lines
4.5KB

  1. use std::env;
  2. use std::path::Path;
  3. use std::sync::mpsc::channel;
  4. use std::time::Duration;
  5. use std::thread;
  6. use iron::{Iron, Request, IronResult, Response, status};
  7. use iron::modifiers::Header;
  8. use mount::Mount;
  9. use staticfile::Static;
  10. use notify::{Watcher, RecursiveMode, watcher};
  11. use ws::{WebSocket};
  12. use site::Site;
  13. use errors::{Result};
  14. const LIVE_RELOAD: &'static [u8; 37809] = include_bytes!("livereload.js");
  15. fn livereload_handler(req: &mut Request) -> IronResult<Response> {
  16. Ok(Response::with((status::Ok, String::from_utf8(LIVE_RELOAD.to_vec()).unwrap())))
  17. }
  18. // Most of it taken from mdbook
  19. pub fn serve(interface: &str, port: &str) -> Result<()> {
  20. let mut site = Site::new(true)?;
  21. site.build()?;
  22. let address = format!("{}:{}", interface, port);
  23. let ws_address = format!("{}:{}", interface, "1112");
  24. // Start a webserver that serves the `public` directory
  25. let mut mount = Mount::new();
  26. mount.mount("/", Static::new(Path::new("public/")));
  27. mount.mount("/livereload.js", livereload_handler);
  28. let server = Iron::new(mount).http(address.clone()).unwrap();
  29. println!("Web server is available at http://{}", address);
  30. println!("Press CTRL+C to stop");
  31. // The websocket for livereload
  32. let ws_server = WebSocket::new(|_| {
  33. |_| {
  34. Ok(())
  35. }
  36. }).unwrap();
  37. let broadcaster = ws_server.broadcaster();
  38. thread::spawn(move || {
  39. ws_server.listen(&*ws_address).unwrap();
  40. });
  41. // And finally watching/reacting on file changes
  42. let (tx, rx) = channel();
  43. let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap();
  44. watcher.watch("content/", RecursiveMode::Recursive).unwrap();
  45. watcher.watch("static/", RecursiveMode::Recursive).unwrap();
  46. watcher.watch("templates/", RecursiveMode::Recursive).unwrap();
  47. let pwd = env::current_dir().unwrap();
  48. println!("Listening for changes in {}/{{content, static, templates}}", pwd.display());
  49. use notify::DebouncedEvent::*;
  50. loop {
  51. // See https://github.com/spf13/hugo/blob/master/commands/hugo.go
  52. // for a more complete version of that
  53. match rx.recv() {
  54. Ok(event) => match event {
  55. NoticeWrite(path) |
  56. NoticeRemove(path) |
  57. Create(path) |
  58. Write(path) |
  59. Remove(path) |
  60. Rename(_, path) => {
  61. if !is_temp_file(&path) {
  62. println!("Change detected in {}", path.display());
  63. match site.rebuild() {
  64. Ok(_) => {
  65. println!("Site rebuilt");
  66. broadcaster.send(r#"
  67. {
  68. "command": "reload",
  69. "path": "",
  70. "originalPath": "",
  71. "liveCSS": true,
  72. "liveImg": true,
  73. "protocol": ["http://livereload.com/protocols/official-7"]
  74. }"#).unwrap();
  75. },
  76. Err(e) => {
  77. println!("Failed to build the site");
  78. println!("Error: {}", e);
  79. for e in e.iter().skip(1) {
  80. println!("Reason: {}", e)
  81. }
  82. }
  83. }
  84. }
  85. }
  86. _ => {}
  87. },
  88. Err(e) => println!("Watch error: {:?}", e),
  89. };
  90. }
  91. }
  92. /// Returns whether the path we received corresponds to a temp file create
  93. /// by an editor
  94. fn is_temp_file(path: &Path) -> bool {
  95. let ext = path.extension();
  96. match ext {
  97. Some(ex) => match ex.to_str().unwrap() {
  98. "swp" | "swx" | "tmp" | ".DS_STORE" => true,
  99. // jetbrains IDE
  100. x if x.ends_with("jb_old___") => true,
  101. x if x.ends_with("jb_tmp___") => true,
  102. x if x.ends_with("jb_bak___") => true,
  103. // byword
  104. x if x.starts_with("sb-") => true,
  105. // gnome
  106. x if x.starts_with(".gooutputstream") => true,
  107. _ => {
  108. if let Some(filename) = path.file_stem() {
  109. // emacs
  110. filename.to_str().unwrap().starts_with("#")
  111. } else {
  112. false
  113. }
  114. }
  115. },
  116. None => false,
  117. }
  118. }