<commit_before>#![feature(phase)]
extern crate serialize;
#[phase(plugin)] extern crate docopt_macros;
extern crate docopt;
extern crate core;
extern crate collections;
extern crate regex;
#[phase(plugin)] extern crate regex_macros;

use docopt::FlagParser;
use std::io::BufferedReader;
use std::io::BufferedWriter;
use std::io::File;
use core::result::Result;
use collections::string::String;
use collections::vec::Vec;
use collections::PriorityQueue;

// Interleaver takes N files (e.g., i_1.txt ... i_N.txt) and transforms them into N output files
// named o_1.txt ... o_N.txt.  The lines of the input files must be prefixed with time stamps.  The
// output files contain the same content as the input files but only one file is allowed to make
// progress on a given line.  The other files are empty.

docopt!(Args, "
Usage:
  interleaver <file>...

Options:
  -h, --help    Show this screen.
", )

type BrType = BufferedReader<core::result::Result<std::io::fs::File,std::io::IoError>>;
type BwType = BufferedWriter<core::result::Result<std::io::fs::File,std::io::IoError>>;

#[deriving(Show,PartialEq,PartialOrd,Eq,Ord)]
enum Month { Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec }

fn string_to_month (s : &str) -> Option<Month> {
  match s {
    "Jan" => Some (Jan),
    "Feb" => Some (Feb),
    "Mar" => Some (Mar),
    "Apr" => Some (Apr),
    "May" => Some (May),
    "Jun" => Some (Jun),
    "Jul" => Some (Jul),
    "Aug" => Some (Aug),
    "Sep" => Some (Sep),
    "Oct" => Some (Oct),
    "Nov" => Some (Nov),
    "Dec" => Some (Dec),
    _     => None
  }
}

#[deriving(Show,PartialEq,Eq)]
struct TimedLine {
  l       : String,
  month   : Month,
  day     : u8,
  hour    : u8,
  minute  : u8,
  second  : u8,
  usecond : u32,
  target  : uint
}

impl TimedLine {
  fn new (l : &str, target : uint) -> TimedLine {
    let re = regex! (r"^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) +(\d+) (\d+):(\d+):(\d+) (\d+):.*$");
    let caps = match re.captures (l) {
      None => fail! ("Could not analyze line: \"{}\"", l),
      Some (caps) => caps
    };
    let (month, day, hour, minute, second, usecond) =
      match (string_to_month (caps.at(1)), from_str::<u8>(caps.at(2)), from_str::<u8> (caps.at(3)), from_str::<u8> (caps.at(4)), from_str::<u8> (caps.at(5)), from_str::<u32> (caps.at(6))
      ) {
        (Some (month), Some (day), Some (hour), Some (minute), Some (second), Some (usecond)) => (month, day, hour, minute, second, usecond*100),
        _ => fail! ("Could not extract fields from line: \"{}\"", l)
      };

    TimedLine {
      l       : String::from_str (l),
      month   : month,
      day     : day,
      hour    : hour,
      minute  : minute,
      second  : second,
      usecond : usecond,
      target  : target
    }
  }
}

impl Ord for TimedLine {
  fn cmp(&self, other: &TimedLine) -> Ordering {
    let res = {
      let month_cmp = self.month.cmp (&other.month);
      if month_cmp == Equal {
        let day_cmp = self.day.cmp (&other.day);
        if day_cmp == Equal {
          let hour_cmp = self.hour.cmp (&other.hour);
          if hour_cmp == Equal {
            let minute_cmp = self.minute.cmp (&other.minute);
            if minute_cmp == Equal {
              let second_cmp = self.second.cmp (&other.second);
              if second_cmp == Equal {
                self.usecond.cmp (&other.usecond)
              }
              else { second_cmp }
            }
            else { minute_cmp }
          }
          else { hour_cmp }
        }
        else { day_cmp }
      }
      else { month_cmp }
    };

    match res {
      Greater => Less,
      Equal => Equal,
      Less => Greater
    }
  }
}

impl PartialOrd for TimedLine {
  fn partial_cmp(&self, other: &TimedLine) -> Option<Ordering> {
    Some (self.cmp (other))
  }
}

struct TimedLineQueue<'a> {
  q : PriorityQueue<TimedLine>,
  readers : &'a mut Vec<BrType>
}

impl<'a> TimedLineQueue<'a> {
  fn fill_que (&mut self, target : uint) {
    let reader = self.readers.get_mut (target);
    let mut lines = reader.lines ();
    let line_opt = lines.next ();
    match line_opt {
      Some (line) => {
        let line = line.unwrap();
        let line = line.as_slice();
        let line = line.trim_chars('\n');
        let timed_line = TimedLine::new (line, target);
        self.q.push (timed_line);
      }
      None => ()
    }
  }

  fn new (readers : & 'a mut Vec<BrType>) -> TimedLineQueue {
    let mut res = TimedLineQueue {
      q : PriorityQueue::new (),
      readers : readers
    };

    for (i, _) in res.readers.mut_iter().enumerate () {
      res.fill_que(i);
    }

    res
  }
}

#[test]
fn test_priority_queue () {
  let mut q = PriorityQueue::new ();

  let s1 = String::from_str ("Sep  2 14:25:02 8993: (main|info): --- NODE STARTED ---");
  let s2 = String::from_str ("Sep  2 14:25:02 9488: (main|info): --- NODE STARTED ---");

  let tl_1 = TimedLine::new (s1.as_slice(), 5u);
  let tl_2 = TimedLine::new (s2.as_slice(), 5u);

  q.push (s1.clone ());
  q.push (s2.clone ());

  assert_eq! (q.pop(), Some(s2.clone ()));
  assert_eq! (q.pop(), Some(s1.clone ()));

  q.push (s2.clone ());
  q.push (s1.clone ());

  assert_eq! (q.pop(), Some(s2.clone ()));
  assert_eq! (q.pop(), Some(s1.clone ()));
}

impl<'a> Iterator<TimedLine> for TimedLineQueue<'a> {
  fn next (&mut self) -> Option<TimedLine> {
    let timed_line_opt = self.q.pop ();
    match timed_line_opt {
      None => (),
      Some (ref timed_line) => {
        self.fill_que(timed_line.target);
      }
    }

    timed_line_opt
  }
}

#[test]
fn test_timed_line_ordering () {
  let s1 = String::from_str ("Sep  2 14:25:02 8993: (main|info): --- NODE STARTED ---");
  let s2 = String::from_str ("Sep  2 14:25:02 9488: (main|info): --- NODE STARTED ---");

  let tl_1 = TimedLine::new (s1.as_slice(), 5u);
  let tl_2 = TimedLine::new (s2.as_slice(), 5u);

  assert! (tl_1.cmp (&tl_2) == Less);
  assert! (tl_2.cmp (&tl_1) == Greater);
  assert! (tl_1.cmp (&tl_1) == Equal);
  assert! (tl_2.cmp (&tl_2) == Equal);
}

fn main() {
  let args: Args = FlagParser::parse().unwrap_or_else(|e| e.exit());

  let file_names = args.arg_file;
  let file_count = file_names.len ();
  let paths_it = file_names.iter ().map (|n| Path::new(n.clone ()));
  let files_it = paths_it.map (|path| File::open(&path));
  let mut buffered_readers_it = files_it.map (|file| BufferedReader::new (file));
  let mut buffered_readers_vec : Vec<BrType> = buffered_readers_it.collect ();

  let paths_out_it = range (0u, file_names.len ()).map (|i| Path::new (format!("{}.txt", i)));
  let files_out_it = paths_out_it.map (|path_out| {
    let res = File::create(&path_out);
    match res {
      Ok(_)  => (),
      Err(e) => fail!("Could not open output file: {}", e)
    };
    res
  }
  );
  let mut buffered_writers_it = files_out_it.map (|file_out| BufferedWriter::new (file_out) );

  let mut buffered_writers_vec : Vec<BwType> = buffered_writers_it.collect ();

  let mut tlq = TimedLineQueue::new (& mut buffered_readers_vec);
  for timed_line in tlq {
    let target = timed_line.target;
    {
      let writer = buffered_writers_vec.get_mut (target);
      let res = writer.write_line (format!("{}", timed_line.l).as_slice ());
      match res {
        Ok(_) => (),
        Err(e) => {
          let ref file_name = file_names[target];
          fail! ("Could not write to {}: {}", file_name, e);
        }
      };
    }

    for i in range (0, file_count).filter (|i| *i != target) {
      let writer = buffered_writers_vec.get_mut (i);
      let res = writer.write_line ("");
      match res {
        Ok(_) => (),
        Err(e) => {
          let ref file_name = file_names[i];
          fail! ("Could not write to {}: {}", file_name, e);
        }
      };
    }
  }
}
<commit_msg>Keep up with Rust compiler<commit_after>#![feature(phase)]
extern crate serialize;
#[phase(plugin)] extern crate docopt_macros;
extern crate docopt;
extern crate core;
extern crate collections;
extern crate regex;
#[phase(plugin)] extern crate regex_macros;

use docopt::FlagParser;
use std::io::BufferedReader;
use std::io::BufferedWriter;
use std::io::File;
use core::result::Result;
use collections::string::String;
use collections::vec::Vec;
use collections::PriorityQueue;

// Interleaver takes N files (e.g., i_1.txt ... i_N.txt) and transforms them into N output files
// named o_1.txt ... o_N.txt.  The lines of the input files must be prefixed with time stamps.  The
// output files contain the same content as the input files but only one file is allowed to make
// progress on a given line.  The other files are empty.

docopt!(Args, "
Usage:
  interleaver <file>...

Options:
  -h, --help    Show this screen.
", )

type BrType = BufferedReader<core::result::Result<std::io::fs::File,std::io::IoError>>;
type BwType = BufferedWriter<core::result::Result<std::io::fs::File,std::io::IoError>>;

#[deriving(Show,PartialEq,PartialOrd,Eq,Ord)]
enum Month { Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec }

fn string_to_month (s : &str) -> Option<Month> {
  match s {
    "Jan" => Some (Jan),
    "Feb" => Some (Feb),
    "Mar" => Some (Mar),
    "Apr" => Some (Apr),
    "May" => Some (May),
    "Jun" => Some (Jun),
    "Jul" => Some (Jul),
    "Aug" => Some (Aug),
    "Sep" => Some (Sep),
    "Oct" => Some (Oct),
    "Nov" => Some (Nov),
    "Dec" => Some (Dec),
    _     => None
  }
}

#[deriving(Show,PartialEq,Eq)]
struct TimedLine {
  l       : String,
  month   : Month,
  day     : u8,
  hour    : u8,
  minute  : u8,
  second  : u8,
  usecond : u32,
  target  : uint
}

impl TimedLine {
  fn new (l : &str, target : uint) -> TimedLine {
    let re = regex! (r"^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) +(\d+) (\d+):(\d+):(\d+) (\d+):.*$");
    let caps = match re.captures (l) {
      None => fail! ("Could not analyze line: \"{}\"", l),
      Some (caps) => caps
    };
    let (month, day, hour, minute, second, usecond) =
      match (string_to_month (caps.at(1)), from_str::<u8>(caps.at(2)), from_str::<u8> (caps.at(3)), from_str::<u8> (caps.at(4)), from_str::<u8> (caps.at(5)), from_str::<u32> (caps.at(6))
      ) {
        (Some (month), Some (day), Some (hour), Some (minute), Some (second), Some (usecond)) => (month, day, hour, minute, second, usecond*100),
        _ => fail! ("Could not extract fields from line: \"{}\"", l)
      };

    TimedLine {
      l       : String::from_str (l),
      month   : month,
      day     : day,
      hour    : hour,
      minute  : minute,
      second  : second,
      usecond : usecond,
      target  : target
    }
  }
}

impl Ord for TimedLine {
  fn cmp(&self, other: &TimedLine) -> Ordering {
    let res = {
      let month_cmp = self.month.cmp (&other.month);
      if month_cmp == Equal {
        let day_cmp = self.day.cmp (&other.day);
        if day_cmp == Equal {
          let hour_cmp = self.hour.cmp (&other.hour);
          if hour_cmp == Equal {
            let minute_cmp = self.minute.cmp (&other.minute);
            if minute_cmp == Equal {
              let second_cmp = self.second.cmp (&other.second);
              if second_cmp == Equal {
                self.usecond.cmp (&other.usecond)
              }
              else { second_cmp }
            }
            else { minute_cmp }
          }
          else { hour_cmp }
        }
        else { day_cmp }
      }
      else { month_cmp }
    };

    match res {
      Greater => Less,
      Equal => Equal,
      Less => Greater
    }
  }
}

impl PartialOrd for TimedLine {
  fn partial_cmp(&self, other: &TimedLine) -> Option<Ordering> {
    Some (self.cmp (other))
  }
}

struct TimedLineQueue<'a> {
  q : PriorityQueue<TimedLine>,
  readers : &'a mut Vec<BrType>
}

impl<'a> TimedLineQueue<'a> {
  fn fill_que (&mut self, target : uint) {
    let reader = self.readers.get_mut (target);
    let mut lines = reader.lines ();
    let line_opt = lines.next ();
    match line_opt {
      Some (line) => {
        let line = line.unwrap();
        let line = line.as_slice();
        let line = line.trim_chars('\n');
        let timed_line = TimedLine::new (line, target);
        self.q.push (timed_line);
      }
      None => ()
    }
  }

  fn new (readers : & 'a mut Vec<BrType>) -> TimedLineQueue {
    let readers_count = readers.len ();

    let mut res = TimedLineQueue {
      q : PriorityQueue::new (),
      readers : readers
    };

    for i in range (0, readers_count) {
      res.fill_que(i);
    }

    res
  }
}

#[test]
fn test_priority_queue () {
  let mut q = PriorityQueue::new ();

  let s1 = String::from_str ("Sep  2 14:25:02 8993: (main|info): --- NODE STARTED ---");
  let s2 = String::from_str ("Sep  2 14:25:02 9488: (main|info): --- NODE STARTED ---");

  let tl_1 = TimedLine::new (s1.as_slice(), 5u);
  let tl_2 = TimedLine::new (s2.as_slice(), 5u);

  q.push (s1.clone ());
  q.push (s2.clone ());

  assert_eq! (q.pop(), Some(s2.clone ()));
  assert_eq! (q.pop(), Some(s1.clone ()));

  q.push (s2.clone ());
  q.push (s1.clone ());

  assert_eq! (q.pop(), Some(s2.clone ()));
  assert_eq! (q.pop(), Some(s1.clone ()));
}

impl<'a> Iterator<TimedLine> for TimedLineQueue<'a> {
  fn next (&mut self) -> Option<TimedLine> {
    let timed_line_opt = self.q.pop ();
    match timed_line_opt {
      None => (),
      Some (ref timed_line) => {
        self.fill_que(timed_line.target);
      }
    }

    timed_line_opt
  }
}

#[test]
fn test_timed_line_ordering () {
  let s1 = String::from_str ("Sep  2 14:25:02 8993: (main|info): --- NODE STARTED ---");
  let s2 = String::from_str ("Sep  2 14:25:02 9488: (main|info): --- NODE STARTED ---");

  let tl_1 = TimedLine::new (s1.as_slice(), 5u);
  let tl_2 = TimedLine::new (s2.as_slice(), 5u);

  assert! (tl_1.cmp (&tl_2) == Less);
  assert! (tl_2.cmp (&tl_1) == Greater);
  assert! (tl_1.cmp (&tl_1) == Equal);
  assert! (tl_2.cmp (&tl_2) == Equal);
}

fn main() {
  let args: Args = FlagParser::parse().unwrap_or_else(|e| e.exit());

  let file_names = args.arg_file;
  let file_count = file_names.len ();
  let paths_it = file_names.iter ().map (|n| Path::new(n.clone ()));
  let files_it = paths_it.map (|path| File::open(&path));
  let mut buffered_readers_it = files_it.map (|file| BufferedReader::new (file));
  let mut buffered_readers_vec : Vec<BrType> = buffered_readers_it.collect ();

  let paths_out_it = range (0u, file_names.len ()).map (|i| Path::new (format!("{}.txt", i)));
  let files_out_it = paths_out_it.map (|path_out| {
    let res = File::create(&path_out);
    match res {
      Ok(_)  => (),
      Err(e) => fail!("Could not open output file: {}", e)
    };
    res
  }
  );
  let mut buffered_writers_it = files_out_it.map (|file_out| BufferedWriter::new (file_out) );

  let mut buffered_writers_vec : Vec<BwType> = buffered_writers_it.collect ();

  let mut tlq = TimedLineQueue::new (& mut buffered_readers_vec);
  for timed_line in tlq {
    let target = timed_line.target;
    {
      let writer = buffered_writers_vec.get_mut (target);
      let res = writer.write_line (format!("{}", timed_line.l).as_slice ());
      match res {
        Ok(_) => (),
        Err(e) => {
          let ref file_name = file_names[target];
          fail! ("Could not write to {}: {}", file_name, e);
        }
      };
    }

    for i in range (0, file_count).filter (|i| *i != target) {
      let writer = buffered_writers_vec.get_mut (i);
      let res = writer.write_line ("");
      match res {
        Ok(_) => (),
        Err(e) => {
          let ref file_name = file_names[i];
          fail! ("Could not write to {}: {}", file_name, e);
        }
      };
    }
  }
}
<|endoftext|>


<commit_before>#![feature(globs)]

extern crate rgtk;

use rgtk::*;
use rgtk::gtk::signals;

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

    let mut window = gtk::Window::new(gtk::window_type::TopLevel).unwrap();
    window.set_title("SolidOak");
    window.set_border_width(10);
    window.set_window_position(gtk::window_position::Center);
    window.set_default_size(1242, 768);

    window.connect(signals::DeleteEvent::new(|_| {
        gtk::main_quit();
        true
    }));

    window.show_all();
    gtk::main();
}
<commit_msg>Add four main panes<commit_after>#![feature(globs)]
extern crate rgtk;

use rgtk::*;
use rgtk::gtk::signals;

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

    let width = 1242;
    let height = 768;
    let sidebar_width = ((width as f32) * 0.32) as i32;
    let content_width = width - sidebar_width;
    let top_height = ((height as f32) * 0.8) as i32;

    let mut window = gtk::Window::new(gtk::window_type::TopLevel).unwrap();
    window.set_title("SolidOak");
    window.set_window_position(gtk::window_position::Center);
    window.set_default_size(width, height);

    window.connect(signals::DeleteEvent::new(|_| {
        gtk::main_quit();
        true
    }));

    let project_pane = gtk::TreeView::new().unwrap();
    let rusti_pane = gtk::TextView::new().unwrap();
    project_pane.set_size_request(sidebar_width, top_height);
    let mut vbox_1 = gtk::Box::new(gtk::orientation::Vertical, 0).unwrap();
    vbox_1.pack_start(&project_pane, false, true, 0);
    vbox_1.pack_start(&rusti_pane, true, true, 0);

    let editor_pane = gtk::TextView::new().unwrap();
    let build_pane = gtk::TextView::new().unwrap();
    editor_pane.set_size_request(content_width, top_height);
    let mut vbox_2 = gtk::Box::new(gtk::orientation::Vertical, 0).unwrap();
    vbox_2.pack_start(&editor_pane, false, true, 0);
    vbox_2.pack_start(&build_pane, true, true, 0);

    let mut hbox = gtk::Box::new(gtk::orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&vbox_1, false, true, 0);
    hbox.pack_start(&vbox_2, true, true, 0);
    window.add(&hbox);

    window.show_all();
    gtk::main();
}
<|endoftext|>


<commit_before>extern crate nalgebra as na;
extern crate kiss3d;
extern crate rand;
extern crate num;
extern crate clap;

use kiss3d::window::Window;
use na::{Pnt2, Pnt3, Vec2, Vec3, Norm, FloatPnt, FloatVec};
use rand::{Rng, Closed01};
use num::Zero;
use clap::{Arg, App};
use std::str::FromStr;

struct SpaceColonization<T, F>
    where T: FloatPnt<f32, F>,
          F: FloatVec<f32> + Zero + Copy
{
    node_parents: Vec<usize>,
    node_positions: Vec<T>,
    leaf_node_pointers: Vec<usize>, // XXX: how can a branch be terminated?
    leaf_node_grow_directions: Vec<F>,
}

impl<T, F> SpaceColonization<T, F>
    where T: FloatPnt<f32, F>,
          F: FloatVec<f32> + Zero + Copy
{
    fn new() -> SpaceColonization<T, F> {
        SpaceColonization {
            node_parents: Vec::new(),
            node_positions: Vec::new(),
            leaf_node_pointers: Vec::new(),
            leaf_node_grow_directions: Vec::new(),
        }
    }

    // adds initial root node(s)
    fn add_root_node(&mut self, pos: T) {
        debug_assert!(self.node_positions.len() == self.node_parents.len()); // invariant
        debug_assert!(self.leaf_node_pointers.len() == self.leaf_node_grow_directions.len()); // invariant

        let idx = self.node_positions.len();
        self.node_positions.push(pos);

        // a root node has it's own index as parent
        self.node_parents.push(idx);

        // a root node creates a new "leaf" node (better: branch!)
        self.leaf_node_pointers.push(idx);
        self.leaf_node_grow_directions.push(Zero::zero());

        debug_assert!(self.node_positions.len() == self.node_parents.len()); // invariant
        debug_assert!(self.leaf_node_pointers.len() == self.leaf_node_grow_directions.len()); // invariant
    }

    fn add_moved_leaf_node(&mut self, leaf_node_slot: usize, v: F) {
        let parent = self.leaf_node_pointers[leaf_node_slot];
        let parent_pos = self.node_positions[parent];

        self.update_leaf_node(leaf_node_slot, parent_pos + v)
    }

    fn update_leaf_node(&mut self, leaf_node_slot: usize, new_pos: T) {
        let parent = self.leaf_node_pointers[leaf_node_slot];

        // create new node
        let idx = self.node_positions.len();
        self.node_positions.push(new_pos);
        self.node_parents.push(parent);

        // update the leaf node pointer
        self.leaf_node_pointers[leaf_node_slot] = idx;
    }

    fn find_nearest_leaf_for_attraction_point(&self,
                                              attraction_pt: &T,
                                              radius_influence_sq: f32)
                                              -> Option<(usize, f32)> {
        let mut nearest_leaf: Option<usize> = None;
        let mut nearest_distance_sq: f32 = radius_influence_sq;

        for (i, &leaf_node_ptr) in self.leaf_node_pointers.iter().enumerate() {
            let leaf_pos = &self.node_positions[leaf_node_ptr];
            let dist_sq = leaf_pos.sqdist(attraction_pt);

            // is the attraction point within the radius of influence
            // and closed than the currently best
            if dist_sq < nearest_distance_sq {
                nearest_distance_sq = dist_sq;
                nearest_leaf = Some(i);
            }
        }

        return nearest_leaf.map(|idx| (idx, nearest_distance_sq));
    }

    fn iter_segments<C>(&self, callback: &mut C)
        where C: FnMut(&T, &T)
    {
        for (i, &parent) in self.node_parents.iter().enumerate() {
            if i != parent {
                callback(&self.node_positions[i], &self.node_positions[parent]);
            }
        }
    }

    fn iterate(&mut self,
               attraction_points: &mut [(T, bool)],
               radius_influce_sq: f32,
               move_distance: f32,
               kill_distance_sq: f32) {
        // reset all attraction forces
        for force in &mut self.leaf_node_grow_directions {
            *force = Zero::zero();
        }

        // for each attraction_point, find the nearest (leaf) node that it
        // influences.
        for ap in attraction_points.iter_mut() {
            let active = ap.1;
            if !active {
                continue;
            }
            let nearest_leaf_opt = self.find_nearest_leaf_for_attraction_point(&ap.0,
                                                                               radius_influce_sq);
            if let Some((nearest_leaf_slot, d_sq)) = nearest_leaf_opt {
                let nearest_leaf_node_idx = self.leaf_node_pointers[nearest_leaf_slot];
                // update the force with the normalized vector towards the attraction point
                let v = (ap.0 - self.node_positions[nearest_leaf_node_idx]).normalize();
                self.leaf_node_grow_directions[nearest_leaf_slot] =
                    self.leaf_node_grow_directions[nearest_leaf_slot] + v;

                if d_sq < kill_distance_sq {
                    // set inactive
                    ap.1 = false;
                }
            }
        }

        // now create new leaf nodes
        for i in 0..self.leaf_node_grow_directions.len() {
            let force = self.leaf_node_grow_directions[i];

            // update leaf node only if there is a force!
            if !force.is_zero() {
                let n = force.normalize();
                self.add_moved_leaf_node(i, n * move_distance);
            }
        }

    }
}

fn random_closed01<R: Rng>(rng: &mut R) -> f32 {
    rng.gen::<Closed01<f32>>().0
}

fn random_coord<R: Rng>(rng: &mut R) -> f32 {
    2.0 * random_closed01(rng) - 1.0
}

trait MyPoint {
    fn into_pnt3(self) -> Pnt3<f32>;
    fn random<R: Rng>(rng: &mut R) -> Self;
}

impl MyPoint for Pnt3<f32> {
    fn into_pnt3(self) -> Pnt3<f32> {
        self
    }

    fn random<R: Rng>(rng: &mut R) -> Pnt3<f32> {
        Pnt3::new(random_coord(rng), random_coord(rng), random_coord(rng))
    }
}

impl MyPoint for Pnt2<f32> {
    fn into_pnt3(self) -> Pnt3<f32> {
        Pnt3::new(self.x, self.y, 3.0)
    }

    fn random<R: Rng>(rng: &mut R) -> Pnt2<f32> {
        Pnt2::new(random_coord(rng), random_coord(rng))
    }
}

#[derive(Debug)]
struct Config {
    n_attraction_points: usize,
    n_roots: usize,
    influence_radius: f32,
    move_distance: f32,
    kill_distance: f32,
    use_3d: bool,
    max_iter: Option<usize>,
}

impl Config {
    fn config1() -> Config {
        Config {
            n_attraction_points: 10_000,
            n_roots: 5,
            influence_radius: 0.44,
            kill_distance: 0.22,
            move_distance: 0.02,
            use_3d: true,
            max_iter: None,
        }
    }

    fn config2() -> Config {
        Config {
            n_attraction_points: 10_000,
            n_roots: 5,
            influence_radius: 0.1,
            kill_distance: 0.07,
            move_distance: 0.01,
            use_3d: false,
            max_iter: None,
        }
    }

    fn from_cmd() -> Config {
        let matches = App::new("space-colonization")
                          .arg(Arg::with_name("NUM_POINTS")
                                   .long("num-points")
                                   .help("Number of attraction points (default: 1000)")
                                   .takes_value(true)
                                   .required(false))
                          .arg(Arg::with_name("NUM_ROOTS")
                                   .long("num-roots")
                                   .help("Number of root nodes (default: 1)")
                                   .takes_value(true)
                                   .required(false))
                          .arg(Arg::with_name("IR")
                                   .long("radius")
                                   .help("Influence radius (default: 0.25)")
                                   .takes_value(true)
                                   .required(false))
                          .arg(Arg::with_name("KD")
                                   .long("kill-distance")
                                   .help("Kill distance (default: 0.1)")
                                   .takes_value(true)
                                   .required(false))
                          .arg(Arg::with_name("MD")
                                   .long("move-distance")
                                   .help("Kill distance (default: 0.05)")
                                   .takes_value(true)
                                   .required(false))
                          .arg(Arg::with_name("MAX_ITER")
                                   .long("max-iter")
                                   .help("Maximum iterations (default: infinite)")
                                   .takes_value(true)
                                   .required(false))
                          .arg(Arg::with_name("USE_3D")
                                   .long("use-3d")
                                   .help("Use 3d mode"))
                          .get_matches();

        Config {
            n_attraction_points: usize::from_str(matches.value_of("NUM_POINTS").unwrap_or("1000"))
                                     .unwrap(),
            n_roots: usize::from_str(matches.value_of("NUM_ROOTS").unwrap_or("1")).unwrap(),
            influence_radius: f32::from_str(matches.value_of("IR").unwrap_or("0.25")).unwrap(),
            kill_distance: f32::from_str(matches.value_of("KD").unwrap_or("0.1")).unwrap(),
            move_distance: f32::from_str(matches.value_of("MD").unwrap_or("0.05")).unwrap(),
            use_3d: matches.is_present("USE_3D"),
            max_iter: usize::from_str(matches.value_of("MAX_ITER").unwrap_or("INVALID")).ok(),
        }
    }
}

fn run<T, F>(config: &Config)
    where T: MyPoint + FloatPnt<f32, F>,
          F: FloatVec<f32> + Zero + Copy
{
    let mut rng = rand::thread_rng();

    // generate initial attraction point configuration
    // stores status of attraction point in the second tuple element (true=active, false=used)
    let mut attraction_points: Vec<(T, bool)> = (0..config.n_attraction_points)
                                                    .into_iter()
                                                    .map(|_| {
                                                        (<T as MyPoint>::random(&mut rng), true)
                                                    })
                                                    .collect();

    let mut sc: SpaceColonization<T, F> = SpaceColonization::new();
    for _ in 0..config.n_roots {
        sc.add_root_node(<T as MyPoint>::random(&mut rng));
    }

    let mut window = Window::new("Space Colonization");
    let white = Pnt3::new(1.0, 1.0, 1.0);
    let red = Pnt3::new(1.0, 0.0, 0.0);

    let mut i = 0;

    while window.render() {
        i += 1;

        if let Some(m) = config.max_iter {
            if i > m {
                break;
            }
        }

        println!("Iteration: {}", i);
        for &(pt, status) in &attraction_points {
            if status {
                window.draw_point(&pt.into_pnt3(), &white);
            }
        }

        sc.iter_segments(&mut |&a, &b| {
            window.draw_line(&a.into_pnt3(), &b.into_pnt3(), &red);
        });

        sc.iterate(&mut attraction_points,
                   config.influence_radius * config.influence_radius,
                   config.move_distance,
                   config.kill_distance * config.kill_distance);

    }
}

fn main() {
    let config = Config::from_cmd();

    println!("{:?}", config);

    if config.use_3d {
        run::<Pnt3<f32>, Vec3<f32>>(&config);
    } else {
        run::<Pnt2<f32>, Vec2<f32>>(&config);
    }
}
<commit_msg>Prune ALL attraction points when they are closed than the kill distance<commit_after>extern crate nalgebra as na;
extern crate kiss3d;
extern crate rand;
extern crate num;
extern crate clap;

use kiss3d::window::Window;
use na::{Pnt2, Pnt3, Vec2, Vec3, Norm, FloatPnt, FloatVec};
use rand::{Rng, Closed01};
use num::Zero;
use clap::{Arg, App};
use std::str::FromStr;

struct SpaceColonization<T, F>
    where T: FloatPnt<f32, F>,
          F: FloatVec<f32> + Zero + Copy
{
    node_parents: Vec<usize>,
    node_positions: Vec<T>,
    leaf_node_pointers: Vec<usize>, // XXX: how can a branch be terminated?
    leaf_node_grow_directions: Vec<F>,
}

impl<T, F> SpaceColonization<T, F>
    where T: FloatPnt<f32, F>,
          F: FloatVec<f32> + Zero + Copy
{
    fn new() -> SpaceColonization<T, F> {
        SpaceColonization {
            node_parents: Vec::new(),
            node_positions: Vec::new(),
            leaf_node_pointers: Vec::new(),
            leaf_node_grow_directions: Vec::new(),
        }
    }

    // adds initial root node(s)
    fn add_root_node(&mut self, pos: T) {
        debug_assert!(self.node_positions.len() == self.node_parents.len()); // invariant
        debug_assert!(self.leaf_node_pointers.len() == self.leaf_node_grow_directions.len()); // invariant

        let idx = self.node_positions.len();
        self.node_positions.push(pos);

        // a root node has it's own index as parent
        self.node_parents.push(idx);

        // a root node creates a new "leaf" node (better: branch!)
        self.leaf_node_pointers.push(idx);
        self.leaf_node_grow_directions.push(Zero::zero());

        debug_assert!(self.node_positions.len() == self.node_parents.len()); // invariant
        debug_assert!(self.leaf_node_pointers.len() == self.leaf_node_grow_directions.len()); // invariant
    }

    fn add_moved_leaf_node(&mut self, leaf_node_slot: usize, v: F) {
        let parent = self.leaf_node_pointers[leaf_node_slot];
        let parent_pos = self.node_positions[parent];

        self.update_leaf_node(leaf_node_slot, parent_pos + v)
    }

    fn update_leaf_node(&mut self, leaf_node_slot: usize, new_pos: T) {
        let parent = self.leaf_node_pointers[leaf_node_slot];

        // create new node
        let idx = self.node_positions.len();
        self.node_positions.push(new_pos);
        self.node_parents.push(parent);

        // update the leaf node pointer
        self.leaf_node_pointers[leaf_node_slot] = idx;
    }

    fn find_nearest_leaf_for_attraction_point(&self,
                                              attraction_pt: &T,
                                              radius_influence_sq: f32)
                                              -> Option<(usize, f32)> {
        let mut nearest_leaf: Option<usize> = None;
        let mut nearest_distance_sq: f32 = radius_influence_sq;

        for (i, &leaf_node_ptr) in self.leaf_node_pointers.iter().enumerate() {
            let leaf_pos = &self.node_positions[leaf_node_ptr];
            let dist_sq = leaf_pos.sqdist(attraction_pt);

            // is the attraction point within the radius of influence
            // and closed than the currently best
            if dist_sq < nearest_distance_sq {
                nearest_distance_sq = dist_sq;
                nearest_leaf = Some(i);
            }
        }

        return nearest_leaf.map(|idx| (idx, nearest_distance_sq));
    }

    fn iter_segments<C>(&self, callback: &mut C)
        where C: FnMut(&T, &T)
    {
        for (i, &parent) in self.node_parents.iter().enumerate() {
            if i != parent {
                callback(&self.node_positions[i], &self.node_positions[parent]);
            }
        }
    }

    fn iterate(&mut self,
               attraction_points: &mut [(T, bool)],
               radius_influce_sq: f32,
               move_distance: f32,
               kill_distance_sq: f32) {
        // reset all attraction forces
        for force in &mut self.leaf_node_grow_directions {
            *force = Zero::zero();
        }

        // for each attraction_point, find the nearest (leaf) node that it
        // influences.
        for ap in attraction_points.iter_mut() {
            let active = ap.1;
            if !active {
                continue;
            }
            let nearest_leaf_opt = self.find_nearest_leaf_for_attraction_point(&ap.0,
                                                                               radius_influce_sq);
            if let Some((nearest_leaf_slot, d_sq)) = nearest_leaf_opt {
                if d_sq < kill_distance_sq {
                    // set inactive
                    ap.1 = false;
                } else {
                    let nearest_leaf_node_idx = self.leaf_node_pointers[nearest_leaf_slot];
                    // update the force with the normalized vector towards the attraction point
                    let v = (ap.0 - self.node_positions[nearest_leaf_node_idx]).normalize();
                    self.leaf_node_grow_directions[nearest_leaf_slot] =
                        self.leaf_node_grow_directions[nearest_leaf_slot] + v;
                }
            }
        }

        // now create new leaf nodes
        for i in 0..self.leaf_node_grow_directions.len() {
            let force = self.leaf_node_grow_directions[i];

            // update leaf node only if there is a force!
            if !force.is_zero() {
                let n = force.normalize();
                self.add_moved_leaf_node(i, n * move_distance);
            }
        }

    }
}

fn random_closed01<R: Rng>(rng: &mut R) -> f32 {
    rng.gen::<Closed01<f32>>().0
}

fn random_coord<R: Rng>(rng: &mut R) -> f32 {
    2.0 * random_closed01(rng) - 1.0
}

trait MyPoint {
    fn into_pnt3(self) -> Pnt3<f32>;
    fn random<R: Rng>(rng: &mut R) -> Self;
}

impl MyPoint for Pnt3<f32> {
    fn into_pnt3(self) -> Pnt3<f32> {
        self
    }

    fn random<R: Rng>(rng: &mut R) -> Pnt3<f32> {
        Pnt3::new(random_coord(rng), random_coord(rng), random_coord(rng))
    }
}

impl MyPoint for Pnt2<f32> {
    fn into_pnt3(self) -> Pnt3<f32> {
        Pnt3::new(self.x, self.y, 3.0)
    }

    fn random<R: Rng>(rng: &mut R) -> Pnt2<f32> {
        Pnt2::new(random_coord(rng), random_coord(rng))
    }
}

#[derive(Debug)]
struct Config {
    n_attraction_points: usize,
    n_roots: usize,
    influence_radius: f32,
    move_distance: f32,
    kill_distance: f32,
    use_3d: bool,
    max_iter: Option<usize>,
}

impl Config {
    fn config1() -> Config {
        Config {
            n_attraction_points: 10_000,
            n_roots: 5,
            influence_radius: 0.44,
            kill_distance: 0.22,
            move_distance: 0.02,
            use_3d: true,
            max_iter: None,
        }
    }

    fn config2() -> Config {
        Config {
            n_attraction_points: 10_000,
            n_roots: 5,
            influence_radius: 0.1,
            kill_distance: 0.07,
            move_distance: 0.01,
            use_3d: false,
            max_iter: None,
        }
    }

    fn from_cmd() -> Config {
        let matches = App::new("space-colonization")
                          .arg(Arg::with_name("NUM_POINTS")
                                   .long("num-points")
                                   .help("Number of attraction points (default: 1000)")
                                   .takes_value(true)
                                   .required(false))
                          .arg(Arg::with_name("NUM_ROOTS")
                                   .long("num-roots")
                                   .help("Number of root nodes (default: 1)")
                                   .takes_value(true)
                                   .required(false))
                          .arg(Arg::with_name("IR")
                                   .long("radius")
                                   .help("Influence radius (default: 0.25)")
                                   .takes_value(true)
                                   .required(false))
                          .arg(Arg::with_name("KD")
                                   .long("kill-distance")
                                   .help("Kill distance (default: 0.1)")
                                   .takes_value(true)
                                   .required(false))
                          .arg(Arg::with_name("MD")
                                   .long("move-distance")
                                   .help("Kill distance (default: 0.05)")
                                   .takes_value(true)
                                   .required(false))
                          .arg(Arg::with_name("MAX_ITER")
                                   .long("max-iter")
                                   .help("Maximum iterations (default: infinite)")
                                   .takes_value(true)
                                   .required(false))
                          .arg(Arg::with_name("USE_3D")
                                   .long("use-3d")
                                   .help("Use 3d mode"))
                          .get_matches();

        Config {
            n_attraction_points: usize::from_str(matches.value_of("NUM_POINTS").unwrap_or("1000"))
                                     .unwrap(),
            n_roots: usize::from_str(matches.value_of("NUM_ROOTS").unwrap_or("1")).unwrap(),
            influence_radius: f32::from_str(matches.value_of("IR").unwrap_or("0.25")).unwrap(),
            kill_distance: f32::from_str(matches.value_of("KD").unwrap_or("0.1")).unwrap(),
            move_distance: f32::from_str(matches.value_of("MD").unwrap_or("0.05")).unwrap(),
            use_3d: matches.is_present("USE_3D"),
            max_iter: usize::from_str(matches.value_of("MAX_ITER").unwrap_or("INVALID")).ok(),
        }
    }
}

fn run<T, F>(config: &Config)
    where T: MyPoint + FloatPnt<f32, F>,
          F: FloatVec<f32> + Zero + Copy
{
    let mut rng = rand::thread_rng();

    // generate initial attraction point configuration
    // stores status of attraction point in the second tuple element (true=active, false=used)
    let mut attraction_points: Vec<(T, bool)> = (0..config.n_attraction_points)
                                                    .into_iter()
                                                    .map(|_| {
                                                        (<T as MyPoint>::random(&mut rng), true)
                                                    })
                                                    .collect();

    let mut sc: SpaceColonization<T, F> = SpaceColonization::new();
    for _ in 0..config.n_roots {
        sc.add_root_node(<T as MyPoint>::random(&mut rng));
    }

    let mut window = Window::new("Space Colonization");
    let white = Pnt3::new(1.0, 1.0, 1.0);
    let red = Pnt3::new(1.0, 0.0, 0.0);

    let mut i = 0;

    while window.render() {
        i += 1;

        if let Some(m) = config.max_iter {
            if i > m {
                break;
            }
        }

        println!("Iteration: {}", i);
        for &(pt, status) in &attraction_points {
            if status {
                window.draw_point(&pt.into_pnt3(), &white);
            }
        }

        sc.iter_segments(&mut |&a, &b| {
            window.draw_line(&a.into_pnt3(), &b.into_pnt3(), &red);
        });

        sc.iterate(&mut attraction_points,
                   config.influence_radius * config.influence_radius,
                   config.move_distance,
                   config.kill_distance * config.kill_distance);

    }
}

fn main() {
    let config = Config::from_cmd();

    println!("{:?}", config);

    if config.use_3d {
        run::<Pnt3<f32>, Vec3<f32>>(&config);
    } else {
        run::<Pnt2<f32>, Vec2<f32>>(&config);
    }
}
<|endoftext|>


<commit_before>#![allow(unused_must_use)]

mod irc;
mod vn64c;
mod demc;

#[macro_use]
extern crate regex;
extern crate toml;
extern crate time;

use std::fs::File;
use std::io::Read;

use regex::Regex;

use time::Duration;

use vn64c::{Controller, ButtonName};
use demc::DemC;
use demc::TimedInputCommand;
use vn64c::InputCommand;

const CONFIG_FILE_PATH: &'static str = "tppm.toml";
const VJOY_DEVICE_NUMBER: u8 = 1;

// Parse the TPPM toml configuration file; return the server, password, nick, and channel
fn parse_config_file() -> (String, String, String, String) {
    let mut config_file = File::open(CONFIG_FILE_PATH).unwrap();
    let mut config_string = String::new();
    config_file.read_to_string(&mut config_string);

    //@todo understand this generics magic
    let toml_tree: toml::Value = config_string.parse().unwrap();

    let server = String::from(toml_tree.lookup("irc.server").unwrap().as_str().unwrap());
    let pass = String::from(toml_tree.lookup("irc.pass").unwrap().as_str().unwrap());
    let nick = String::from(toml_tree.lookup("irc.nick").unwrap().as_str().unwrap());
    let channel = String::from(toml_tree.lookup("irc.channel").unwrap().as_str().unwrap());

    (server, pass, nick, channel)
}

// Attempt to parse an IRC message into a list of controller commands
//@todo just use a custom state machine rather than regex, this has to be insanely slow
//@todo this function is huge
fn parse_irc_message(msg: &String, re: &Regex) -> Option<Vec<TimedInputCommand>> {
    let mut last_cap_end = None;
    let mut cumulative_delay: u32 = 0;
    let mut last_command: Option<TimedInputCommand> = None;
    let mut cap_start_zero = false;
    let mut final_cap_end: usize = 0;
    let mut res: Vec<TimedInputCommand> = Vec::new();

    for cap in re.captures_iter(&msg.to_lowercase()) {
        let (cap_start, cap_end) = cap.pos(0).unwrap();

        // Raise a flag if this capture starts at the first string position - after iterating over all captures, we'll
        // make sure it was raised, and if it wasn't, we didn't parse a valid command message
        if cap_start == 0 {
            cap_start_zero = true;
        }

        // Make sure that all captures are continuous; that we're parsing commands and only commands
        // eg. we don't want "hahah" to parse as two "a" commands
        if let Some(last_cap_end) = last_cap_end {
            if last_cap_end != cap_start {
                return None;
            }
        }

        // Store the last capture's ending position - after iterating over all captures, we'll make sure the last one
        // ended
        final_cap_end = cap_end;

        // Our regex should match on exactly one of three groups: "joystick", "button", or "delay"
        if let Some(_) = cap.name("joystick") {
            match last_command {
                Some(command) => {
                    cumulative_delay += command.duration.num_milliseconds() as u32;
                    match command.command {
                        InputCommand::Joystick{direction: _, strength: _} => {
                            cumulative_delay += 51;
                        },
                        InputCommand::Button{name: _, value: _} => {
                            ()
                        }
                    }
                },
                None =>  ()
            }
            // joystick command - should have one, two, or four groups:
            // "joystick_strength" (optional)
            // "joystick_direction" (mandatory)
            // "joystick_duration" (optional),
            // "joystick_duration_units" (optional; must be present if joystick_duration is)
            let mut joystick_strength: f32 = 1.0;
            let mut joystick_direction: u16 = 0;
            let mut joystick_duration: u32 = 500;
            if let Some(jscap) = cap.name("joystick_strength") {
                let strength_u8: u8 = jscap.parse().unwrap();
                joystick_strength = strength_u8 as f32 / 100.0;
            }
            if let Some(jdcap) = cap.name("joystick_direction") {
                match jdcap {
                    "up" => { joystick_direction = 90; },
                    "down" => { joystick_direction = 270; },
                    "left" => { joystick_direction = 180; },
                    "right" => { joystick_direction = 0; },
                    _ => ()
                }
            } else {
                return None;
            }
            if let Some(jdcap) = cap.name("joystick_duration") {
                joystick_duration = jdcap.parse().unwrap();
                if let Some(jdcap_units) = cap.name("joystick_duration_units") {
                    if jdcap_units == "s" {
                        joystick_duration *= 1000;
                    }
                } else {
                    return None;
                }
            }

            // treat joystick commands with strength <0%, >100% or duration >5s as invalid
            if joystick_strength > 1.0 || joystick_strength < 0.0 || joystick_duration > 5000 {
                return None;
            }

            let time_now = time::get_time();
            let command = TimedInputCommand { start_time: time_now + time::Duration::milliseconds(cumulative_delay as i64),
                                                             duration: time::Duration::milliseconds(joystick_duration as i64),
                                                             command: InputCommand::Joystick { direction: joystick_direction,
                                                                                strength: joystick_strength} };
            res.push(command);

            last_command = Some(command.clone());
        } else if let Some(bcap) = cap.name("button") {
            match last_command {
                Some(command) => {
                    match command.command {
                        InputCommand::Joystick{direction: _, strength: _} => {
                            cumulative_delay += command.duration.num_milliseconds() as u32;
                            if command.duration.num_milliseconds() >= 17 {
                                cumulative_delay -= 17;
                            }
                        },
                        InputCommand::Button{name: _, value: _} => {
                            if command.duration.num_milliseconds() == 167 {
                                cumulative_delay += 500; //massive hack
                            } else {
                                cumulative_delay += command.duration.num_milliseconds() as u32;
                            }
                            cumulative_delay += 51;
                        }
                    }
                },
                None =>  ()
            }
            // button command - should have one or three groups:
            // "button_name" (mandatory)
            // "button_duration" (optional),
            // "button_duration_units" (optional; must be present if joystick_duration is)
            println!("button command: {:?}", bcap);

            let mut button_name;
            let mut button_duration: u32 = 167;

            if let Some(bncap) = cap.name("button_name") {
                button_name = match bncap {
                    "a" => ButtonName::A,
                    "b" => ButtonName::B,
                    "z" => ButtonName::Z,
                    "l" => ButtonName::L,
                    "r" => ButtonName::R,
                    "start" => ButtonName::Start,
                    "cup" => ButtonName::Cup,
                    "cdown" => ButtonName::Cdown,
                    "cleft" => ButtonName::Cleft,
                    "cright" => ButtonName::Cright,
                    "dup" => ButtonName::Dup,
                    "ddown" => ButtonName::Ddown,
                    "dleft" => ButtonName::Dleft,
                    "dright" => ButtonName::Dright,
                    _ => return None
                };
            } else {
                return None;
            }

            if let Some(bdcap) = cap.name("button_duration") {
                button_duration = bdcap.parse().unwrap();
                if let Some(bdcap_units) = cap.name("button_duration_units") {
                    if bdcap_units == "s" {
                        button_duration *= 1000;
                    }
                } else {
                    return None;
                }
            }

            let time_now = time::get_time();
            let command = TimedInputCommand { start_time: time_now + Duration::milliseconds(cumulative_delay as i64),
                                              duration: time::Duration::milliseconds(button_duration as i64),
                                              command: InputCommand::Button { name: button_name, value: true } };
            res.push(command);

            last_command = Some(command.clone());
        } else if let Some(dcap) = cap.name("delay") {
            // delay command - only one argument, the delay to insert
            println!("delay command: {:?}", dcap);
            match dcap {
                "+" => { cumulative_delay += 17; },
                "!" => { cumulative_delay += 217; },
                _ => { return None; }
            }
            last_command = None
        }

        last_cap_end = Some(cap_end);
    }

    if final_cap_end != msg.len() {
        return None;
    }

    if cap_start_zero != true {
        return None;
    }

    return Some(res);
}

fn main() {
    // Parse our configuration file
    let (server, pass, nick, channel) = parse_config_file();

    // Initialize a democratized virtual N64 controller
    let controller = Controller::new(VJOY_DEVICE_NUMBER).unwrap();
    let dem_controller = DemC::new(controller);

    // Start our IRC connection
    let irc_connection = irc::IrcConnection::spawn(server, pass, nick, channel).unwrap();

    // Our regex for parsing IRC messages - this is here so that it need not be instantiated every
    // time we handle an IRC message
    let re = Regex::new(r"\s*((?P<joystick>((?P<joystick_strength>[:digit:]+)%\s*)?(?P<joystick_direction>up|down|left|right)(\s*(?P<joystick_duration>[:digit:]+)(?P<joystick_duration_units>s|ms))?)|(?P<button>((?P<button_name>start|cup|cdown|cleft|cright|dup|ddown|dleft|dright|a|b|z|l|r)(\s*(?P<button_duration>[:digit:]+)(?P<button_duration_units>s|ms))?))|(?P<delay>[\+!]))\s*").unwrap();

    // Poll the IRC connection and handle its messages forever
    loop {
        match irc_connection.receive_privmsg() {
            Ok(msg_vec) => {
                //@todo remove this 1 hardcode (which is there to ignore the channel name parameter)
                match msg_vec.get(1) {
                    Some(string) => {
                        if let Some(cmds) = parse_irc_message(string, &re) {
                            for &cmd in cmds.iter() {
                                dem_controller.add_command(cmd);
                            }
                        }
                    },
                    _ => ()
                }
            },
            _ => ()
        }
    }
}
<commit_msg>Don't call unwrap on parsed strings, thanks nojohns<commit_after>#![allow(unused_must_use)]

mod irc;
mod vn64c;
mod demc;

#[macro_use]
extern crate regex;
extern crate toml;
extern crate time;

use std::fs::File;
use std::io::Read;

use regex::Regex;

use time::Duration;

use vn64c::{Controller, ButtonName};
use demc::DemC;
use demc::TimedInputCommand;
use vn64c::InputCommand;

const CONFIG_FILE_PATH: &'static str = "tppm.toml";
const VJOY_DEVICE_NUMBER: u8 = 1;

// Parse the TPPM toml configuration file; return the server, password, nick, and channel
fn parse_config_file() -> (String, String, String, String) {
    let mut config_file = File::open(CONFIG_FILE_PATH).unwrap();
    let mut config_string = String::new();
    config_file.read_to_string(&mut config_string);

    //@todo understand this generics magic
    let toml_tree: toml::Value = config_string.parse().unwrap();

    let server = String::from(toml_tree.lookup("irc.server").unwrap().as_str().unwrap());
    let pass = String::from(toml_tree.lookup("irc.pass").unwrap().as_str().unwrap());
    let nick = String::from(toml_tree.lookup("irc.nick").unwrap().as_str().unwrap());
    let channel = String::from(toml_tree.lookup("irc.channel").unwrap().as_str().unwrap());

    (server, pass, nick, channel)
}

// Attempt to parse an IRC message into a list of controller commands
//@todo just use a custom state machine rather than regex, this has to be insanely slow
//@todo this function is huge
fn parse_irc_message(msg: &String, re: &Regex) -> Option<Vec<TimedInputCommand>> {
    let mut last_cap_end = None;
    let mut cumulative_delay: u32 = 0;
    let mut last_command: Option<TimedInputCommand> = None;
    let mut cap_start_zero = false;
    let mut final_cap_end: usize = 0;
    let mut res: Vec<TimedInputCommand> = Vec::new();

    for cap in re.captures_iter(&msg.to_lowercase()) {
        let (cap_start, cap_end) = cap.pos(0).unwrap();

        // Raise a flag if this capture starts at the first string position - after iterating over all captures, we'll
        // make sure it was raised, and if it wasn't, we didn't parse a valid command message
        if cap_start == 0 {
            cap_start_zero = true;
        }

        // Make sure that all captures are continuous; that we're parsing commands and only commands
        // eg. we don't want "hahah" to parse as two "a" commands
        if let Some(last_cap_end) = last_cap_end {
            if last_cap_end != cap_start {
                return None;
            }
        }

        // Store the last capture's ending position - after iterating over all captures, we'll make sure the last one
        // ended
        final_cap_end = cap_end;

        // Our regex should match on exactly one of three groups: "joystick", "button", or "delay"
        if let Some(_) = cap.name("joystick") {
            match last_command {
                Some(command) => {
                    cumulative_delay += command.duration.num_milliseconds() as u32;
                    match command.command {
                        InputCommand::Joystick{direction: _, strength: _} => {
                            cumulative_delay += 51;
                        },
                        InputCommand::Button{name: _, value: _} => {
                            ()
                        }
                    }
                },
                None =>  ()
            }
            // joystick command - should have one, two, or four groups:
            // "joystick_strength" (optional)
            // "joystick_direction" (mandatory)
            // "joystick_duration" (optional),
            // "joystick_duration_units" (optional; must be present if joystick_duration is)
            let mut joystick_strength: f32 = 1.0;
            let mut joystick_direction: u16 = 0;
            let mut joystick_duration: u32 = 500;
            if let Some(jscap) = cap.name("joystick_strength") {
                match jscap.parse::<u8>() {
                    Ok(strength_u8) => { joystick_strength = strength_u8 as f32 / 100.0; },
                    _ => return None
                }
            }
            if let Some(jdcap) = cap.name("joystick_direction") {
                match jdcap {
                    "up" => { joystick_direction = 90; },
                    "down" => { joystick_direction = 270; },
                    "left" => { joystick_direction = 180; },
                    "right" => { joystick_direction = 0; },
                    _ => ()
                }
            } else {
                return None;
            }
            if let Some(jdcap) = cap.name("joystick_duration") {
                match jdcap.parse::<u32>() {
                    Ok(duration_u32) => { joystick_duration = duration_u32; },
                    _ => return None
                }

                if let Some(jdcap_units) = cap.name("joystick_duration_units") {
                    if jdcap_units == "s" {
                        joystick_duration *= 1000;
                    }
                } else {
                    return None;
                }
            }

            // treat joystick commands with strength <0%, >100% or duration >5s as invalid
            if joystick_strength > 1.0 || joystick_strength < 0.0 || joystick_duration > 5000 {
                return None;
            }

            let time_now = time::get_time();
            let command = TimedInputCommand { start_time: time_now + time::Duration::milliseconds(cumulative_delay as i64),
                                                             duration: time::Duration::milliseconds(joystick_duration as i64),
                                                             command: InputCommand::Joystick { direction: joystick_direction,
                                                                                strength: joystick_strength} };
            res.push(command);

            last_command = Some(command.clone());
        } else if let Some(bcap) = cap.name("button") {
            match last_command {
                Some(command) => {
                    match command.command {
                        InputCommand::Joystick{direction: _, strength: _} => {
                            cumulative_delay += command.duration.num_milliseconds() as u32;
                            if command.duration.num_milliseconds() >= 17 {
                                cumulative_delay -= 17;
                            }
                        },
                        InputCommand::Button{name: _, value: _} => {
                            if command.duration.num_milliseconds() == 167 {
                                cumulative_delay += 500; //massive hack
                            } else {
                                cumulative_delay += command.duration.num_milliseconds() as u32;
                            }
                            cumulative_delay += 51;
                        }
                    }
                },
                None =>  ()
            }
            // button command - should have one or three groups:
            // "button_name" (mandatory)
            // "button_duration" (optional),
            // "button_duration_units" (optional; must be present if joystick_duration is)
            println!("button command: {:?}", bcap);

            let mut button_name;
            let mut button_duration: u32 = 167;

            if let Some(bncap) = cap.name("button_name") {
                button_name = match bncap {
                    "a" => ButtonName::A,
                    "b" => ButtonName::B,
                    "z" => ButtonName::Z,
                    "l" => ButtonName::L,
                    "r" => ButtonName::R,
                    "start" => ButtonName::Start,
                    "cup" => ButtonName::Cup,
                    "cdown" => ButtonName::Cdown,
                    "cleft" => ButtonName::Cleft,
                    "cright" => ButtonName::Cright,
                    "dup" => ButtonName::Dup,
                    "ddown" => ButtonName::Ddown,
                    "dleft" => ButtonName::Dleft,
                    "dright" => ButtonName::Dright,
                    _ => return None
                };
            } else {
                return None;
            }

            if let Some(bdcap) = cap.name("button_duration") {
                match bdcap.parse::<u32>() {
                    Ok(duration_u32) => { button_duration = duration_u32; },
                    _ => return None
                }

                if let Some(bdcap_units) = cap.name("button_duration_units") {
                    if bdcap_units == "s" {
                        button_duration *= 1000;
                    }
                } else {
                    return None;
                }
            }

            let time_now = time::get_time();
            let command = TimedInputCommand { start_time: time_now + Duration::milliseconds(cumulative_delay as i64),
                                              duration: time::Duration::milliseconds(button_duration as i64),
                                              command: InputCommand::Button { name: button_name, value: true } };
            res.push(command);

            last_command = Some(command.clone());
        } else if let Some(dcap) = cap.name("delay") {
            // delay command - only one argument, the delay to insert
            println!("delay command: {:?}", dcap);
            match dcap {
                "+" => { cumulative_delay += 17; },
                "!" => { cumulative_delay += 217; },
                _ => { return None; }
            }
            last_command = None
        }

        last_cap_end = Some(cap_end);
    }

    if final_cap_end != msg.len() {
        return None;
    }

    if cap_start_zero != true {
        return None;
    }

    return Some(res);
}

fn main() {
    // Parse our configuration file
    let (server, pass, nick, channel) = parse_config_file();

    // Initialize a democratized virtual N64 controller
    let controller = Controller::new(VJOY_DEVICE_NUMBER).unwrap();
    let dem_controller = DemC::new(controller);

    // Start our IRC connection
    let irc_connection = irc::IrcConnection::spawn(server, pass, nick, channel).unwrap();

    // Our regex for parsing IRC messages - this is here so that it need not be instantiated every
    // time we handle an IRC message
    let re = Regex::new(r"\s*((?P<joystick>((?P<joystick_strength>[:digit:]+)%\s*)?(?P<joystick_direction>up|down|left|right)(\s*(?P<joystick_duration>[:digit:]+)(?P<joystick_duration_units>s|ms))?)|(?P<button>((?P<button_name>start|cup|cdown|cleft|cright|dup|ddown|dleft|dright|a|b|z|l|r)(\s*(?P<button_duration>[:digit:]+)(?P<button_duration_units>s|ms))?))|(?P<delay>[\+!]))\s*").unwrap();

    // Poll the IRC connection and handle its messages forever
    loop {
        match irc_connection.receive_privmsg() {
            Ok(msg_vec) => {
                //@todo remove this 1 hardcode (which is there to ignore the channel name parameter)
                match msg_vec.get(1) {
                    Some(string) => {
                        if let Some(cmds) = parse_irc_message(string, &re) {
                            for &cmd in cmds.iter() {
                                dem_controller.add_command(cmd);
                            }
                        }
                    },
                    _ => ()
                }
            },
            _ => ()
        }
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
#![feature(collections, os, io, std_misc, core, libc, path)]

extern crate libc;

mod args;
mod ansi;
mod matching;
mod screen;

use std::os;
use std::old_io;
use std::cmp::min;
use screen::Screen;
use screen::Key;
use screen::Key::*;
use self::SearchState::*;

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

    if args.help { return; }

    let choices = read_choices();
    let initial_search = args.initial_search.clone();
    if args.use_first {
        let matches = matching::compute_matches(&choices, initial_search.as_slice());
        println!("{}", matches.get(0).unwrap_or(&&"".to_string()));
        return;
    } else {
        event_loop(choices, initial_search.as_slice());
    }
}

fn event_loop(choices: Vec<String>, initial_search: &str) {
    let mut search = Search::new(&choices, initial_search.to_string());
    let mut screen = Screen::open_screen();

    loop {
        search.recompute_matches();

        match search.state {
            InProgress => draw_screen(&mut screen, &search),
            _ => break,
        }

        let keys = screen.get_buffered_keys();
        for key in keys.iter() {
            handle_key(&mut search, key, screen.visible_choices);
        }
    }

    screen.move_cursor_to_bottom();
    println!("{}", search.get_selection());
}

fn handle_key(search: &mut Search, key: &Key, visible_choices: u16) {
    match *key {
        Char(x) => search.append(x),
        Backspace => search.backspace(),
        Control('h') => search.backspace(),
        Control('w') => search.delete_word(),
        Control('u') => search.clear_query(),
        Control('c') => search.cancel(),
        Control('g') => search.cancel(),
        Control('n') => search.down(visible_choices),
        Control('p') => search.up(),
        Enter => search.done(),
        _ => {}
    }
}

struct Search<'a> {
    choices: &'a Vec<String>,
    query: String,
    matches: Vec<&'a String>,
    stale: bool,
    index: usize,
    state: SearchState,
}

#[derive(PartialEq, Eq)]
enum SearchState {
    InProgress,
    Done,
    Canceled,
}

impl<'a> Search<'a> {
    fn new(choices: &'a Vec<String>, initial_search: String) -> Search<'a> {
        Search {
            choices: choices,
            query: initial_search,
            matches: Vec::new(),
            stale: true,
            index: 0,
            state: InProgress,
        }
    }

    fn up(&mut self) {
        self.index = if self.index == 0 { 0 } else { self.index - 1 };
    }

    fn down(&mut self, visible_choices: u16) {
        let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
        self.index = min(self.index + 1, limit);
    }

    fn backspace(&mut self) {
        self.query.pop();
        self.stale = true;
        self.index = 0;
    }

    fn delete_word(&mut self) {
        self.stale = true;
        delete_last_word(&mut self.query);
    }

    fn append(&mut self, c: char) {
        self.query.push(c);
        self.stale = true;
        self.index = 0;
    }

    fn clear_query(&mut self) {
        self.query.clear();
        self.stale = true;
    }

    fn recompute_matches(&mut self) {
        if self.stale {
            self.matches = matching::compute_matches(self.choices, self.query.as_slice());
            self.stale = false;
        }
    }

    fn get_selection(&mut self) -> String {
        if self.state == Canceled {
            "".to_string()
        } else {
            self.recompute_matches();
            self.matches.get(self.index).unwrap_or(&&"".to_string()).to_string()
        }
    }

    fn cancel(&mut self) {
        self.state = Canceled
    }

    fn done(&mut self) {
        self.state = Done
    }
}

fn draw_screen(screen: &mut Screen, search: &Search) {
    screen.hide_cursor();
    screen.blank_screen();
    screen.write(format!("> {} ({} choices)\n", search.query, search.choices.len()).as_slice());

    print_matches(screen, &search.matches, search.index);

    let start = screen.start_line;
    screen.move_cursor(start, 2 + search.query.len() as u16);
    screen.show_cursor();
}

fn print_matches(screen: &mut Screen, matches: &Vec<&String>, index: usize) {
    let mut i = 1;
    for choice in matches.iter() {
        if i == index + 1 {
            screen.write_inverted(choice.as_slice());
        } else {
            screen.write(choice.as_slice());
        }
        if i >= screen.visible_choices as usize {
            return;
        } else {
            screen.write("\n");
        }
        i += 1;
    }
}

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

    while let Ok(mut s) = stdin.read_line() {
        trim(&mut s);
        lines.push(s);
    }

    lines
}

pub fn trim(s: &mut String) {
    while let Some(x) = s.pop() {
        if x != '\n' {
            s.push(x);
            return;
        }
    }
}

fn delete_last_word(s: &mut String) {
    let mut deleted_something = false;
    while let Some(x) = s.pop() {
        if x == ' ' {
            if deleted_something {
                s.push(x);
                return;
            }
        } else {
            deleted_something = true;
        }
    }
}

#[test]
fn trim_test() {
    fn should_become(before: &str, after: &str) {
        let mut x = before.to_string();
        trim(&mut x);
        assert_eq!(after.to_string(), x);
    }
    should_become("", "");
    should_become("\n", "");
    should_become("\n\n", "");
    should_become("asdf", "asdf");
    should_become("asdf\n", "asdf");
    should_become("asdf\nasdf\n", "asdf\nasdf");
}

#[test]
fn delete_word_test() {
    fn should_become(before: &str, after: &str) {
        let mut x = before.to_string();
        delete_last_word(&mut x);
        assert_eq!(after.to_string(), x);
    }
    should_become("", "");
    should_become("a", "");
    should_become("asdf", "");
    should_become("asdf asdf asdf", "asdf asdf ");
    should_become("asdf asdf asdf ", "asdf asdf ");
    should_become("asdf asdf asdf  ", "asdf asdf ");
}
<commit_msg>Allow unused_features<commit_after>#![allow(dead_code, unused_features)]
#![feature(collections, os, io, std_misc, core, libc, path)]

extern crate libc;

mod args;
mod ansi;
mod matching;
mod screen;

use std::os;
use std::old_io;
use std::cmp::min;
use screen::Screen;
use screen::Key;
use screen::Key::*;
use self::SearchState::*;

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

    if args.help { return; }

    let choices = read_choices();
    let initial_search = args.initial_search.clone();
    if args.use_first {
        let matches = matching::compute_matches(&choices, initial_search.as_slice());
        println!("{}", matches.get(0).unwrap_or(&&"".to_string()));
        return;
    } else {
        event_loop(choices, initial_search.as_slice());
    }
}

fn event_loop(choices: Vec<String>, initial_search: &str) {
    let mut search = Search::new(&choices, initial_search.to_string());
    let mut screen = Screen::open_screen();

    loop {
        search.recompute_matches();

        match search.state {
            InProgress => draw_screen(&mut screen, &search),
            _ => break,
        }

        let keys = screen.get_buffered_keys();
        for key in keys.iter() {
            handle_key(&mut search, key, screen.visible_choices);
        }
    }

    screen.move_cursor_to_bottom();
    println!("{}", search.get_selection());
}

fn handle_key(search: &mut Search, key: &Key, visible_choices: u16) {
    match *key {
        Char(x) => search.append(x),
        Backspace => search.backspace(),
        Control('h') => search.backspace(),
        Control('w') => search.delete_word(),
        Control('u') => search.clear_query(),
        Control('c') => search.cancel(),
        Control('g') => search.cancel(),
        Control('n') => search.down(visible_choices),
        Control('p') => search.up(),
        Enter => search.done(),
        _ => {}
    }
}

struct Search<'a> {
    choices: &'a Vec<String>,
    query: String,
    matches: Vec<&'a String>,
    stale: bool,
    index: usize,
    state: SearchState,
}

#[derive(PartialEq, Eq)]
enum SearchState {
    InProgress,
    Done,
    Canceled,
}

impl<'a> Search<'a> {
    fn new(choices: &'a Vec<String>, initial_search: String) -> Search<'a> {
        Search {
            choices: choices,
            query: initial_search,
            matches: Vec::new(),
            stale: true,
            index: 0,
            state: InProgress,
        }
    }

    fn up(&mut self) {
        self.index = if self.index == 0 { 0 } else { self.index - 1 };
    }

    fn down(&mut self, visible_choices: u16) {
        let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
        self.index = min(self.index + 1, limit);
    }

    fn backspace(&mut self) {
        self.query.pop();
        self.stale = true;
        self.index = 0;
    }

    fn delete_word(&mut self) {
        self.stale = true;
        delete_last_word(&mut self.query);
    }

    fn append(&mut self, c: char) {
        self.query.push(c);
        self.stale = true;
        self.index = 0;
    }

    fn clear_query(&mut self) {
        self.query.clear();
        self.stale = true;
    }

    fn recompute_matches(&mut self) {
        if self.stale {
            self.matches = matching::compute_matches(self.choices, self.query.as_slice());
            self.stale = false;
        }
    }

    fn get_selection(&mut self) -> String {
        if self.state == Canceled {
            "".to_string()
        } else {
            self.recompute_matches();
            self.matches.get(self.index).unwrap_or(&&"".to_string()).to_string()
        }
    }

    fn cancel(&mut self) {
        self.state = Canceled
    }

    fn done(&mut self) {
        self.state = Done
    }
}

fn draw_screen(screen: &mut Screen, search: &Search) {
    screen.hide_cursor();
    screen.blank_screen();
    screen.write(format!("> {} ({} choices)\n", search.query, search.choices.len()).as_slice());

    print_matches(screen, &search.matches, search.index);

    let start = screen.start_line;
    screen.move_cursor(start, 2 + search.query.len() as u16);
    screen.show_cursor();
}

fn print_matches(screen: &mut Screen, matches: &Vec<&String>, index: usize) {
    let mut i = 1;
    for choice in matches.iter() {
        if i == index + 1 {
            screen.write_inverted(choice.as_slice());
        } else {
            screen.write(choice.as_slice());
        }
        if i >= screen.visible_choices as usize {
            return;
        } else {
            screen.write("\n");
        }
        i += 1;
    }
}

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

    while let Ok(mut s) = stdin.read_line() {
        trim(&mut s);
        lines.push(s);
    }

    lines
}

pub fn trim(s: &mut String) {
    while let Some(x) = s.pop() {
        if x != '\n' {
            s.push(x);
            return;
        }
    }
}

fn delete_last_word(s: &mut String) {
    let mut deleted_something = false;
    while let Some(x) = s.pop() {
        if x == ' ' {
            if deleted_something {
                s.push(x);
                return;
            }
        } else {
            deleted_something = true;
        }
    }
}

#[test]
fn trim_test() {
    fn should_become(before: &str, after: &str) {
        let mut x = before.to_string();
        trim(&mut x);
        assert_eq!(after.to_string(), x);
    }
    should_become("", "");
    should_become("\n", "");
    should_become("\n\n", "");
    should_become("asdf", "asdf");
    should_become("asdf\n", "asdf");
    should_become("asdf\nasdf\n", "asdf\nasdf");
}

#[test]
fn delete_word_test() {
    fn should_become(before: &str, after: &str) {
        let mut x = before.to_string();
        delete_last_word(&mut x);
        assert_eq!(after.to_string(), x);
    }
    should_become("", "");
    should_become("a", "");
    should_become("asdf", "");
    should_become("asdf asdf asdf", "asdf asdf ");
    should_become("asdf asdf asdf ", "asdf asdf ");
    should_become("asdf asdf asdf  ", "asdf asdf ");
}
<|endoftext|>


<commit_before>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

#[macro_use]
extern crate clap;
extern crate requests;
extern crate serde;
extern crate serde_json;

mod crates;

use clap::{App, SubCommand, Arg, AppSettings, ArgMatches};
use serde_json::ser::to_string_pretty;

const CARGO: &'static str = "cargo";

#[derive(Debug, PartialEq)]
enum Flag {
    Repository,
    Documentation,
    Downloads,
    Homepage,
    Default,
}

#[derive(Debug)]
struct Report {
    flags: Vec<Flag>,
    verbose: bool,
    json: bool,
}

impl Report {
    pub fn new(info: &ArgMatches) -> Self {

        let mut flags: Vec<Flag> = vec![];
        if info.is_present("repository") {
            flags.push(Flag::Repository);
        }
        if info.is_present("documentation") {
            flags.push(Flag::Documentation);
        }
        if info.is_present("downloads") {
            flags.push(Flag::Downloads);
        }
        if info.is_present("homepage") {
            flags.push(Flag::Homepage);
        }

        if flags.is_empty() {
            flags.push(Flag::Default);
        }

        Report {
            flags: flags,
            verbose: info.is_present("verbose"),
            json: info.is_present("json"),
        }
    }

    pub fn report(&self, name: &str) {
        let response = query(name);
        if self.json {
            self.report_json(&response);
        } else {
            if let Some(krate) = get_crate(&response) {
                self.report_crate(&krate);
            }
        }
    }

    pub fn report_json(&self, response: &requests::Response) {
        if self.verbose {
            if let Some(json) = response.text() {
                println!("{}", json);
            }
        } else {
            if let Some(Ok(json)) = response.from_json::<crates::Reply>()
                                            .as_ref()
                                            .map(to_string_pretty::<crates::Reply>) {
                println!("{}", json);
            }
        }
    }

    pub fn report_crate(&self, krate: &crates::Crate) {
        for flag in &self.flags {
            match *flag {
                Flag::Repository => krate.print_repository(self.verbose),
                Flag::Documentation => krate.print_documentation(self.verbose),
                Flag::Downloads => krate.print_downloads(self.verbose),
                Flag::Homepage => krate.print_homepage(self.verbose),
                Flag::Default => reportv(&krate, self.verbose),
            }
        }
    }
}

fn reportv(krate: &crates::Crate, verbose: bool) {
    if verbose {
        println!("{:#}", krate);
    } else {
        println!("{}", krate);
    }
}

fn query(krate: &str) -> requests::Response {
    requests::get(&format!("http://crates.io/api/v1/crates/{}", krate)).unwrap()
    // println!("{:?}", response.json().unwrap());
    // response.from_json::<crates::Reply>()
}

fn get_crate(response: &requests::Response) -> Option<crates::Crate> {
    response.from_json::<crates::Reply>().map(|r| r.krate)
}

// use std::fmt;
// fn debug<T>(item: &T)
//     where T: fmt::Debug
// {
//     println!("{:#?}", item);
// }

fn main() {

    let matches = App::new(CARGO)
        .bin_name(CARGO)
        .author(crate_authors!())
        .version(crate_version!())
        .about("Query crates.io registry for crates details")
        .setting(AppSettings::GlobalVersion)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .subcommand(SubCommand::with_name("info")
            .setting(AppSettings::ArgRequiredElseHelp)
            .setting(AppSettings::TrailingVarArg)
            .arg(Arg::with_name("documentation")
                .short("d")
                .long("documentation")
                .help("Report documentation URL"))
            // .arg_from_usage("-d, --documentation 'Report documentation URL'")
            .arg_from_usage("-D, --downloads 'Report number of crate downloads'")
            .arg_from_usage("-H, --homepage 'Report home page URL'")
            .arg_from_usage("-r, --repository 'Report crate repository URL'")
            .arg(Arg::with_name("json")
                .short("j")
                .long("json")
                .help("Report raw JSON data from crates.io")
                .conflicts_with_all(&["documentation", "downloads", "homepage", "repository"]))
            // .arg_from_usage("-j, --json 'Report raw json data from crates.io'")
            .arg_from_usage("-v, --verbose 'Report more details'")
            .arg_from_usage("<crate>... 'crate to query'"))
        .get_matches();

    if let Some(info) = matches.subcommand_matches("info") {
        if let Some(crates) = info.values_of("crate") {
            let rep = Report::new(info);
            for krate in crates {
                // debug(&krate);
                rep.report(krate);
            }
        }
    }
}
<commit_msg>Fix verbose mode to pretty print JSON<commit_after>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

#[macro_use]
extern crate clap;
extern crate requests;
extern crate serde;
extern crate serde_json;

mod crates;

use clap::{App, SubCommand, Arg, AppSettings, ArgMatches};
use serde_json::ser::to_string_pretty;

const CARGO: &'static str = "cargo";

#[derive(Debug, PartialEq)]
enum Flag {
    Repository,
    Documentation,
    Downloads,
    Homepage,
    Default,
}

#[derive(Debug)]
struct Report {
    flags: Vec<Flag>,
    verbose: bool,
    json: bool,
}

impl Report {
    pub fn new(info: &ArgMatches) -> Self {

        let mut flags: Vec<Flag> = vec![];
        if info.is_present("repository") {
            flags.push(Flag::Repository);
        }
        if info.is_present("documentation") {
            flags.push(Flag::Documentation);
        }
        if info.is_present("downloads") {
            flags.push(Flag::Downloads);
        }
        if info.is_present("homepage") {
            flags.push(Flag::Homepage);
        }

        if flags.is_empty() {
            flags.push(Flag::Default);
        }

        Report {
            flags: flags,
            verbose: info.is_present("verbose"),
            json: info.is_present("json"),
        }
    }

    pub fn report(&self, name: &str) {
        let response = query(name);
        if self.json {
            self.report_json(&response);
        } else {
            if let Some(krate) = get_crate(&response) {
                self.report_crate(&krate);
            }
        }
    }

    pub fn report_json(&self, response: &requests::Response) {
        if self.verbose {
            if let Some(Ok(json)) = response.from_json::<crates::Reply>()
                .as_ref()
                .map(to_string_pretty) {
                println!("{}", json);
            }
        } else {
            if let Some(json) = response.text() {
                println!("{}", json);
            }
        }
    }

    pub fn report_crate(&self, krate: &crates::Crate) {
        for flag in &self.flags {
            match *flag {
                Flag::Repository => krate.print_repository(self.verbose),
                Flag::Documentation => krate.print_documentation(self.verbose),
                Flag::Downloads => krate.print_downloads(self.verbose),
                Flag::Homepage => krate.print_homepage(self.verbose),
                Flag::Default => reportv(&krate, self.verbose),
            }
        }
    }
}

fn reportv(krate: &crates::Crate, verbose: bool) {
    if verbose {
        println!("{:#}", krate);
    } else {
        println!("{}", krate);
    }
}

fn query(krate: &str) -> requests::Response {
    requests::get(&format!("http://crates.io/api/v1/crates/{}", krate)).unwrap()
    // println!("{:?}", response.json().unwrap());
    // response.from_json::<crates::Reply>()
}

fn get_crate(response: &requests::Response) -> Option<crates::Crate> {
    response.from_json::<crates::Reply>().map(|r| r.krate)
}

// use std::fmt;
// fn debug<T>(item: &T)
//     where T: fmt::Debug
// {
//     println!("{:#?}", item);
// }

fn main() {

    let matches = App::new(CARGO)
        .bin_name(CARGO)
        .author(crate_authors!())
        .version(crate_version!())
        .about("Query crates.io registry for crates details")
        .setting(AppSettings::GlobalVersion)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .subcommand(SubCommand::with_name("info")
            .setting(AppSettings::ArgRequiredElseHelp)
            .setting(AppSettings::TrailingVarArg)
            .arg(Arg::with_name("documentation")
                .short("d")
                .long("documentation")
                .help("Report documentation URL"))
            // .arg_from_usage("-d, --documentation 'Report documentation URL'")
            .arg_from_usage("-D, --downloads 'Report number of crate downloads'")
            .arg_from_usage("-H, --homepage 'Report home page URL'")
            .arg_from_usage("-r, --repository 'Report crate repository URL'")
            .arg(Arg::with_name("json")
                .short("j")
                .long("json")
                .help("Report raw JSON data from crates.io")
                .conflicts_with_all(&["documentation", "downloads", "homepage", "repository"]))
            // .arg_from_usage("-j, --json 'Report raw json data from crates.io'")
            .arg_from_usage("-v, --verbose 'Report more details'")
            .arg_from_usage("<crate>... 'crate to query'"))
        .get_matches();

    if let Some(info) = matches.subcommand_matches("info") {
        if let Some(crates) = info.values_of("crate") {
            let rep = Report::new(info);
            for krate in crates {
                // debug(&krate);
                rep.report(krate);
            }
        }
    }
}
<|endoftext|>