<commit_before>use std::env;
use std::fs;
use std::io::Read;
use std::path::Path;

const NUM_GPREG: usize = 32;
const NUM_FPREG: usize = 32;

struct Cpu {
    reg_gprs: [u64; NUM_GPREG],
    reg_fprs: [u64; NUM_FPREG],

    reg_pc: u64,

    reg_hi: u64,
    reg_lo: u64,

    reg_llbit: bool,

    reg_fcr0: u32,
    reg_fcr31: u32,

    cp0: CP0
}

impl Cpu {
    fn new() -> Cpu {
        Cpu {
            reg_gprs: [0; NUM_GPREG],
            reg_fprs: [0; NUM_FPREG],

            reg_pc: 0,

            reg_hi: 0,
            reg_lo: 0,

            reg_llbit: false,

            reg_fcr0: 0,
            reg_fcr31: 0,

            cp0: CP0::new()
        }
    }

    fn power_on_reset(&mut self) {
        self.cp0.power_on_reset();
    }
}

struct CP0 {
    reg_index: u64,
    reg_random: u64,
    reg_entrylo0: u64,
    reg_entrylo1: u64,
    reg_context: u64,
    reg_pagemask: u64,
    reg_wired: u64,
    reg_badvaddr: u64,
    reg_count: u64,
    reg_entryhi: u64,
    reg_compare: u64,
    reg_status: u64,
    reg_cause: u64,
    reg_epc: u64,
    reg_prid: u64,
    reg_config: u64,
    reg_lladdr: u64,
    reg_watchlo: u64,
    reg_watchhi: u64,
    reg_xcontext: u64,
    reg_taglo: u64,
    reg_taghi: u64,
    reg_errorepc: u64
}

impl CP0 {
    fn new() -> CP0 {
        CP0 {
            reg_index: 0,
            reg_random: 0,
            reg_entrylo0: 0,
            reg_entrylo1: 0,
            reg_context: 0,
            reg_pagemask: 0,
            reg_wired: 0,
            reg_badvaddr: 0,
            reg_count: 0,
            reg_entryhi: 0,
            reg_compare: 0,
            reg_status: 0,
            reg_cause: 0,
            reg_epc: 0,
            reg_prid: 0,
            reg_config: 0,
            reg_lladdr: 0,
            reg_watchlo: 0,
            reg_watchhi: 0,
            reg_xcontext: 0,
            reg_taglo: 0,
            reg_taghi: 0,
            reg_errorepc: 0
        }
    }

    fn power_on_reset(&mut self) {

    }
}

fn main() {
    let pif_file_name = env::args().nth(1).unwrap();
    let rom_file_name = env::args().nth(2).unwrap();

    let pif = load_bin(pif_file_name);
    let rom = load_bin(rom_file_name);

    let mut cpu = Cpu::new();
}

fn load_bin<P: AsRef<Path>>(path: P) -> Vec<u8> {
    let mut file = fs::File::open(path).unwrap();
    let mut file_buf = Vec::new();
    file.read_to_end(&mut file_buf).unwrap();
    file_buf
}
<commit_msg>use Default trait<commit_after>use std::env;
use std::fs;
use std::io::Read;
use std::path::Path;

const NUM_GPREG: usize = 32;
const NUM_FPREG: usize = 32;

#[derive(Default)]
struct Cpu {
    reg_gprs: [u64; NUM_GPREG],
    reg_fprs: [u64; NUM_FPREG],

    reg_pc: u64,

    reg_hi: u64,
    reg_lo: u64,

    reg_llbit: bool,

    reg_fcr0: u32,
    reg_fcr31: u32,

    cp0: CP0
}

impl Cpu {
    fn new() -> Cpu {
        Cpu::default()
    }

    fn power_on_reset(&mut self) {
        self.cp0.power_on_reset();
    }
}

#[derive(Default)]
struct CP0 {
    reg_index: u64,
    reg_random: u64,
    reg_entrylo0: u64,
    reg_entrylo1: u64,
    reg_context: u64,
    reg_pagemask: u64,
    reg_wired: u64,
    reg_badvaddr: u64,
    reg_count: u64,
    reg_entryhi: u64,
    reg_compare: u64,
    reg_status: u64,
    reg_cause: u64,
    reg_epc: u64,
    reg_prid: u64,
    reg_config: u64,
    reg_lladdr: u64,
    reg_watchlo: u64,
    reg_watchhi: u64,
    reg_xcontext: u64,
    reg_taglo: u64,
    reg_taghi: u64,
    reg_errorepc: u64
}

impl CP0 {
    fn new() -> CP0 {
        CP0::default()
    }

    fn power_on_reset(&mut self) {

    }
}

fn main() {
    let pif_file_name = env::args().nth(1).unwrap();
    let rom_file_name = env::args().nth(2).unwrap();

    let pif = load_bin(pif_file_name);
    let rom = load_bin(rom_file_name);

    let mut cpu = Cpu::new();
}

fn load_bin<P: AsRef<Path>>(path: P) -> Vec<u8> {
    let mut file = fs::File::open(path).unwrap();
    let mut file_buf = Vec::new();
    file.read_to_end(&mut file_buf).unwrap();
    file_buf
}
<|endoftext|>


<commit_before>#![feature(collections)]
#![feature(convert)]

extern crate time;

use std::env;

extern crate getopts;
use getopts::Options;

extern crate glob;
use std::io::{ BufRead, BufReader };
use std::fs::File;
use std::path::Path;
use glob::glob;

#[derive(Debug)]
enum DateRequest {
  Month { year: i32, month: i32 },
  Day { year:i32, month:i32, day: i32 }
}

fn parse_date(cli_opts: &getopts::Matches) -> DateRequest {
  match cli_opts.opt_str("d").and_then(|d| {
      time::strptime(d.as_str(), "%Y-%m-%d").and_then(|t| {
        Ok(DateRequest::Day{
          year:  t.tm_year + 1900_i32,
          month: t.tm_mon + 1,
          day:   t.tm_mday,
        })
      }).ok()
    }) {
    Some(d) => return d,
    None => (),
  };

  let now = time::now();

  let mut year  = now.tm_year + 1900_i32;
  let mut month = now.tm_mon + 1;

  cli_opts.opt_str("m").and_then(|m| {
    m.parse::<i32>().and_then(|m|{
      month = m;
      Ok(m)
    }).ok()
  });

  cli_opts.opt_str("y").and_then(|y|{
    y.parse::<i32>().and_then(|y|{
      year = y;
      Ok(y)
    }).ok()
  });

  time::strptime(format!("{}-{}-{}", year, month, 1).as_str(), "%Y-%m-%d").map_err(|_| {
    year = now.tm_year + 1900_i32;
    month = now.tm_mon + 1;
  });

  return DateRequest::Month{
    year:  year,
    month: month,
  };
}

fn project_path(project: &str, date: &DateRequest) -> String {
  let mut data_dir = String::from_str( env!("TRAQ_DATA_DIR") );
  data_dir.push_str("/");
  data_dir.push_str(project);
  data_dir.push_str("/");
  match *date {
    DateRequest::Month{ year: y, month:_} => {
      data_dir.push_str(y.to_string().as_str());
    }
    DateRequest::Day{ year:y, month:_, day:_} => {
      data_dir.push_str(y.to_string().as_str());
    }
  }
  return data_dir;
}

fn evaluate(project: &str, date: &DateRequest, running: bool) {

}

fn print_file(path: &Path) {
  let mut file = BufReader::new(File::open(&path).unwrap());
  for line in file.lines() {
    print!("{}\n", line.unwrap());
  }
  print!("%%\n")
}

fn print_date(project: &str, date: &DateRequest) {
  match *date {
    DateRequest::Month{ year: y, month: m} => {
      for entry in glob(format!("{}/{}-{:02}-*", project_path(project, &date).as_str(), y, m).as_str()).unwrap().filter_map(Result::ok) {
        print_file(entry.as_path());
      }
    }
    DateRequest::Day{ year:y, month:m, day: d} => {
      print_file(Path::new(format!("{}/{}-{:02}-{:02}", project_path(project, &date).as_str(), y, m, d).as_str()))
    }
  }
}

#[cfg(not(test))]
fn main() {
  let args: Vec<String> = env::args().collect();

  let mut opts = Options::new();
  opts.optopt("m",  "", "print tracked times for a given month"   , "MONTH");
  opts.optopt("y",  "", "print tracked times for a given year"    , "YEAR");
  opts.optopt("p",  "timestamps", "print data for a given project", "PROJECT");
  opts.optopt("d",  ""          , "print tracked times for a given date", "DATE");
  opts.optflag("e", "evaluate"  , "evaluate times by tag");
  opts.optflag("r", "running"   , "include active tags in evaluation");

  let cli_opts = match opts.parse(&args[1..]) {
      Ok(m) => { m }
      Err(f) => { panic!(f.to_string()) }
  };

  let project = cli_opts.opt_str("p").unwrap_or( String::from_str( "timestamps" ) );

  let date = parse_date(&cli_opts);

  if cli_opts.opt_present("e") {
    let running_evaluation = cli_opts.opt_present("r");
    evaluate( project.as_str(), &date, running_evaluation );
  } else {
    let command = if cli_opts.free.len() > 0 {
      cli_opts.free[0].as_str()
    } else {
      ""
    };

    if command == "" {
      print_date( project.as_str(), &date);
    } else {
      println!("store! {}", command);
    }
  }
}
<commit_msg>WIP: support time evaluation<commit_after>#![feature(collections)]
#![feature(convert)]

extern crate time;

use std::env;

extern crate getopts;
use getopts::Options;

extern crate glob;
use std::io::{ BufRead, BufReader };
use std::fs::File;
use std::path::Path;
use glob::glob;

#[derive(Debug)]
enum DateRequest {
  Month { year: i32, month: i32 },
  Day { year:i32, month:i32, day: i32 }
}

#[derive(Debug)]
struct TraqEntry {
  startsAt: i64,
  endsAt:   i64,
  tag:      String
}

#[derive(Debug)]
struct TraqEval {
  tag: String,
  duration: i64
}

fn parse_date(cli_opts: &getopts::Matches) -> DateRequest {
  match cli_opts.opt_str("d").and_then(|d| {
      time::strptime(d.as_str(), "%Y-%m-%d").and_then(|t| {
        Ok(DateRequest::Day{
          year:  t.tm_year + 1900_i32,
          month: t.tm_mon + 1,
          day:   t.tm_mday,
        })
      }).ok()
    }) {
    Some(d) => return d,
    None => (),
  };

  let now = time::now();

  let mut year  = now.tm_year + 1900_i32;
  let mut month = now.tm_mon + 1;

  cli_opts.opt_str("m").and_then(|m| {
    m.parse::<i32>().and_then(|m|{
      month = m;
      Ok(m)
    }).ok()
  });

  cli_opts.opt_str("y").and_then(|y|{
    y.parse::<i32>().and_then(|y|{
      year = y;
      Ok(y)
    }).ok()
  });

  time::strptime(format!("{}-{}-{}", year, month, 1).as_str(), "%Y-%m-%d").map_err(|_| {
    year = now.tm_year + 1900_i32;
    month = now.tm_mon + 1;
  });

  return DateRequest::Month{
    year:  year,
    month: month,
  };
}

fn project_path(project: &str, date: &DateRequest) -> String {
  let mut data_dir = String::from_str( env!("TRAQ_DATA_DIR") );
  data_dir.push_str("/");
  data_dir.push_str(project);
  data_dir.push_str("/");
  match *date {
    DateRequest::Month{ year: y, month:_} => {
      data_dir.push_str(y.to_string().as_str());
    }
    DateRequest::Day{ year:y, month:_, day:_} => {
      data_dir.push_str(y.to_string().as_str());
    }
  }
  return data_dir;
}

fn evaluate(project: &str, date: &DateRequest, running: bool) {
  match *date {
    DateRequest::Month{ year: y, month: m} => {
      for entry in glob(format!("{}/{}-{:02}-*", project_path(project, &date).as_str(), y, m).as_str()).unwrap().filter_map(Result::ok) {
        let entries = evaluate_file(entry.as_path());
        println!("{:?}", entries);
      }
    }
    DateRequest::Day{ year:y, month:m, day: d} => {
      let entries = evaluate_file(Path::new(format!("{}/{}-{:02}-{:02}", project_path(project, &date).as_str(), y, m, d).as_str()));
      println!("{:?}", entries);
    }
  }
}

fn evaluate_file(path: &Path) -> Vec<TraqEntry> {
  let file = BufReader::new(File::open(&path).unwrap());
  let mut vec = Vec::<TraqEntry>::new();
  let mut current: TraqEntry = TraqEntry{ startsAt: 0i64, endsAt: 0i64, tag: String::from_str("") };
  for line in file.lines() {
    let l = line.unwrap();
    let v: Vec<&str> = l.split(';').collect();
    let tag = v[1];
    if current.startsAt == 0i64 {
      current.tag = String::from_str(tag);

      match time::strptime(v[0], "%a %b %e %T %z %Y") {
        Ok(t) => { current.startsAt = t.to_timespec().sec }
        Err(e) => {
          panic!("failed to parse {}", path.display());
        }
      }
      continue
    }

    if tag == "stop" {
      match time::strptime(v[0], "%a %b %e %T %z %Y") {
        Ok(t) => { current.endsAt = t.to_timespec().sec }
        Err(e) => {
          panic!("failed to parse {}", path.display());
        }
      };
      vec.push(current);
      current = TraqEntry{ startsAt: 0i64, endsAt: 0i64, tag: String::from_str("") };
    } else {
      let mut n: TraqEntry = TraqEntry{ startsAt: 0i64, endsAt: 0i64, tag: String::from_str("") };
      match time::strptime(v[0], "%a %b %e %T %z %Y") {
        Ok(t) => {
          n.startsAt = t.to_timespec().sec;
          current.endsAt = t.to_timespec().sec }
        Err(e) => {
          panic!("failed to parse {}", path.display());
        }
      };
      vec.push(current);
      current =n;
    }
  }
  return vec
}

fn print_file(path: &Path) {
  match File::open(&path) {
    Ok(file) => {
      let file = BufReader::new(file);
      for line in file.lines() {
        print!("{}\n", line.unwrap());
      }
      print!("%%\n");
    }
    Err(e) => { println!("{}: {:?}", path.display(), e); }
  }
}

fn print_date(project: &str, date: &DateRequest) {
  match *date {
    DateRequest::Month{ year: y, month: m} => {
      for entry in glob(format!("{}/{}-{:02}-*", project_path(project, &date).as_str(), y, m).as_str()).unwrap().filter_map(Result::ok) {
        print_file(entry.as_path());
      }
    }
    DateRequest::Day{ year:y, month:m, day: d} => {
      print_file(Path::new(format!("{}/{}-{:02}-{:02}", project_path(project, &date).as_str(), y, m, d).as_str()))
    }
  }
}

#[cfg(not(test))]
fn main() {
  let args: Vec<String> = env::args().collect();

  let mut opts = Options::new();
  opts.optopt("m",  "", "print tracked times for a given month"   , "MONTH");
  opts.optopt("y",  "", "print tracked times for a given year"    , "YEAR");
  opts.optopt("p",  "timestamps", "print data for a given project", "PROJECT");
  opts.optopt("d",  ""          , "print tracked times for a given date", "DATE");
  opts.optflag("e", "evaluate"  , "evaluate times by tag");
  opts.optflag("r", "running"   , "include active tags in evaluation");

  let cli_opts = match opts.parse(&args[1..]) {
      Ok(m) => { m }
      Err(f) => { panic!(f.to_string()) }
  };

  let project = cli_opts.opt_str("p").unwrap_or( String::from_str( "timestamps" ) );

  let date = parse_date(&cli_opts);

  if cli_opts.opt_present("e") {
    let running_evaluation = cli_opts.opt_present("r");
    evaluate( project.as_str(), &date, running_evaluation );
  } else {
    let command = if cli_opts.free.len() > 0 {
      cli_opts.free[0].as_str()
    } else {
      ""
    };

    if command == "" {
      print_date( project.as_str(), &date);
    } else {
      println!("store! {}", command);
    }
  }
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate router;

use std::io::Read;
use std::collections::HashMap;

use iron::prelude::*;
use iron::status;
use router::Router;

#[derive(Debug)]
struct Index;

impl Index {
    fn new() -> Index {
        Index
    }
}

fn main() {
    let mut indices = HashMap::new();
    indices.insert("wagtail", Index::new());

    let mut router = Router::new();

    fn index(_: &mut Request) -> IronResult<Response> {
        Ok(Response::with((status::Ok, "Hello World!")))
    }

    router.get("/", index);

    fn count(req: &mut Request) -> IronResult<Response> {
        let ref index = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");

        let mut payload = String::new();
        req.body.read_to_string(&mut payload).unwrap();

        // TODO

        let mut response = Response::with((status::Ok, "{\"count\": 0}"));
        response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
        Ok(response)
    }

    router.get("/:index/_count", count);

    fn search(req: &mut Request) -> IronResult<Response> {
        let ref index = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");

        let mut payload = String::new();
        req.body.read_to_string(&mut payload).unwrap();

        // TODO

        let mut response = Response::with((status::Ok, "{\"hits\": {\"total\": 0, \"hits\": []}}"));
        response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
        Ok(response)
    }

    router.get("/:index/_search", search);

    fn put(req: &mut Request) -> IronResult<Response> {
        let ref index = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");
        let ref mapping = req.extensions.get::<Router>().unwrap().find("mapping").unwrap_or("");
        let ref doc = req.extensions.get::<Router>().unwrap().find("doc").unwrap_or("");

        let mut payload = String::new();
        req.body.read_to_string(&mut payload).unwrap();

        // TODO

        let mut response = Response::with((status::Ok, "{}"));
        response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
        Ok(response)
    }

    router.put("/:index/:mapping/:doc", put);

    Iron::new(router).http("localhost:9200").unwrap();
}
<commit_msg>Made all view functions closures<commit_after>extern crate iron;
extern crate router;

use std::io::Read;
use std::collections::HashMap;

use iron::prelude::*;
use iron::status;
use router::Router;

#[derive(Debug)]
struct Index;

impl Index {
    fn new() -> Index {
        Index
    }
}

fn main() {
    let mut indices = HashMap::new();
    indices.insert("wagtail", Index::new());

    let mut router = Router::new();

    router.get("/", |_: &mut Request| -> IronResult<Response> {
        Ok(Response::with((status::Ok, "Hello World!")))
    });

    router.get("/:index/_count", |req: &mut Request| -> IronResult<Response> {
        let ref index = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");

        let mut payload = String::new();
        req.body.read_to_string(&mut payload).unwrap();

        // TODO

        let mut response = Response::with((status::Ok, "{\"count\": 0}"));
        response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
        Ok(response)
    });

    router.get("/:index/_search", |req: &mut Request| -> IronResult<Response> {
        let ref index = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");

        let mut payload = String::new();
        req.body.read_to_string(&mut payload).unwrap();

        // TODO

        let mut response = Response::with((status::Ok, "{\"hits\": {\"total\": 0, \"hits\": []}}"));
        response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
        Ok(response)
    });

    router.put("/:index/:mapping/:doc", |req: &mut Request| -> IronResult<Response> {
        let ref index = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");
        let ref mapping = req.extensions.get::<Router>().unwrap().find("mapping").unwrap_or("");
        let ref doc = req.extensions.get::<Router>().unwrap().find("doc").unwrap_or("");

        let mut payload = String::new();
        req.body.read_to_string(&mut payload).unwrap();

        // TODO

        let mut response = Response::with((status::Ok, "{}"));
        response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
        Ok(response)
    });

    Iron::new(router).http("localhost:9200").unwrap();
}
<|endoftext|>


<commit_before>use colours::{Plain, Style, Black, Red, Green, Yellow, Blue, Purple, Cyan};
use std::io::{fs, IoResult};
use std::io;

use column::{Column, Permissions, FileName, FileSize, User, Group};
use format::{format_metric_bytes, format_IEC_bytes};
use unix::{get_user_name, get_group_name};
use sort::SortPart;
use dir::Dir;
use filetype::HasType;

// Instead of working with Rust's Paths, we have our own File object
// that holds the Path and various cached information. Each file is
// definitely going to have its filename used at least once, its stat
// information queried at least once, and its file extension extracted
// at least once, so we may as well carry around that information with
// the actual path.

pub struct File<'a> {
    pub name: &'a str,
    pub dir:  &'a Dir<'a>,
    pub ext:  Option<&'a str>,
    pub path: &'a Path,
    pub stat: io::FileStat,
    pub parts: Vec<SortPart>,
}

impl<'a> File<'a> {
    pub fn from_path(path: &'a Path, parent: &'a Dir) -> IoResult<File<'a>> {
        // Getting the string from a filename fails whenever it's not
        // UTF-8 representable - just assume it is for now.
        let filename: &str = path.filename_str().unwrap();

        // Use lstat here instead of file.stat(), as it doesn't follow
        // symbolic links. Otherwise, the stat() call will fail if it
        // encounters a link that's target is non-existent.

        fs::lstat(path).map(|stat| File {
            path:  path,
            dir:   parent,
            stat:  stat,
            name:  filename,
            ext:   File::ext(filename),
            parts: SortPart::split_into_parts(filename),
        })
    }

    fn ext(name: &'a str) -> Option<&'a str> {
        // The extension is the series of characters after a dot at
        // the end of a filename. This deliberately also counts
        // dotfiles - the ".git" folder has the extension "git".
        let re = regex!(r"\.([^.]+)$");
        re.captures(name).map(|caps| caps.at(1))
    }

    pub fn is_dotfile(&self) -> bool {
        self.name.starts_with(".")
    }

    pub fn is_tmpfile(&self) -> bool {
        self.name.ends_with("~") || (self.name.starts_with("#") && self.name.ends_with("#"))
    }

    // Highlight the compiled versions of files. Some of them, like .o,
    // get special highlighting when they're alone because there's no
    // point in existing without their source. Others can be perfectly
    // content without their source files, such as how .js is valid
    // without a .coffee.

    pub fn get_source_files(&self) -> Vec<Path> {
        match self.ext {
            Some("class") => vec![self.path.with_extension("java")],  // Java
            Some("elc") => vec![self.path.with_extension("el")],  // Emacs Lisp
            Some("hi") => vec![self.path.with_extension("hs")],  // Haskell
            Some("o") => vec![self.path.with_extension("c"), self.path.with_extension("cpp")],  // C, C++
            Some("pyc") => vec![self.path.with_extension("py")],  // Python
            Some("js") => vec![self.path.with_extension("coffee"), self.path.with_extension("ts")],  // CoffeeScript, TypeScript
            Some("css") => vec![self.path.with_extension("sass"), self.path.with_extension("less")],  // SASS, Less

            Some("aux") => vec![self.path.with_extension("tex")],  // TeX: auxiliary file
            Some("bbl") => vec![self.path.with_extension("tex")],  // BibTeX bibliography file
            Some("blg") => vec![self.path.with_extension("tex")],  // BibTeX log file
            Some("lof") => vec![self.path.with_extension("tex")],  // list of figures
            Some("log") => vec![self.path.with_extension("tex")],  // TeX log file
            Some("lot") => vec![self.path.with_extension("tex")],  // list of tables
            Some("toc") => vec![self.path.with_extension("tex")],  // table of contents

            _ => vec![],
        }
    }
    pub fn display(&self, column: &Column) -> String {
        match *column {
            Permissions => self.permissions_string(),
            FileName => self.file_colour().paint(self.name),
            FileSize(use_iec) => self.file_size(use_iec),

            // Display the ID if the user/group doesn't exist, which
            // usually means it was deleted but its files weren't.
            User(uid) => {
                let style = if uid == self.stat.unstable.uid { Yellow.bold() } else { Plain };
                let string = get_user_name(self.stat.unstable.uid as i32).unwrap_or(self.stat.unstable.uid.to_str());
                return style.paint(string.as_slice());
            },
            Group => get_group_name(self.stat.unstable.gid as u32).unwrap_or(self.stat.unstable.gid.to_str()),
        }
    }

    fn file_size(&self, use_iec_prefixes: bool) -> String {
        // Don't report file sizes for directories. I've never looked
        // at one of those numbers and gained any information from it.
        if self.stat.kind == io::TypeDirectory {
            Black.bold().paint("-")
        } else {
            let (size, suffix) = if use_iec_prefixes {
                format_IEC_bytes(self.stat.size)
            } else {
                format_metric_bytes(self.stat.size)
            };

            return format!("{}{}", Green.bold().paint(size.as_slice()), Green.paint(suffix.as_slice()));
        }
    }

    fn type_char(&self) -> String {
        return match self.stat.kind {
            io::TypeFile         => ".".to_string(),
            io::TypeDirectory    => Blue.paint("d"),
            io::TypeNamedPipe    => Yellow.paint("|"),
            io::TypeBlockSpecial => Purple.paint("s"),
            io::TypeSymlink      => Cyan.paint("l"),
            _                    => "?".to_string(),
        }
    }

    fn file_colour(&self) -> Style {
        self.get_type().style()
    }

    fn permissions_string(&self) -> String {
        let bits = self.stat.perm;
        return format!("{}{}{}{}{}{}{}{}{}{}",
            self.type_char(),

            // The first three are bold because they're the ones used
            // most often.
            File::permission_bit(bits, io::UserRead,     "r", Yellow.bold()),
            File::permission_bit(bits, io::UserWrite,    "w", Red.bold()),
            File::permission_bit(bits, io::UserExecute,  "x", Green.bold().underline()),
            File::permission_bit(bits, io::GroupRead,    "r", Yellow.normal()),
            File::permission_bit(bits, io::GroupWrite,   "w", Red.normal()),
            File::permission_bit(bits, io::GroupExecute, "x", Green.normal()),
            File::permission_bit(bits, io::OtherRead,    "r", Yellow.normal()),
            File::permission_bit(bits, io::OtherWrite,   "w", Red.normal()),
            File::permission_bit(bits, io::OtherExecute, "x", Green.normal()),
       );
    }

    fn permission_bit(bits: io::FilePermission, bit: io::FilePermission, character: &'static str, style: Style) -> String {
        if bits.contains(bit) {
            style.paint(character.as_slice())
        } else {
            Black.bold().paint("-".as_slice())
        }
    }
}
<commit_msg>Show files on the end of symlinks<commit_after>use colours::{Plain, Style, Black, Red, Green, Yellow, Blue, Purple, Cyan};
use std::io::{fs, IoResult};
use std::io;

use column::{Column, Permissions, FileName, FileSize, User, Group};
use format::{format_metric_bytes, format_IEC_bytes};
use unix::{get_user_name, get_group_name};
use sort::SortPart;
use dir::Dir;
use filetype::HasType;

// Instead of working with Rust's Paths, we have our own File object
// that holds the Path and various cached information. Each file is
// definitely going to have its filename used at least once, its stat
// information queried at least once, and its file extension extracted
// at least once, so we may as well carry around that information with
// the actual path.

pub struct File<'a> {
    pub name:  &'a str,
    pub dir:   &'a Dir<'a>,
    pub ext:   Option<&'a str>,
    pub path:  &'a Path,
    pub stat:  io::FileStat,
    pub parts: Vec<SortPart>,
}

impl<'a> File<'a> {
    pub fn from_path(path: &'a Path, parent: &'a Dir) -> IoResult<File<'a>> {
        // Getting the string from a filename fails whenever it's not
        // UTF-8 representable - just assume it is for now.
        let filename: &str = path.filename_str().unwrap();

        // Use lstat here instead of file.stat(), as it doesn't follow
        // symbolic links. Otherwise, the stat() call will fail if it
        // encounters a link that's target is non-existent.

        fs::lstat(path).map(|stat| File {
            path:  path,
            dir:   parent,
            stat:  stat,
            name:  filename,
            ext:   File::ext(filename),
            parts: SortPart::split_into_parts(filename),
        })
    }

    fn ext(name: &'a str) -> Option<&'a str> {
        // The extension is the series of characters after a dot at
        // the end of a filename. This deliberately also counts
        // dotfiles - the ".git" folder has the extension "git".
        let re = regex!(r"\.([^.]+)$");
        re.captures(name).map(|caps| caps.at(1))
    }

    pub fn is_dotfile(&self) -> bool {
        self.name.starts_with(".")
    }

    pub fn is_tmpfile(&self) -> bool {
        self.name.ends_with("~") || (self.name.starts_with("#") && self.name.ends_with("#"))
    }

    // Highlight the compiled versions of files. Some of them, like .o,
    // get special highlighting when they're alone because there's no
    // point in existing without their source. Others can be perfectly
    // content without their source files, such as how .js is valid
    // without a .coffee.

    pub fn get_source_files(&self) -> Vec<Path> {
        match self.ext {
            Some("class") => vec![self.path.with_extension("java")],  // Java
            Some("elc") => vec![self.path.with_extension("el")],  // Emacs Lisp
            Some("hi") => vec![self.path.with_extension("hs")],  // Haskell
            Some("o") => vec![self.path.with_extension("c"), self.path.with_extension("cpp")],  // C, C++
            Some("pyc") => vec![self.path.with_extension("py")],  // Python
            Some("js") => vec![self.path.with_extension("coffee"), self.path.with_extension("ts")],  // CoffeeScript, TypeScript
            Some("css") => vec![self.path.with_extension("sass"), self.path.with_extension("less")],  // SASS, Less

            Some("aux") => vec![self.path.with_extension("tex")],  // TeX: auxiliary file
            Some("bbl") => vec![self.path.with_extension("tex")],  // BibTeX bibliography file
            Some("blg") => vec![self.path.with_extension("tex")],  // BibTeX log file
            Some("lof") => vec![self.path.with_extension("tex")],  // list of figures
            Some("log") => vec![self.path.with_extension("tex")],  // TeX log file
            Some("lot") => vec![self.path.with_extension("tex")],  // list of tables
            Some("toc") => vec![self.path.with_extension("tex")],  // table of contents

            _ => vec![],
        }
    }

    pub fn display(&self, column: &Column) -> String {
        match *column {
            Permissions => self.permissions_string(),
            FileName => self.file_name(),
            FileSize(use_iec) => self.file_size(use_iec),

            // Display the ID if the user/group doesn't exist, which
            // usually means it was deleted but its files weren't.
            User(uid) => {
                let style = if uid == self.stat.unstable.uid { Yellow.bold() } else { Plain };
                let string = get_user_name(self.stat.unstable.uid as i32).unwrap_or(self.stat.unstable.uid.to_str());
                return style.paint(string.as_slice());
            },
            Group => get_group_name(self.stat.unstable.gid as u32).unwrap_or(self.stat.unstable.gid.to_str()),
        }
    }

    fn file_name(&self) -> String {
        let displayed_name = self.file_colour().paint(self.name);
        if self.stat.kind == io::TypeSymlink {
            match fs::readlink(self.path) {
                Ok(path) => format!("{} => {}", displayed_name, path.display()),
                Err(e) => {
                    println!("{}", e);
                    displayed_name
                },
            }
        }
        else {
            displayed_name
        }
    }

    fn file_size(&self, use_iec_prefixes: bool) -> String {
        // Don't report file sizes for directories. I've never looked
        // at one of those numbers and gained any information from it.
        if self.stat.kind == io::TypeDirectory {
            Black.bold().paint("-")
        } else {
            let (size, suffix) = if use_iec_prefixes {
                format_IEC_bytes(self.stat.size)
            } else {
                format_metric_bytes(self.stat.size)
            };

            return format!("{}{}", Green.bold().paint(size.as_slice()), Green.paint(suffix.as_slice()));
        }
    }

    fn type_char(&self) -> String {
        return match self.stat.kind {
            io::TypeFile         => ".".to_string(),
            io::TypeDirectory    => Blue.paint("d"),
            io::TypeNamedPipe    => Yellow.paint("|"),
            io::TypeBlockSpecial => Purple.paint("s"),
            io::TypeSymlink      => Cyan.paint("l"),
            _                    => "?".to_string(),
        }
    }

    fn file_colour(&self) -> Style {
        self.get_type().style()
    }

    fn permissions_string(&self) -> String {
        let bits = self.stat.perm;
        return format!("{}{}{}{}{}{}{}{}{}{}",
            self.type_char(),

            // The first three are bold because they're the ones used
            // most often.
            File::permission_bit(bits, io::UserRead,     "r", Yellow.bold()),
            File::permission_bit(bits, io::UserWrite,    "w", Red.bold()),
            File::permission_bit(bits, io::UserExecute,  "x", Green.bold().underline()),
            File::permission_bit(bits, io::GroupRead,    "r", Yellow.normal()),
            File::permission_bit(bits, io::GroupWrite,   "w", Red.normal()),
            File::permission_bit(bits, io::GroupExecute, "x", Green.normal()),
            File::permission_bit(bits, io::OtherRead,    "r", Yellow.normal()),
            File::permission_bit(bits, io::OtherWrite,   "w", Red.normal()),
            File::permission_bit(bits, io::OtherExecute, "x", Green.normal()),
       );
    }

    fn permission_bit(bits: io::FilePermission, bit: io::FilePermission, character: &'static str, style: Style) -> String {
        if bits.contains(bit) {
            style.paint(character.as_slice())
        } else {
            Black.bold().paint("-".as_slice())
        }
    }
}
<|endoftext|>


<commit_before>extern crate walkdir;
extern crate regex;
extern crate getopts;
extern crate ansi_term;

use std::env;
use std::error::Error;
use std::ffi::OsStr;
use std::io::Write;
use std::path::Path;
use std::process;

use walkdir::{WalkDir, DirEntry, WalkDirIterator};
use regex::{Regex, RegexBuilder};
use getopts::Options;
use ansi_term::Colour;

struct FdOptions {
    case_sensitive: bool,
    search_full_path: bool,
    follow_links: bool,
    colored: bool
}

/// Print a search result to the console.
fn print_entry(entry: &DirEntry, path_str: &str, config: &FdOptions) {
    if config.colored {
        let style = match entry {
            e if e.path_is_symbolic_link() => Colour::Purple,
            e if e.path().is_dir()         => Colour::Cyan,
            _                              => Colour::White
        };
        println!("{}", style.paint(path_str));
    } else {
        println!("{}", path_str);
    }
}

/// Check if filename of entry starts with a dot.
fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name()
         .to_str()
         .map(|s| s.starts_with("."))
         .unwrap_or(false)
}

/// Recursively scan the given root path and search for files / pathnames
/// matching the pattern.
fn scan(root: &Path, pattern: &Regex, config: &FdOptions) {
    let walker = WalkDir::new(root)
                     .follow_links(config.follow_links)
                     .into_iter()
                     .filter_entry(|e| !is_hidden(e))
                     .filter_map(|e| e.ok())
                     .filter(|e| e.path() != root);

    for entry in walker {
        if let Ok(path_relative) = entry.path().strip_prefix(root) {
            if let Some(path_str) = path_relative.to_str() {
                let haystack = if config.search_full_path {
                                   path_str
                               } else {
                                   path_relative.file_name()
                                                .and_then(OsStr::to_str)
                                                .unwrap()
                               };
                pattern.find(haystack)
                       .map(|_| print_entry(&entry, path_str, &config));
            }
        }
    }
}

/// Print error message to stderr and exit with status `1`.
fn error(message: &str) -> ! {
    writeln!(&mut std::io::stderr(), "{}", message)
        .expect("Failed writing to stderr");
    process::exit(1);
}

fn main() {
    let args: Vec<String> = env::args().collect();

    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help message");
    opts.optflag("s", "sensitive", "case-sensitive search (default: smart case)");
    opts.optflag("f", "full-path", "search full path (default: only filename)");
    opts.optflag("F", "follow", "follow symlinks (default: off)");
    opts.optflag("n", "no-color", "do not colorize output");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(e) => error(e.description())
    };

    if matches.opt_present("h") {
        let brief = "Usage: fd [PATTERN]";
        print!("{}", opts.usage(&brief));
        process::exit(1);
    }

    let empty = String::new();
    let pattern = matches.free.get(0).unwrap_or(&empty);

    let current_dir_buf = match env::current_dir() {
        Ok(cd) => cd,
        Err(_) => error("Could not get current directory!")
    };
    let current_dir = current_dir_buf.as_path();

    let config = FdOptions {
        // The search will be case-sensitive if the command line flag is set or if
        // the pattern has an uppercase character (smart case).
        case_sensitive:    matches.opt_present("s") ||
                           pattern.chars().any(char::is_uppercase),
        search_full_path:  matches.opt_present("f"),
        colored:          !matches.opt_present("n"),
        follow_links:      matches.opt_present("F")
    };

    match RegexBuilder::new(pattern)
              .case_insensitive(!config.case_sensitive)
              .build() {
        Ok(re) => scan(&current_dir, &re, &config),
        Err(err) => error(err.description())
    }
}
<commit_msg>--filename instead of --full-path<commit_after>extern crate walkdir;
extern crate regex;
extern crate getopts;
extern crate ansi_term;

use std::env;
use std::error::Error;
use std::ffi::OsStr;
use std::io::Write;
use std::path::Path;
use std::process;

use walkdir::{WalkDir, DirEntry, WalkDirIterator};
use regex::{Regex, RegexBuilder};
use getopts::Options;
use ansi_term::Colour;

struct FdOptions {
    case_sensitive: bool,
    search_full_path: bool,
    follow_links: bool,
    colored: bool
}

/// Print a search result to the console.
fn print_entry(entry: &DirEntry, path_str: &str, config: &FdOptions) {
    if config.colored {
        let style = match entry {
            e if e.path_is_symbolic_link() => Colour::Purple,
            e if e.path().is_dir()         => Colour::Cyan,
            _                              => Colour::White
        };
        println!("{}", style.paint(path_str));
    } else {
        println!("{}", path_str);
    }
}

/// Check if filename of entry starts with a dot.
fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name()
         .to_str()
         .map(|s| s.starts_with("."))
         .unwrap_or(false)
}

/// Recursively scan the given root path and search for files / pathnames
/// matching the pattern.
fn scan(root: &Path, pattern: &Regex, config: &FdOptions) {
    let walker = WalkDir::new(root)
                     .follow_links(config.follow_links)
                     .into_iter()
                     .filter_entry(|e| !is_hidden(e))
                     .filter_map(|e| e.ok())
                     .filter(|e| e.path() != root);

    for entry in walker {
        let path_rel = match entry.path().strip_prefix(root) {
            Ok(p) => p,
            Err(_) => continue
        };

        if let Some(path_str) = path_rel.to_str() {
            let res =
                if config.search_full_path {
                    pattern.find(path_str)
                } else {
                    path_rel.file_name()
                            .and_then(OsStr::to_str)
                            .and_then(|s| pattern.find(s))
                };

            res.map(|_| print_entry(&entry, path_str, &config));
        }
    }
}

/// Print error message to stderr and exit with status `1`.
fn error(message: &str) -> ! {
    writeln!(&mut std::io::stderr(), "{}", message)
        .expect("Failed writing to stderr");
    process::exit(1);
}

fn main() {
    let args: Vec<String> = env::args().collect();

    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help message");
    opts.optflag("s", "sensitive",
                      "case-sensitive search (default: smart case)");
    opts.optflag("f", "filename",
                      "search filenames only (default: full path)");
    opts.optflag("F", "follow", "follow symlinks (default: off)");
    opts.optflag("n", "no-color", "do not colorize output");

    let matches = match opts.parse(&args[1..]) {
        Ok(m)  => m,
        Err(e) => error(e.description())
    };

    if matches.opt_present("h") {
        let brief = "Usage: fd [PATTERN]";
        print!("{}", opts.usage(&brief));
        process::exit(1);
    }

    let empty = String::new();
    let pattern = matches.free.get(0).unwrap_or(&empty);

    let current_dir_buf = match env::current_dir() {
        Ok(cd) => cd,
        Err(_) => error("Could not get current directory!")
    };
    let current_dir = current_dir_buf.as_path();

    let config = FdOptions {
        // The search will be case-sensitive if the command line flag is set or
        // if the pattern has an uppercase character (smart case).
        case_sensitive:    matches.opt_present("s") ||
                           pattern.chars().any(char::is_uppercase),
        search_full_path: !matches.opt_present("f"),
        colored:          !matches.opt_present("n"),
        follow_links:      matches.opt_present("F")
    };

    match RegexBuilder::new(pattern)
              .case_insensitive(!config.case_sensitive)
              .build() {
        Ok(re)   => scan(&current_dir, &re, &config),
        Err(err) => error(err.description())
    }
}
<|endoftext|>


<commit_before>#![feature(conservative_impl_trait)]

extern crate curl;
extern crate env_logger;
extern crate futures;
extern crate tokio_core;
extern crate tokio_curl;

#[macro_use]
extern crate clap;
extern crate chrono;
extern crate histogram;

#[macro_use]
#[cfg(test)]
extern crate quickcheck;

mod args;
mod misc;

use args::{Config, parse_args};

use curl::easy::Easy;
use futures::{Future, stream};
use futures::future::{ok, loop_fn, Loop};
use futures::Stream;
use tokio_core::reactor::Core;
use tokio_curl::{Session, PerformError};
use chrono::*;
use std::sync::mpsc::channel;
use histogram::*;
use std::thread;

struct RequestResult {
    latency: Duration,
}

impl RequestResult {
    fn new(latency: Duration) -> Self {
        RequestResult { latency: latency }
    }
}

impl Default for RequestResult {
    fn default() -> Self {
        RequestResult { latency: Duration::zero() }
    }
}

fn send_request(easy_request: Easy,
                session: &Session)
                -> impl Future<Item = (RequestResult, Easy), Error = PerformError> {
    session.perform(easy_request)
        .and_then(|mut easy| {
            let latency = easy.total_time()
                .ok()
                .and_then(|x| Duration::from_std(x).ok())
                .unwrap();
            let request_result = RequestResult::new(latency);
            Ok((request_result, easy))
        })
}

struct RunInfo {
    /// Number of requests successfully completed
    pub requests_completed: usize,
    pub duration: Duration,
    histogram: Histogram,
    /// Number of failed requests
    num_failed_requests: usize,
}

impl RunInfo {
    fn new(duration: Duration) -> Self {
        RunInfo {
            requests_completed: 0,
            num_failed_requests: 0,
            duration: duration,
            histogram: Histogram::new(),
        }
    }
    pub fn requests_per_second(&self) -> f32 {
        (self.requests_completed as f32) / (self.duration.num_seconds() as f32)
    }

    fn merge(&mut self, other: &Self) {
        self.requests_completed += other.requests_completed;
        self.num_failed_requests += other.num_failed_requests;
        self.histogram.merge(&other.histogram);
    }
}

/// Delegates and manages all the work.
struct Boss {
    /// Number of requests successfully completed
    pub requests_completed: usize,
    /// Number of active connections
    pub connections: usize,
    num_threads: usize,
}

fn make_easy(url: &str, timeout: Duration) -> Easy {
    let mut easy_request = Easy::new();
    easy_request.get(true).unwrap();
    easy_request.url(url).unwrap();
    easy_request.timeout(timeout.to_std().unwrap()).unwrap();
    easy_request
}

impl Boss {
    pub fn new(num_threads: usize) -> Self {
        Boss {
            requests_completed: 0,
            connections: 0,
            num_threads: num_threads,
        }
    }

    pub fn start_workforce(&self, config: &Config) -> RunInfo {
        let (tx, rx) = channel();
        let desired_connections_per_worker_iter = misc::split_number(config.num_connections,
                                                                     self.num_threads);
        // start num_threads workers
        for desired_connections_per_worker in desired_connections_per_worker_iter {
            let tx = tx.clone();
            let url = config.url.clone();
            let duration = config.duration;
            let timeout = config.timeout;
            thread::spawn(move || {

                let mut lp = Core::new().unwrap();
                let start_time = Local::now();
                let wanted_end_time = start_time + duration;
                let session = Session::new(lp.handle());

                let iterator = (0..desired_connections_per_worker).map(|_| {

                    let runinfo = RunInfo::new(duration);
                    let easy_request = make_easy(&url, timeout);
                    loop_fn((runinfo, easy_request), |(mut runinfo, easy_request)| {
                        send_request(easy_request, &session)
                            .then(|res| -> Result<_, ()> { match res {
                                // on request success
                                Ok((request_res, easy_request)) => {
                                    // update histogram
                                    request_res.latency.num_nanoseconds()
                                        .map(|x| runinfo.histogram.increment(x as u64).ok());
                                    runinfo.requests_completed += 1;

                                    let state = (runinfo, easy_request);

                                    let now_time = Local::now();
                                    if now_time < wanted_end_time {
                                        Ok(Loop::Continue(state))
                                    } else {
                                        Ok(Loop::Break(state))
                                    }
                                }
                                // on request failure
                                Err(mut err) => {
                                    runinfo.num_failed_requests += 1;
                                    // attempt to recover the easy handle from the error,
                                    // else make a new handle
                                    let easy_request = err.take_easy().unwrap_or_else(|| make_easy(&url, timeout));
                                    let state = (runinfo, easy_request);
                                    let now_time = Local::now();
                                    if now_time < wanted_end_time {
                                        Ok(Loop::Continue(state))
                                    } else {
                                        Ok(Loop::Break(state))
                                    }
                                }
                            }})
                            .map_err(|_| ())
                    })
                        // Extract latency histogram
                        .map(|(histogram, _)| histogram)
                });
                let future = stream::futures_unordered(iterator)
                    .fold(RunInfo::new(duration), |mut runinfo_acc, runinfo| {
                        runinfo_acc.merge(&runinfo);
                        ok(runinfo_acc)
                    });
                let res = lp.run(future).unwrap();
                tx.send(res).unwrap();
            });
        }
        // collect information from all workers
        rx.iter()
            .take(self.num_threads)
            .fold(RunInfo::new(config.duration), |mut runinfo_acc, runinfo| {
                runinfo_acc.merge(&runinfo);
                runinfo_acc
            })
    }
}

fn start(config: Config) -> RunInfo {
    let boss = Boss::new(config.num_threads);
    boss.start_workforce(&config)
}

fn nanoseconds_to_milliseconds(nanoseconds: u64) -> f64 {
    nanoseconds as f64 / 1_000_000_f64
}

/// Presents the results to the user
fn present(run_info: RunInfo) {
    let requests_completed = run_info.requests_completed;
    println!("{} requests in {}s",
             requests_completed,
             run_info.duration.num_seconds());
    println!("{} failed requests", run_info.num_failed_requests);
    println!("Requests per second: {}", run_info.requests_per_second());

    // we can only print the latency distribution if there is data in the histogram
    if run_info.histogram.entries() > 0 {
        println!("Latency distribution: \n50%: {} ms\n75%: {} ms\n90%: {} ms\n95%: {} ms\n99%: {} ms",
            run_info.histogram.percentile(50.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(75.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(90.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(95.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(99.0).map(nanoseconds_to_milliseconds).unwrap(),
        );
    }

}

fn main() {
    env_logger::init().unwrap();
    let config = parse_args();
    let run_info = start(config);
    present(run_info)
}
<commit_msg>format<commit_after>#![feature(conservative_impl_trait)]

extern crate curl;
extern crate env_logger;
extern crate futures;
extern crate tokio_core;
extern crate tokio_curl;

#[macro_use]
extern crate clap;
extern crate chrono;
extern crate histogram;

#[macro_use]
#[cfg(test)]
extern crate quickcheck;

mod args;
mod misc;

use args::{Config, parse_args};

use curl::easy::Easy;
use futures::{Future, stream};
use futures::future::{ok, loop_fn, Loop};
use futures::Stream;
use tokio_core::reactor::Core;
use tokio_curl::{Session, PerformError};
use chrono::*;
use std::sync::mpsc::channel;
use histogram::*;
use std::thread;

struct RequestResult {
    latency: Duration,
}

impl RequestResult {
    fn new(latency: Duration) -> Self {
        RequestResult { latency: latency }
    }
}

impl Default for RequestResult {
    fn default() -> Self {
        RequestResult { latency: Duration::zero() }
    }
}

fn send_request(easy_request: Easy,
                session: &Session)
                -> impl Future<Item = (RequestResult, Easy), Error = PerformError> {
    session.perform(easy_request)
        .and_then(|mut easy| {
            let latency = easy.total_time()
                .ok()
                .and_then(|x| Duration::from_std(x).ok())
                .unwrap();
            let request_result = RequestResult::new(latency);
            Ok((request_result, easy))
        })
}

struct RunInfo {
    /// Number of requests successfully completed
    pub requests_completed: usize,
    pub duration: Duration,
    histogram: Histogram,
    /// Number of failed requests
    num_failed_requests: usize,
}

impl RunInfo {
    fn new(duration: Duration) -> Self {
        RunInfo {
            requests_completed: 0,
            num_failed_requests: 0,
            duration: duration,
            histogram: Histogram::new(),
        }
    }
    pub fn requests_per_second(&self) -> f32 {
        (self.requests_completed as f32) / (self.duration.num_seconds() as f32)
    }

    fn merge(&mut self, other: &Self) {
        self.requests_completed += other.requests_completed;
        self.num_failed_requests += other.num_failed_requests;
        self.histogram.merge(&other.histogram);
    }
}

/// Delegates and manages all the work.
struct Boss {
    /// Number of requests successfully completed
    pub requests_completed: usize,
    /// Number of active connections
    pub connections: usize,
    num_threads: usize,
}

fn make_easy(url: &str, timeout: Duration) -> Easy {
    let mut easy_request = Easy::new();
    easy_request.get(true).unwrap();
    easy_request.url(url).unwrap();
    easy_request.timeout(timeout.to_std().unwrap()).unwrap();
    easy_request
}

impl Boss {
    pub fn new(num_threads: usize) -> Self {
        Boss {
            requests_completed: 0,
            connections: 0,
            num_threads: num_threads,
        }
    }

    pub fn start_workforce(&self, config: &Config) -> RunInfo {
        let (tx, rx) = channel();
        let desired_connections_per_worker_iter = misc::split_number(config.num_connections,
                                                                     self.num_threads);
        // start num_threads workers
        for desired_connections_per_worker in desired_connections_per_worker_iter {
            let tx = tx.clone();
            let url = config.url.clone();
            let duration = config.duration;
            let timeout = config.timeout;
            thread::spawn(move || {

                let mut lp = Core::new().unwrap();
                let start_time = Local::now();
                let wanted_end_time = start_time + duration;
                let session = Session::new(lp.handle());

                let iterator = (0..desired_connections_per_worker).map(|_| {

                    let runinfo = RunInfo::new(duration);
                    let easy_request = make_easy(&url, timeout);
                    loop_fn((runinfo, easy_request), |(mut runinfo, easy_request)| {
                        send_request(easy_request, &session)
                            .then(|res| -> Result<_, ()> { match res {
                                // on request success
                                Ok((request_res, easy_request)) => {
                                    // update histogram
                                    request_res.latency.num_nanoseconds()
                                        .map(|x| runinfo.histogram.increment(x as u64).ok());
                                    runinfo.requests_completed += 1;

                                    let state = (runinfo, easy_request);

                                    let now_time = Local::now();
                                    if now_time < wanted_end_time {
                                        Ok(Loop::Continue(state))
                                    } else {
                                        Ok(Loop::Break(state))
                                    }
                                }
                                // on request failure
                                Err(mut err) => {
                                    runinfo.num_failed_requests += 1;
                                    // attempt to recover the easy handle from the error,
                                    // else make a new handle
                                    let easy_request = err.take_easy()
                                        .unwrap_or_else(|| make_easy(&url, timeout));
                                    let state = (runinfo, easy_request);
                                    let now_time = Local::now();
                                    if now_time < wanted_end_time {
                                        Ok(Loop::Continue(state))
                                    } else {
                                        Ok(Loop::Break(state))
                                    }
                                }
                            }})
                            .map_err(|_| ())
                    })
                        // Extract latency histogram
                        .map(|(histogram, _)| histogram)
                });
                let future = stream::futures_unordered(iterator)
                    .fold(RunInfo::new(duration), |mut runinfo_acc, runinfo| {
                        runinfo_acc.merge(&runinfo);
                        ok(runinfo_acc)
                    });
                let res = lp.run(future).unwrap();
                tx.send(res).unwrap();
            });
        }
        // collect information from all workers
        rx.iter()
            .take(self.num_threads)
            .fold(RunInfo::new(config.duration), |mut runinfo_acc, runinfo| {
                runinfo_acc.merge(&runinfo);
                runinfo_acc
            })
    }
}

fn start(config: Config) -> RunInfo {
    let boss = Boss::new(config.num_threads);
    boss.start_workforce(&config)
}

fn nanoseconds_to_milliseconds(nanoseconds: u64) -> f64 {
    nanoseconds as f64 / 1_000_000_f64
}

/// Presents the results to the user
fn present(run_info: RunInfo) {
    let requests_completed = run_info.requests_completed;
    println!("{} requests in {}s",
             requests_completed,
             run_info.duration.num_seconds());
    println!("{} failed requests", run_info.num_failed_requests);
    println!("Requests per second: {}", run_info.requests_per_second());

    // we can only print the latency distribution if there is data in the histogram
    if run_info.histogram.entries() > 0 {
        println!("Latency distribution: \n50%: {} ms\n75%: {} ms\n90%: {} ms\n95%: {} ms\n99%: {} ms",
            run_info.histogram.percentile(50.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(75.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(90.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(95.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(99.0).map(nanoseconds_to_milliseconds).unwrap(),
        );
    }

}

fn main() {
    env_logger::init().unwrap();
    let config = parse_args();
    let run_info = start(config);
    present(run_info)
}
<|endoftext|>


<commit_before>extern crate image;

use std::fs;
use std::env;
use std::path::Path;
use std::path::PathBuf;
use std::io;
use std::io::BufReader;
use std::io::BufRead;
use std::fs::File;
use image::GenericImage;

const IMPORTED: &'static str = "./.imported";
const OUT_PATH: &'static str = "~/Documemts/Notebook";

//should be a trait.  I am not sure how to impliment one for only Vec <Vec <bool>> and not Vec <T>
fn boundless_insert(y: i64, x: i64, value: bool, img: &mut Vec<Vec <bool>>) {
	let mut tx = x;
	let mut ty = y;
	for i in 0..img.len(){
		let mut ttx = x;
		while ttx < 0 {
			img[i].insert(0, false);
			ttx += 1;
		}
		assert!(((x < 0) && (ttx == 0)) || (x == ttx), "Expected ttx = zero or ttx = x.  Found {}", ttx);
		while img[i].len() <= ttx as usize {
			img[i].push(false);
		}
		tx = ttx
	}
	let mut row:Vec <bool> = Vec::new();
	for i in 0..img[0].len() {
		row.push(false);
	}
	while ty < 0 {
		img.insert(0, row.clone());
		ty += 1;
	}
	assert!(((y < 0) && (ty == 0)) || (y == ty), "Expected ty = zero or ty = y.  Found {}", ty);
	while img.len() <= ty as usize {
		img.push(row.clone())
	}
	img[ty as usize][tx as usize] = value;
}

struct ImgBlob {
	blob_type: u8, //0: object, 1: line
	top_right: [usize; 2],
	middle: [usize; 2],
	bitmap: Vec <Vec <bool>>
}

impl ImgBlob {
	fn from_top_right(x: usize, y: usize, claim: &mut Vec <Vec <bool>>, img: &Vec <Vec <bool>>) -> Option<ImgBlob>{
		let mut right = x;
		let mut top = y; //pretty sure that this is not supposed to change and can be eliminated
		let mut bitmap: Vec <Vec <bool>> = Vec::new();
		let mut queue: Vec <[usize; 2]> = Vec::new();
		claim[y][x] = true;
		queue.push([y+1, x]);
		queue.push([y, x+1]);
		bitmap.push(vec![true]);
		while queue.len() > 0 {
			let tempx = queue[0][1];
			let tempy = queue[0][0];
			if (tempy < img.len()) && (tempx < img[0].len()) && (tempy > 0) && (tempx > 0) {
				if img[tempy][tempx] && !(claim[tempy][tempx]){
					boundless_insert(((tempy as i64)-(top as i64)), ((tempx as i64)-(right as i64)), true, &mut bitmap);
					if tempx < right {
						right = tempx;
					}
					if tempy < top {
						top = tempy;
					}
					claim[tempy][tempx] = true;
					if queue[0][1] > top {
						queue.push([tempy-1, tempx]);
					}
					queue.push([tempy, tempx-1]);
					queue.push([tempy+1, tempx]);
					queue.push([tempy, tempx+1]);
				}
			}
			queue.remove(0);
		}
		if (bitmap[0].len() + bitmap.len() > 6) && (bitmap.len() > 3) && (bitmap[0].len() > 3) {
			println!("Found: {} x {}", bitmap[0].len(), bitmap.len());
			Some(ImgBlob {
				blob_type: if (bitmap[0].len() / bitmap.len()) > 10 {1} else {0},
				top_right: [right, top],
				middle: [right+(bitmap[0].len()/2), top+(bitmap.len()/2)],
				bitmap: bitmap
			})
		} else {
			None
		}
	}
}

struct Page {
	blobs: Vec <ImgBlob>,
	dimensions: [u32; 2]
}

struct Chapter {
	heading: ImgBlob,
	blobs: Vec <ImgBlob>,
	subchapters: Vec <Chapter>
}

fn get_images() -> Vec <String> {
	fn get_imported_images() -> Vec <String> {
		println!("test");
		if Path::new(IMPORTED).exists() {
			println!("test");
			let mut list: Vec <String> = Vec::new();
			let f = (File::open(IMPORTED)).unwrap();
			let mut file = BufReader::new(&f);
			for line in file.lines() {
				println!("test");
				let templ = line.unwrap();
				println!("{}", &templ);
				list.push(templ);
			}
			list
		} else {
			Vec::new()
		}
	}
	fn parse_input(uin: String, mpaths: Vec <String>, new: &mut Vec <String>) -> Vec <String> {
		let mut selected: Vec <String> = Vec::new();
		let stringified: Vec <String> = uin.split(' ').map(|x| x.to_string()).collect();
		for sel in stringified {
			if sel == "+" {
				selected.append(new);
			} else if sel.to_string().contains("-") {
				let numbers: Vec <String> = uin.split('-').map(|x| x.to_string()).collect();
				let start = numbers[0].parse::<usize>().unwrap();
				let end = numbers[1].parse::<usize>().unwrap();
				for i in start..end {
					selected.push(mpaths[i].clone());
				}
			} else {
				let i = sel.to_string().parse::<usize>().unwrap();
				selected.push(mpaths[i].clone());
			}
		}
		println!("{:?}", selected);
		selected
	}
	let paths = fs::read_dir("./").unwrap();
	let mut mpaths: Vec <String> = Vec::new();
	let mut new: Vec <String> = Vec::new();
	let mut imported: Vec <String> = get_imported_images();
	for p in paths {
		let path = p.unwrap().path();
		if !(path.extension() == None) {
			//The next line needs to be cleaned up.  It is written like this to appease the borrow checker
			if path.extension().unwrap() == "png" || path.extension().unwrap() == "jpg" || path.extension().unwrap() == "bpm" || path.extension().unwrap() == "gif" {
				mpaths.push(path.into_os_string().into_string().unwrap());//ugly hack but as_path().to_string() does not work
			}
		}
	}
	mpaths.sort();
	let mut fiter:usize = 0;
	for p in &mpaths {
		if !imported.contains(p) {
			print!("+");
			new.push(p.clone());//cannot pass borrowed var w/o cloning
		}
		println!("	{}:	{}", fiter, p);
		fiter += 1;
	}
	println!("Enter an number to select an image to import.  ");
	println!("Enter 5-6 to import images 5 through 6.  ");
	println!("Enter + to import the images you have not imported.  (These images are indicated in the list by + signs)");
	println!("Use spaces to seperate multiple selections.  ");
	println!("select: ");
	let mut uin = String::new();
	io::stdin().read_line(&mut uin).ok().expect("Error reading line");
	uin.pop();
	parse_input(uin, mpaths, &mut new)
}

fn main() {
	//iterate through images pulling out blobs
	//iterate through pages parsing blobs and creating chapters
	let selected = get_images();
	//need to run some kind of sort
	/*let mut claimed: Vec <Vec <bool>> = Vec::new();
	let mut thresh: Vec <Vec <bool>> = Vec::new();
	let mut rblobs: Vec <ImgBlob> = Vec::new();
	let mut gblobs: Vec <ImgBlob> = Vec::new();
	let mut bblobs: Vec <ImgBlob> = Vec::new();
	let img = image::open(&Path::new("target.jpg")).unwrap();
	let mut row:Vec <bool> = Vec::new();
	for x in 0..img.width() {
		row.push(false);
	}
	for y in 0..img.height() {
		claimed.push(row.clone());
		thresh.push(row.clone());
	}
	let rgbimg = img.to_rgb();
	for (x, y, pixel) in rgbimg.enumerate_pixels() {
		thresh[y as usize][x as usize] = if (pixel[0] > 140) && (pixel[1] <= 170) && (pixel[2] <= 170) {true} else {false};
	}
	for y in 0..thresh.len() {
		for x in 0..thresh[0].len() {
			if thresh[y][x] {
				match ImgBlob::from_top_right(x, y, &mut claimed, &thresh){
					Some(o) => rblobs.push(o),
					None => {},
				}
			}
		}
	}
	println!("Finished R");
	for (x, y, pixel) in rgbimg.enumerate_pixels() {
		thresh[y as usize][x as usize] = if (pixel[1] > 140) && (pixel[0] <= 170) && (pixel[2] <= 170) {true} else {false};
	}
	for y in 0..thresh.len() {
		for x in 0..thresh[0].len() {
			if thresh[y][x] {
				match ImgBlob::from_top_right(x, y, &mut claimed, &thresh){
					Some(o) => gblobs.push(o),
					None => {},
				}
			}
		}
	}
	println!("Finished G");
	for (x, y, pixel) in rgbimg.enumerate_pixels() {
		thresh[y as usize][x as usize] = if (pixel[2] > 140) && (pixel[1] <= 170) && (pixel[0] <= 170) {true} else {false};
	}
	for y in 0..thresh.len() {
		for x in 0..thresh[0].len() {
			if thresh[y][x] {
				match ImgBlob::from_top_right(x, y, &mut claimed, &thresh){
					Some(o) => bblobs.push(o),
					None => {},
				}
			}
		}
	}
	println!("Finished B");*/
}
<commit_msg>Organization and multiple image parsing<commit_after>extern crate image;

use std::fs;
use std::env;
use std::path::Path;
use std::path::PathBuf;
use std::io;
use std::io::BufReader;
use std::io::BufRead;
use std::fs::File;
use image::GenericImage;

const IMPORTED: &'static str = "./.imported";
const OUT_PATH: &'static str = "~/Documemts/Notebook";

//should be a trait.  I am not sure how to impliment one for only Vec <Vec <bool>> and not Vec <T>
fn boundless_insert(y: i64, x: i64, value: bool, img: &mut Vec<Vec <bool>>) {
	let mut tx = x;
	let mut ty = y;
	for i in 0..img.len(){
		let mut ttx = x;
		while ttx < 0 {
			img[i].insert(0, false);
			ttx += 1;
		}
		assert!(((x < 0) && (ttx == 0)) || (x == ttx), "Expected ttx = zero or ttx = x.  Found {}", ttx);
		while img[i].len() <= ttx as usize {
			img[i].push(false);
		}
		tx = ttx
	}
	let mut row:Vec <bool> = Vec::new();
	for i in 0..img[0].len() {
		row.push(false);
	}
	while ty < 0 {
		img.insert(0, row.clone());
		ty += 1;
	}
	assert!(((y < 0) && (ty == 0)) || (y == ty), "Expected ty = zero or ty = y.  Found {}", ty);
	while img.len() <= ty as usize {
		img.push(row.clone())
	}
	img[ty as usize][tx as usize] = value;
}

struct ImgBlob {
	blob_type: u8, //0: object, 1: line
	top_right: [usize; 2],
	middle: [usize; 2],
	bitmap: Vec <Vec <bool>>
}

impl ImgBlob {
	fn from_top_right(x: usize, y: usize, claim: &mut Vec <Vec <bool>>, img: &Vec <Vec <bool>>) -> Option<ImgBlob>{
		let mut right = x;
		let mut top = y; //pretty sure that this is not supposed to change and can be eliminated
		let mut bitmap: Vec <Vec <bool>> = Vec::new();
		let mut queue: Vec <[usize; 2]> = Vec::new();
		claim[y][x] = true;
		queue.push([y+1, x]);
		queue.push([y, x+1]);
		bitmap.push(vec![true]);
		while queue.len() > 0 {
			let tempx = queue[0][1];
			let tempy = queue[0][0];
			if (tempy < img.len()) && (tempx < img[0].len()) && (tempy > 0) && (tempx > 0) {
				if img[tempy][tempx] && !(claim[tempy][tempx]){
					boundless_insert(((tempy as i64)-(top as i64)), ((tempx as i64)-(right as i64)), true, &mut bitmap);
					if tempx < right {
						right = tempx;
					}
					if tempy < top {
						top = tempy;
					}
					claim[tempy][tempx] = true;
					if queue[0][1] > top {
						queue.push([tempy-1, tempx]);
					}
					queue.push([tempy, tempx-1]);
					queue.push([tempy+1, tempx]);
					queue.push([tempy, tempx+1]);
				}
			}
			queue.remove(0);
		}
		if (bitmap[0].len() + bitmap.len() > 6) && (bitmap.len() > 3) && (bitmap[0].len() > 3) {
			println!("Found: {} x {}", bitmap[0].len(), bitmap.len());
			Some(ImgBlob {
				blob_type: if (bitmap[0].len() / bitmap.len()) > 10 {1} else {0},
				top_right: [right, top],
				middle: [right+(bitmap[0].len()/2), top+(bitmap.len()/2)],
				bitmap: bitmap
			})
		} else {
			None
		}
	}
}

struct Page {
	rblobs: Vec <ImgBlob>,
	gblobs: Vec <ImgBlob>,
	bblobs: Vec <ImgBlob>,
	dimensions: [u32; 2]
}

impl Page {
	fn from_path(path: String) -> Page {
		let mut claimed: Vec <Vec <bool>> = Vec::new();
		let mut thresh: Vec <Vec <bool>> = Vec::new();
		let mut rblobs: Vec <ImgBlob> = Vec::new();
		let mut gblobs: Vec <ImgBlob> = Vec::new();
		let mut bblobs: Vec <ImgBlob> = Vec::new();
		let img = image::open(&Path::new(&path)).unwrap();
		let mut row:Vec <bool> = Vec::new();
		for x in 0..img.width() {
			row.push(false);
		}
		for y in 0..img.height() {
			claimed.push(row.clone());
			thresh.push(row.clone());
		}
		let rgbimg = img.to_rgb();
		for (x, y, pixel) in rgbimg.enumerate_pixels() {
			thresh[y as usize][x as usize] = if (pixel[0] > 140) && (pixel[1] <= 170) && (pixel[2] <= 170) {true} else {false};
		}
		for y in 0..thresh.len() {
			for x in 0..thresh[0].len() {
				if thresh[y][x] {
					match ImgBlob::from_top_right(x, y, &mut claimed, &thresh){
						Some(o) => rblobs.push(o),
						None => {},
					}
				}
			}
		}
		println!("Finished R");
		for (x, y, pixel) in rgbimg.enumerate_pixels() {
			thresh[y as usize][x as usize] = if (pixel[1] > 140) && (pixel[0] <= 170) && (pixel[2] <= 170) {true} else {false};
		}
		for y in 0..thresh.len() {
			for x in 0..thresh[0].len() {
				if thresh[y][x] {
					match ImgBlob::from_top_right(x, y, &mut claimed, &thresh){
						Some(o) => gblobs.push(o),
						None => {},
					}
				}
			}
		}
		println!("Finished G");
		for (x, y, pixel) in rgbimg.enumerate_pixels() {
			thresh[y as usize][x as usize] = if (pixel[2] > 140) && (pixel[1] <= 170) && (pixel[0] <= 170) {true} else {false};
		}
		for y in 0..thresh.len() {
			for x in 0..thresh[0].len() {
				if thresh[y][x] {
					match ImgBlob::from_top_right(x, y, &mut claimed, &thresh){
						Some(o) => bblobs.push(o),
						None => {},
					}
				}
			}
		}
		println!("Finished B");
		Page {
			rblobs: rblobs,
			gblobs: gblobs,
			bblobs: bblobs,
			dimensions: [img.width(), img.height()]
		}
	}
}

struct Chapter {
	heading: ImgBlob,
	blobs: Vec <ImgBlob>,
	subchapters: Vec <Chapter>
}

fn get_images() -> Vec <String> {
	fn get_imported_images() -> Vec <String> {
		println!("test");
		if Path::new(IMPORTED).exists() {
			println!("test");
			let mut list: Vec <String> = Vec::new();
			let f = (File::open(IMPORTED)).unwrap();
			let mut file = BufReader::new(&f);
			for line in file.lines() {
				println!("test");
				let templ = line.unwrap();
				println!("{}", &templ);
				list.push(templ);
			}
			list
		} else {
			Vec::new()
		}
	}
	fn parse_input(uin: String, mpaths: Vec <String>, new: &mut Vec <String>) -> Vec <String> {
		let mut selected: Vec <String> = Vec::new();
		let stringified: Vec <String> = uin.split(' ').map(|x| x.to_string()).collect();
		for sel in stringified {
			if sel == "+" {
				selected.append(new);
			} else if sel.to_string().contains("-") {
				let numbers: Vec <String> = uin.split('-').map(|x| x.to_string()).collect();
				let start = numbers[0].parse::<usize>().unwrap();
				let end = numbers[1].parse::<usize>().unwrap();
				for i in start..end {
					selected.push(mpaths[i].clone());
				}
			} else {
				let i = sel.to_string().parse::<usize>().unwrap();
				selected.push(mpaths[i].clone());
			}
		}
		selected
	}
	let paths = fs::read_dir("./").unwrap();
	let mut mpaths: Vec <String> = Vec::new();
	let mut new: Vec <String> = Vec::new();
	let mut imported: Vec <String> = get_imported_images();
	for p in paths {
		let path = p.unwrap().path();
		if !(path.extension() == None) {
			//The next line needs to be cleaned up.  It is written like this to appease the borrow checker
			if path.extension().unwrap() == "png" || path.extension().unwrap() == "jpg" || path.extension().unwrap() == "bpm" || path.extension().unwrap() == "gif" {
				mpaths.push(path.into_os_string().into_string().unwrap());//ugly hack but as_path().to_string() does not work
			}
		}
	}
	mpaths.sort();
	let mut fiter:usize = 0;
	for p in &mpaths {
		if !imported.contains(p) {
			print!("+");
			new.push(p.clone());//cannot pass borrowed var w/o cloning
		}
		println!("	{}:	{}", fiter, p);
		fiter += 1;
	}
	println!("Enter an number to select an image to import.  ");
	println!("Enter 5-6 to import images 5 through 6.  ");
	println!("Enter + to import the images you have not imported.  (These images are indicated in the list by + signs)");
	println!("Use spaces to seperate multiple selections.  ");
	println!("select: ");
	let mut uin = String::new();
	io::stdin().read_line(&mut uin).ok().expect("Error reading line");
	uin.pop();
	parse_input(uin, mpaths, &mut new)
}

fn main() {
	//iterate through images pulling out blobs
	//iterate through pages parsing blobs and creating chapters
	let selected = get_images();
	let mut pages: Vec <Page> = Vec::new();
	for img in selected {
		pages.push(Page::from_path(img));
	}
}
<|endoftext|>


<commit_before>#[macro_use] extern crate log;
extern crate url;
extern crate rustc_serialize;
extern crate clap;

use clap::{Arg, App};
use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::os::unix::process::ExitStatusExt;
use std::process::Command;
use std::thread;
use url::Url;

fn main() {
    let matches = App::new("ffto")
        .version("0.0.2")
        .author("Timothée Ravier <tim@siosm.fr>")
        .about("Open URLs received as input in the default browser")
        .arg(Arg::with_name("command")
             .short("c")
             .long("command")
             .value_name("cmd")
             .help("Command executed for each URL received")
             .default_value("xdg-open"))
        .arg(Arg::with_name("listen address")
             .short("l")
             .long("listen-address")
             .value_name("addr")
             .help("Address and port to listen to")
             .default_value("127.0.0.1:7777"))
        .get_matches();

    let browser_command = matches.value_of("command").unwrap().to_string();
    let address = matches.value_of("listen address").unwrap();

    let listener = match TcpListener::bind(&address[..]) {
        Ok(l)  => l,
        Err(e) => panic!("Could not bind to {}: {}", address, e)
    };

    for stream in listener.incoming() {
        match stream {
            Ok(stream)  => {
                debug!("Accepted new incoming connection");
                let command = browser_command.clone();
                thread::spawn(move || {
                    debug!("Spawned new thread to handle connection");
                    handle_client(stream, command)
                });
            },
            Err(e) => panic!("Could not handle incoming connection: {}", e)
        }
    }

    drop(listener);
}

fn handle_client(mut stream: TcpStream, browser_command: String) {
    let mut message = String::new();
    match stream.read_to_string(& mut message) {
        Ok(_) => {},
        Err(e) => {
            error!("Input isn't a valid UTF-8 string: {}", e);
            return
        }
    };

    for line in message.split('\n') {
        debug!("Current line is: {}", line);
        match Url::parse(line) {
            Ok(u)  => {
                debug!("Found URL in: {}", line);
                if url_valid(&u) {
                    debug!("Found URL is valid");
                    spawn_browser(&browser_command, &format!("{}", u));
                }
            },
            Err(e) => info!("No URL found in '{}': {}", line, e)
        }
    }
}

fn url_valid(u: &Url) -> bool {
    (u.scheme() == "http" || u.scheme() == "https")
        && u.host().is_some()
}

fn spawn_browser(command: &str, url: &str) {
    debug!("Spawning process: {} {}", command, url);
    let output = match Command::new(command).arg(url).output() {
        Ok(output) => output,
        Err(e) => panic!("Failed to spawn process '{} {}': {}", command, url, e)
    };
    if output.status.success() {
        debug!("Process exited successfully");
    } else {
        match output.status.code() {
            None => {
                match output.status.signal() {
                    None => panic!("Should never happen!"),
                    Some(i) => panic!("Process received signal: {}", i)
                }
            }
            Some(i) => panic!("Process exited with status: {}", i)
        }
        info!("stdout:\n{:?}\n\nstderr:\n{:?}", output.stdout, output.stderr);
    }
}
<commit_msg>Display stdout & stderr before panic!<commit_after>#[macro_use] extern crate log;
extern crate url;
extern crate rustc_serialize;
extern crate clap;

use clap::{Arg, App};
use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::os::unix::process::ExitStatusExt;
use std::process::Command;
use std::thread;
use url::Url;

fn main() {
    let matches = App::new("ffto")
        .version("0.0.2")
        .author("Timothée Ravier <tim@siosm.fr>")
        .about("Open URLs received as input in the default browser")
        .arg(Arg::with_name("command")
             .short("c")
             .long("command")
             .value_name("cmd")
             .help("Command executed for each URL received")
             .default_value("xdg-open"))
        .arg(Arg::with_name("listen address")
             .short("l")
             .long("listen-address")
             .value_name("addr")
             .help("Address and port to listen to")
             .default_value("127.0.0.1:7777"))
        .get_matches();

    let browser_command = matches.value_of("command").unwrap().to_string();
    let address = matches.value_of("listen address").unwrap();

    let listener = match TcpListener::bind(&address[..]) {
        Ok(l)  => l,
        Err(e) => panic!("Could not bind to {}: {}", address, e)
    };

    for stream in listener.incoming() {
        match stream {
            Ok(stream)  => {
                debug!("Accepted new incoming connection");
                let command = browser_command.clone();
                thread::spawn(move || {
                    debug!("Spawned new thread to handle connection");
                    handle_client(stream, command)
                });
            },
            Err(e) => panic!("Could not handle incoming connection: {}", e)
        }
    }

    drop(listener);
}

fn handle_client(mut stream: TcpStream, browser_command: String) {
    let mut message = String::new();
    match stream.read_to_string(& mut message) {
        Ok(_) => {},
        Err(e) => {
            error!("Input isn't a valid UTF-8 string: {}", e);
            return
        }
    };

    for line in message.split('\n') {
        debug!("Current line is: {}", line);
        match Url::parse(line) {
            Ok(u)  => {
                debug!("Found URL in: {}", line);
                if url_valid(&u) {
                    debug!("Found URL is valid");
                    spawn_browser(&browser_command, &format!("{}", u));
                }
            },
            Err(e) => info!("No URL found in '{}': {}", line, e)
        }
    }
}

fn url_valid(u: &Url) -> bool {
    (u.scheme() == "http" || u.scheme() == "https")
        && u.host().is_some()
}

fn spawn_browser(command: &str, url: &str) {
    debug!("Spawning process: {} {}", command, url);
    let output = match Command::new(command).arg(url).output() {
        Ok(output) => output,
        Err(e) => panic!("Failed to spawn process '{} {}': {}", command, url, e)
    };
    if output.status.success() {
        debug!("Process exited successfully");
    } else {
        info!("stdout:\n{:?}\n\nstderr:\n{:?}",
              output.stdout,
              output.stderr);
        match output.status.code() {
            None => {
                match output.status.signal() {
                    None => panic!("Should never happen!"),
                    Some(i) => panic!("Process received signal: {}", i)
                }
            }
            Some(i) => panic!("Process exited with status: {}", i)
        }
    }
}
<|endoftext|>