Skip to content

Commit

Permalink
Commandline options!
Browse files Browse the repository at this point in the history
  • Loading branch information
danthedeckie committed Aug 14, 2019
1 parent 6f2b7f2 commit e29787f
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 27 deletions.
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "livedevel"
version = "0.0.0"
name = "never_f5"
version = "0.1.0"
publish = false
edition = "2018"

Expand All @@ -16,3 +16,5 @@ listenfd = "0.3" # auto reloading

crossbeam-channel = "0.3.9"
notify = "=5.0.0-pre.1" # files changing

structopt = "0.2.18" # command line arguments!
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,8 @@ Now, whenever any files in that directory are changed, it'll refresh the page.

## NEXT STEPS:

0) *Commandline arguments.*
Need to have port & ip options, directory, websocket address, and injection
file, to allow easy inserting extra callbacks, doing clever CSS stuff,
event debounce time, etc.
0) *More Commandline arguments.*
Need to have directory, websocket route address, doing clever CSS stuff, etc.

1) *Tidying up* - general fixing / cleaning / organising / documenting / refactoring.

Expand All @@ -54,4 +52,5 @@ process. It seems to work very well.
Since it's sometimes nice to have state in web apps or pages (who knew, right)
there's a couple of callbacks you can add to your page: `window._autoreload_save`
and `window._autoreload_load`, which you can use to save and reload your state
to local storage or whatever before and after reloads.
to local storage or whatever before and after reloads. If you want to do more
complex stuff, you can specify your own javascript file to append on to `!` files.
17 changes: 10 additions & 7 deletions src/filewatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ impl Actor for WatcherHandler {
}

impl WatcherHandler {
pub fn new(watchdir: &str, clientlist: Recipient<SomethingChanged>) -> WatcherHandler {
pub fn new(watchdir: &str, clientlist: Recipient<SomethingChanged>, debouncetime: u64) -> WatcherHandler {
let (tx, rx) = unbounded();
let mut watcher: RecommendedWatcher = Watcher::new(tx.clone(), Duration::from_millis(10)).unwrap();
let mut watcher: RecommendedWatcher = Watcher::new(tx.clone(), Duration::from_millis(debouncetime)).unwrap();

let a = Arbiter::new();

Expand Down Expand Up @@ -84,17 +84,20 @@ impl WatcherHandler {
let result = me.try_send(SomethingChanged {filename: String::from(path.to_str().unwrap()) });
match result {
Ok(()) => (),
Err(e) => println!("Error telling main thread: {:?}", e),
Err(e) => {
eprintln!("Error telling main thread: {:?}", e);
break
},
}
}
},
Ok(Err(err)) => println!("recieved an error? {:?}", err),
Ok(Err(err)) => eprintln!("recieved an error? {:?}", err),
Err(RecvError) => {
// Channel Disconnected. Goodbye!
break
},
Err(err) => {
println!("watch error... {:?}", err)
eprintln!("watch error... {:?}", err)
},
};
}
Expand All @@ -109,7 +112,7 @@ impl Handler<SomethingChanged> for WatcherHandler {
if self.watching.contains(&evt.filename) {
match self.clientlist.try_send(evt) {
Ok(()) => (),
Err(e) => println!("error sending to clientlist! {:?}", e),
Err(e) => eprintln!("error sending to clientlist! {:?}", e),
}
}
}
Expand All @@ -124,7 +127,7 @@ impl Handler<PleaseWatch> for WatcherHandler {
if let Some(fullname) = self.watchdir.join(msg.filename.trim_matches('/').trim_end_matches('!')).to_str() {
self.watching.insert(fullname.to_string());
} else {
println!("couldn't add path...");
eprintln!("couldn't add path...");
}
println!("Watching: {:?}", self.watching);
Ok(true)
Expand Down
78 changes: 66 additions & 12 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::fs::File;
use std::fs::{File, read_to_string};
use std::io::prelude::*;

use actix_web::{web, http, App, HttpRequest, HttpServer, HttpResponse, Error};
Expand All @@ -11,12 +11,39 @@ use futures::future::Future;

use actix::prelude::*;

#[macro_use]
extern crate structopt;

use std::path::PathBuf;
use structopt::StructOpt;

mod filewatcher;
use crate::filewatcher::*;

mod websockets;
use crate::websockets::*;

////////////////////////////////////////////////////////////////////////////////
/// Main Commandline Options:
////////////////////////////////////////////////////////////////////////////////

#[derive(Debug, StructOpt, Clone)]
#[structopt(name="serveme", about="Autoreloading web development server")]
pub struct Options {
/// Address + port
#[structopt(short="a", long="address", default_value="127.0.0.1:8088")]
address: String,
/// Quiet mode (Doesn't do much yet)
#[structopt(short="q", long="quiet")]
quiet: bool,
/// File System Event Debounce Time
#[structopt(short="d", long="debounce", default_value="10")]
debouncetime: u64,
/// JS Injection file
#[structopt(short="j", long="javascript-file")]
js: Option<PathBuf>,
}

////////////////////////////////////////////////////////////////////////////////
// Static files adding the websockets injection:
////////////////////////////////////////////////////////////////////////////////
Expand All @@ -43,7 +70,7 @@ const WS_INJECTION: &str = "
})();
</script>";

fn nonstatic_handler(req: HttpRequest) -> Result<HttpResponse, Error> {
fn nonstatic_handler(req: HttpRequest, data: web::Data<AppState>) -> Result<HttpResponse, Error> {
let uri = req.path();

let filename: &str = if uri.ends_with("!") {
Expand All @@ -54,43 +81,62 @@ fn nonstatic_handler(req: HttpRequest) -> Result<HttpResponse, Error> {
let mut contents = String::new();
file.read_to_string(&mut contents)?;
if uri.ends_with("!") {
contents.push_str(WS_INJECTION);
if let Some(js_filename) = &data.config.js {
let js_file = read_to_string(js_filename);
if let Ok(js_contents) = js_file {
contents.push_str("<script>");
contents.push_str(&js_contents);
contents.push_str("</script>");
}

} else {
contents.push_str(WS_INJECTION);
}
}
Ok(HttpResponse::Ok()
.content_type("text/html")
.body(contents))
.content_type("text/html")
.body(contents))
}



////////////////////////////////////////////////////////////////////////////////
// The actual server stuff:
////////////////////////////////////////////////////////////////////////////////
pub struct AppState {
pub watcher: Addr<WatcherHandler>,
pub clientlist: Addr<websockets::ClientList>,
pub config: Options,
}

pub fn ws_route(req: HttpRequest, stream: web::Payload, srv: web::Data<AppState>) -> Result<HttpResponse, Error> {
println!("client connected.");
if ! srv.config.quiet {
println!("client connected.");
}
ws::start( websockets::Client { clientlist: srv.clientlist.clone() }, &req, stream)
}

fn main() {

fn start_server(options: Options) {
let mut listenfd = ListenFd::from_env();

let _sys = System::new("example");
let my_clientlist = ClientList::start_default();
let c2 = my_clientlist.clone();

let my_watcher = WatcherHandler::new(".", my_clientlist.recipient()).start();
let my_watcher = WatcherHandler::new(".", my_clientlist.recipient(), options.debouncetime).start();

if let Some(js_file) = &options.js {
if let Some(js_filestring) = js_file.to_str() {
my_watcher.do_send(PleaseWatch{filename: js_filestring.to_string()});
}
}

let state = web::Data::new(AppState {
watcher: my_watcher,
clientlist: c2,
config: options.clone(),
});


let mut server = HttpServer::new(
move || {
let state2 = state.clone();
Expand Down Expand Up @@ -120,10 +166,18 @@ fn main() {
server = if let Some(l) = listenfd.take_tcp_listener(0).unwrap() {
server.listen(l).unwrap()
} else {
server.bind("127.0.0.1:8088").unwrap()
server.bind(&options.address).unwrap()
};

println!("Listening on 127.0.0.1:8088");
if ! &options.quiet {
println!("Listening on {}", &options.address);
}

server.run().unwrap();
}

fn main() {
let options = Options::from_args();
println!("{:?}", options);
start_server(options);
}
2 changes: 1 addition & 1 deletion src/websockets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl Handler<SomethingChanged> for ClientList {
for client in self.sessions.iter() {
match client.try_send(client_event.clone()) {
Ok(()) => (),
Err(e) => println!("Error sending to client. {:?}", e),
Err(e) => eprintln!("Error sending to client. {:?}", e),
}

}
Expand Down

0 comments on commit e29787f

Please sign in to comment.