<commit_before>//!
//! WebSocket Serial Server
//!
//! WebSocket Serial Server is a program that allows
//! that allows browsers to access serial ports
//! on localhost

// `error_chain!` can recurse deeply
#![recursion_limit = "1024"]

extern crate argparse;
extern crate base64;
extern crate env_logger;
#[macro_use]
extern crate error_chain;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate rand;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serialport;
extern crate websocket;

mod serial_support;

use std::io::Write;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender};
use std::thread;
use std::time::Duration;

use argparse::{ArgumentParser, Store};
use websocket::client::Writer;
use websocket::result::WebSocketError;
use websocket::message::Type;
use websocket::{Server, Message};
use websocket::server::upgrade::WsUpgrade;
use hyper::Server as HttpServer;
use hyper::net::Fresh;
use hyper::server::request::Request;
use hyper::server::response::Response;
use rand::{Rng, thread_rng};

use serial_support::manage::Manager;
use serial_support::messages::*;
use serial_support::errors as e;

// Max number of failures we allow when trying to send
// data to client before exiting
// TODO: Make configurable
const MAX_SEND_ERROR_COUNT: u32 = 5;

fn main() {

  // Init logger
  env_logger::init().unwrap();

  let sr = SerialRequest::List {};
  info!("serialized = {}", serde_json::to_string(&sr).unwrap());

  // Default port number
  let mut port = 8080;
  // Parse cmdline args
  {
    let mut ap = ArgumentParser::new();
    ap.set_description("Provide access to serial ports over JSON Websockets");
    ap.refer(&mut port)
      .add_option(&["-p", "--port"], Store, "Http Port");
    ap.parse_args_or_exit();
  }

  // websocket port
  let ws_port = port + 1;

  // html file for landing page
  let websocket_html = include_str!("websockets.html").replace(
    "__WS_PORT__ = 8081",
    &format!("__WS_PORT__ = {}", ws_port),
  );

  // The HTTP server handler
  let http_handler = move |_: Request, response: Response<Fresh>| {
    let mut response = response.start().expect(&"Could not start response");
    // Send a client webpage
    response
      .write_all(websocket_html.as_bytes())
      .expect(&"Could not get template as bytes");
    response.end().expect(&"Send response failed");
  };

  info!("Using ports {} {}", port, ws_port);

  // Set up channels and Manager
  let (sub_tx, sub_rx) = channel::<SubscriptionRequest>();
  let (sreq_tx, sreq_rx) = channel::<(String, SerialRequest)>();
  Manager::spawn(sreq_rx, sub_rx);

  // Start listening for http connections
  thread::spawn(
    move || {
      let http_server = HttpServer::http(format!("127.0.0.1:{}", port)).expect(
        &format!(
          "Failed to create http server on port {}",
          port
        ),
      );
      http_server
        .handle(http_handler)
        .expect(&"Failed to listen");
    },
  );

  // Start listening for WebSocket connections
  let ws_server = Server::bind(format!("127.0.0.1:{}", ws_port)).expect(
    &format!(
      "Failed bind on websocket port {}",
      ws_port
    ),
  );

  // Continuously iterate over connections,
  // spawning handlers
  for connection in ws_server.filter_map(Result::ok) {
    // Set up subscription id
    // let ts = SystemTime::now() - UNIX_EPOCH
    let prefix: String = thread_rng().gen_ascii_chars().take(8).collect();
    let sub_id = format!("thread-{}-{}", prefix, rand::random::<u16>());
    debug!("{}: spawned.", sub_id);

    // Spawn a new thread for each connection.
    let sub_tx_clone = sub_tx.clone();
    let sreq_tx_clone = sreq_tx.clone();
    spawn_ws_handler(sub_id, sub_tx_clone, sreq_tx_clone, connection);
  }
}

/// Spawn a websocket handler into its own thread
fn spawn_ws_handler(
  sub_id: String,
  sub_tx_clone: Sender<SubscriptionRequest>,
  sreq_tx_clone: Sender<(String, SerialRequest)>,
  connection: WsUpgrade<TcpStream>,
) {
  thread::spawn(move || ws_handler(sub_id, &sub_tx_clone, &sreq_tx_clone, connection));
}

/// Websocket handler
fn ws_handler(
  sub_id: String,
  sub_tx: &Sender<SubscriptionRequest>,
  sreq_tx: &Sender<(String, SerialRequest)>,
  connection: WsUpgrade<TcpStream>,
) {

  if !connection
        .protocols()
        .contains(&"websocket-serial-json".to_string()) {
    connection
      .reject()
      .expect(&"Connection rejection failed.");
    return;
  }

  connection
    .tcp_stream()
    .set_nonblocking(true)
    .expect(&"Setting stream non-blocking failed.");

  // Create response channel
  let (sub_resp_tx, sub_resp_rx) = channel::<SerialResponse>();

  // Register sub_id with manager
  sub_tx
    .send(
      SubscriptionRequest {
        sub_id: sub_id.clone(),
        subscriber: sub_resp_tx,
      },
    )
    .expect(&format!("{}: Registering with manager failed.", sub_id));

  let client = connection
    .use_protocol(format!("websocket-serial-json"))
    .accept()
    .expect(&format!("{}: Accept protocol failed.", sub_id));

  let ip = client
    .peer_addr()
    .expect(&format!("{}: Could not get peer address", sub_id));

  info!("{}: Connection from {}", sub_id, ip);

  //   let message = Message::text("Hello".to_string());
  //   client.send_message(&message).unwrap();

  let (mut receiver, mut sender) = client
    .split()
    .expect(&format!("{}: WS client error", sub_id));

  let mut send_error_count = 0;

  let sleep_dur = Duration::from_millis(33);

  'msg_loop: loop {
    // Try and read a WS message
    match receiver.recv_message::<Message, _, _>() {
      Ok(message) => {
        match message.opcode {

          Type::Close => {
            let message = Message::close();
            sender
              .send_message(&message)
              .unwrap_or(info!("{}: Client {} hung up!", sub_id, ip));
            // Send close request to cleanup resources
            sreq_tx
              .send((sub_id.clone(), SerialRequest::Close { port: None }))
              .unwrap_or_else(
                |e| {
                  warn!(
                    "Client exit cleanup failed for sub_id '{}', cause '{}'",
                    sub_id,
                    e
                  )
                },
              );
            info!("{}: Client {} disconnected", sub_id, ip);
            break 'msg_loop;
          }

          Type::Ping => {
            let message = Message::pong(message.payload);
            sender
              .send_message(&message)
              .unwrap_or(info!("{}:  Could not ping client {}!", sub_id, ip));
          }

          _ => {

            // Get the payload, in a lossy manner
            let msg = String::from_utf8_lossy(&message.payload);

            // So we will get a result <SerialRequest::*,SerialResponse::Error> back
            match serde_json::from_str(&msg) {
              Ok(req) => {
                match sreq_tx.send((sub_id.clone(), req)) {
                  Err(err) => {
                    let error = e::ErrorKind::SendRequest(err).into();
                    send_serial_response_error(&sub_id, &mut sender, error);

                  }
                  _ => {}
                };
              }
              Err(err) => {
                let error = e::ErrorKind::Json(err).into();
                send_serial_response_error(&sub_id, &mut sender, error);
              }
            };
          }
        };
      }
      Err(e) => {
        match e {
          WebSocketError::NoDataAvailable => { /*Logging?*/ }
          _ => { /*Logging?*/ }
        };
      }
    }

    // Send on any serial responses
    match sub_resp_rx.try_recv() {
      Ok(resp) => {
        match serde_json::to_string(&resp) {
          Ok(json) => {
            let reply = Message::text(json.clone());
            sender
              .send_message(&reply)
              .unwrap_or_else(
                |e| {
                  send_error_count += 1;
                  info!(
                    "{}: Could not send message '{}' to client '{}', cause '{}'",
                    sub_id,
                    json,
                    ip,
                    e
                  )
                },
              );
          }
          Err(_) => {}
        }
      }
      _ => { /*Logging*/ }
    };

    if send_error_count > MAX_SEND_ERROR_COUNT {
      warn!(
        "{}: Client send error count exceeded! Shutting down msg loop.",
        &sub_id
      );
      break 'msg_loop;
    }

    thread::sleep(sleep_dur);
  }

  info!("{}: Shutting down!", sub_id);

}

fn send_serial_response_error(sub_id: &String, sender: &mut Writer<TcpStream>, error: e::Error) {
  let error = e::to_serial_response_error(error);
  serde_json::to_string(&error)
    .map_err(|err| e::ErrorKind::Json(err))
    .map(|json| Message::text(json))
    .map(
      |msg| {
        sender
          .send_message::<Message, _>(&msg)
          .map_err::<e::Error, _>(|err| e::ErrorKind::SendWsMessage(err).into())
      },
    )
    .unwrap_or_else(
      |_| {
        warn!("{}: Problem sending bad json error response", sub_id);
        Ok(())
      },
    )
    .is_ok(); // This shouldn't be needed?

}
<commit_msg>Remove some debug code<commit_after>//!
//! WebSocket Serial Server
//!
//! WebSocket Serial Server is a program that allows
//! that allows browsers to access serial ports
//! on localhost

// `error_chain!` can recurse deeply
#![recursion_limit = "1024"]

extern crate argparse;
extern crate base64;
extern crate env_logger;
#[macro_use]
extern crate error_chain;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate rand;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serialport;
extern crate websocket;

mod serial_support;

use std::io::Write;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender};
use std::thread;
use std::time::Duration;

use argparse::{ArgumentParser, Store};
use websocket::client::Writer;
use websocket::result::WebSocketError;
use websocket::message::Type;
use websocket::{Server, Message};
use websocket::server::upgrade::WsUpgrade;
use hyper::Server as HttpServer;
use hyper::net::Fresh;
use hyper::server::request::Request;
use hyper::server::response::Response;
use rand::{Rng, thread_rng};

use serial_support::manage::Manager;
use serial_support::messages::*;
use serial_support::errors as e;

// Max number of failures we allow when trying to send
// data to client before exiting
// TODO: Make configurable
const MAX_SEND_ERROR_COUNT: u32 = 5;

fn main() {

  // Init logger
  env_logger::init().unwrap();

  // Default port number
  let mut port = 8080;
  // Parse cmdline args
  {
    let mut ap = ArgumentParser::new();
    ap.set_description("Provide access to serial ports over JSON Websockets");
    ap.refer(&mut port)
      .add_option(&["-p", "--port"], Store, "Http Port");
    ap.parse_args_or_exit();
  }

  // websocket port
  let ws_port = port + 1;

  // html file for landing page
  let websocket_html = include_str!("websockets.html").replace(
    "__WS_PORT__ = 8081",
    &format!("__WS_PORT__ = {}", ws_port),
  );

  // The HTTP server handler
  let http_handler = move |_: Request, response: Response<Fresh>| {
    let mut response = response.start().expect(&"Could not start response");
    // Send a client webpage
    response
      .write_all(websocket_html.as_bytes())
      .expect(&"Could not get template as bytes");
    response.end().expect(&"Send response failed");
  };

  info!("Using ports {} {}", port, ws_port);

  // Set up channels and Manager
  let (sub_tx, sub_rx) = channel::<SubscriptionRequest>();
  let (sreq_tx, sreq_rx) = channel::<(String, SerialRequest)>();
  Manager::spawn(sreq_rx, sub_rx);

  // Start listening for http connections
  thread::spawn(
    move || {
      let http_server = HttpServer::http(format!("127.0.0.1:{}", port)).expect(
        &format!(
          "Failed to create http server on port {}",
          port
        ),
      );
      http_server
        .handle(http_handler)
        .expect(&"Failed to listen");
    },
  );

  // Start listening for WebSocket connections
  let ws_server = Server::bind(format!("127.0.0.1:{}", ws_port)).expect(
    &format!(
      "Failed bind on websocket port {}",
      ws_port
    ),
  );

  // Continuously iterate over connections,
  // spawning handlers
  for connection in ws_server.filter_map(Result::ok) {
    // Set up subscription id
    // let ts = SystemTime::now() - UNIX_EPOCH
    let prefix: String = thread_rng().gen_ascii_chars().take(8).collect();
    let sub_id = format!("thread-{}-{}", prefix, rand::random::<u16>());
    debug!("{}: spawned.", sub_id);

    // Spawn a new thread for each connection.
    let sub_tx_clone = sub_tx.clone();
    let sreq_tx_clone = sreq_tx.clone();
    spawn_ws_handler(sub_id, sub_tx_clone, sreq_tx_clone, connection);
  }
}

/// Spawn a websocket handler into its own thread
fn spawn_ws_handler(
  sub_id: String,
  sub_tx_clone: Sender<SubscriptionRequest>,
  sreq_tx_clone: Sender<(String, SerialRequest)>,
  connection: WsUpgrade<TcpStream>,
) {
  thread::spawn(move || ws_handler(sub_id, &sub_tx_clone, &sreq_tx_clone, connection));
}

/// Websocket handler
fn ws_handler(
  sub_id: String,
  sub_tx: &Sender<SubscriptionRequest>,
  sreq_tx: &Sender<(String, SerialRequest)>,
  connection: WsUpgrade<TcpStream>,
) {

  if !connection
        .protocols()
        .contains(&"websocket-serial-json".to_string()) {
    connection
      .reject()
      .expect(&"Connection rejection failed.");
    return;
  }

  connection
    .tcp_stream()
    .set_nonblocking(true)
    .expect(&"Setting stream non-blocking failed.");

  // Create response channel
  let (sub_resp_tx, sub_resp_rx) = channel::<SerialResponse>();

  // Register sub_id with manager
  sub_tx
    .send(
      SubscriptionRequest {
        sub_id: sub_id.clone(),
        subscriber: sub_resp_tx,
      },
    )
    .expect(&format!("{}: Registering with manager failed.", sub_id));

  let client = connection
    .use_protocol(format!("websocket-serial-json"))
    .accept()
    .expect(&format!("{}: Accept protocol failed.", sub_id));

  let ip = client
    .peer_addr()
    .expect(&format!("{}: Could not get peer address", sub_id));

  info!("{}: Connection from {}", sub_id, ip);

  //   let message = Message::text("Hello".to_string());
  //   client.send_message(&message).unwrap();

  let (mut receiver, mut sender) = client
    .split()
    .expect(&format!("{}: WS client error", sub_id));

  let mut send_error_count = 0;

  let sleep_dur = Duration::from_millis(33);

  'msg_loop: loop {
    // Try and read a WS message
    match receiver.recv_message::<Message, _, _>() {
      Ok(message) => {
        match message.opcode {

          Type::Close => {
            let message = Message::close();
            sender
              .send_message(&message)
              .unwrap_or(info!("{}: Client {} hung up!", sub_id, ip));
            // Send close request to cleanup resources
            sreq_tx
              .send((sub_id.clone(), SerialRequest::Close { port: None }))
              .unwrap_or_else(
                |e| {
                  warn!(
                    "Client exit cleanup failed for sub_id '{}', cause '{}'",
                    sub_id,
                    e
                  )
                },
              );
            info!("{}: Client {} disconnected", sub_id, ip);
            break 'msg_loop;
          }

          Type::Ping => {
            let message = Message::pong(message.payload);
            sender
              .send_message(&message)
              .unwrap_or(info!("{}:  Could not ping client {}!", sub_id, ip));
          }

          _ => {

            // Get the payload, in a lossy manner
            let msg = String::from_utf8_lossy(&message.payload);

            // So we will get a result <SerialRequest::*,SerialResponse::Error> back
            match serde_json::from_str(&msg) {
              Ok(req) => {
                match sreq_tx.send((sub_id.clone(), req)) {
                  Err(err) => {
                    let error = e::ErrorKind::SendRequest(err).into();
                    send_serial_response_error(&sub_id, &mut sender, error);

                  }
                  _ => {}
                };
              }
              Err(err) => {
                let error = e::ErrorKind::Json(err).into();
                send_serial_response_error(&sub_id, &mut sender, error);
              }
            };
          }
        };
      }
      Err(e) => {
        match e {
          WebSocketError::NoDataAvailable => { /*Logging?*/ }
          _ => { /*Logging?*/ }
        };
      }
    }

    // Send on any serial responses
    match sub_resp_rx.try_recv() {
      Ok(resp) => {
        match serde_json::to_string(&resp) {
          Ok(json) => {
            let reply = Message::text(json.clone());
            sender
              .send_message(&reply)
              .unwrap_or_else(
                |e| {
                  send_error_count += 1;
                  info!(
                    "{}: Could not send message '{}' to client '{}', cause '{}'",
                    sub_id,
                    json,
                    ip,
                    e
                  )
                },
              );
          }
          Err(_) => {}
        }
      }
      _ => { /*Logging*/ }
    };

    if send_error_count > MAX_SEND_ERROR_COUNT {
      warn!(
        "{}: Client send error count exceeded! Shutting down msg loop.",
        &sub_id
      );
      break 'msg_loop;
    }

    thread::sleep(sleep_dur);
  }

  info!("{}: Shutting down!", sub_id);

}

fn send_serial_response_error(sub_id: &String, sender: &mut Writer<TcpStream>, error: e::Error) {
  let error = e::to_serial_response_error(error);
  serde_json::to_string(&error)
    .map_err(|err| e::ErrorKind::Json(err))
    .map(|json| Message::text(json))
    .map(
      |msg| {
        sender
          .send_message::<Message, _>(&msg)
          .map_err::<e::Error, _>(|err| e::ErrorKind::SendWsMessage(err).into())
      },
    )
    .unwrap_or_else(
      |_| {
        warn!("{}: Problem sending bad json error response", sub_id);
        Ok(())
      },
    )
    .is_ok(); // This shouldn't be needed?

}
<|endoftext|>


<commit_before>extern crate gtk;
extern crate hyper;
extern crate serde_json;
#[macro_use] extern crate serde_derive;

use gtk::prelude::*;
use gtk::{AboutDialog, Builder, CellRendererText, CellRendererProgress, ListStore,
    Statusbar, Type, TreeView, TreeViewColumn, MenuItem, Window};

mod client;
#[macro_use] mod macros;

#[allow(non_snake_case)]
#[derive(Debug, Deserialize)]
struct Group {
    NZBNicename: String,
    Status: String,
    FileSizeLo: u32,
    FileSizeHi: u32,
    DownloadedSizeLo: u32,
    DownloadedSizeHi: u32,
    RemainingSizeLo: u32,
    RemainingSizeHi: u32
}

fn main() {
    if gtk::init().is_err() {
        println!("Failed to initialize GTK.");
        return;
    }

    let builder = Builder::new_from_string(include_str!("interface.glade"));

    let window: Window = builder.get_object("main_window").unwrap();
    let about_item :MenuItem = builder.get_object("about_item").unwrap();
    let about_dialog :AboutDialog = builder.get_object("about_dialog").unwrap();
    let status_bar :Statusbar = builder.get_object("status_bar").unwrap();
    let files_tree: TreeView = builder.get_object("files_tree").unwrap();

    about_item.connect_activate(move |_| {
        about_dialog.run();
    });

    let context_id = status_bar.get_context_id("");
    status_bar.push(context_id, "nzbget-ui");

    let files_store = ListStore::new(&[Type::String, Type::String, Type::F32]);
    add_text_column!(files_tree, "Title", 0);
    add_text_column!(files_tree, "Status", 1);
    add_progress_column!(files_tree, "Progress", 2);

    files_tree.set_model(Some(&files_store));

    window.show_all();

    let groups = load_groups();

    for group in groups {
        let file_size = format!("{}{}", group.FileSizeHi, group.FileSizeLo).parse::<f32>().unwrap();
        let downloaded_size = format!("{}{}", group.DownloadedSizeHi, group.DownloadedSizeLo).parse::<f32>().unwrap();

        let progress = downloaded_size / file_size * 100.0;

        files_store.insert_with_values(None, &[0, 1, 2], &[&group.NZBNicename, &group.Status, &progress]);
    }

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });

    gtk::main();
}

fn load_groups() -> Vec<Group> {
    let client = client::Client::new("http://localhost:6789");
    let response = client.call_method("listgroups");

    serde_json::from_value(response.result).unwrap()
}
<commit_msg>Fix "about app" dialog<commit_after>extern crate gtk;
extern crate hyper;
extern crate serde_json;
#[macro_use] extern crate serde_derive;

use gtk::prelude::*;
use gtk::{AboutDialog, Builder, CellRendererText, CellRendererProgress, ListStore,
    Statusbar, Type, TreeView, TreeViewColumn, MenuItem, Window};

mod client;
#[macro_use] mod macros;

#[allow(non_snake_case)]
#[derive(Debug, Deserialize)]
struct Group {
    NZBNicename: String,
    Status: String,
    FileSizeLo: u32,
    FileSizeHi: u32,
    DownloadedSizeLo: u32,
    DownloadedSizeHi: u32,
    RemainingSizeLo: u32,
    RemainingSizeHi: u32
}

fn main() {
    if gtk::init().is_err() {
        println!("Failed to initialize GTK.");
        return;
    }

    let builder = Builder::new_from_string(include_str!("interface.glade"));

    let window: Window = builder.get_object("main_window").unwrap();
    let about_item :MenuItem = builder.get_object("about_item").unwrap();
    let about_dialog :AboutDialog = builder.get_object("about_dialog").unwrap();
    let status_bar :Statusbar = builder.get_object("status_bar").unwrap();
    let files_tree: TreeView = builder.get_object("files_tree").unwrap();

    about_item.connect_activate(move |_| {
        about_dialog.run();
        about_dialog.hide();
    });

    let context_id = status_bar.get_context_id("");
    status_bar.push(context_id, "nzbget-ui");

    let files_store = ListStore::new(&[Type::String, Type::String, Type::F32]);
    add_text_column!(files_tree, "Title", 0);
    add_text_column!(files_tree, "Status", 1);
    add_progress_column!(files_tree, "Progress", 2);

    files_tree.set_model(Some(&files_store));

    window.show_all();

    let groups = load_groups();

    for group in groups {
        let file_size = format!("{}{}", group.FileSizeHi, group.FileSizeLo).parse::<f32>().unwrap();
        let downloaded_size = format!("{}{}", group.DownloadedSizeHi, group.DownloadedSizeLo).parse::<f32>().unwrap();

        let progress = downloaded_size / file_size * 100.0;

        files_store.insert_with_values(None, &[0, 1, 2], &[&group.NZBNicename, &group.Status, &progress]);
    }

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });

    gtk::main();
}

fn load_groups() -> Vec<Group> {
    let client = client::Client::new("http://localhost:6789");
    let response = client.call_method("listgroups");

    serde_json::from_value(response.result).unwrap()
}
<|endoftext|>


<commit_before>extern crate serde_json;
#[macro_use]
extern crate quick_error;
#[macro_use]
extern crate clap;
extern crate colored;

extern crate rustfix;

use std::fs::File;
use std::io::{Read, Write};
use std::error::Error;
use std::process::Command;

use colored::Colorize;
use clap::{Arg, App};

use rustfix::Suggestion;
use rustfix::diagnostics::Diagnostic;

const USER_OPTIONS: &'static str = "What do you want to do? \
    [r]eplace | [s]kip | save and [q]uit | [a]bort (without saving)";

fn main() {
    let program = try_main();
    match program {
        Ok(_) => std::process::exit(0),
        Err(ProgramError::UserAbort) => {
            writeln!(std::io::stdout(), "{}", ProgramError::UserAbort).unwrap();
            std::process::exit(0);
        }
        Err(error) => {
            writeln!(std::io::stderr(), "An error occured: {}", error).unwrap();
            writeln!(std::io::stderr(), "{:?}", error).unwrap();
            if let Some(cause) = error.cause() {
                writeln!(std::io::stderr(), "Cause: {:?}", cause).unwrap();
            }
            std::process::exit(1);
        }
    }
}

macro_rules! flush {
    () => (try!(std::io::stdout().flush());)
}

fn try_main() -> Result<(), ProgramError> {
    let matches = App::new("rustfix")
        .about("Automatically apply suggestions made by rustc")
        .version(crate_version!())
        .arg(Arg::with_name("clippy")
            .long("clippy")
            .help("Use `cargo clippy` for suggestions"))
        .arg(Arg::with_name("from_file")
            .long("from-file")
            .value_name("FILE")
            .takes_value(true)
            .help("Read suggestions from file (each line is a JSON object)"))
        .get_matches();

    // Get JSON output from rustc...
    let json = if let Some(file_name) = matches.value_of("from_file") {
        // either by reading a file the user saved (probably only for debugging rustfix itself)...
        try!(json_from_file(&file_name))
    } else {
        // or by spawning a subcommand that runs rustc.
        let subcommand = if matches.is_present("clippy") {
            "clippy"
        } else {
            "rustc"
        };
        try!(json_from_subcommand(subcommand))
    };

    let suggestions: Vec<Suggestion> = json.lines()
        .filter(not_empty)
        // Convert JSON string (and eat parsing errors)
        .flat_map(|line| serde_json::from_str::<Diagnostic>(line))
        // One diagnostic line might have multiple suggestions
        .flat_map(|diagnostic| rustfix::collect_suggestions(&diagnostic, None))
        .collect();

    try!(handle_suggestions(&suggestions));

    Ok(())
}

fn json_from_file(file_name: &str) -> Result<String, ProgramError> {
    read_file_to_string(file_name).map_err(From::from)
}

fn json_from_subcommand(subcommand: &str) -> Result<String, ProgramError> {
    let output = try!(Command::new("cargo")
        .args(&[subcommand,
                "--quiet",
                "--color", "never",
                "--",
                "-Z", "unstable-options",
                "--error-format", "json"])
        .output());

    let content = try!(String::from_utf8(output.stderr));

    Ok(content)
}

fn handle_suggestions(suggestions: &[Suggestion]) -> Result<(), ProgramError> {
    let mut accepted_suggestions: Vec<&Suggestion> = vec![];

    if suggestions.is_empty() {
        println!("I don't have any suggestions for you right now. Check back later!");
        return Ok(());
    }

    'suggestions: for suggestion in suggestions {
        println!("\n\n{info}: {message}\n\
            {arrow} {file}:{range}\n\
            {suggestion}\n\n\
            {lead}{text}{tail}\n\n\
            {with}\n\n\
            {replacement}\n",
            info = "Info".green().bold(),
            message = split_at_lint_name(&suggestion.message),
            arrow = "  -->".blue().bold(),
            suggestion = "Suggestion - Replace:".yellow().bold(),
            file = suggestion.file_name,
            range = suggestion.line_range,
            lead = indent(4, &suggestion.text.0),
            text = suggestion.text.1.red(),
            tail = suggestion.text.2,
            with = "with:".yellow().bold(),
            replacement = indent(4, &suggestion.replacement));

        'userinput: loop {
            print!("{arrow} {user_options}\n\
                {prompt} ",
                arrow = "==>".green().bold(),
                prompt = "  >".green().bold(),
                user_options = USER_OPTIONS.green());

            flush!();
            let mut input = String::new();
            try!(std::io::stdin().read_line(&mut input));

            match input.trim() {
                "s" => {
                    println!("Skipped.");
                    continue 'suggestions;
                }
                "r" => {
                    accepted_suggestions.push(suggestion);
                    println!("Suggestion accepted. I'll remember that and apply it later.");
                    continue 'suggestions;
                }
                "q" => {
                    println!("Thanks for playing!");
                    break 'suggestions;
                }
                "a" => {
                    return Err(ProgramError::UserAbort);
                }
                _ => {
                    println!("{error}: I didn't quite get that. {user_options}",
                                error = "Error".red().bold(),
                                user_options = USER_OPTIONS);
                    continue 'userinput;
                }
            }
        }
    }

    if !accepted_suggestions.is_empty() {
        println!("Good work. Let me just apply these {} changes!",
                 accepted_suggestions.len());

        for suggestion in accepted_suggestions.iter().rev() {
            try!(apply_suggestion(suggestion));

            print!(".");
            flush!();
        }

        println!("\nDone.");
    }

    Ok(())
}

quick_error! {
    /// All possible errors in programm lifecycle
    #[derive(Debug)]
    pub enum ProgramError {
        UserAbort {
            display("Let's get outta here!")
        }
        /// Missing File
        NoFile {
            display("No input file given")
        }
        SubcommandError(subcommand: String, output: String) {
            display("Error executing subcommand `{}`", subcommand)
            description(output)
        }
        /// Error while dealing with file or stdin/stdout
        Io(err: std::io::Error) {
            from()
            cause(err)
            display("I/O error")
            description(err.description())
        }
        Utf8Error(err: std::string::FromUtf8Error) {
            from()
            display("Error reading input as UTF-8")
        }
        /// Error with deserialization
        Serde(err: serde_json::Error) {
            from()
            cause(err)
            display("Serde JSON error")
            description(err.description())
        }
    }
}

// Helpers
// -------

fn read_file_to_string(file_name: &str) -> Result<String, std::io::Error> {
    let mut file = try!(File::open(file_name));
    let mut buffer = String::new();
    try!(file.read_to_string(&mut buffer));
    Ok(buffer)
}

fn not_empty(s: &&str) -> bool {
    !s.trim().is_empty()
}

fn split_at_lint_name(s: &str) -> String {
    s.split(", #[")
        .collect::<Vec<_>>()
        .join("\n      #[") // Length of whitespace == length of "Info: "
}

fn indent(size: u32, s: &str) -> String {
    let whitespace: String = std::iter::repeat(' ').take(size as usize).collect();

    s.lines()
        .map(|l| format!("{}{}", whitespace, l))
        .collect::<Vec<_>>()
        .join("\n")
}

/// Apply suggestion to a file
///
/// Please beware of ugly hacks below! Originally, I wanted to replace byte ranges, but sadly the
/// ranges rustc's JSON output gives me do not correspond to the parts of the file they are meant
/// to correspond to. So, for now, let's just replace lines!
///
/// This function is as stupid as possible. Make sure you call for the replacemnts in one file in
/// reverse order to not mess up the lines for replacements further down the road.
fn apply_suggestion(suggestion: &Suggestion) -> Result<(), ProgramError> {
    use std::cmp::max;

    let file_content = try!(read_file_to_string(&suggestion.file_name));
    let mut new_content = String::new();

    // Add the lines before the section we want to replace
    new_content.push_str(&file_content.lines()
        .take(max(suggestion.line_range.start.line - 1, 0) as usize)
        .collect::<Vec<_>>()
        .join("\n"));
    new_content.push_str("\n");

    // Parts of line before replacement
    new_content.push_str(&file_content.lines()
        .nth(suggestion.line_range.start.line - 1)
        .unwrap_or("")
        .chars()
        .take(suggestion.line_range.start.column - 1)
        .collect::<String>());

    // Insert new content! Finally!
    new_content.push_str(&suggestion.replacement);

    // Parts of line after replacement
    new_content.push_str(&file_content.lines()
        .nth(suggestion.line_range.end.line - 1)
        .unwrap_or("")
        .chars()
        .skip(suggestion.line_range.end.column - 1)
        .collect::<String>());

    // Add the lines after the section we want to replace
    new_content.push_str("\n");
    new_content.push_str(&file_content.lines()
        .skip(suggestion.line_range.end.line as usize)
        .collect::<Vec<_>>()
        .join("\n"));
    new_content.push_str("\n");

    let mut file = try!(File::create(&suggestion.file_name));
    let new_content = new_content.as_bytes();

    try!(file.set_len(new_content.len() as u64));
    try!(file.write_all(&new_content));

    Ok(())
}
<commit_msg>Use `cargo --message-format` instead of rustc-flags<commit_after>#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate quick_error;
#[macro_use]
extern crate clap;
extern crate colored;

extern crate rustfix;

use std::fs::File;
use std::io::{Read, Write};
use std::error::Error;
use std::process::Command;

use colored::Colorize;
use clap::{Arg, App};

use rustfix::Suggestion;
use rustfix::diagnostics::Diagnostic;

const USER_OPTIONS: &'static str = "What do you want to do? \
    [r]eplace | [s]kip | save and [q]uit | [a]bort (without saving)";

fn main() {
    let program = try_main();
    match program {
        Ok(_) => std::process::exit(0),
        Err(ProgramError::UserAbort) => {
            writeln!(std::io::stdout(), "{}", ProgramError::UserAbort).unwrap();
            std::process::exit(0);
        }
        Err(error) => {
            writeln!(std::io::stderr(), "An error occured: {}", error).unwrap();
            writeln!(std::io::stderr(), "{:?}", error).unwrap();
            if let Some(cause) = error.cause() {
                writeln!(std::io::stderr(), "Cause: {:?}", cause).unwrap();
            }
            std::process::exit(1);
        }
    }
}

macro_rules! flush {
    () => (try!(std::io::stdout().flush());)
}

fn try_main() -> Result<(), ProgramError> {
    let matches = App::new("rustfix")
        .about("Automatically apply suggestions made by rustc")
        .version(crate_version!())
        .arg(Arg::with_name("clippy")
            .long("clippy")
            .help("Use `cargo clippy` for suggestions"))
        .get_matches();

    // Get JSON output from rustc...
        let subcommand = if matches.is_present("clippy") {
            "clippy"
        } else {
            "rustc"
        };
        try!(json_from_subcommand(subcommand))

    let suggestions: Vec<Suggestion> = json.lines()
        .filter(not_empty)
        // Convert JSON string (and eat parsing errors)
        .flat_map(|line| serde_json::from_str::<CargoMessage>(line))
        // One diagnostic line might have multiple suggestions
        .flat_map(|cargo_msg| rustfix::collect_suggestions(&cargo_msg.message, None))
        .collect();

    try!(handle_suggestions(&suggestions));

    Ok(())
}

#[derive(Deserialize)]
struct CargoMessage {
    message: Diagnostic,
}

fn json_from_subcommand(subcommand: &str) -> Result<String, ProgramError> {
    let output = try!(Command::new("cargo")
        .args(&[subcommand, "--message-format", "json"])
        .output());

    let content = try!(String::from_utf8(output.stderr));

    Ok(content)
}

fn handle_suggestions(suggestions: &[Suggestion]) -> Result<(), ProgramError> {
    let mut accepted_suggestions: Vec<&Suggestion> = vec![];

    if suggestions.is_empty() {
        println!("I don't have any suggestions for you right now. Check back later!");
        return Ok(());
    }

    'suggestions: for suggestion in suggestions {
        println!("\n\n{info}: {message}\n\
            {arrow} {file}:{range}\n\
            {suggestion}\n\n\
            {lead}{text}{tail}\n\n\
            {with}\n\n\
            {replacement}\n",
            info = "Info".green().bold(),
            message = split_at_lint_name(&suggestion.message),
            arrow = "  -->".blue().bold(),
            suggestion = "Suggestion - Replace:".yellow().bold(),
            file = suggestion.file_name,
            range = suggestion.line_range,
            lead = indent(4, &suggestion.text.0),
            text = suggestion.text.1.red(),
            tail = suggestion.text.2,
            with = "with:".yellow().bold(),
            replacement = indent(4, &suggestion.replacement));

        'userinput: loop {
            print!("{arrow} {user_options}\n\
                {prompt} ",
                arrow = "==>".green().bold(),
                prompt = "  >".green().bold(),
                user_options = USER_OPTIONS.green());

            flush!();
            let mut input = String::new();
            try!(std::io::stdin().read_line(&mut input));

            match input.trim() {
                "s" => {
                    println!("Skipped.");
                    continue 'suggestions;
                }
                "r" => {
                    accepted_suggestions.push(suggestion);
                    println!("Suggestion accepted. I'll remember that and apply it later.");
                    continue 'suggestions;
                }
                "q" => {
                    println!("Thanks for playing!");
                    break 'suggestions;
                }
                "a" => {
                    return Err(ProgramError::UserAbort);
                }
                _ => {
                    println!("{error}: I didn't quite get that. {user_options}",
                                error = "Error".red().bold(),
                                user_options = USER_OPTIONS);
                    continue 'userinput;
                }
            }
        }
    }

    if !accepted_suggestions.is_empty() {
        println!("Good work. Let me just apply these {} changes!",
                 accepted_suggestions.len());

        for suggestion in accepted_suggestions.iter().rev() {
            try!(apply_suggestion(suggestion));

            print!(".");
            flush!();
        }

        println!("\nDone.");
    }

    Ok(())
}

quick_error! {
    /// All possible errors in programm lifecycle
    #[derive(Debug)]
    pub enum ProgramError {
        UserAbort {
            display("Let's get outta here!")
        }
        /// Missing File
        NoFile {
            display("No input file given")
        }
        SubcommandError(subcommand: String, output: String) {
            display("Error executing subcommand `{}`", subcommand)
            description(output)
        }
        /// Error while dealing with file or stdin/stdout
        Io(err: std::io::Error) {
            from()
            cause(err)
            display("I/O error")
            description(err.description())
        }
        Utf8Error(err: std::string::FromUtf8Error) {
            from()
            display("Error reading input as UTF-8")
        }
        /// Error with deserialization
        Serde(err: serde_json::Error) {
            from()
            cause(err)
            display("Serde JSON error")
            description(err.description())
        }
    }
}

// Helpers
// -------

fn read_file_to_string(file_name: &str) -> Result<String, std::io::Error> {
    let mut file = try!(File::open(file_name));
    let mut buffer = String::new();
    try!(file.read_to_string(&mut buffer));
    Ok(buffer)
}

fn not_empty(s: &&str) -> bool {
    !s.trim().is_empty()
}

fn split_at_lint_name(s: &str) -> String {
    s.split(", #[")
        .collect::<Vec<_>>()
        .join("\n      #[") // Length of whitespace == length of "Info: "
}

fn indent(size: u32, s: &str) -> String {
    let whitespace: String = std::iter::repeat(' ').take(size as usize).collect();

    s.lines()
        .map(|l| format!("{}{}", whitespace, l))
        .collect::<Vec<_>>()
        .join("\n")
}

/// Apply suggestion to a file
///
/// Please beware of ugly hacks below! Originally, I wanted to replace byte ranges, but sadly the
/// ranges rustc's JSON output gives me do not correspond to the parts of the file they are meant
/// to correspond to. So, for now, let's just replace lines!
///
/// This function is as stupid as possible. Make sure you call for the replacemnts in one file in
/// reverse order to not mess up the lines for replacements further down the road.
fn apply_suggestion(suggestion: &Suggestion) -> Result<(), ProgramError> {
    use std::cmp::max;

    let file_content = try!(read_file_to_string(&suggestion.file_name));
    let mut new_content = String::new();

    // Add the lines before the section we want to replace
    new_content.push_str(&file_content.lines()
        .take(max(suggestion.line_range.start.line - 1, 0) as usize)
        .collect::<Vec<_>>()
        .join("\n"));
    new_content.push_str("\n");

    // Parts of line before replacement
    new_content.push_str(&file_content.lines()
        .nth(suggestion.line_range.start.line - 1)
        .unwrap_or("")
        .chars()
        .take(suggestion.line_range.start.column - 1)
        .collect::<String>());

    // Insert new content! Finally!
    new_content.push_str(&suggestion.replacement);

    // Parts of line after replacement
    new_content.push_str(&file_content.lines()
        .nth(suggestion.line_range.end.line - 1)
        .unwrap_or("")
        .chars()
        .skip(suggestion.line_range.end.column - 1)
        .collect::<String>());

    // Add the lines after the section we want to replace
    new_content.push_str("\n");
    new_content.push_str(&file_content.lines()
        .skip(suggestion.line_range.end.line as usize)
        .collect::<Vec<_>>()
        .join("\n"));
    new_content.push_str("\n");

    let mut file = try!(File::create(&suggestion.file_name));
    let new_content = new_content.as_bytes();

    try!(file.set_len(new_content.len() as u64));
    try!(file.write_all(&new_content));

    Ok(())
}
<|endoftext|>


<commit_before>// UDP stuff adapted from http://illegalargumentexception.blogspot.co.uk/2015/05/rust-send-and-receive-on-localhost-with.html
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, UdpSocket};
#[macro_use]
extern crate clap;
use clap::{Arg, App};

fn socket(listen_on: SocketAddr) -> UdpSocket {
    let attempt = UdpSocket::bind(listen_on);
    let socket;
    match attempt {
        Ok(sock) => {
            socket = sock;
        }
        Err(err) => panic!("Could not bind: {}", err),
    }
    socket
}

fn send_message(send_addr: SocketAddr, target: SocketAddr, data: &[u8]) {
    let _ = socket(send_addr).send_to(data, target);
}

fn main() {
    let command_vals = ["white",
                        "red",
                        "orange",
                        "yellow",
                        "green",
                        "cyan",
                        "blue",
                        "purple",
                        "black",
                        "question",
                        "exclamation",
                        "quit"];
    let command_params = App::new("rust_anybar")
        .version(&crate_version!()[..])
        .author("Stephan Hügel <urschrei@gmail.com>")
        .about("A Rust command-line client for Anybar")
        .args_from_usage("-p --port=[PORT] 'Set destination UDP port. Input must be 0 – 65535, \
                          and defaults to 17388")
        .arg(Arg::with_name("COMMAND")
            .help("The command you wish to send to Anybar")
            .index(1)
            .possible_values(&command_vals)
            .required(true))
        .get_matches();
    // bind to the correct UDP port
    let numeric_port = value_t!(command_params.value_of("PORT"), u16).unwrap_or(1738);
    // it's safe to unwrap here, cos we already checked success
    let to_send = command_params.value_of("COMMAND").unwrap();
    let ip = Ipv4Addr::new(127, 0, 0, 1);
    let listen_addr = SocketAddrV4::new(ip, numeric_port);
    let send_addr = SocketAddrV4::new(ip, 0);
    // and send our message
    send_message(SocketAddr::V4(send_addr),
                 SocketAddr::V4(listen_addr),
                 to_send.as_bytes());
}
<commit_msg>Spin up a thread to send the message<commit_after>// UDP stuff adapted from http://illegalargumentexception.blogspot.co.uk/2015/05/rust-send-and-receive-on-localhost-with.html
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, UdpSocket};
use std::process::exit;
use std::thread;
#[macro_use]
extern crate clap;
use clap::{Arg, App};

fn socket(listen_on: SocketAddr) -> UdpSocket {
    let attempt = UdpSocket::bind(listen_on);
    let socket;
    match attempt {
        Ok(sock) => {
            socket = sock;
        }
        Err(err) => panic!("Could not bind: {}", err),
    }
    socket
}

fn send_message(send_addr: SocketAddr, target: SocketAddr, data: &[u8]) {
    let _ = socket(send_addr).send_to(data, target);
}

fn main() {
    let command_vals = ["white",
                        "red",
                        "orange",
                        "yellow",
                        "green",
                        "cyan",
                        "blue",
                        "purple",
                        "black",
                        "question",
                        "exclamation",
                        "quit"];
    let command_params = App::new("rust_anybar")
        .version(&crate_version!()[..])
        .author("Stephan Hügel <urschrei@gmail.com>")
        .about("A Rust command-line client for Anybar")
        .args_from_usage("-p --port=[PORT] 'Set destination UDP port. Input must be 0 – 65535, \
                          and defaults to 17388")
        .arg(Arg::with_name("COMMAND")
            .help("The command you wish to send to Anybar")
            .index(1)
            .possible_values(&command_vals)
            .required(true))
        .get_matches();
    let handle = thread::spawn(move || {
        // bind to the correct UDP port
        let numeric_port = value_t!(command_params.value_of("PORT"), u16).unwrap_or(1738);
        // it's safe to unwrap here, cos we already checked success
        let to_send = command_params.value_of("COMMAND").unwrap();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let listen_addr = SocketAddrV4::new(ip, numeric_port);
        let send_addr = SocketAddrV4::new(ip, 0);
        // and send our message
        send_message(SocketAddr::V4(send_addr),
                     SocketAddr::V4(listen_addr),
                     to_send.as_bytes());
    });
    let _ = handle.join();
}
<|endoftext|>


<commit_before>use std::env;
use std::f32;
use std::thread::sleep;
use std::time::Duration;

extern crate getopts;
use getopts::Options;
extern crate rand;
use rand::Rng;
use rand::distributions::{IndependentSample,Range};
extern crate sacn;
use sacn::DmxSource;

const DECAY_FACTOR: f32 = 2_f32;
const LIGHT_THRESHOLD: f32 = 0.10;
const MAX_BRIGHTNESS: f32 = 75_f32;

const UNIVERSE_SIZE: usize = 510;

#[derive(Debug)]
struct Params {
    decay:          f32,
    threshold:      f32,
    max_brightness: f32
}

#[derive(Debug)]
#[derive(Clone)]
struct Pixel { level: i16, age: i16 }
// impl Pixel {
//     fn clone(&self) -> Pixel {
//         let mut pixel = Pixel { level: self.level, age: self.age };
//     }
// }

struct Zone  { head: u8, body: u8, tail: u8, name: String }

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut params = Params { decay: 2_f32, threshold: 0.1, max_brightness: 75_f32 };
    let mut opts = Options::new();
    opts.optopt("d", "decay", "slow decay by this factor, defaults to 2", "DECAY");
    opts.optopt("t", "threshold", "probablity that a pixel lights up, default 0.10", "THRESHOLD");
    opts.optopt("m", "maxbrightness", "maximum brightness, 1..255, default 75", "MAX");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("d") {
        params.decay = matches.opt_str("d").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("t") {
        params.threshold = matches.opt_str("t").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("m") {
        params.max_brightness = matches.opt_str("m").unwrap().parse::<f32>().unwrap();
    }

    let dmx = DmxSource::new("Controller").unwrap();

    let zones: [Zone; 6] = [
        Zone { head: 3, body: 47, tail: 0, name: "10".to_string() },
        Zone { head: 2, body: 92, tail: 2, name: "11a".to_string() },
        Zone { head: 2, body: 92, tail: 2, name: "11b".to_string() },
        Zone { head: 2, body: 90, tail: 3, name: "12a".to_string() },
        Zone { head: 2, body: 91, tail: 3, name: "12b".to_string() },
        Zone { head: 2, body: 43, tail: 0, name: "13".to_string() }
    ];

    let mut lights: Vec<Pixel> = vec![];
    for zone in zones.iter() {
        for i in 1..zone.body {
            let pixel = Pixel { level: 0, age: 0 };
            lights.push(pixel);
        }
    }

    // half of a second
    let refresh = Duration::new(1, 500_000_000);
    let mut rng = rand::thread_rng();
    let bright_range = Range::new(0_f32, params.max_brightness);
    let zero_to_one = Range::new(0_f32, 1_f32);

    loop {
        for light in lights.iter_mut() {
            if light.level == 0 {
                // light is currently dark
                // test to see if we want to light it
                if zero_to_one.ind_sample(&mut rng) < params.threshold {
                    // we do, so pick a random
                    light.level = bright_range.ind_sample(&mut rng) as i16;
                    light.age += 1;
                }
            } else {
                // light is lit
                // test to see if we rise or fall
                // probability of falling should go up as light ages
                if zero_to_one.ind_sample(&mut rng) > 1.0/(light.age as f32) {
                    // falling
                    light.level -= (bright_range.ind_sample(&mut rng)/params.decay) as i16;
                    light.age += 1;
                    // test to see if we bottomed out
                    if light.level <= 0 {
                        light.level = 0;
                        light.age = 0;
                    }
                } else {
                    // rising
                    light.level += (zero_to_one.ind_sample(&mut rng) * (params.max_brightness - light.level as f32)) as i16;
                    light.age += 1;
                }
            }
        }
        render(&lights, &zones, &dmx);
        sleep(refresh);
    }
    dmx.terminate_stream(1);
}

// debug, rendor output to console
// fn render( lights: &[Pixel], zones: &[Zone], dmx: &DmxSource ) {
//    let mut out: Vec<i16> = vec![];
//    for light in lights {
//        out.push(light.level);
//   }
//    println!("{:?}", out)
//     let mut lit: u16 = 0;
//     let mut level: i16 = 0;
//     for light in lights {
//         if light.level > 0 {
//             lit += 1;
//             level += light.level;
//         }
//     }
//     println!("{} pixels, {} lit, {} avg level", lights.len(), lit, (level as f32)/(lit as f32));
// }

// output to lighting controller via sACN
fn render( lights: &[Pixel], zones: &[Zone], dmx: &DmxSource ) {
    let mut out: Vec<u8> = vec![];
    let mut copy: Vec<Pixel> = vec![];
    copy.extend_from_slice(lights);
    let mut idx: usize = 0;
    for zone in zones {
        // null pixels at head
        for i in 1..zone.head {
            out.push(0); // Red
            out.push(0); // Green
            out.push(0); // Blue
        }
        // set via light.level in the body
        for i in 0..(zone.body - 1) {
            out.push(copy[idx].level as u8); // Red
            out.push(copy[idx].level as u8); // Green
            out.push(copy[idx].level as u8); // Blue
            idx += 1;
        }
        // null pixels at tail
        for i in 1..zone.tail {
            out.push(0); // Red
            out.push(0); // Green
            out.push(0); // Blue
        }
    }
    let mut universes = Vec::new();
    while out.len() > UNIVERSE_SIZE {
        let u = out.split_off(UNIVERSE_SIZE);
        universes.push(u);
    }
    universes.push(out);
    // println!("{:?}", universes);
    let mut universe: u16 = 1;
    while let Some(u) = universes.pop() {
        dmx.send(universe, &u);
        universe += 1;
    }
}

<commit_msg>make sleep interval a commandline option<commit_after>use std::env;
use std::f32;
use std::thread::sleep;
use std::time::Duration;

extern crate getopts;
use getopts::Options;
extern crate rand;
use rand::Rng;
use rand::distributions::{IndependentSample,Range};
extern crate sacn;
use sacn::DmxSource;

const DECAY_FACTOR: f32 = 2_f32;
const LIGHT_THRESHOLD: f32 = 0.10;
const MAX_BRIGHTNESS: f32 = 75_f32;

const UNIVERSE_SIZE: usize = 510;

#[derive(Debug)]
struct Params {
    decay:          f32,
    threshold:      f32,
    max_brightness: f32,
    sleep:          Duration
}

#[derive(Debug)]
#[derive(Clone)]
struct Pixel { level: i16, age: i16 }
// impl Pixel {
//     fn clone(&self) -> Pixel {
//         let mut pixel = Pixel { level: self.level, age: self.age };
//     }
// }

struct Zone  { head: u8, body: u8, tail: u8, name: String }

fn build_params () -> Params {
    // seed default params
    let mut params = Params { decay: 2_f32, threshold: 0.1, max_brightness: 75_f32, sleep: Duration::new(1, 500_000_000) };

    // parse command line args and adjust params accordingly
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("d", "decay", "slow decay by this factor, defaults to 2", "DECAY");
    opts.optopt("t", "threshold", "probablity that a pixel lights up, default 0.10", "THRESHOLD");
    opts.optopt("m", "maxbrightness", "maximum brightness, 1..255, default 75", "MAX");
    opts.optopt("s", "sleep", "sleep interval in seconds, default 1.5", "SECONDS");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("d") {
        params.decay = matches.opt_str("d").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("t") {
        params.threshold = matches.opt_str("t").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("m") {
        params.max_brightness = matches.opt_str("m").unwrap().parse::<f32>().unwrap();
    }
    if matches.opt_present("s") {
        // take float seconds
        // convert to int seconds and nanoseconds to make Duration happy
        let seconds: f32 = matches.opt_str("s").unwrap().parse::<f32>().unwrap();
        let whole_seconds: u64 = seconds as u64;
        let nano_seconds: u32 = ((seconds - whole_seconds as f32) * 1_000_000_000_f32) as u32;
        params.sleep = Duration::new(whole_seconds, nano_seconds);
    }
    return params;
}

fn main() {
    let params = build_params();

    let dmx = DmxSource::new("Controller").unwrap();

    let zones: [Zone; 6] = [
        Zone { head: 3, body: 47, tail: 0, name: "10".to_string() },
        Zone { head: 2, body: 92, tail: 2, name: "11a".to_string() },
        Zone { head: 2, body: 92, tail: 2, name: "11b".to_string() },
        Zone { head: 2, body: 90, tail: 3, name: "12a".to_string() },
        Zone { head: 2, body: 91, tail: 3, name: "12b".to_string() },
        Zone { head: 2, body: 43, tail: 0, name: "13".to_string() }
    ];

    let mut lights: Vec<Pixel> = vec![];
    // TODO: probably a more idiomatic way to built the default state
    for zone in zones.iter() {
        for i in 1..zone.body {
            let pixel = Pixel { level: 0, age: 0 };
            lights.push(pixel);
        }
    }

    let mut rng = rand::thread_rng();
    let bright_range = Range::new(0_f32, params.max_brightness);
    let zero_to_one = Range::new(0_f32, 1_f32);

    loop {
        for light in lights.iter_mut() {
            if light.level == 0 {
                // light is currently dark
                // test to see if we want to light it
                if zero_to_one.ind_sample(&mut rng) < params.threshold {
                    // we do, so pick a random
                    light.level = bright_range.ind_sample(&mut rng) as i16;
                    light.age += 1;
                }
            } else {
                // light is lit
                // test to see if we rise or fall
                // probability of falling should go up as light ages
                if zero_to_one.ind_sample(&mut rng) > 1.0/(light.age as f32) {
                    // falling
                    light.level -= (bright_range.ind_sample(&mut rng)/params.decay) as i16;
                    light.age += 1;
                    // test to see if we bottomed out
                    if light.level <= 0 {
                        light.level = 0;
                        light.age = 0;
                    }
                } else {
                    // rising
                    light.level += (zero_to_one.ind_sample(&mut rng) * (params.max_brightness - light.level as f32)) as i16;
                    light.age += 1;
                }
            }
        }
        render(&lights, &zones, &dmx);
        sleep(params.sleep);
    }
    dmx.terminate_stream(1);
}

// debug, rendor output to console
// fn render( lights: &[Pixel], zones: &[Zone], dmx: &DmxSource ) {
//    let mut out: Vec<i16> = vec![];
//    for light in lights {
//        out.push(light.level);
//   }
//    println!("{:?}", out)
//     let mut lit: u16 = 0;
//     let mut level: i16 = 0;
//     for light in lights {
//         if light.level > 0 {
//             lit += 1;
//             level += light.level;
//         }
//     }
//     println!("{} pixels, {} lit, {} avg level", lights.len(), lit, (level as f32)/(lit as f32));
// }

// output to lighting controller via sACN
fn render( lights: &[Pixel], zones: &[Zone], dmx: &DmxSource ) {
    let mut out: Vec<u8> = vec![];
    let mut copy: Vec<Pixel> = vec![];
    copy.extend_from_slice(lights);
    let mut idx: usize = 0;
    for zone in zones {
        // null pixels at head
        for i in 1..zone.head {
            out.push(0); // Red
            out.push(0); // Green
            out.push(0); // Blue
        }
        // set via light.level in the body
        for i in 0..(zone.body - 1) {
            out.push(copy[idx].level as u8); // Red
            out.push(copy[idx].level as u8); // Green
            out.push(copy[idx].level as u8); // Blue
            idx += 1;
        }
        // null pixels at tail
        for i in 1..zone.tail {
            out.push(0); // Red
            out.push(0); // Green
            out.push(0); // Blue
        }
    }
    let mut universes = Vec::new();
    while out.len() > UNIVERSE_SIZE {
        let u = out.split_off(UNIVERSE_SIZE);
        universes.push(u);
    }
    universes.push(out);
    // println!("{:?}", universes);
    let mut universe: u16 = 1;
    while let Some(u) = universes.pop() {
        dmx.send(universe, &u);
        universe += 1;
    }
}

<|endoftext|>


<commit_before>#![feature(core)]
#![feature(path)]

extern crate git2;
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

use git2::{Repository, Branch, BranchType, DiffLine,
    Commit, Diff, DiffFormat, DiffDelta, DiffHunk, Error, Oid};
use docopt::Docopt;
use std::old_io as io;
use std::old_io::BufferedReader;
use std::old_io::File;
use std::str;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit <repo> [<old> <new>]

If no commits are given, transit will revwalk from latest to oldest.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    arg_repo: String,
    arg_old: Option<String>,
    arg_new: Option<String>,
}

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    // Pull up the revwalk.
    let path = Path::new(args.arg_repo);
    let repo = Repository::discover(&path)
        .ok().expect("Unable to find repo.");
    // If we can destructures the two optional args into real things.
    if let (Some(old_string), Some(new_string)) = (args.arg_old, args.arg_new) {
        // Compare a specific commit pair.
        // Fist, get the commits. (Error checked)
        let old = Oid::from_str(&old_string[]).and_then(|oid| repo.find_commit(oid));
        let new = Oid::from_str(&new_string[]).and_then(|oid| repo.find_commit(oid));
        if old.is_ok() && new.is_ok() {
            find_moves(&repo, &old.unwrap(), &new.unwrap()).unwrap();
        } else {
            panic!("Commit ids were not valid.");
        }
    } else {
        // Revwalk.
        let mut revwalk = repo.revwalk()
            .ok().expect("Unable to get revwalk.");
        // Setup some options.
        revwalk.simplify_first_parent(); // TODO: Maybe remove?
        let mut flags = git2::Sort::empty();
        flags.insert(git2::SORT_TIME);
        flags.insert(git2::SORT_TOPOLOGICAL);
        revwalk.set_sorting(flags);
        // Push HEAD to the revwalk.
        revwalk.push_head()
            .ok().expect("Unable to push HEAD.");
        // We sadly must collect here to use `.windows()`
        let history = revwalk.filter_map(|id| repo.find_commit(id).ok())
            .collect::<Vec<Commit>>();
        // Walk through each pair of commits.
        for pair in history.windows(2) {
            let (old, new) = (&pair[1], &pair[0]);
            find_moves(&repo, old.clone(), new.clone()).unwrap();
        }
    }

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<u64, Error> {
        let old_tree = try!(old.tree());
        let new_tree = try!(new.tree());
        // Build up a diff of the two trees.
        let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));
        // State. TODO: Don't just use a u64.
        let moved = 0;
        // Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
        // It's a bit weird, but I think it will provide the necessary information.
        diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {
            // Filter out all the boring context lines.
            // If we're not interested in this line just return since it will iterate to the next.
            match line.origin() {
                // Context
                ' ' | '=' => true,
                // Headers
                'F' | 'H' => true,
                // Additions
                '+' | '>' => {
                    println!("+ {}", str::from_utf8(line.content()).unwrap());
                    true
                },
                // Deletions
                '-' | '<' => {
                    println!("- {}", str::from_utf8(line.content()).unwrap());
                    true
                },
                // Other (We don't care about these.)
                _         => true,
            }

            // Look at that match statement, what a hunk. So hunky.
            // match maybe_hunk {
            //     Some(hunk) => unimplemented!(),
            //     None => unimplemented!(),
            // }
        });
        Ok(moved)
    }
}
<commit_msg>Output struct<commit_after>#![feature(core)]
#![feature(path)]

extern crate git2;
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

use git2::{Repository, Branch, BranchType, DiffLine,
    Commit, Diff, DiffFormat, DiffDelta, DiffHunk, Error, Oid};
use docopt::Docopt;
use std::old_io as io;
use std::old_io::BufferedReader;
use std::old_io::File;
use std::str;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit <repo> [<old> <new>]

If no commits are given, transit will revwalk from latest to oldest.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    arg_repo: String,
    arg_old: Option<String>,
    arg_new: Option<String>,
}

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    // Pull up the revwalk.
    let path = Path::new(args.arg_repo);
    let repo = Repository::discover(&path)
        .ok().expect("Unable to find repo.");
    // If we can destructures the two optional args into real things.
    if let (Some(old_string), Some(new_string)) = (args.arg_old, args.arg_new) {
        // Compare a specific commit pair.
        // Fist, get the commits. (Error checked)
        let old = Oid::from_str(&old_string[]).and_then(|oid| repo.find_commit(oid));
        let new = Oid::from_str(&new_string[]).and_then(|oid| repo.find_commit(oid));
        if old.is_ok() && new.is_ok() {
            find_moves(&repo, &old.unwrap(), &new.unwrap()).unwrap();
        } else {
            panic!("Commit ids were not valid.");
        }
    } else {
        // Revwalk.
        let mut revwalk = repo.revwalk()
            .ok().expect("Unable to get revwalk.");
        // Setup some options.
        revwalk.simplify_first_parent(); // TODO: Maybe remove?
        let mut flags = git2::Sort::empty();
        flags.insert(git2::SORT_TIME);
        flags.insert(git2::SORT_TOPOLOGICAL);
        revwalk.set_sorting(flags);
        // Push HEAD to the revwalk.
        revwalk.push_head()
            .ok().expect("Unable to push HEAD.");
        // We sadly must collect here to use `.windows()`
        let history = revwalk.filter_map(|id| repo.find_commit(id).ok())
            .collect::<Vec<Commit>>();
        // Walk through each pair of commits.
        for pair in history.windows(2) {
            let (old, new) = (&pair[1], &pair[0]);
            find_moves(&repo, old.clone(), new.clone()).unwrap();
        }
    }

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, Error> {
        let old_tree = try!(old.tree());
        let new_tree = try!(new.tree());
        // Build up a diff of the two trees.
        let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));
        // State. TODO: Don't just use a u64.
        let moved = 0;
        let mut current_hunk: Vec<String> = Vec::new();
        // Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
        // It's a bit weird, but I think it will provide the necessary information.
        diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {
            // Thinking:
            //  * If is not a hunk, keep going.
            //  * If it's a hunk, do regex magic.
            //  * Stick regex output into a hashmap as a hash.
            //  * Later, we will iterate through and output pased on the values.
            // Filter out all the boring context lines.
            // If we're not interested in this line just return since it will iterate to the next.
            if maybe_hunk.is_none() { return true }; // Return early.
            match line.origin() {
                // Context
                ' ' | '=' => true,
                // Headers
                'F' | 'H' => true,
                // Additions
                '+' | '>' => {
                    println!("+ {}", str::from_utf8(line.content()).unwrap());
                    true
                },
                // Deletions
                '-' | '<' => {
                    println!("- {}", str::from_utf8(line.content()).unwrap());
                    true
                },
                // Other (We don't care about these.)
                _         => true,
            }
                // Look at that match statement, what a hunk. So hunky.
                // match maybe_hunk {
                //     Some(hunk) => unimplemented!(),
                //     None => unimplemented!(),
                // }
        });
        Ok(vec![Output {
            old_commit: old.id(),
            new_commit: new.id(),
            origin_line: 0,
            destination_line: 0,
            num_lines: 0
        }])
    }
}

struct Output {
    old_commit: Oid,
    new_commit: Oid,
    origin_line: u32,
    destination_line: u32,
    num_lines: u32,
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;

extern crate clap;
extern crate rand;
use clap::{Arg, App};

mod cat;

fn main() {
    let mut c = parse_cli_args();
    let stdin = io::stdin(); // For lifetime reasons

    for line in stdin.lock().lines() {
        cat::print_with_lolcat(line.unwrap(), &mut c);
    }
}

fn parse_cli_args() -> cat::Control {
    let matches = App::new("lolcat")
        .version("0.1.0")
        .author("Umang Raghuvanshi <u@umangis.me>")
        .about("The good ol' lolcat, now with fearless concurrency.")
        .arg(Arg::with_name("seed")
            .short("s")
            .long("seed")
            .help("A seed for your lolcat. Setting this to 0 randomizes the seed."))
        .arg(Arg::with_name("spread")
            .short("S")
            .long("spread")
            .help("How much should we spread dem colors? Defaults to 3.0"))
        .arg(Arg::with_name("frequency")
            .short("f")
            .long("frequency")
            .help("Frequency - used in our math. Defaults to 0.1"))
        .get_matches();
    let seed = matches.value_of("seed").unwrap_or("0.0");
    let spread = matches.value_of("spread").unwrap_or("3.0");
    let frequency = matches.value_of("frequency").unwrap_or("0.1");

    let mut seed: f64 = seed.parse().unwrap();
    let spread: f64 = spread.parse().unwrap();
    let frequency: f64 = frequency.parse().unwrap();

    if seed == 0.0 {
        seed = rand::random::<f64>();
    }

    cat::Control{seed: seed, spread: spread, frequency: frequency}
}
<commit_msg>Our arguments take values<commit_after>use std::io;
use std::io::prelude::*;

extern crate clap;
extern crate rand;
use clap::{Arg, App};

mod cat;

fn main() {
    let mut c = parse_cli_args();
    let stdin = io::stdin(); // For lifetime reasons

    for line in stdin.lock().lines() {
        cat::print_with_lolcat(line.unwrap(), &mut c);
    }
}

fn parse_cli_args() -> cat::Control {
    let matches = App::new("lolcat")
        .version("0.1.0")
        .author("Umang Raghuvanshi <u@umangis.me>")
        .about("The good ol' lolcat, now with fearless concurrency.")
        .arg(Arg::with_name("seed")
            .short("s")
            .long("seed")
            .help("A seed for your lolcat. Setting this to 0 randomizes the seed.")
            .takes_value(true))
        .arg(Arg::with_name("spread")
            .short("S")
            .long("spread")
            .help("How much should we spread dem colors? Defaults to 3.0")
            .takes_value(true))
        .arg(Arg::with_name("frequency")
            .short("f")
            .long("frequency")
            .help("Frequency - used in our math. Defaults to 0.1")
            .takes_value(true))
        .get_matches();
    let seed = matches.value_of("seed").unwrap_or("0.0");
    let spread = matches.value_of("spread").unwrap_or("3.0");
    let frequency = matches.value_of("frequency").unwrap_or("0.1");

    let mut seed: f64 = seed.parse().unwrap();
    let spread: f64 = spread.parse().unwrap();
    let frequency: f64 = frequency.parse().unwrap();

    if seed == 0.0 {
        seed = rand::random::<f64>();
    }

    cat::Control {
        seed: seed,
        spread: spread,
        frequency: frequency,
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;

extern crate time;

use std::collections::BinaryHeap;
use std::sync::{Condvar, Mutex};
use std::thread;
use std::sync::{Arc};
use std::sync::mpsc::{Sender, Receiver, channel};
use std::cmp::{Ordering, min, max};

use time::{SteadyTime, Duration};

struct TimerEvent {
    when: SteadyTime,
    completion_sink: Sender<()>,
    period: Option<u32>,
}
impl Ord for TimerEvent {
    fn cmp(&self, other: &TimerEvent) -> Ordering {
        other.when.cmp(&self.when)
    }
}
impl PartialEq for TimerEvent {
    fn eq(&self, other: &TimerEvent) -> bool {
        let self_ptr: *const TimerEvent = self;
        let other_ptr: *const TimerEvent = other;

        self_ptr == other_ptr
    }
}
impl Eq for TimerEvent {}
impl PartialOrd for TimerEvent {
    fn partial_cmp(&self, other: &TimerEvent) -> Option<Ordering> {
        other.when.partial_cmp(&self.when)
    }
}

struct TimerRequest {
    duration: u32,
    periodic: bool,
    completion_sink: Sender<()>,
}

struct TimerInterface {
    trigger: Arc<Condvar>,
    adder: Sender<TimerRequest>,
}

struct TimerWorker {
    trigger: Arc<Condvar>,
    request_source: Receiver<TimerRequest>,
    schedule: BinaryHeap<TimerEvent>,
}

impl TimerWorker {
    fn new(trigger: Arc<Condvar>, request_source: Receiver<TimerRequest>) -> TimerWorker {
        TimerWorker{
            trigger: trigger,
            request_source: request_source,
            schedule: BinaryHeap::new(),
        }
    }

    fn run(&mut self) {
        let m = Mutex::new(false);
        let mut g = m.lock().unwrap();

        loop {
            let now = SteadyTime::now();

            while let Ok(request) = self.request_source.try_recv() {
                println!("Scheduling a new timeout for {} ms from now", request.duration);
                self.schedule.push(TimerEvent{
                    when: now + Duration::milliseconds(request.duration as i64),
                    period: if request.periodic { Some(request.duration) } else { None },
                    completion_sink: request.completion_sink
                });
            }

            // Fire off as many events as we are supposed to.
            loop {
                let ready = if let Some(evt) = self.schedule.peek() {
                    evt.when < now
                } else {
                    false
                };

                if ready {
                    println!("Firing an event!");
                    if let Some(evt) = self.schedule.pop() {
                        match evt.completion_sink.send( () ) {
                            Ok( () ) => {
                                println!("Send succeeded!");
                                if let Some(period) = evt.period.clone() {
                                    self.schedule.push(TimerEvent{
                                        when: now + Duration::milliseconds(period as i64),
                                        period: evt.period,
                                        completion_sink: evt.completion_sink,
                                    });
                                }
                            }
                            Err(_) => {
                                // The receiver is no longer waiting for us
                            }
                        }
                    }
                } else {
                    break;
                }
            }

            let wait_millis =
                if let Some(evt) = self.schedule.peek() {
                    max(0, min((evt.when - now).num_milliseconds(), 100000))  as u32
                } else {
                    100000
                };

            println!("Timer is waiting for {}!", wait_millis);
            g = self.trigger.wait_timeout_ms(g, wait_millis).unwrap().0;
            println!("Timer is done waiting");
        }
    }
}

lazy_static! {
    static ref TIMER_INTERFACE  : Mutex<TimerInterface> = {
        let (sender, receiver) = channel();
        let trigger = Arc::new(Condvar::new());
        let trigger2 = trigger.clone();
        thread::spawn(move|| {
            TimerWorker::new(trigger2, receiver).run();
        });

        let interface = TimerInterface {
            trigger: trigger,
            adder: sender
        };

        Mutex::new(interface)
    };
}

fn add_request(duration_ms: u32, periodic: bool) -> Receiver<()> {
    let (sender, receiver) = channel();

    let interface = TIMER_INTERFACE.lock().unwrap();
    interface.adder.send(TimerRequest{
        duration:duration_ms,
        completion_sink:sender,
        periodic: periodic
    }).unwrap();

    interface.trigger.notify_one();

    receiver
}

pub fn oneshot_ms(ms: u32) -> Receiver<()> {
    add_request(ms, false)
}

pub fn periodic_ms(ms: u32) -> Receiver<()> {
    add_request(ms, true)
}

fn main() {
    {
        let timeout = oneshot_ms(2000);
        // do some work
        println!("Main waits for a timeout");
        //timeout.recv().ok().expect("main's recv didn't work"); // wait for the timeout to expire
        println!("Main gets a timeout!");
    }

    //thread::sleep_ms(10000);

    let timeout4 = oneshot_ms(4000);
    let timeout6 = oneshot_ms(6000);
    timeout4.recv().ok().expect("main's recv didn't work"); // wait for the timeout to expire
    println!("Four more seconds elapsed!");
    timeout6.recv().ok().expect("main's recv didn't work"); // wait for the timeout to expire
    println!("Six seconds elapsed!");
}

#[test]
fn it_works() {

}<commit_msg>Break the worker into several methods<commit_after>#[macro_use]
extern crate lazy_static;

extern crate time;

use std::collections::BinaryHeap;
use std::sync::{Condvar, Mutex};
use std::thread;
use std::sync::{Arc};
use std::sync::mpsc::{Sender, Receiver, channel};
use std::cmp::{Ordering, min, max};

use time::{SteadyTime, Duration};

struct TimerEvent {
    when: SteadyTime,
    completion_sink: Sender<()>,
    period: Option<u32>,
}
impl Ord for TimerEvent {
    fn cmp(&self, other: &TimerEvent) -> Ordering {
        other.when.cmp(&self.when)
    }
}
impl PartialEq for TimerEvent {
    fn eq(&self, other: &TimerEvent) -> bool {
        let self_ptr: *const TimerEvent = self;
        let other_ptr: *const TimerEvent = other;

        self_ptr == other_ptr
    }
}
impl Eq for TimerEvent {}
impl PartialOrd for TimerEvent {
    fn partial_cmp(&self, other: &TimerEvent) -> Option<Ordering> {
        other.when.partial_cmp(&self.when)
    }
}

struct TimerRequest {
    duration: u32,
    periodic: bool,
    completion_sink: Sender<()>,
}

struct TimerInterface {
    trigger: Arc<Condvar>,
    adder: Sender<TimerRequest>,
}

struct TimerWorker {
    trigger: Arc<Condvar>,
    request_source: Receiver<TimerRequest>,
    schedule: BinaryHeap<TimerEvent>,
}

impl TimerWorker {
    fn new(trigger: Arc<Condvar>, request_source: Receiver<TimerRequest>) -> TimerWorker {
        TimerWorker{
            trigger: trigger,
            request_source: request_source,
            schedule: BinaryHeap::new(),
        }
    }

    fn drain_request_queue(&mut self) {
        while let Ok(request) = self.request_source.try_recv() {
            println!("Scheduling a new timeout for {} ms from now", request.duration);
            self.schedule.push(TimerEvent{
                when: SteadyTime::now() + Duration::milliseconds(request.duration as i64),
                period: if request.periodic { Some(request.duration) } else { None },
                completion_sink: request.completion_sink
            });
        }
    }

    fn has_event_now(&self) -> bool {
        if let Some(evt) = self.schedule.peek() {
            evt.when < SteadyTime::now()
        } else {
            false
        }
    }

    fn fire_event(&mut self) {
        println!("Firing an event!");
        if let Some(evt) = self.schedule.pop() {
            match evt.completion_sink.send( () ) {
                Ok( () ) => {
                    println!("Send succeeded!");
                    if let Some(period) = evt.period.clone() {
                        self.schedule.push(TimerEvent{
                            when: SteadyTime::now() + Duration::milliseconds(period as i64),
                            period: evt.period,
                            completion_sink: evt.completion_sink,
                        });
                    }
                }
                Err(_) => {
                    // The receiver is no longer waiting for us
                }
            }
        }
    }

    fn ms_until_next_event(&self) -> u32 {
        if let Some(evt) = self.schedule.peek() {
            max(0, min((evt.when - SteadyTime::now()).num_milliseconds(), 100000))  as u32
        } else {
            100000
        }
    }

    fn run(&mut self) {
        let m = Mutex::new(false);
        let mut g = m.lock().unwrap();

        loop {
            self.drain_request_queue();

            // Fire off as many events as we are supposed to.
            loop {
                if self.has_event_now() {
                    self.fire_event();
                } else {
                    break;
                }
            }

            let wait_millis = self.ms_until_next_event();

            println!("Timer is waiting for {}!", wait_millis);
            g = self.trigger.wait_timeout_ms(g, wait_millis).unwrap().0;
            println!("Timer is done waiting");
        }
    }
}

lazy_static! {
    static ref TIMER_INTERFACE  : Mutex<TimerInterface> = {
        let (sender, receiver) = channel();
        let trigger = Arc::new(Condvar::new());
        let trigger2 = trigger.clone();
        thread::spawn(move|| {
            TimerWorker::new(trigger2, receiver).run();
        });

        let interface = TimerInterface {
            trigger: trigger,
            adder: sender
        };

        Mutex::new(interface)
    };
}

fn add_request(duration_ms: u32, periodic: bool) -> Receiver<()> {
    let (sender, receiver) = channel();

    let interface = TIMER_INTERFACE.lock().unwrap();
    interface.adder.send(TimerRequest{
        duration:duration_ms,
        completion_sink:sender,
        periodic: periodic
    }).unwrap();

    interface.trigger.notify_one();

    receiver
}

pub fn oneshot_ms(ms: u32) -> Receiver<()> {
    add_request(ms, false)
}

pub fn periodic_ms(ms: u32) -> Receiver<()> {
    add_request(ms, true)
}

fn main() {
    {
        let timeout = oneshot_ms(2000);
        // do some work
        println!("Main waits for a timeout");
        //timeout.recv().ok().expect("main's recv didn't work"); // wait for the timeout to expire
        println!("Main gets a timeout!");
    }

    //thread::sleep_ms(10000);

    let timeout4 = oneshot_ms(4000);
    let timeout6 = oneshot_ms(6000);
    timeout4.recv().ok().expect("main's recv didn't work"); // wait for the timeout to expire
    println!("Four more seconds elapsed!");
    timeout6.recv().ok().expect("main's recv didn't work"); // wait for the timeout to expire
    println!("Six seconds elapsed!");
}

#[test]
fn it_works() {

}<|endoftext|>


<commit_before>#![allow(unstable, dead_code)]

extern crate getopts;
extern crate libc;

mod ansi;
mod matching;
mod screen;

use std::os;
use std::io;
use std::cmp::min;
use screen::Screen;
use screen::Key::*;

fn main() {
  let input_args = os::args();

  let args = match parse_args(&input_args) {
    Some(args) => args,
    None => {
      os::set_exit_status(1);
      return;
    },
  };

  if args.help { return; }

  let mut screen = Screen::open_screen();
  let mut index = 0;

  let choices = read_choices();
  let visible_choices = min(20, screen.height - 1);

  let mut search: String = args.initial_search.clone();
  let start_line = screen.height - visible_choices - 1;
  loop {
    blank_screen(&mut screen, start_line);
    screen.move_cursor(start_line, 0);
    let matches = matching::compute_matches(&choices, search.as_slice());
    let mut i = 1;
    screen.tty.writeln(format!("> {} ({} choices)", search.as_slice(), choices.len()).as_slice());
    for choice in matches.iter() {
      if i == index + 1 {
        screen.tty.write(ansi::inverse().as_slice());
        screen.tty.writeln(choice.as_slice());
        screen.tty.write(ansi::reset().as_slice());
      } else {
        screen.tty.writeln(choice.as_slice());
      }
      if i >= visible_choices as usize {
        break;
      }
      i += 1;
    }

    match screen.tty.getchar() {
      Char(x) => search.push(x),
      Backspace => { search.pop(); }
      Control('h') => { search.pop(); }
      Control('u') => { search.clear(); }
      Control('c') => { return; }
      Control('n') => { index += 1; }
      Control('p') => { index -= 1; }
      Enter => {
        println!("{}", matches[index]);
        break;
      }
      _ => panic!("Unexpected input"),
    }
  }
}

fn blank_screen(screen: &mut screen::Screen, start_line: u16) {
  screen.move_cursor(start_line, 0);
  let mut i = 0;
  while i < screen.height {
    let mut j = 0;
    while j < screen.width {
      screen.tty.write(" ".as_bytes());
      j += 1;
    }
    i += 1;
  }
}

struct Args {
  initial_search: String,
  help: bool,
}

fn parse_args(args: &Vec<String>) -> Option<Args> {
  let opts = [
    getopts::optflag("h", "help", "Show this message"),
    getopts::optopt("s", "search", "Specify an initial search string", "SEARCH"),
  ];

  let matches = match getopts::getopts(args.tail(), &opts) {
    Ok(m) => m,
    Err(f) => {
      println!("{}", f);
      print_usage(args[0].as_slice(), &opts);
      return None;
    }
  };

  let initial_search = match matches.opt_str("search") {
    Some(x) => x.clone(),
    None => String::from_str(""),
  };

  let help = matches.opt_present("help");
  if help {
    print_usage(args[0].as_slice(), &opts);
  }

  Some(Args { initial_search: initial_search, help: help })
}

fn print_usage(program: &str, opts: &[getopts::OptGroup]) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", getopts::usage(brief.as_slice(), opts));
}

fn read_choices() -> Vec<String> {
  let mut stdin = io::stdio::stdin();
  let mut lines = Vec::new();

  loop {
    match stdin.read_line() {
      Ok(x) => lines.push(String::from_str(x.as_slice().trim())),
      Err(_) => break,
    }
  }

  lines
}
<commit_msg>Draw the cursor in the correct position<commit_after>#![allow(unstable, dead_code)]

extern crate getopts;
extern crate libc;

mod ansi;
mod matching;
mod screen;

use std::os;
use std::io;
use std::cmp::min;
use screen::Screen;
use screen::Key::*;

fn main() {
  let input_args = os::args();

  let args = match parse_args(&input_args) {
    Some(args) => args,
    None => {
      os::set_exit_status(1);
      return;
    },
  };

  if args.help { return; }

  let mut screen = Screen::open_screen();
  let mut index = 0;

  let choices = read_choices();
  let visible_choices = min(20, screen.height - 1);

  let mut search: String = args.initial_search.clone();
  let start_line = screen.height - visible_choices - 1;
  loop {
    blank_screen(&mut screen, start_line);
    screen.move_cursor(start_line, 0);
    let matches = matching::compute_matches(&choices, search.as_slice());
    let mut i = 1;
    screen.tty.writeln(format!("> {} ({} choices)", search.as_slice(), choices.len()).as_slice());
    for choice in matches.iter() {
      if i == index + 1 {
        screen.tty.write(ansi::inverse().as_slice());
        screen.tty.writeln(choice.as_slice());
        screen.tty.write(ansi::reset().as_slice());
      } else {
        screen.tty.writeln(choice.as_slice());
      }
      if i >= visible_choices as usize {
        break;
      }
      i += 1;
    }

    screen.move_cursor(start_line, 2 + search.len() as u16);

    match screen.tty.getchar() {
      Char(x) => search.push(x),
      Backspace => { search.pop(); }
      Control('h') => { search.pop(); }
      Control('u') => { search.clear(); }
      Control('c') => { return; }
      Control('n') => { index += 1; }
      Control('p') => { index -= 1; }
      Enter => {
        screen.move_cursor(start_line + visible_choices, 0);
        println!("{}", matches[index]);
        break;
      }
      _ => panic!("Unexpected input"),
    }
  }
}

fn blank_screen(screen: &mut screen::Screen, start_line: u16) {
  screen.move_cursor(start_line, 0);
  let mut i = 0;
  while i < screen.height {
    let mut j = 0;
    while j < screen.width {
      screen.tty.write(" ".as_bytes());
      j += 1;
    }
    i += 1;
  }
}

struct Args {
  initial_search: String,
  help: bool,
}

fn parse_args(args: &Vec<String>) -> Option<Args> {
  let opts = [
    getopts::optflag("h", "help", "Show this message"),
    getopts::optopt("s", "search", "Specify an initial search string", "SEARCH"),
  ];

  let matches = match getopts::getopts(args.tail(), &opts) {
    Ok(m) => m,
    Err(f) => {
      println!("{}", f);
      print_usage(args[0].as_slice(), &opts);
      return None;
    }
  };

  let initial_search = match matches.opt_str("search") {
    Some(x) => x.clone(),
    None => String::from_str(""),
  };

  let help = matches.opt_present("help");
  if help {
    print_usage(args[0].as_slice(), &opts);
  }

  Some(Args { initial_search: initial_search, help: help })
}

fn print_usage(program: &str, opts: &[getopts::OptGroup]) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", getopts::usage(brief.as_slice(), opts));
}

fn read_choices() -> Vec<String> {
  let mut stdin = io::stdio::stdin();
  let mut lines = Vec::new();

  loop {
    match stdin.read_line() {
      Ok(x) => lines.push(String::from_str(x.as_slice().trim())),
      Err(_) => break,
    }
  }

  lines
}
<|endoftext|>


<commit_before>extern crate rustyline;
use rustyline::Editor;
use rustyline::error::ReadlineError;

fn main() {
    let mut line_editor = Editor::<()>::new();
    loop {
        let line_result = line_editor.readline("> ");
        match line_result {
            Ok(line) => println!("{}", line),
            Err(ReadlineError::Interrupted) => {
                return;
            },
            Err(ReadlineError::Eof) => {
                return;
            },
            Err(err) => {
                eprintln!("{:?}", err);
                return;
            }
        }
    }
}
<commit_msg>add basic support with hardcoded git<commit_after>extern crate rustyline;
use rustyline::Editor;
use rustyline::error::ReadlineError;
use std::process::Command;

fn main() {
    let mut line_editor = Editor::<()>::new();
    loop {
        let line_result = line_editor.readline("> ");
        match line_result {
            Ok(line) => {
                let mut process_result = Command::new("git")
                    .args(line.split(" ").collect::<Vec<&str>>())
                    .spawn();
                match process_result {
                    Ok(mut process) => {
                        let _ = process.wait();
                    },
                    Err(err) => println!("error while trying to run command: {:?}", err),
                }
            },
            Err(ReadlineError::Interrupted) => {
                return;
            },
            Err(ReadlineError::Eof) => {
                return;
            },
            Err(err) => {
                eprintln!("{:?}", err);
                return;
            }
        }
    }
}
<|endoftext|>