<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::{thread_rng, 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) {
    // 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_tx_clone, sreq_tx_clone, connection);
  }
}

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

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

  // 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);

  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>Move use of thread_rng out of subthreads as its only needed by the main thread for naming child threads<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();

  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?

}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate itertools;
extern crate walkdir;
extern crate toml;

use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::process::{exit, Command, Output};
use clap::{App, SubCommand, AppSettings};
use walkdir::{DirEntry, WalkDirIterator};

fn announce(banner: &str) {
    let mut line = String::new();
    for _ in 0..banner.len() {
        line.push('-');
    }
    println!("{}\n{}\n{}", line, banner, line);
}

fn print_ident(buf: Vec<u8>) {
    for line in String::from_utf8_lossy(&buf[..]).lines() {
        println!("        {}", line);
    }
}

fn report_output(output: Output) -> std::process::ExitStatus {
    if output.status.success() {
        print_ident(output.stdout);
    }

    // Always print stderr as warnings from cargo are sent to stderr.
    print_ident(output.stderr);

    // I am still not sure what is more idiomatic - the 'if' above or the 'match' below
    //
    // match output.status.success() {
    //     true => print_ident(output.stdout),
    //     false => print_ident(output.stderr),
    // }
    println!("");

    output.status
}

const CARGO: &'static str = "cargo";
const MIN_DEPTH: usize = 1;
const MAX_DEPTH: usize = 1;

fn main() {

    let matches = App::new(CARGO)
        .bin_name(CARGO)
        .version(crate_version!())
        .about("Run cargo command on multiple crates")
        .setting(AppSettings::SubcommandRequired)
        .setting(AppSettings::ArgRequiredElseHelp)
        .subcommand(SubCommand::with_name("multi")
            .version(crate_version!())
            .setting(AppSettings::ArgRequiredElseHelp)
            .setting(AppSettings::TrailingVarArg)
            .arg_from_usage("<cmd>... 'cargo command to run'"))
        .get_matches();

    let mut cargo_cmd = Command::new(CARGO);
    let mut banner = String::from("Executing ") + CARGO;

    if let Some(arg_cmd) = matches.subcommand_matches("multi")
        .and_then(|m| m.values_of("cmd")) {
        for arg in arg_cmd {
            cargo_cmd.arg(arg);
            banner = banner + " " + arg;
        }
    }

    announce(&banner);
    let is_crate = |e: &DirEntry| e.path().join("Cargo.toml").exists();
    let display_path = |p: &PathBuf| println!("{}:", p.to_string_lossy());
    let execute = |p: PathBuf| cargo_cmd.current_dir(p).output().ok();

    // First check if there is a Cargo.toml file with a workspace section in.
    let mut workspace_members = match File::open("Cargo.toml") {
        Ok(mut file) => {
            let mut toml = String::new();
            match file.read_to_string(&mut toml) {
                Ok(_) => {
                    let value: toml::Value = toml.parse().expect("Failed to parse Cargo.toml");

                    match value.lookup("workspace.members") {
                        Some(members) => {
                            Some(members.as_slice()
                                .expect("Failed to read workspace members")
                                .into_iter()
                                .map(|m| PathBuf::from(m.as_str().unwrap()))
                                .collect::<Vec<_>>())
                        }
                        None => None,
                    }
                }
                Err(_) => None,
            }
        }
        Err(_) => None,
    };

    // If there was no workspace members present, add each crate directory
    // present.
    if workspace_members.is_none() {
        workspace_members = match env::current_dir() {
            Ok(cwd) => {
                Some(walkdir::WalkDir::new(cwd)
                    .min_depth(MIN_DEPTH)
                    .max_depth(MAX_DEPTH)
                    .into_iter()
                    .filter_entry(is_crate)
                    .filter_map(|e| e.ok())
                    .map(|m| m.path().to_path_buf())
                    .collect::<Vec<_>>())
            }
            Err(_) => None,
        }
    }

    let failed_commands = match workspace_members {
        Some(members) => {
            members.into_iter()
                .inspect(display_path)
                .filter_map(execute)
                .map(report_output)
                .filter(|x| !x.success())
                .collect::<Vec<_>>()
        }
        None => Vec::new(),
    };

    // If there are any failed commands, return the error code of the
    // first of them.
    if failed_commands.len() > 0 {
        exit(failed_commands[0].code().unwrap());
    }
}
<commit_msg>Address clippy warning<commit_after>#[macro_use]
extern crate clap;
extern crate itertools;
extern crate walkdir;
extern crate toml;

use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::process::{exit, Command, Output};
use clap::{App, SubCommand, AppSettings};
use walkdir::{DirEntry, WalkDirIterator};

fn announce(banner: &str) {
    let mut line = String::new();
    for _ in 0..banner.len() {
        line.push('-');
    }
    println!("{}\n{}\n{}", line, banner, line);
}

fn print_ident(buf: Vec<u8>) {
    for line in String::from_utf8_lossy(&buf[..]).lines() {
        println!("        {}", line);
    }
}

fn report_output(output: Output) -> std::process::ExitStatus {
    if output.status.success() {
        print_ident(output.stdout);
    }

    // Always print stderr as warnings from cargo are sent to stderr.
    print_ident(output.stderr);

    // I am still not sure what is more idiomatic - the 'if' above or the 'match' below
    //
    // match output.status.success() {
    //     true => print_ident(output.stdout),
    //     false => print_ident(output.stderr),
    // }
    println!("");

    output.status
}

const CARGO: &'static str = "cargo";
const MIN_DEPTH: usize = 1;
const MAX_DEPTH: usize = 1;

fn main() {

    let matches = App::new(CARGO)
        .bin_name(CARGO)
        .version(crate_version!())
        .about("Run cargo command on multiple crates")
        .setting(AppSettings::SubcommandRequired)
        .setting(AppSettings::ArgRequiredElseHelp)
        .subcommand(SubCommand::with_name("multi")
            .version(crate_version!())
            .setting(AppSettings::ArgRequiredElseHelp)
            .setting(AppSettings::TrailingVarArg)
            .arg_from_usage("<cmd>... 'cargo command to run'"))
        .get_matches();

    let mut cargo_cmd = Command::new(CARGO);
    let mut banner = String::from("Executing ") + CARGO;

    if let Some(arg_cmd) = matches.subcommand_matches("multi")
        .and_then(|m| m.values_of("cmd")) {
        for arg in arg_cmd {
            cargo_cmd.arg(arg);
            banner = banner + " " + arg;
        }
    }

    announce(&banner);
    let is_crate = |e: &DirEntry| e.path().join("Cargo.toml").exists();
    let display_path = |p: &PathBuf| println!("{}:", p.to_string_lossy());
    let execute = |p: PathBuf| cargo_cmd.current_dir(p).output().ok();

    // First check if there is a Cargo.toml file with a workspace section in.
    let mut workspace_members = match File::open("Cargo.toml") {
        Ok(mut file) => {
            let mut toml = String::new();
            match file.read_to_string(&mut toml) {
                Ok(_) => {
                    let value: toml::Value = toml.parse().expect("Failed to parse Cargo.toml");

                    match value.lookup("workspace.members") {
                        Some(members) => {
                            Some(members.as_slice()
                                .expect("Failed to read workspace members")
                                .into_iter()
                                .map(|m| PathBuf::from(m.as_str().unwrap()))
                                .collect::<Vec<_>>())
                        }
                        None => None,
                    }
                }
                Err(_) => None,
            }
        }
        Err(_) => None,
    };

    // If there was no workspace members present, add each crate directory
    // present.
    if workspace_members.is_none() {
        workspace_members = match env::current_dir() {
            Ok(cwd) => {
                Some(walkdir::WalkDir::new(cwd)
                    .min_depth(MIN_DEPTH)
                    .max_depth(MAX_DEPTH)
                    .into_iter()
                    .filter_entry(is_crate)
                    .filter_map(|e| e.ok())
                    .map(|m| m.path().to_path_buf())
                    .collect::<Vec<_>>())
            }
            Err(_) => None,
        }
    }

    let failed_commands = match workspace_members {
        Some(members) => {
            members.into_iter()
                .inspect(display_path)
                .filter_map(execute)
                .map(report_output)
                .filter(|x| !x.success())
                .collect::<Vec<_>>()
        }
        None => Vec::new(),
    };

    // If there are any failed commands, return the error code of the
    // first of them.
    if !failed_commands.is_empty() {
        exit(failed_commands[0].code().unwrap());
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(rocket_codegen)]
#[macro_use]
extern crate error_chain;
extern crate reqwest;
extern crate rocket;
extern crate toml;
extern crate semver;

use std::collections::HashMap;
use std::fmt::{self, Display, Formatter};
use std::fs::{self, File};
use std::io::{self, Read, Write};
use std::process;

use rocket::request::FromParam;
use semver::Version;

mod errors {
    // Create the Error, ErrorKind, ResultExt, and Result types
    error_chain! { }
}

use errors::ResultExt;

#[derive(Eq, PartialEq, PartialOrd, Ord)]
enum Status {
    OutOfDate,
    UpToDate,
    Unknown,
}

impl Display for Status {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Status::Unknown => write!(f, "unknown"),
            Status::OutOfDate => write!(f, "outofdate"),
            Status::UpToDate => write!(f, "uptodate"),
        }
    }
}

struct MyParam<'r> {
    deps_type: &'r str,
    ext: &'r str,
}

impl<'r> FromParam<'r> for MyParam<'r> {
    type Error = &'r str;

    fn from_param(param: &'r str) -> Result<MyParam<'r>, &'r str> {
        let (status_type, ext) = match param.find('.') {
            Some(i) if i > 0 => (&param[..i], &param[(i + 1)..]),
            _ => return Err(param),
        };

        Ok(MyParam {
            deps_type: match status_type {
                "dev-status" => "dev-dependencies",
                "status" => "dependencies",
                _ => return Err(param),
            },
            ext: match ext {
                "png" => "png",
                "svg" => "svg",
                _ => return Err(param),
            },
        })
    }
}

#[get("/<owner>/<name>/<params>")]
fn index(owner: &str, name: &str, params: MyParam) -> io::Result<File> {
    // TODO: HEADER 'Cache-Control': 'no-cache, no-store, must-revalidate',
    // TODO: HEADER 'Expires': new Date().toUTCString()
    let status = match get_deps_status(owner, name, params.deps_type) {
        Ok(status) => status,
        Err(ref e) => {
            println!("error: {}", e);

            for e in e.iter().skip(1) {
                println!("caused by: {}", e);
            }

            // The backtrace is not always generated. Try to run this example
            // with `RUST_BACKTRACE=1`.
            if let Some(backtrace) = e.backtrace() {
                println!("backtrace: {:?}", backtrace);
            }
            Status::Unknown
        }
    };
    File::open(format!("public/img/status/{}.{}", status, params.ext))
}

fn get_deps_status(owner: &str, name: &str, deps_type: &str) -> errors::Result<Status> {
    let cargo_url = format!("https://raw.githubusercontent.com/{}/{}/master/Cargo.toml",
                            owner,
                            name);

    let mut resp = reqwest::get(cargo_url.as_str())
        .chain_err(|| "Unable to download Cargo.toml")?;

    let mut buffer = String::new();
    match resp.status() {
        &reqwest::StatusCode::Ok => {
            resp.read_to_string(&mut buffer)
                .chain_err(|| "Unable to read Cargo.toml body")?;
            deps_status_from_cargo(owner, name, buffer, deps_type)
                .chain_err(|| "Unable verify status")
        },
        _ => {
            bail!("Bad status code retreiving Cargo.toml");
        }
    }
}

fn deps_status_from_cargo(owner: &str, name: &str, cargo: String, deps_type: &str) -> errors::Result<Status> {

    let root = cargo.as_str()
        .parse::<toml::Value>()
        .chain_err(|| "Unable to parse manifest")?;

    let dependencies = match root.get(deps_type)
        .and_then(|val| val.as_table()) {
        Some(dependencies) => dependencies,
        None => return Ok(Status::UpToDate),
    };

    // 1- Download the Cargo.toml of the project into /tmp/owner/name/Cargo.toml
    // 2- Create a dummy /tmp/owner/name/src/lib.rs (avoid `cargo update` complaint)
    let tmp_dir = format!("/tmp/{}/{}", owner, name);
    let tmp_manifest = format!("{}/Cargo.toml", tmp_dir);
    let tmp_lockfile = format!("{}/Cargo.lock", tmp_dir);
    let tmp_src_dir = format!("{}/src", tmp_dir);
    let tmp_src_lib = format!("{}/lib.rs", tmp_src_dir);

    fs::create_dir_all(tmp_src_dir.as_str())
        .and_then(|_| File::create(tmp_manifest.as_str()))
        .and_then(|mut file| file.write_all(cargo.as_bytes()))
        .and_then(|_| File::create(tmp_src_lib.as_str()))
        .chain_err(|| "Unable to create tmp file structure")?;

    // 3- `cargo update --manifest-path /tmp/owner/name/Cargo.toml`
    process::Command::new("cargo")
        .arg("update")
        .arg("--manifest-path")
        .arg(tmp_manifest.as_str())
        .output()
        .chain_err(|| "Unable to exec cargo update")?;

    // 4- Parse the /tmp/owner/name/Cargo.lock generated
    let mut buffer = String::new();
    File::open(tmp_lockfile)
        .and_then(|mut f| f.read_to_string(&mut buffer))
        .chain_err(|| "Unable to read Cargo.lock")?;

    let tmp_root_lockfile = buffer.as_str()
        .parse::<toml::Value>()
        .chain_err(|| "Unable to parse Cargo.lock")?;

    let tmp_root_table = match tmp_root_lockfile.get("root") {
        Some(root) => root,
        None => bail!("Unable to find root in lockfile"),
    };

    let updated_raw_deps = match tmp_root_table.get("dependencies") {
        Some(&toml::Value::Array(ref raw_deps)) => raw_deps,
        Some(_) => unreachable!(),
        None => bail!("Unable to find dependencies in lockfile"),
    };

    let mut updated_deps = HashMap::new();
    for updated_raw_dep in updated_raw_deps {
        let raw_dep_vec: Vec<_> = updated_raw_dep.as_str().unwrap_or("").split(' ').collect();
        if raw_dep_vec.len() < 2 {
            bail!("Invalid dependency found");
        }
        updated_deps.insert(raw_dep_vec[0], raw_dep_vec[1]);
    }

    // 5- Compare each deps with semver
    let status = dependencies.iter().fold(Status::UpToDate, |oldest, (dep, version_value)| {
        let updated_version = match updated_deps.get::<str>(&dep.to_string()) {
            Some(updated_version) => updated_version,
            None => unreachable!(),
        };
        let version = match version_value.as_str() {
            Some(version) => version,
            None => return oldest,
        };

        if Version::parse(updated_version) > Version::parse(version) {
            Status::OutOfDate
        } else if Status::OutOfDate == oldest {
            Status::OutOfDate
        } else {
            Status::UpToDate
        }
    });

    Ok(status)
}

fn main() {
    rocket::ignite().mount("/", routes![index]).launch();
}
<commit_msg>Add outdated package info<commit_after>#![feature(plugin)]
#![plugin(rocket_codegen)]
#[macro_use]
extern crate error_chain;
extern crate reqwest;
extern crate rocket;
extern crate toml;
extern crate semver;

use std::collections::HashMap;
use std::fmt::{self, Display, Formatter};
use std::fs::{self, File};
use std::io::{self, Read, Write};
use std::process;

use rocket::request::FromParam;
use semver::Version;

mod errors {
    // Create the Error, ErrorKind, ResultExt, and Result types
    error_chain! { }
}

use errors::ResultExt;

#[derive(Eq, PartialEq, PartialOrd, Ord)]
enum Status {
    OutOfDate,
    UpToDate,
    Unknown,
}

impl Display for Status {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Status::Unknown => write!(f, "unknown"),
            Status::OutOfDate => write!(f, "outofdate"),
            Status::UpToDate => write!(f, "uptodate"),
        }
    }
}

struct MyParam<'r> {
    deps_type: &'r str,
    ext: &'r str,
}

impl<'r> FromParam<'r> for MyParam<'r> {
    type Error = &'r str;

    fn from_param(param: &'r str) -> Result<MyParam<'r>, &'r str> {
        let (status_type, ext) = match param.find('.') {
            Some(i) if i > 0 => (&param[..i], &param[(i + 1)..]),
            _ => return Err(param),
        };

        Ok(MyParam {
            deps_type: match status_type {
                "dev-status" => "dev-dependencies",
                "status" => "dependencies",
                _ => return Err(param),
            },
            ext: match ext {
                "png" => "png",
                "svg" => "svg",
                _ => return Err(param),
            },
        })
    }
}

#[get("/<owner>/<name>/<params>")]
fn index(owner: &str, name: &str, params: MyParam) -> io::Result<File> {
    // TODO: HEADER 'Cache-Control': 'no-cache, no-store, must-revalidate',
    // TODO: HEADER 'Expires': new Date().toUTCString()
    let status = match get_deps_status(owner, name, params.deps_type) {
        Ok(status) => status,
        Err(ref e) => {
            println!("error: {}", e);

            for e in e.iter().skip(1) {
                println!("caused by: {}", e);
            }

            // The backtrace is not always generated. Try to run this example
            // with `RUST_BACKTRACE=1`.
            if let Some(backtrace) = e.backtrace() {
                println!("backtrace: {:?}", backtrace);
            }
            Status::Unknown
        }
    };
    File::open(format!("public/img/status/{}.{}", status, params.ext))
}

fn get_deps_status(owner: &str, name: &str, deps_type: &str) -> errors::Result<Status> {
    let cargo_url = format!("https://raw.githubusercontent.com/{}/{}/master/Cargo.toml",
                            owner,
                            name);

    let mut resp = reqwest::get(cargo_url.as_str())
        .chain_err(|| "Unable to download Cargo.toml")?;

    let mut buffer = String::new();
    match resp.status() {
        &reqwest::StatusCode::Ok => {
            resp.read_to_string(&mut buffer)
                .chain_err(|| "Unable to read Cargo.toml body")?;
            deps_status_from_cargo(owner, name, buffer, deps_type)
                .chain_err(|| "Unable verify status")
        },
        _ => {
            bail!("Bad status code retreiving Cargo.toml");
        }
    }
}

fn deps_status_from_cargo(owner: &str, name: &str, cargo: String, deps_type: &str) -> errors::Result<Status> {

    let root = cargo.as_str()
        .parse::<toml::Value>()
        .chain_err(|| "Unable to parse manifest")?;

    let dependencies = match root.get(deps_type)
        .and_then(|val| val.as_table()) {
        Some(dependencies) => dependencies,
        None => return Ok(Status::UpToDate),
    };

    // 1- Download the Cargo.toml of the project into /tmp/owner/name/Cargo.toml
    // 2- Create a dummy /tmp/owner/name/src/lib.rs (avoid `cargo update` complaint)
    let tmp_dir = format!("/tmp/{}/{}", owner, name);
    let tmp_manifest = format!("{}/Cargo.toml", tmp_dir);
    let tmp_lockfile = format!("{}/Cargo.lock", tmp_dir);
    let tmp_src_dir = format!("{}/src", tmp_dir);
    let tmp_src_lib = format!("{}/lib.rs", tmp_src_dir);

    fs::create_dir_all(tmp_src_dir.as_str())
        .and_then(|_| File::create(tmp_manifest.as_str()))
        .and_then(|mut file| file.write_all(cargo.as_bytes()))
        .and_then(|_| File::create(tmp_src_lib.as_str()))
        .chain_err(|| "Unable to create tmp file structure")?;

    // 3- `cargo update --manifest-path /tmp/owner/name/Cargo.toml`
    process::Command::new("cargo")
        .arg("update")
        .arg("--manifest-path")
        .arg(tmp_manifest.as_str())
        .output()
        .chain_err(|| "Unable to exec cargo update")?;

    // 4- Parse the /tmp/owner/name/Cargo.lock generated
    let mut buffer = String::new();
    File::open(tmp_lockfile)
        .and_then(|mut f| f.read_to_string(&mut buffer))
        .chain_err(|| "Unable to read Cargo.lock")?;

    let tmp_root_lockfile = buffer.as_str()
        .parse::<toml::Value>()
        .chain_err(|| "Unable to parse Cargo.lock")?;

    let tmp_root_table = match tmp_root_lockfile.get("root") {
        Some(root) => root,
        None => bail!("Unable to find root in lockfile"),
    };

    let updated_raw_deps = match tmp_root_table.get("dependencies") {
        Some(&toml::Value::Array(ref raw_deps)) => raw_deps,
        Some(_) => unreachable!(),
        None => bail!("Unable to find dependencies in lockfile"),
    };

    let mut updated_deps = HashMap::new();
    for updated_raw_dep in updated_raw_deps {
        let raw_dep_vec: Vec<_> = updated_raw_dep.as_str().unwrap_or("").split(' ').collect();
        if raw_dep_vec.len() < 2 {
            bail!("Invalid dependency found");
        }
        updated_deps.insert(raw_dep_vec[0], raw_dep_vec[1]);
    }

    // 5- Compare each deps with semver
    let status = dependencies.iter().fold(Status::UpToDate, |oldest, (dep, version_value)| {
        let updated_version = match updated_deps.get::<str>(&dep.to_string()) {
            Some(updated_version) => updated_version,
            None => unreachable!(),
        };
        let version = match version_value.as_str() {
            Some(version) => version,
            None => return oldest,
        };

        if Version::parse(updated_version) > Version::parse(version) {
            println!("{} is outdated", dep);
            println!("Specified: {}", version);
            println!("Latest: {}", updated_version);
            println!("");
            Status::OutOfDate
        } else if Status::OutOfDate == oldest {
            Status::OutOfDate
        } else {
            Status::UpToDate
        }
    });

    Ok(status)
}

fn main() {
    rocket::ignite().mount("/", routes![index]).launch();
}
<|endoftext|>


<commit_before>#![feature(plugin)]

#![plugin(clippy)]

extern crate iron;
extern crate router;
extern crate params;

use std::process::Command;
use std::path::Path;

use iron::prelude::*;
use router::{Router};
use iron::status;
use iron::{BeforeMiddleware};

mod hash;

#[macro_use]
mod config;

use hash::sha1sum;

use param_rules::{RequiredParam, get_param};

macro_rules! status_from_io_err {
  ($why:expr) => (

    match $why.kind() {
      std::io::ErrorKind::NotFound => status::NotFound,
      std::io::ErrorKind::PermissionDenied => status::Forbidden,
      _ => status::InternalServerError,
    }

  )
}

#[allow(needless_return)]
fn submit_form_file(req: &mut Request) -> IronResult<Response> {

  use std::fs::{rename,File};

  // thanks to BeforeMiddleware rules we can do unwrap safely
  let passed_sha1sum = &get_param::<String>(req,"sha1sum");
  let file_param = get_param::<params::File>(req, "filename");

  let file_path = format!("{}.ods" , file_param.path().display());

  match rename(&file_param.path(), &file_path) {
    Err(why) => {
      let status = status_from_io_err!(why);
      Ok(Response::with((status, format!("failed to elaborate the received file: {}", why))))
    },
    Ok(_) => {
      println!("elaborating the file {}", file_path);

      match File::open(&file_path) {
        Err(why) => {
          let status = status_from_io_err!(why);
          Ok(Response::with((status, format!("failed to opening the received file: {}", why))))
        },
        Ok(ref mut file) => {
          let calculated_sha1sum = &sha1sum(file);
          if calculated_sha1sum != passed_sha1sum {
            let msg = format!(
              "the sha1sum '{}' calculate for the file {} doesn't correspond to the parameter '{}'",
              calculated_sha1sum, file_path, passed_sha1sum
            );
            return Ok(Response::with((status::PreconditionRequired, msg)))
          }

          let conf = config::get_config();

          // run the cmd
          let working_dir = format!("{}",file_param.path().parent().expect("cannot access the directory of the uploaded temporary file").display());
          let cmd_name = get_config_parameter!(conf,"transformation.cmd");
          let cmd_name = cmd_name.replace("{working_dir}", &working_dir);
          let cmd_name = cmd_name.replace("{file}", &file_path);

          println!("cmd: {}", cmd_name);

          let mut cmd_args: Vec<&str> = cmd_name.split_whitespace()
                                            .collect();

          let res = Command::new(cmd_args.remove(0))
                            .args(&cmd_args)
                            .status()
                            .unwrap_or_else(|e| {
                              println!("failed to execute process: {}", e);
                              panic!("failed to execute process: {}", e);
                            });

          println!("process exited with: {}", res);

          if res.success() {
            let ref file_path = format!("{}.pdf" , file_param.path().display());
            println!("file_path: {}", file_path);
            return Ok(Response::with((status::Ok, Path::new(file_path))))
          }
          else {
            // copy the file in an error dirctory for further investigations
            let err_destination_dir = get_config_parameter!(conf,"transformation.err_destination_dir");
            //             let res = Command::new("mkdir")
            //                               .args(&["-p",err_destination_dir])
            //                               .status()
            //                               .unwrap_or_else(|e| {
            //                                 println!("failed to execute process: {}", e);
            //                                 panic!("failed to execute process: {}", e);
            //                               });

            Command::new("nice")
            .args(&["-n","20","cp","-R", &working_dir, err_destination_dir])
            .spawn()
            .unwrap_or_else(|e| {
              println!("failed to execute process: {}", e);
              panic!("failed to execute process: {}", e);
            });

            return Ok(Response::with(status::InternalServerError))

          }

        }
      }
    }
  }

}

#[macro_use]
mod param_rules;

require_param!(RequireMd5sumParam, "sha1sum", String);
require_param!(RequireFileParam, "filename", params::File);

mod logger;

use logger::Logger;
use iron::{AroundMiddleware};
fn start_server() {
   let mut router = Router::new();
   let mut chain_form_file = Chain::new(submit_form_file);

   chain_form_file.link_before(RequireMd5sumParam);
   chain_form_file.link_before(RequireFileParam);

   router.post("/openact", Logger::default().around(Box::new(chain_form_file)));

   println!("started server at http://localhost:3000/");
   Iron::new(router).http("localhost:3000").unwrap();
}

fn main() {
    println!("Hello, world!");
    start_server();
}
<commit_msg>error rinominating the file .ods instead of .odt<commit_after>#![feature(plugin)]

#![plugin(clippy)]

extern crate iron;
extern crate router;
extern crate params;

use std::process::Command;
use std::path::Path;

use iron::prelude::*;
use router::{Router};
use iron::status;
use iron::{BeforeMiddleware};

mod hash;

#[macro_use]
mod config;

use hash::sha1sum;

use param_rules::{RequiredParam, get_param};

macro_rules! status_from_io_err {
  ($why:expr) => (

    match $why.kind() {
      std::io::ErrorKind::NotFound => status::NotFound,
      std::io::ErrorKind::PermissionDenied => status::Forbidden,
      _ => status::InternalServerError,
    }

  )
}

#[allow(needless_return)]
fn submit_form_file(req: &mut Request) -> IronResult<Response> {

  use std::fs::{rename,File};

  // thanks to BeforeMiddleware rules we can do unwrap safely
  let passed_sha1sum = &get_param::<String>(req,"sha1sum");
  let file_param = get_param::<params::File>(req, "filename");

  let file_path = format!("{}.odt" , file_param.path().display());

  match rename(&file_param.path(), &file_path) {
    Err(why) => {
      let status = status_from_io_err!(why);
      Ok(Response::with((status, format!("failed to elaborate the received file: {}", why))))
    },
    Ok(_) => {
      println!("elaborating the file {}", file_path);

      match File::open(&file_path) {
        Err(why) => {
          let status = status_from_io_err!(why);
          Ok(Response::with((status, format!("failed to opening the received file: {}", why))))
        },
        Ok(ref mut file) => {
          let calculated_sha1sum = &sha1sum(file);
          if calculated_sha1sum != passed_sha1sum {
            let msg = format!(
              "the sha1sum '{}' calculate for the file {} doesn't correspond to the parameter '{}'",
              calculated_sha1sum, file_path, passed_sha1sum
            );
            return Ok(Response::with((status::PreconditionRequired, msg)))
          }

          let conf = config::get_config();

          // run the cmd
          let working_dir = format!("{}",file_param.path().parent().expect("cannot access the directory of the uploaded temporary file").display());
          let cmd_name = get_config_parameter!(conf,"transformation.cmd");
          let cmd_name = cmd_name.replace("{working_dir}", &working_dir);
          let cmd_name = cmd_name.replace("{file}", &file_path);

          println!("cmd: {}", cmd_name);

          let mut cmd_args: Vec<&str> = cmd_name.split_whitespace()
                                            .collect();

          let res = Command::new(cmd_args.remove(0))
                            .args(&cmd_args)
                            .status()
                            .unwrap_or_else(|e| {
                              println!("failed to execute process: {}", e);
                              panic!("failed to execute process: {}", e);
                            });

          println!("process exited with: {}", res);

          if res.success() {
            let ref file_path = format!("{}.pdf" , file_param.path().display());
            println!("file_path: {}", file_path);
            return Ok(Response::with((status::Ok, Path::new(file_path))))
          }
          else {
            // copy the file in an error dirctory for further investigations
            let err_destination_dir = get_config_parameter!(conf,"transformation.err_destination_dir");
            //             let res = Command::new("mkdir")
            //                               .args(&["-p",err_destination_dir])
            //                               .status()
            //                               .unwrap_or_else(|e| {
            //                                 println!("failed to execute process: {}", e);
            //                                 panic!("failed to execute process: {}", e);
            //                               });

            Command::new("nice")
            .args(&["-n","20","cp","-R", &working_dir, err_destination_dir])
            .spawn()
            .unwrap_or_else(|e| {
              println!("failed to execute process: {}", e);
              panic!("failed to execute process: {}", e);
            });

            return Ok(Response::with(status::InternalServerError))

          }

        }
      }
    }
  }

}

#[macro_use]
mod param_rules;

require_param!(RequireMd5sumParam, "sha1sum", String);
require_param!(RequireFileParam, "filename", params::File);

mod logger;

use logger::Logger;
use iron::{AroundMiddleware};
fn start_server() {
   let mut router = Router::new();
   let mut chain_form_file = Chain::new(submit_form_file);

   chain_form_file.link_before(RequireMd5sumParam);
   chain_form_file.link_before(RequireFileParam);

   router.post("/openact", Logger::default().around(Box::new(chain_form_file)));

   println!("started server at http://localhost:3000/");
   Iron::new(router).http("localhost:3000").unwrap();
}

fn main() {
    println!("Hello, world!");
    start_server();
}
<|endoftext|>


<commit_before>extern crate lp_modeler;

use lp_modeler::operations::LpOperations;
use lp_modeler::problem::{Problem, LpProblem, LpObjective, LpFileFormat};
use lp_modeler::solvers::*;
use lp_modeler::variables::{LpInteger};

fn main() {

    let ref a = LpInteger::new("a");
    let ref b = LpInteger::new("b");
    let ref c = LpInteger::new("c");

    let mut problem = LpProblem::new("Problem", LpObjective::Maximize);

    problem += 10.0 * a + 20.0 * b;

    problem += (500*a + 1200*b + 1500*c).le(10000);
    problem += (a + b*2 + c).le(10);
    problem += (a).le(b);

    // Suboptimal instead of unfeasible
    // problem += (a+b).equal(10);

    // let solver = GurobiSolver
    // solver <<= BaseDirectory("/opt/gurobi1.2/...")
    // solver <<= Config().arg("-thread 2").arg("...")
    // let _ = problem.write_lp("toto.lp");

    // let solver = GurobiSolver::new();
    // solver.run(problem);

    match problem.solve(GurobiSolver::new()) {
        Ok((status, res)) => {
            println!("Status {:?}", status);
            for (name, value) in res.iter() {
                println!("value of {} = {}", name, value);
            }
        },
        Err(msg) => println!("{}", msg),
    }

}
<commit_msg>Change semantic<commit_after>extern crate lp_modeler;

use lp_modeler::operations::LpOperations;
use lp_modeler::problem::{LpProblem, LpObjective, LpFileFormat};
use lp_modeler::solvers::*;
use lp_modeler::variables::{LpInteger};

fn main() {

    let ref a = LpInteger::new("a");
    let ref b = LpInteger::new("b");
    let ref c = LpInteger::new("c");

    let mut problem = LpProblem::new("Problem", LpObjective::Maximize);

    problem += 10.0 * a + 20.0 * b;

    problem += (500*a + 1200*b + 1500*c).le(10000);
    problem += (a + b*2 + c).le(10);
    problem += (a).le(b);

    // Suboptimal instead of unfeasible
    // problem += (a+b).equal(10);

    // let solver = GurobiSolver
    // solver <<= BaseDirectory("/opt/gurobi1.2/...")
    // solver <<= Config().arg("-thread 2").arg("...")

    let _ = problem.write_lp("toto.lp");

    let solver = GurobiSolver::new();

    match solver.run(&problem) {
        Ok((status, res)) => {
            println!("Status {:?}", status);
            for (name, value) in res.iter() {
                println!("value of {} = {}", name, value);
            }
        },
        Err(msg) => println!("{}", msg),
    }

}
<|endoftext|>


<commit_before>#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(daedalos::test_runner)]
#![reexport_test_harness_main = "test_main"]

extern crate alloc;

use daedalos::println;

bootloader::entry_point!(kernel_main);

fn kernel_main(boot_info: &'static bootloader::BootInfo) -> ! {
    println!("DaedalOS");
    daedalos::init(boot_info);

    #[cfg(test)]
    test_main();

    println!("It did not crash!");
    daedalos::hlt_loop()
}

#[cfg(not(test))]
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
    daedalos::println!("{}", info);
    daedalos::hlt_loop()
}

#[cfg(test)]
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! { daedalos::test_panic_handler(info) }
<commit_msg>main: no extern crate alloc<commit_after>#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(daedalos::test_runner)]
#![reexport_test_harness_main = "test_main"]

use daedalos::println;

bootloader::entry_point!(kernel_main);

fn kernel_main(boot_info: &'static bootloader::BootInfo) -> ! {
    println!("DaedalOS");
    daedalos::init(boot_info);

    #[cfg(test)]
    test_main();

    println!("It did not crash!");
    daedalos::hlt_loop()
}

#[cfg(not(test))]
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
    daedalos::println!("{}", info);
    daedalos::hlt_loop()
}

#[cfg(test)]
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! { daedalos::test_panic_handler(info) }
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
#[macro_use]
extern crate nom;

extern crate bip_bencode;
extern crate bip_metainfo;
extern crate bip_utracker;
extern crate chrono;
extern crate hyper;
extern crate pnet;
extern crate pnet_macros_support;
extern crate pretty_env_logger;
extern crate rustyline;
extern crate url;

mod packet;

use bip_bencode::Bencode;
use bip_metainfo::MetainfoFile;
use bip_utracker::contact::CompactPeersV4;
use chrono::{TimeZone, UTC};
use hyper::Client;
use hyper::header::Connection;
use packet::peer_pkt::MutablePeerHandshakePacket;
use pnet::packet::Packet;
use rustyline::Editor;
use rustyline::error::ReadlineError;
use url::Url;

use std::fs::File;
use std::io::prelude::*;
use std::net::TcpStream;
use std::str;
use std::string::String;
use std::time::{SystemTime, UNIX_EPOCH};
use std::thread;

const HISTORY_FILE: &'static str = ".rustyline.history";
const PEER_HANDSHAKE_STRUCT_SZ: usize = 68;

/// Print file list from the torrent.
fn print_files(bytes: &[u8]) {
    let metainfo = MetainfoFile::from_bytes(bytes).unwrap();
    let info = metainfo.info();

    println!("File List:");
    println!("Size (bytes)\tPath");
    println!("------------\t----------------------------------------------");
    for file in info.files() {
        println!("{:12}\t{}",
                 file.length(),
                 file.paths().next().unwrap_or("<unknown>"));
    }
}

/// Print general information about the torrent.
fn print_metainfo_overview(bytes: &[u8]) {
    let metainfo = MetainfoFile::from_bytes(bytes).unwrap();
    let info = metainfo.info();
    let info_hash_hex = metainfo.info_hash()
        .as_ref()
        .iter()
        .map(|b| format!("{:02x}", b))
        .fold(String::new(), |mut acc, nex| {
            acc.push_str(&nex);
            acc
        });
    let utc_creation_date = metainfo.creation_date().map(|c| UTC.timestamp(c, 0));

    println!("------Metainfo File Overview-----");

    println!("InfoHash: {}", info_hash_hex);
    println!("Main Tracker: {:?}", metainfo.main_tracker());
    println!("Comment: {:?}", metainfo.comment());
    println!("Creator: {:?}", metainfo.created_by());
    println!("Creation Date: {:?}", utc_creation_date);

    println!("Directory: {:?}", info.directory());
    println!("Piece Length: {:?}", info.piece_length());
    println!("Number Of Pieces: {}", info.pieces().count());
    println!("Number Of Files: {}", info.files().count());
    println!("Total File Size: {}\n",
             info.files().fold(0, |acc, nex| acc + nex.length()));

    print_files(bytes);
}

fn connect_to_tracker(metainfo: MetainfoFile,
                      peer_id: &str,
                      port: u16)
                      -> Result<(Vec<String>, String), String> {
    debug!("connecting to tracker: {:?}", metainfo.main_tracker());

    let info_hash = metainfo.info_hash();
    // TODO figure can this conversion to url-encoded form be done safely?
    let info_hash_str = unsafe { str::from_utf8_unchecked(info_hash.as_ref()) };

    // TODO this needs to be calculated based on what we have
    let total_len = metainfo.info().files().fold(0, |acc, nex| acc + nex.length());

    let mut url = Url::parse(metainfo.main_tracker().unwrap()).unwrap();
    url.query_pairs_mut()
        .append_pair("info_hash", info_hash_str)
        .append_pair("peer_id", peer_id)
        .append_pair("port", &(port.to_string()))
        // TODO parametrize this
        .append_pair("uploaded", "0")
        // TODO parametrize this
        .append_pair("downloaded", "0")
        // TODO see note on total_len above
        .append_pair("left", &(total_len.to_string()))
        .append_pair("compact", "1")
        .append_pair("event", "started")
        .append_pair("supportcrypto", "0");
    trace!("URL {:?}", url);

    let client = Client::new();
    let mut res = client.get(url).header(Connection::close()).send().unwrap();
    let mut buffer = Vec::new();
    res.read_to_end(&mut buffer).unwrap();
    debug!("{:?}", res);
    let bencode = Bencode::decode(&buffer).unwrap();
    trace!("{:?}", bencode);
    let (_, peers) = CompactPeersV4::from_bytes(bencode.dict()
            .unwrap()
            .lookup("peers")
            .unwrap()
            .bytes()
            .unwrap())
        .unwrap();
    trace!("{:?}", peers);
    let mut ip_ports: Vec<String> = Vec::new();
    for peer in peers.iter() {
        debug!("{:?}", peer);
        ip_ports.push(peer.to_string());
    }
    Ok((ip_ports, info_hash_str.to_string()))
}

fn peer_connections(peer_ip_ports: Vec<String>,
                    info_hash: &str,
                    peer_id: &str)
                    -> Result<(), String> {
    for peer_ip_port in peer_ip_ports {
        let peer_ip_port_cl = peer_ip_port.clone();
        let info_hash_cl = info_hash.to_string().clone();
        let peer_id_cl = peer_id.to_string().clone();
        thread::spawn(move || {
            match handshake_peer(&peer_ip_port_cl, &info_hash_cl, &peer_id_cl) {
                Ok(mut stream) => {
                    debug!("Communicating with peer {:?}", peer_ip_port_cl);
                    let mut buff = [0; 128];
                    let bytes_read = stream.read(&mut buff).unwrap();
                    debug!("Bytes read: {:?}", bytes_read);
                    debug!("Resp: {:?}", buff.to_vec());
                }
                Err(_) => {
                    error!("Closing thread with peer {:?}", peer_ip_port_cl);
                }
            }
        });
    }
    Ok(())
}

fn handshake_peer(peer_ip_port: &str, info_hash: &str, peer_id: &str) -> Result<TcpStream, String> {
    let mut buf = vec![0u8; PEER_HANDSHAKE_STRUCT_SZ];
    let mut ph = MutablePeerHandshakePacket::new(&mut buf).unwrap();
    ph.set_pstrlen("BitTorrent protocol".len() as u8);
    ph.set_pstr(&String::from("BitTorrent protocol").into_bytes());
    ph.set_reserved(&[0; 8]);
    ph.set_info_hash(info_hash.as_bytes());
    ph.set_peer_id(peer_id.as_bytes());
    match TcpStream::connect(peer_ip_port) {
        Ok(mut stream) => {
            debug!("Connection to peer {:?} successful!", peer_ip_port);
            match stream.write(ph.packet()) {
                Ok(_) => {
                    debug!("Sending message successful!");
                    debug!("{:?}", ph.packet());
                    Ok((stream))
                }
                Err(_) => {
                    error!("Sending message failed!");
                    Err("Sending message failed!".to_owned())
                }
            }
        }
        Err(e) => {
            error!("Connection to peer {:?} failed! {:?}", peer_ip_port, e);
            Err("Connection to peer failed!".to_owned())
        }
    }
}

fn main() {
    pretty_env_logger::init();

    let mut rl = Editor::<()>::new();
    if rl.load_history(HISTORY_FILE).is_err() {
        info!("No previous history!");
    }

    let now = SystemTime::now();
    let duration = now.duration_since(UNIX_EPOCH).unwrap();
    let client_id = "-bittorrent-rs-".to_owned() + &format!("{}", duration.as_secs() % 100_000);

    loop {
        let readline = rl.readline("> ");
        match readline {
            Ok(cmd) => {
                rl.add_history_entry(&cmd);

                let cmd = cmd.trim().split(' ').collect::<Vec<&str>>();

                match cmd[0] {
                    "help" | "h" => {
                        println!("Commands:
parse/p <torrent file path>      - show Metainfo File Overview
connect/c <torrent file path>    - initiate connection to tracker, and handshake with peers
showfiles/sf <torrent file path> - show files in the torrent
help/h                           - show this help");
                    }
                    "parse" | "p" => {
                        if cmd.len() != 2 {
                            error!("usage: parse <torrent file>");
                        } else {
                            let path = cmd[1];
                            debug!("MetainfoFile: {:?}", path);

                            match File::open(path) {
                                Ok(mut f) => {
                                    let mut bytes: Vec<u8> = Vec::new();
                                    f.read_to_end(&mut bytes).unwrap();
                                    print_metainfo_overview(&bytes);
                                }
                                Err(e) => error!("{:?}", e),
                            }
                        }
                    }
                    "connect" | "c" => {
                        if cmd.len() != 2 {
                            error!("usage: connect <torrent file>");
                        } else {
                            let path = cmd[1];
                            match File::open(path) {
                                Ok(mut f) => {
                                    let mut bytes: Vec<u8> = Vec::new();
                                    f.read_to_end(&mut bytes).unwrap();

                                    // TODO: generate peer ID
                                    let result =
                                        connect_to_tracker(MetainfoFile::from_bytes(&bytes)
                                                               .unwrap(),
                                                           &client_id,
                                                           6882)
                                            .unwrap();
                                    peer_connections(result.0, &result.1, &client_id).unwrap();
                                }
                                Err(e) => error!("{:?}", e),
                            }
                        }
                    }
                    "showfiles" | "sf" => {
                        if cmd.len() != 2 {
                            error!("usage: connect <torrent file>");
                        } else {
                            let path = cmd[1];
                            match File::open(path) {
                                Ok(mut f) => {
                                    let mut bytes: Vec<u8> = Vec::new();
                                    f.read_to_end(&mut bytes).unwrap();

                                    print_files(&bytes);
                                }
                                Err(e) => error!("{:?}", e),
                            }
                        }
                    }
                    "" => {}
                    _ => {
                        println!("invalid command, see \"help\"");
                    }
                }
            }
            Err(ReadlineError::Interrupted) |
            Err(ReadlineError::Eof) => {
                break;
            }
            Err(err) => {
                error!("Error: {:?}", err);
                break;
            }
        }
    }
    rl.save_history(HISTORY_FILE).unwrap();
}
<commit_msg>Distinguish peer list received from tracker<commit_after>#[macro_use]
extern crate log;
#[macro_use]
extern crate nom;

extern crate bip_bencode;
extern crate bip_metainfo;
extern crate bip_utracker;
extern crate chrono;
extern crate hyper;
extern crate pnet;
extern crate pnet_macros_support;
extern crate pretty_env_logger;
extern crate rustyline;
extern crate url;

mod packet;

use bip_bencode::Bencode;
use bip_metainfo::MetainfoFile;
use bip_utracker::contact::CompactPeersV4;
use chrono::{TimeZone, UTC};
use hyper::Client;
use hyper::header::Connection;
use packet::peer_pkt::MutablePeerHandshakePacket;
use pnet::packet::Packet;
use rustyline::Editor;
use rustyline::error::ReadlineError;
use url::Url;

use std::fs::File;
use std::io::prelude::*;
use std::net::TcpStream;
use std::str;
use std::string::String;
use std::time::{SystemTime, UNIX_EPOCH};
use std::thread;

const HISTORY_FILE: &'static str = ".rustyline.history";
const PEER_HANDSHAKE_STRUCT_SZ: usize = 68;

/// Print file list from the torrent.
fn print_files(bytes: &[u8]) {
    let metainfo = MetainfoFile::from_bytes(bytes).unwrap();
    let info = metainfo.info();

    println!("File List:");
    println!("Size (bytes)\tPath");
    println!("------------\t----------------------------------------------");
    for file in info.files() {
        println!("{:12}\t{}",
                 file.length(),
                 file.paths().next().unwrap_or("<unknown>"));
    }
}

/// Print general information about the torrent.
fn print_metainfo_overview(bytes: &[u8]) {
    let metainfo = MetainfoFile::from_bytes(bytes).unwrap();
    let info = metainfo.info();
    let info_hash_hex = metainfo.info_hash()
        .as_ref()
        .iter()
        .map(|b| format!("{:02x}", b))
        .fold(String::new(), |mut acc, nex| {
            acc.push_str(&nex);
            acc
        });
    let utc_creation_date = metainfo.creation_date().map(|c| UTC.timestamp(c, 0));

    println!("------Metainfo File Overview-----");

    println!("InfoHash: {}", info_hash_hex);
    println!("Main Tracker: {:?}", metainfo.main_tracker());
    println!("Comment: {:?}", metainfo.comment());
    println!("Creator: {:?}", metainfo.created_by());
    println!("Creation Date: {:?}", utc_creation_date);

    println!("Directory: {:?}", info.directory());
    println!("Piece Length: {:?}", info.piece_length());
    println!("Number Of Pieces: {}", info.pieces().count());
    println!("Number Of Files: {}", info.files().count());
    println!("Total File Size: {}\n",
             info.files().fold(0, |acc, nex| acc + nex.length()));

    print_files(bytes);
}

fn connect_to_tracker(metainfo: MetainfoFile,
                      peer_id: &str,
                      port: u16)
                      -> Result<(Vec<String>, String), String> {
    debug!("connecting to tracker: {:?}", metainfo.main_tracker());

    let info_hash = metainfo.info_hash();
    // TODO figure can this conversion to url-encoded form be done safely?
    let info_hash_str = unsafe { str::from_utf8_unchecked(info_hash.as_ref()) };

    // TODO this needs to be calculated based on what we have
    let total_len = metainfo.info().files().fold(0, |acc, nex| acc + nex.length());

    let mut url = Url::parse(metainfo.main_tracker().unwrap()).unwrap();
    url.query_pairs_mut()
        .append_pair("info_hash", info_hash_str)
        .append_pair("peer_id", peer_id)
        .append_pair("port", &(port.to_string()))
        // TODO parametrize this
        .append_pair("uploaded", "0")
        // TODO parametrize this
        .append_pair("downloaded", "0")
        // TODO see note on total_len above
        .append_pair("left", &(total_len.to_string()))
        .append_pair("compact", "1")
        .append_pair("event", "started")
        .append_pair("supportcrypto", "0");
    trace!("URL {:?}", url);

    let client = Client::new();
    let mut res = client.get(url).header(Connection::close()).send().unwrap();
    let mut buffer = Vec::new();
    res.read_to_end(&mut buffer).unwrap();
    debug!("{:?}", res);
    let bencode = Bencode::decode(&buffer).unwrap();
    trace!("{:?}", bencode);
    let (_, peers) = CompactPeersV4::from_bytes(bencode.dict()
            .unwrap()
            .lookup("peers")
            .unwrap()
            .bytes()
            .unwrap())
        .unwrap();
    trace!("{:?}", peers);
    let mut ip_ports: Vec<String> = Vec::new();
    debug!("Peer list received:");
    for peer in peers.iter() {
        debug!("{:?}", peer);
        ip_ports.push(peer.to_string());
    }
    Ok((ip_ports, info_hash_str.to_string()))
}

fn peer_connections(peer_ip_ports: Vec<String>,
                    info_hash: &str,
                    peer_id: &str)
                    -> Result<(), String> {
    for peer_ip_port in peer_ip_ports {
        let peer_ip_port_cl = peer_ip_port.clone();
        let info_hash_cl = info_hash.to_string().clone();
        let peer_id_cl = peer_id.to_string().clone();
        thread::spawn(move || {
            match handshake_peer(&peer_ip_port_cl, &info_hash_cl, &peer_id_cl) {
                Ok(mut stream) => {
                    debug!("Communicating with peer {:?}", peer_ip_port_cl);
                    let mut buff = [0; 128];
                    let bytes_read = stream.read(&mut buff).unwrap();
                    debug!("Bytes read: {:?}", bytes_read);
                    debug!("Resp: {:?}", buff.to_vec());
                }
                Err(_) => {
                    error!("Closing thread with peer {:?}", peer_ip_port_cl);
                }
            }
        });
    }
    Ok(())
}

fn handshake_peer(peer_ip_port: &str, info_hash: &str, peer_id: &str) -> Result<TcpStream, String> {
    let mut buf = vec![0u8; PEER_HANDSHAKE_STRUCT_SZ];
    let mut ph = MutablePeerHandshakePacket::new(&mut buf).unwrap();
    ph.set_pstrlen("BitTorrent protocol".len() as u8);
    ph.set_pstr(&String::from("BitTorrent protocol").into_bytes());
    ph.set_reserved(&[0; 8]);
    ph.set_info_hash(info_hash.as_bytes());
    ph.set_peer_id(peer_id.as_bytes());
    match TcpStream::connect(peer_ip_port) {
        Ok(mut stream) => {
            debug!("Connection to peer {:?} successful!", peer_ip_port);
            match stream.write(ph.packet()) {
                Ok(_) => {
                    debug!("Sending message successful!");
                    debug!("{:?}", ph.packet());
                    Ok((stream))
                }
                Err(_) => {
                    error!("Sending message failed!");
                    Err("Sending message failed!".to_owned())
                }
            }
        }
        Err(e) => {
            error!("Connection to peer {:?} failed! {:?}", peer_ip_port, e);
            Err("Connection to peer failed!".to_owned())
        }
    }
}

fn main() {
    pretty_env_logger::init();

    let mut rl = Editor::<()>::new();
    if rl.load_history(HISTORY_FILE).is_err() {
        info!("No previous history!");
    }

    let now = SystemTime::now();
    let duration = now.duration_since(UNIX_EPOCH).unwrap();
    let client_id = "-bittorrent-rs-".to_owned() + &format!("{}", duration.as_secs() % 100_000);

    loop {
        let readline = rl.readline("> ");
        match readline {
            Ok(cmd) => {
                rl.add_history_entry(&cmd);

                let cmd = cmd.trim().split(' ').collect::<Vec<&str>>();

                match cmd[0] {
                    "help" | "h" => {
                        println!("Commands:
parse/p <torrent file path>      - show Metainfo File Overview
connect/c <torrent file path>    - initiate connection to tracker, and handshake with peers
showfiles/sf <torrent file path> - show files in the torrent
help/h                           - show this help");
                    }
                    "parse" | "p" => {
                        if cmd.len() != 2 {
                            error!("usage: parse <torrent file>");
                        } else {
                            let path = cmd[1];
                            debug!("MetainfoFile: {:?}", path);

                            match File::open(path) {
                                Ok(mut f) => {
                                    let mut bytes: Vec<u8> = Vec::new();
                                    f.read_to_end(&mut bytes).unwrap();
                                    print_metainfo_overview(&bytes);
                                }
                                Err(e) => error!("{:?}", e),
                            }
                        }
                    }
                    "connect" | "c" => {
                        if cmd.len() != 2 {
                            error!("usage: connect <torrent file>");
                        } else {
                            let path = cmd[1];
                            match File::open(path) {
                                Ok(mut f) => {
                                    let mut bytes: Vec<u8> = Vec::new();
                                    f.read_to_end(&mut bytes).unwrap();

                                    // TODO: generate peer ID
                                    let result =
                                        connect_to_tracker(MetainfoFile::from_bytes(&bytes)
                                                               .unwrap(),
                                                           &client_id,
                                                           6882)
                                            .unwrap();
                                    peer_connections(result.0, &result.1, &client_id).unwrap();
                                }
                                Err(e) => error!("{:?}", e),
                            }
                        }
                    }
                    "showfiles" | "sf" => {
                        if cmd.len() != 2 {
                            error!("usage: connect <torrent file>");
                        } else {
                            let path = cmd[1];
                            match File::open(path) {
                                Ok(mut f) => {
                                    let mut bytes: Vec<u8> = Vec::new();
                                    f.read_to_end(&mut bytes).unwrap();

                                    print_files(&bytes);
                                }
                                Err(e) => error!("{:?}", e),
                            }
                        }
                    }
                    "" => {}
                    _ => {
                        println!("invalid command, see \"help\"");
                    }
                }
            }
            Err(ReadlineError::Interrupted) |
            Err(ReadlineError::Eof) => {
                break;
            }
            Err(err) => {
                error!("Error: {:?}", err);
                break;
            }
        }
    }
    rl.save_history(HISTORY_FILE).unwrap();
}
<|endoftext|>


<commit_before>#![warn(rust_2018_idioms)]
#![warn(clippy::pedantic)]
#![warn(clippy::cargo)]
#![warn(clippy::nursery)]
#![allow(clippy::non_ascii_literal)]
#![deny(unsafe_code)]

use std::fmt::{self, Display};
use std::{result::Result as StdResult, time::Duration};

use anyhow::{anyhow, Context, Error};
use clap::ArgMatches;
use rgol::World;

mod screen;

const TICK_MS: u64 = 50;
const MAPS_MSG: &str = "\
MAPS:
    Maps must be rectangular. Whitespace is ignored.
    '·' (U+00B7 MIDDLE DOT) is a dead cell. Anything else is a living cell.";

type Result<T = ()> = anyhow::Result<T>;

fn main() {
    if let Err(err) = run() {
        eprintln!("error{}", DisplayCauses(err));
        std::process::exit(1);
    }
}

fn run() -> Result {
    use clap::{clap_app, crate_authors, crate_version};

    #[allow(clippy::needless_pass_by_value)]
    fn is_number(s: String) -> StdResult<(), String> {
        if s.chars().all(|c| c.is_digit(10)) {
            Ok(())
        } else {
            Err(format!("expected a number, found \"{}\"", s))
        }
    }

    let matches = clap_app!(rgol =>
        (about: "Conway's game of life for terminal in Rust")
        (author: crate_authors!())
        (version: crate_version!())
        (after_help: MAPS_MSG)
        (@setting ColoredHelp)
        (@setting SubcommandRequiredElseHelp)
        (@setting VersionlessSubcommands)
        (@subcommand genmap =>
            (about: "Prints an empty map")
            (@setting ColoredHelp)
            (@arg NROW: {is_number} * "Number of rows")
            (@arg NCOL: {is_number} * "Number of columns")
            (@arg space: -s --space "Adds spaces to the map"))
        (@subcommand play =>
            (about: "Plays the game (CTRL-c to exit)")
            (@setting ColoredHelp)
            (@arg FILE: * "File containing the map")
            (@arg TICK_MS: {is_number} "Elapsed time between iterations in ms")
        )
    )
    .get_matches();

    match matches.subcommand() {
        ("genmap", Some(args)) => genmap(args),
        ("play", Some(args)) => play(args),
        _ => anyhow::bail!("subcommands failed to match properly"),
    }
}

/// "genmap" subcommand.
///
/// Prints an empty map.
fn genmap(args: &ArgMatches<'_>) -> Result {
    let nrow = args
        .value_of("NROW")
        .ok_or_else(|| anyhow!("NROW has no value"))?
        .parse()
        .context("NROW is not a number")?;
    let ncol = args
        .value_of("NCOL")
        .ok_or_else(|| anyhow!("NCOL has no value"))?
        .parse()
        .context("NCOL is not a number")?;
    if nrow == 0 || ncol == 0 {
        return Ok(());
    }
    let line = if args.is_present("space") {
        let mut line = "· ".repeat(ncol);
        line.pop();
        line
    } else {
        "·".repeat(ncol)
    };
    for _ in 0..nrow {
        println!("{}", line);
    }
    Ok(())
}

/// "play" subcommand.
///
/// Loads a map from a file and seeds the world with it, then plays it.
fn play(args: &ArgMatches<'_>) -> Result {
    let filename = args.value_of("FILE").ok_or_else(|| anyhow!("FILE has no value"))?;
    let tick_ms = args
        .value_of("TICK_MS")
        .map_or(Some(TICK_MS), |t_ms| t_ms.parse().ok())
        .ok_or_else(|| anyhow!("TICK_MS is not a number"))?;
    let tick = Duration::from_millis(tick_ms);
    let world = World::load(filename).with_context(|| filename.to_string())?;
    play_world(world, tick)
}

/// Plays the world.
///
/// Prints every generation to the terminal screen.
fn play_world(mut world: World, tick: Duration) -> Result {
    use std::time::Instant;

    use screen::Screen;
    use signal::{trap::Trap, Signal};

    let sigtrap = Trap::trap(&[Signal::SIGINT]);
    let screen = Screen::init()?;
    let mut deadline = Instant::now();
    loop {
        screen.clear();
        println!("{}", world);
        world.next();
        deadline += tick;
        if sigtrap.wait(deadline).is_some() {
            return Ok(());
        }
    }
}

/// Displays the causes of an `Error` recursively.
struct DisplayCauses(Error);

impl Display for DisplayCauses {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for cause in self.0.chain() {
            write!(f, ": {}", cause)?;
        }
        Ok(())
    }
}
<commit_msg>Add support for macOS<commit_after>#![warn(rust_2018_idioms)]
#![warn(clippy::pedantic)]
#![warn(clippy::cargo)]
#![warn(clippy::nursery)]
#![allow(clippy::non_ascii_literal)]
#![deny(unsafe_code)]

use std::{
    fmt::{self, Display},
    result::Result as StdResult,
    time::Duration,
};

use anyhow::{anyhow, Context, Error};
use clap::ArgMatches;
use rgol::World;

mod screen;

const TICK_MS: u64 = 50;
const MAPS_MSG: &str = "\
MAPS:
    Maps must be rectangular. Whitespace is ignored.
    '·' (U+00B7 MIDDLE DOT) is a dead cell. Anything else is a living cell.";

type Result<T = ()> = anyhow::Result<T>;

fn main() {
    if let Err(err) = run() {
        eprintln!("error{}", DisplayCauses(err));
        std::process::exit(1);
    }
}

fn run() -> Result {
    use clap::{clap_app, crate_authors, crate_version};

    #[allow(clippy::needless_pass_by_value)]
    fn is_number(s: String) -> StdResult<(), String> {
        if s.chars().all(|c| c.is_digit(10)) {
            Ok(())
        } else {
            Err(format!("expected a number, found \"{}\"", s))
        }
    }

    let matches = clap_app!(rgol =>
        (about: "Conway's game of life for terminal in Rust")
        (author: crate_authors!())
        (version: crate_version!())
        (after_help: MAPS_MSG)
        (@setting ColoredHelp)
        (@setting SubcommandRequiredElseHelp)
        (@setting VersionlessSubcommands)
        (@subcommand genmap =>
            (about: "Prints an empty map")
            (@setting ColoredHelp)
            (@arg NROW: {is_number} * "Number of rows")
            (@arg NCOL: {is_number} * "Number of columns")
            (@arg space: -s --space "Adds spaces to the map"))
        (@subcommand play =>
            (about: "Plays the game (CTRL-c to exit)")
            (@setting ColoredHelp)
            (@arg FILE: * "File containing the map")
            (@arg TICK_MS: {is_number} "Elapsed time between iterations in ms")
        )
    )
    .get_matches();

    match matches.subcommand() {
        ("genmap", Some(args)) => genmap(args),
        ("play", Some(args)) => play(args),
        _ => anyhow::bail!("subcommands failed to match properly"),
    }
}

/// "genmap" subcommand.
///
/// Prints an empty map.
fn genmap(args: &ArgMatches<'_>) -> Result {
    let nrow = args
        .value_of("NROW")
        .ok_or_else(|| anyhow!("NROW has no value"))?
        .parse()
        .context("NROW is not a number")?;
    let ncol = args
        .value_of("NCOL")
        .ok_or_else(|| anyhow!("NCOL has no value"))?
        .parse()
        .context("NCOL is not a number")?;
    if nrow == 0 || ncol == 0 {
        return Ok(());
    }
    let line = if args.is_present("space") {
        let mut line = "· ".repeat(ncol);
        line.pop();
        line
    } else {
        "·".repeat(ncol)
    };
    for _ in 0..nrow {
        println!("{}", line);
    }
    Ok(())
}

/// "play" subcommand.
///
/// Loads a map from a file and seeds the world with it, then plays it.
fn play(args: &ArgMatches<'_>) -> Result {
    let filename = args.value_of("FILE").ok_or_else(|| anyhow!("FILE has no value"))?;
    let tick_ms = args
        .value_of("TICK_MS")
        .map_or(Some(TICK_MS), |t_ms| t_ms.parse().ok())
        .ok_or_else(|| anyhow!("TICK_MS is not a number"))?;
    let tick = Duration::from_millis(tick_ms);
    let world = World::load(filename).with_context(|| filename.to_string())?;
    play_world(world, tick)
}

/// Plays the world.
///
/// Prints every generation to the terminal screen.
#[cfg(target_os = "linux")]
// The `signal` crate only defines `Trap::wait` for linux, though it should
// work anywhere `sigtimedwait` is defined (which doesn’t include macOS).
fn play_world(mut world: World, tick: Duration) -> Result {
    use std::time::Instant;

    use screen::Screen;
    use signal::{trap::Trap, Signal};

    let sigtrap = Trap::trap(&[Signal::SIGINT]);
    let screen = Screen::init()?;
    let mut deadline = Instant::now();
    loop {
        screen.clear();
        println!("{}", world);
        world.next();
        deadline += tick;
        if sigtrap.wait(deadline).is_some() {
            return Ok(());
        }
    }
}

/// Plays the world.
///
/// Prints every generation to the terminal screen.
#[cfg(all(unix, not(target_os = "linux")))]
// Anywhere `Trap::wait` isn’t defined we’ll have to rely on `sigwait`
// (through `Iterator::next`) though presumably some of those targets
// do define `sigtimedwait`.
fn play_world(mut world: World, tick: Duration) -> Result {
    use std::{
        panic,
        sync::mpsc::{channel, RecvTimeoutError},
        thread,
        time::Instant,
    };

    use screen::Screen;
    use signal::{trap::Trap, Signal};

    let sigtrap = Trap::trap(&[Signal::SIGINT]);
    let screen = Screen::init()?;
    let mut deadline = Instant::now();
    let (sender, receiver) = channel();
    let player = thread::spawn(move || {
        loop {
            screen.clear();
            println!("{}", world);
            world.next();
            deadline += tick;
            let duration = deadline.saturating_duration_since(Instant::now());
            // TODO Use `Receiver::recv_deadline` when it becomes stable.
            match receiver.recv_timeout(duration) {
                Err(RecvTimeoutError::Timeout) => {}
                Ok(()) | Err(RecvTimeoutError::Disconnected) => return,
            }
        }
    });
    let signal = sigtrap.into_iter().next();
    sender.send(())?;
    if let Err(e) = player.join() {
        panic::resume_unwind(e);
    }
    match signal {
        Some(Signal::SIGINT) => Ok(()),
        Some(_) => anyhow::bail!("`Trap` returned with unexpected {:?} signal", signal),
        None => anyhow::bail!("`Trap` returned but no signal was received"),
    }
}

/// Displays the causes of an `Error` recursively.
struct DisplayCauses(Error);

impl Display for DisplayCauses {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for cause in self.0.chain() {
            write!(f, ": {}", cause)?;
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate clap;

use clap::{Arg, App};
use std::fs::File;
use std::io;

fn main() {
    let matches = App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .author("Scott Olson <scott@solson.me>")
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .arg(Arg::with_name("INPUT")
             .multiple(true)
             .required(true)
             .help("The file(s) to display"))
        .get_matches();

    let files: Vec<&str> = matches.values_of("INPUT").unwrap().collect();

    let mut first = true;
    for &filename in &files {
        if first { first = false; } else { println!(""); }

        if files.len() > 1 {
            println!("# {}", filename);
        }

        io::copy(&mut File::open(filename).unwrap(), &mut io::stdout()).unwrap();
    }
}
<commit_msg>Print help message when given no arguments.<commit_after>extern crate clap;

use clap::{Arg, App};
use std::ffi::OsStr;
use std::fs::File;
use std::io;

fn main() {
    let app = App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .author("Scott Olson <scott@solson.me>")
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .arg(Arg::with_name("FILE")
             .multiple(true)
             .required(true)
             .help("The file(s) to display"));

    if std::env::args().len() == 1 {
        app.print_help().unwrap();
        println!("");
        return;
    }

    let matches = app.get_matches();
    let files: Vec<&OsStr> = matches.values_of_os("FILE").unwrap().collect();

    let mut first = true;
    for &filename in &files {
        if first { first = false; } else { println!(""); }

        if files.len() > 1 {
            println!("# {}", filename.to_string_lossy());
        }

        io::copy(&mut File::open(filename).unwrap(), &mut io::stdout()).unwrap();
    }
}
<|endoftext|>