<commit_before>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate rand;

pub mod basetypes;
pub mod chess_move;
pub mod position;
pub mod search;
pub mod uci;

use std::process::exit;
use std::sync::Arc;
use std::time::SystemTime;
use std::collections::VecDeque;
use basetypes::*;
use position::Position;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use search::{TimeManagement, SearchStatus, MultipvSearch};
use search::tt::*;

// The version of the program.
const VERSION: &'static str = "0.1";

// The name of the program.
const NAME: &'static str = "Alcibiades";

// The author of the program.
const AUTHOR: &'static str = "Evgeni Pandurski";

// The starting position in Forsyth–Edwards notation (FEN).
const STARTING_POSITION: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 1";

// Represents a condition for terminating the search.
enum PlayWhen {
    TimeManagement(TimeManagement), // Stop when `TimeManagement` says.
    MoveTime(u64), // Stop after the given number of milliseconds.
    Nodes(NodeCount), // Stop when the given number of nodes has been searched.
    Depth(u8), // Stop after the given search depth has been reached.
    Never, // An infinite search.
}

/// Implements `UciEngine` trait.
pub struct Engine {
    tt: Arc<TranspositionTable>,
    position: Position,
    current_depth: u8,
    current_value: Value,

    // `Engine` will hand over the real work to `MultipvSearch`.
    search: MultipvSearch,

    // Tells the engine when it must stop thinking and play the best
    // move it has found.
    play_when: PlayWhen,

    // Tells the engine if it will be allowed to ponder. This option
    // is needed because the engine might change its time management
    // algorithm when pondering is allowed.
    pondering_is_allowed: bool,

    // `true` if the engine is thinking in pondering mode.
    is_pondering: bool,

    // Marks the last time when a message was sent by the engine to
    // the GUI, or if no message has been sent yet -- the time when
    // the search stared.
    silent_since: SystemTime,

    // A queue for the messages send by the engine to the GUI.
    queue: VecDeque<EngineReply>,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);

        Engine {
            tt: tt.clone(),
            position: Position::from_fen(STARTING_POSITION).ok().unwrap(),
            current_depth: 0,
            current_value: VALUE_UNKNOWN,
            search: MultipvSearch::new(tt),
            play_when: PlayWhen::Never,
            pondering_is_allowed: false,
            is_pondering: false,
            silent_since: SystemTime::now(),
            queue: VecDeque::new(),
        }
    }

    // A helper method. It it adds a progress report message to
    // `self.queue`.
    fn queue_progress_report(&mut self) {
        let SearchStatus { depth, searched_nodes, nps, .. } = *self.search.status();
        self.queue.push_back(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", depth)),
            ("nodes".to_string(), format!("{}", searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
        ]));
        self.silent_since = SystemTime::now();
    }

    // A helper method. It it adds a message containing the current PV
    // to `self.queue`.
    fn queue_pv(&mut self) {
        let SearchStatus { depth, value, bound, ref pv, searched_time, searched_nodes, nps, .. } =
            *self.search.status();
        let score_suffix = match bound {
            BOUND_EXACT => "",
            BOUND_UPPER => " upperbound",
            BOUND_LOWER => " lowerbound",
            _ => panic!("unexpected bound type"),
        };
        let mut pv_string = String::new();
        for m in pv {
            pv_string.push_str(&m.notation());
            pv_string.push(' ');
        }
        self.queue.push_back(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", depth)),
            ("score".to_string(), format!("cp {}{}", value, score_suffix)),
            ("time".to_string(), format!("{}", searched_time)),
            ("nodes".to_string(), format!("{}", searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
            ("pv".to_string(), pv_string),
        ]));
        self.silent_since = SystemTime::now();
    }

    // A helper method. It it adds a message containing the current
    // best move to `self.queue`.
    fn queue_best_move(&mut self) {
        // TODO: Use `self.status.best_move`.
        let pv = &self.search.status().pv;
        self.queue.push_back(EngineReply::BestMove {
            best_move: pv.get(0).map_or("0000".to_string(), |m| m.notation()),
            ponder_move: pv.get(1).map(|m| m.notation()),
        });
        self.silent_since = SystemTime::now();
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        if !self.is_thinking() {
            self.tt.clear();
        }
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking() {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking() {
            // NOTE: We ignore the "mate" parameter.

            self.tt.new_search();
            self.current_depth = 0;
            self.current_value = VALUE_UNKNOWN;
            self.is_pondering = ponder;
            self.play_when = if infinite {
                PlayWhen::Never
            } else if movetime.is_some() {
                PlayWhen::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                PlayWhen::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                PlayWhen::Depth(depth.unwrap() as u8)
            } else {
                PlayWhen::TimeManagement(TimeManagement::new(self.position.board().to_move(),
                                                             self.pondering_is_allowed,
                                                             wtime,
                                                             btime,
                                                             winc,
                                                             binc,
                                                             movestogo))
            };
            self.silent_since = SystemTime::now();
            self.search.start(&self.position, searchmoves);
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking() {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking() {
            self.search.stop();
            self.queue_best_move();
        }
    }

    #[inline]
    fn is_thinking(&self) -> bool {
        !self.search.status().done
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        if self.is_thinking() {
            let SearchStatus { done, depth, value, searched_nodes, searched_time, .. } =
                *self.search.update_status();

            // Send the PV when changed.
            if depth > self.current_depth || value != self.current_value {
                self.current_depth = depth;
                self.current_value = value;
                if searched_nodes > 0 {
                    self.queue_pv();
                }
            }

            // Send periodic progress reports.
            if self.silent_since.elapsed().unwrap().as_secs() > 10 {
                self.queue_progress_report();
            }

            // Check if we must play now.
            if !self.is_pondering &&
               match self.play_when {
                PlayWhen::TimeManagement(ref tm) => done || tm.must_play(self.search.status()),
                PlayWhen::MoveTime(t) => done || searched_time >= t,
                PlayWhen::Nodes(n) => done || searched_nodes >= n,
                PlayWhen::Depth(d) => done || depth >= d,
                PlayWhen::Never => false,
            } {
                self.stop();
            }
        }

        self.queue.pop_front()
    }

    fn exit(&mut self) {
        self.search.exit();
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("{} {}", NAME, VERSION)
    }

    fn author(&self) -> String {
        AUTHOR.to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

fn main() {
    if let Ok(mut uci_loop) = uci::Server::wait_for_hanshake(EngineFactory) {
        match uci_loop.serve() {
            Ok(_) => {
                exit(0);
            }
            Err(_) => {
                exit(1);
            }
        }
    }
    exit(2);
}
<commit_msg>comment<commit_after>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate rand;

pub mod basetypes;
pub mod chess_move;
pub mod position;
pub mod search;
pub mod uci;

use std::process::exit;
use std::sync::Arc;
use std::time::SystemTime;
use std::collections::VecDeque;
use basetypes::*;
use position::Position;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use search::{TimeManagement, SearchStatus, MultipvSearch};
use search::tt::*;

// The version of the program.
const VERSION: &'static str = "0.1";

// The name of the program.
const NAME: &'static str = "Alcibiades";

// The author of the program.
const AUTHOR: &'static str = "Evgeni Pandurski";

// The starting position in Forsyth–Edwards notation (FEN).
const STARTING_POSITION: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 1";

// Represents a condition for terminating the search.
enum PlayWhen {
    TimeManagement(TimeManagement), // Stop when `TimeManagement` says.
    MoveTime(u64), // Stop after the given number of milliseconds.
    Nodes(NodeCount), // Stop when the given number of nodes has been searched.
    Depth(u8), // Stop when the given search depth has been reached.
    Never, // An infinite search.
}

/// Implements `UciEngine` trait.
pub struct Engine {
    tt: Arc<TranspositionTable>,
    position: Position,
    current_depth: u8,
    current_value: Value,

    // `Engine` will hand over the real work to `MultipvSearch`.
    search: MultipvSearch,

    // Tells the engine when it must stop thinking and play the best
    // move it has found.
    play_when: PlayWhen,

    // Tells the engine if it will be allowed to ponder. This option
    // is needed because the engine might change its time management
    // algorithm when pondering is allowed.
    pondering_is_allowed: bool,

    // `true` if the engine is thinking in pondering mode.
    is_pondering: bool,

    // Marks the last time when a message was sent by the engine to
    // the GUI, or if no message has been sent yet -- the time when
    // the search stared.
    silent_since: SystemTime,

    // A queue for the messages send by the engine to the GUI.
    queue: VecDeque<EngineReply>,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);

        Engine {
            tt: tt.clone(),
            position: Position::from_fen(STARTING_POSITION).ok().unwrap(),
            current_depth: 0,
            current_value: VALUE_UNKNOWN,
            search: MultipvSearch::new(tt),
            play_when: PlayWhen::Never,
            pondering_is_allowed: false,
            is_pondering: false,
            silent_since: SystemTime::now(),
            queue: VecDeque::new(),
        }
    }

    // A helper method. It it adds a progress report message to
    // `self.queue`.
    fn queue_progress_report(&mut self) {
        let SearchStatus { depth, searched_nodes, nps, .. } = *self.search.status();
        self.queue.push_back(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", depth)),
            ("nodes".to_string(), format!("{}", searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
        ]));
        self.silent_since = SystemTime::now();
    }

    // A helper method. It it adds a message containing the current PV
    // to `self.queue`.
    fn queue_pv(&mut self) {
        let SearchStatus { depth, value, bound, ref pv, searched_time, searched_nodes, nps, .. } =
            *self.search.status();
        let score_suffix = match bound {
            BOUND_EXACT => "",
            BOUND_UPPER => " upperbound",
            BOUND_LOWER => " lowerbound",
            _ => panic!("unexpected bound type"),
        };
        let mut pv_string = String::new();
        for m in pv {
            pv_string.push_str(&m.notation());
            pv_string.push(' ');
        }
        self.queue.push_back(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", depth)),
            ("score".to_string(), format!("cp {}{}", value, score_suffix)),
            ("time".to_string(), format!("{}", searched_time)),
            ("nodes".to_string(), format!("{}", searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
            ("pv".to_string(), pv_string),
        ]));
        self.silent_since = SystemTime::now();
    }

    // A helper method. It it adds a message containing the current
    // best move to `self.queue`.
    fn queue_best_move(&mut self) {
        // TODO: Use `self.status.best_move`.
        let pv = &self.search.status().pv;
        self.queue.push_back(EngineReply::BestMove {
            best_move: pv.get(0).map_or("0000".to_string(), |m| m.notation()),
            ponder_move: pv.get(1).map(|m| m.notation()),
        });
        self.silent_since = SystemTime::now();
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        if !self.is_thinking() {
            self.tt.clear();
        }
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking() {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking() {
            // NOTE: We ignore the "mate" parameter.

            self.tt.new_search();
            self.current_depth = 0;
            self.current_value = VALUE_UNKNOWN;
            self.is_pondering = ponder;
            self.play_when = if infinite {
                PlayWhen::Never
            } else if movetime.is_some() {
                PlayWhen::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                PlayWhen::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                PlayWhen::Depth(depth.unwrap() as u8)
            } else {
                PlayWhen::TimeManagement(TimeManagement::new(self.position.board().to_move(),
                                                             self.pondering_is_allowed,
                                                             wtime,
                                                             btime,
                                                             winc,
                                                             binc,
                                                             movestogo))
            };
            self.silent_since = SystemTime::now();
            self.search.start(&self.position, searchmoves);
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking() {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking() {
            self.search.stop();
            self.queue_best_move();
        }
    }

    #[inline]
    fn is_thinking(&self) -> bool {
        !self.search.status().done
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        if self.is_thinking() {
            let SearchStatus { done, depth, value, searched_nodes, searched_time, .. } =
                *self.search.update_status();

            // Send the PV when changed.
            if depth > self.current_depth || value != self.current_value {
                self.current_depth = depth;
                self.current_value = value;
                if searched_nodes > 0 {
                    self.queue_pv();
                }
            }

            // Send periodic progress reports.
            if self.silent_since.elapsed().unwrap().as_secs() > 10 {
                self.queue_progress_report();
            }

            // Check if we must play now.
            if !self.is_pondering &&
               match self.play_when {
                PlayWhen::TimeManagement(ref tm) => done || tm.must_play(self.search.status()),
                PlayWhen::MoveTime(t) => done || searched_time >= t,
                PlayWhen::Nodes(n) => done || searched_nodes >= n,
                PlayWhen::Depth(d) => done || depth >= d,
                PlayWhen::Never => false,
            } {
                self.stop();
            }
        }

        self.queue.pop_front()
    }

    fn exit(&mut self) {
        self.search.exit();
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("{} {}", NAME, VERSION)
    }

    fn author(&self) -> String {
        AUTHOR.to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

fn main() {
    if let Ok(mut uci_loop) = uci::Server::wait_for_hanshake(EngineFactory) {
        match uci_loop.serve() {
            Ok(_) => {
                exit(0);
            }
            Err(_) => {
                exit(1);
            }
        }
    }
    exit(2);
}
<|endoftext|>


<commit_before>extern crate image;
extern crate rand;
extern crate docopt;
extern crate "rustc-serialize" as rustc_serialize;
extern crate tray_rust;

use std::vec::Vec;
use std::sync::{Arc, TaskPool};
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};
use std::time::duration::Duration;
use std::os;
use rand::StdRng;
use docopt::Docopt;

use tray_rust::film;
use tray_rust::geometry::Geometry;
use tray_rust::sampler;
use tray_rust::sampler::{Sampler};
use tray_rust::scene;
use tray_rust::integrator::Integrator;

static WIDTH: usize = 800;
static HEIGHT: usize = 600;
static USAGE: &'static str = "
Usage: tray_rust [options]

Options:
  -o <file>     Specify the output file to save the image. Supported formats are
                PNG, JPG, PPM and BMP (although the BMP won't currently be picked up with the flag).
                Default is 'out.png'.
  -n <number>   Specify the number of threads to use for rendering. Defaults to the number of cores
                on the system.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_o: Option<String>,
    flag_n: Option<usize>,
}

/// Threads are each sent a sender end of the channel that is
/// read from by the render target thread which then saves the
/// values recieved to the render target
fn thread_work(tx: Sender<(f32, f32, film::Colorf)>, queue: Arc<sampler::BlockQueue>,
               scene: Arc<scene::Scene>) {
    let mut sampler = sampler::LowDiscrepancy::new(queue.block_dim(), 64);
    let mut samples = Vec::with_capacity(sampler.max_spp());
    let mut sample_pos = Vec::with_capacity(sampler.max_spp());
    let mut rng = match StdRng::new() {
        Ok(r) => r,
        Err(e) => { println!("Failed to get StdRng, {}", e); return }
    };
    // Grab a block from the queue and start working on it, submitting samples
    // to the render target thread after each pixel
    for b in queue.iter() {
        sampler.select_block(b);
        while sampler.has_samples() {
            // Get samples for a pixel and render them
            sampler.get_samples(&mut sample_pos, &mut rng);
            for s in sample_pos.iter() {
                let mut ray = scene.camera.generate_ray(s);
                if let Some(hit) = scene.intersect(&mut ray) {
                    let c = scene.integrator.illumination(&*scene, &ray, &hit, &mut sampler, &mut rng);
                    samples.push((s.0, s.1, c));
                }
            }
            for s in samples.iter() {
                if let Err(e) = tx.send(*s) {
                    println!("Worker thread exiting with send error {:?}", e);
                    return;
                }
            }
            samples.clear();
        }
    }
}

/// Spawn `n` worker threads to render the scene in parallel. Returns the receive end
/// of the channel where the threads will write their samples so that the receiver
/// can write these samples to the render target
fn spawn_workers(pool: &TaskPool, n: usize, scene: Arc<scene::Scene>) -> Receiver<(f32, f32, film::Colorf)> {
    let (tx, rx) = mpsc::channel();
    let block_queue = Arc::new(sampler::BlockQueue::new((WIDTH as u32, HEIGHT as u32), (8, 8)));
    for _ in 0..n {
        let q = block_queue.clone();
        let t = tx.clone();
        let s = scene.clone();
        pool.execute(move || {
            thread_work(t, q, s);
        });
    }
    rx
}

/// Render the scene in parallel to the render target
fn render_parallel(rt: &mut film::RenderTarget, n: usize){
    let scene = Arc::new(scene::Scene::new(WIDTH, HEIGHT));
    let pool = TaskPool::new(n);
    let rx = spawn_workers(&pool, n, scene);
    for m in rx.iter() {
        rt.write(m.0, m.1, &m.2);
    }
}

fn main() {
    let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());

    let mut rt = film::RenderTarget::new(WIDTH, HEIGHT);
    let n = match args.flag_n {
        Some(n) => n,
        None => os::num_cpus(),
    };
    let d = Duration::span(|| render_parallel(&mut rt, n));
    println!("Rendering took {}ms", d.num_milliseconds());
    let img = rt.get_render();
    let out_file = match args.flag_o {
        Some(f) => f,
        None => "out.png".to_string(),
    };
    match image::save_buffer(&Path::new(out_file), &img[..], WIDTH as u32, HEIGHT as u32, image::RGB(8)) {
        Ok(_) => {},
        Err(e) => println!("Error saving image, {}", e),
    };
}

<commit_msg>add a help option as well<commit_after>extern crate image;
extern crate rand;
extern crate docopt;
extern crate "rustc-serialize" as rustc_serialize;
extern crate tray_rust;

use std::vec::Vec;
use std::sync::{Arc, TaskPool};
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};
use std::time::duration::Duration;
use std::os;
use rand::StdRng;
use docopt::Docopt;

use tray_rust::film;
use tray_rust::geometry::Geometry;
use tray_rust::sampler;
use tray_rust::sampler::{Sampler};
use tray_rust::scene;
use tray_rust::integrator::Integrator;

static WIDTH: usize = 800;
static HEIGHT: usize = 600;
static USAGE: &'static str = "
Usage: tray_rust [options]

Options:
  -o <file>     Specify the output file to save the image. Supported formats are
                PNG, JPG, PPM and BMP (although the BMP won't currently be picked up with the flag).
                Default is 'out.png'.
  -n <number>   Specify the number of threads to use for rendering. Defaults to the number of cores
                on the system.
  -h, --help    Show this message.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_o: Option<String>,
    flag_n: Option<usize>,
}

/// Threads are each sent a sender end of the channel that is
/// read from by the render target thread which then saves the
/// values recieved to the render target
fn thread_work(tx: Sender<(f32, f32, film::Colorf)>, queue: Arc<sampler::BlockQueue>,
               scene: Arc<scene::Scene>) {
    let mut sampler = sampler::LowDiscrepancy::new(queue.block_dim(), 64);
    let mut samples = Vec::with_capacity(sampler.max_spp());
    let mut sample_pos = Vec::with_capacity(sampler.max_spp());
    let mut rng = match StdRng::new() {
        Ok(r) => r,
        Err(e) => { println!("Failed to get StdRng, {}", e); return }
    };
    // Grab a block from the queue and start working on it, submitting samples
    // to the render target thread after each pixel
    for b in queue.iter() {
        sampler.select_block(b);
        while sampler.has_samples() {
            // Get samples for a pixel and render them
            sampler.get_samples(&mut sample_pos, &mut rng);
            for s in sample_pos.iter() {
                let mut ray = scene.camera.generate_ray(s);
                if let Some(hit) = scene.intersect(&mut ray) {
                    let c = scene.integrator.illumination(&*scene, &ray, &hit, &mut sampler, &mut rng);
                    samples.push((s.0, s.1, c));
                }
            }
            for s in samples.iter() {
                if let Err(e) = tx.send(*s) {
                    println!("Worker thread exiting with send error {:?}", e);
                    return;
                }
            }
            samples.clear();
        }
    }
}

/// Spawn `n` worker threads to render the scene in parallel. Returns the receive end
/// of the channel where the threads will write their samples so that the receiver
/// can write these samples to the render target
fn spawn_workers(pool: &TaskPool, n: usize, scene: Arc<scene::Scene>) -> Receiver<(f32, f32, film::Colorf)> {
    let (tx, rx) = mpsc::channel();
    let block_queue = Arc::new(sampler::BlockQueue::new((WIDTH as u32, HEIGHT as u32), (8, 8)));
    for _ in 0..n {
        let q = block_queue.clone();
        let t = tx.clone();
        let s = scene.clone();
        pool.execute(move || {
            thread_work(t, q, s);
        });
    }
    rx
}

/// Render the scene in parallel to the render target
fn render_parallel(rt: &mut film::RenderTarget, n: usize){
    let scene = Arc::new(scene::Scene::new(WIDTH, HEIGHT));
    let pool = TaskPool::new(n);
    let rx = spawn_workers(&pool, n, scene);
    for m in rx.iter() {
        rt.write(m.0, m.1, &m.2);
    }
}

fn main() {
    let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());

    let mut rt = film::RenderTarget::new(WIDTH, HEIGHT);
    let n = match args.flag_n {
        Some(n) => n,
        None => os::num_cpus(),
    };
    let d = Duration::span(|| render_parallel(&mut rt, n));
    println!("Rendering took {}ms", d.num_milliseconds());
    let img = rt.get_render();
    let out_file = match args.flag_o {
        Some(f) => f,
        None => "out.png".to_string(),
    };
    match image::save_buffer(&Path::new(out_file), &img[..], WIDTH as u32, HEIGHT as u32, image::RGB(8)) {
        Ok(_) => {},
        Err(e) => println!("Error saving image, {}", e),
    };
}

<|endoftext|>


<commit_before>#![feature(duration_float)]
#![feature(euclidean_division)]
#![feature(try_trait)]

use std::path::{Path, PathBuf};
use std::time::{Duration, Instant};

use chrono::Local;

use glium::glutin::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
use glium::Surface;

mod aabb;
mod bsdf;
mod bvh;
mod camera;
mod color;
mod config;
mod consts;
mod float;
mod gl_renderer;
mod index_ptr;
mod input;
mod intersect;
mod light;
mod load;
mod material;
mod mesh;
mod obj_load;
mod pt_renderer;
mod sample;
mod scattering;
mod scene;
mod stats;
mod texture;
mod triangle;
mod util;
mod vertex;

use self::config::RenderConfig;
use self::gl_renderer::GLRenderer;
use self::input::InputState;
use self::pt_renderer::PTRenderer;

fn main() {
    match std::env::args().nth(1).as_ref().map(|s| s.as_str()) {
        Some("hq") => high_quality(),
        Some("b") => benchmark("bdpt", RenderConfig::bdpt_benchmark()),
        Some(_) => benchmark("", RenderConfig::benchmark()),
        None => online_render(),
    }
}

fn high_quality() {
    // TODO: Add command line switches to select scenes and config settings
    let scenes = ["cornell-glossy"];
    let tag = "hq";
    let config = RenderConfig::high_quality();
    let output_dir = PathBuf::from("results").join("hq");
    offline_render(&scenes, tag, &output_dir, config);
}

fn benchmark(tag: &str, config: RenderConfig) {
    let scenes = [
        "cornell-sphere",
        "cornell-glossy",
        "cornell-water",
        "indirect",
        "conference",
        "sponza",
    ];
    let output_dir = PathBuf::from("results");
    offline_render(&scenes, tag, &output_dir, config);
}

fn offline_render(scenes: &[&str], tag: &str, output_dir: &Path, config: RenderConfig) {
    let tag = if tag.is_empty() {
        tag.to_string()
    } else {
        format!("_{}", tag)
    };
    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let output_dir = root_dir.join(output_dir);
    std::fs::create_dir_all(output_dir.clone()).unwrap();
    let time_stamp = Local::now().format("%F_%H%M%S").to_string();

    // Initialize an OpenGL context that is needed for post-processing
    let events_loop = glium::glutin::EventsLoop::new();
    // Preferably this wouldn't need use a window at all but alas this is the closest I have gotten.
    // There exists HeadlessContext but that still pops up a window (atleast on Windows).
    // TODO: Maybe change this such that the window displays the current render?
    let window = glium::glutin::WindowBuilder::new()
        .with_dimensions(glium::glutin::dpi::LogicalSize::new(0.0, 0.0))
        .with_visibility(false)
        .with_decorations(false)
        .with_title("Rusty");
    let context = glium::glutin::ContextBuilder::new();
    let display = glium::Display::new(window, context, &events_loop).unwrap();

    for scene_name in scenes {
        stats::new_scene(scene_name);
        let _t = stats::time("Total");
        println!("{}...", scene_name);
        let (scene, camera) = load::cpu_scene_from_name(scene_name, &config);
        let pt_renderer = PTRenderer::offline_render(&display, &scene, &camera, &config);

        stats::time("Post-process");
        let scene_prefix = format!("{}{}", scene_name, tag);
        let scene_dir = output_dir.join(&scene_prefix);
        std::fs::create_dir_all(scene_dir.clone()).unwrap();
        let timestamped_image = scene_dir.join(format!("{}_{}.png", scene_prefix, time_stamp));
        pt_renderer.save_image(&display, &timestamped_image);
        // Make a copy to the main output directory
        let default_image = output_dir.join(scene_prefix).with_extension("png");
        std::fs::copy(timestamped_image, default_image).unwrap();
    }
    let stats_dir = output_dir.join(format!("stats{}", tag));
    std::fs::create_dir_all(stats_dir.clone()).unwrap();
    let stats_file = stats_dir.join(format!("stats{}_{}.txt", tag, time_stamp));
    stats::print_and_save(&stats_file);
}

fn online_render() {
    let mut config = RenderConfig::bdpt();
    let mut events_loop = glium::glutin::EventsLoop::new();
    let window = glium::glutin::WindowBuilder::new()
        .with_dimensions(config.dimensions())
        .with_resizable(false); // TODO: enable resizing
    let context = glium::glutin::ContextBuilder::new().with_depth_buffer(24);
    let display =
        glium::Display::new(window, context, &events_loop).expect("Failed to create display");

    let (mut scene, mut gpu_scene, mut camera) =
        load::gpu_scene_from_key(&display, VirtualKeyCode::Key1, &config).unwrap();
    let gl_renderer = GLRenderer::new(&display);
    let mut pt_renderer: Option<PTRenderer> = None;

    let mut input = InputState::new();
    let mut quit = false;
    let mut last_frame = Instant::now();

    loop {
        let mut target = display.draw();
        target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
        if let Some(renderer) = &mut pt_renderer {
            renderer.update_image();
            renderer.render_image(&display, &mut target);
        } else {
            gl_renderer.render(&mut target, &gpu_scene, &camera);
        }
        target.finish().unwrap();

        events_loop.poll_events(|event| {
            input.update(&event);
            match event {
                Event::WindowEvent {
                    event: WindowEvent::KeyboardInput { input, .. },
                    ..
                } => match input {
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(VirtualKeyCode::Space),
                        ..
                    } => {
                        if pt_renderer.is_some() {
                            pt_renderer = None;
                        } else {
                            pt_renderer =
                                Some(PTRenderer::start_render(&display, &scene, &camera, &config));
                        }
                    }
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(VirtualKeyCode::C),
                        ..
                    } => println!("camera: {:?}", camera.pos),
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(keycode),
                        ..
                    } => {
                        if pt_renderer.is_none() {
                            if let Some(res) = load::gpu_scene_from_key(&display, keycode, &config)
                            {
                                scene = res.0;
                                gpu_scene = res.1;
                                camera = res.2;
                            }
                            config.handle_key(keycode);
                        }
                    }
                    _ => (),
                },
                Event::WindowEvent {
                    event: WindowEvent::CloseRequested,
                    ..
                } => quit = true,
                Event::WindowEvent {
                    event: WindowEvent::DroppedFile(path),
                    ..
                } => {
                    if pt_renderer.is_none() {
                        // TODO: don't crash on bad scenes
                        if let Some(res) = load::gpu_scene_from_path(&display, &path, &config) {
                            scene = res.0;
                            gpu_scene = res.1;
                            camera = res.2;
                            // TODO: would be nice if this grabbed the focus
                        }
                    }
                }
                _ => (),
            }
        });
        if pt_renderer.is_none() {
            camera.process_input(&input);
        }
        input.reset_deltas();
        if quit {
            return;
        }
        // Limit frame rate
        let frame_time = Duration::from_millis(5);
        if last_frame.elapsed() < frame_time {
            std::thread::park_timeout(frame_time - last_frame.elapsed());
        }
        last_frame = Instant::now();
    }
}
<commit_msg>Fix potential underflow in timeout calculation<commit_after>#![feature(duration_float)]
#![feature(euclidean_division)]
#![feature(try_trait)]

use std::path::{Path, PathBuf};
use std::time::{Duration, Instant};

use chrono::Local;

use glium::glutin::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
use glium::Surface;

mod aabb;
mod bsdf;
mod bvh;
mod camera;
mod color;
mod config;
mod consts;
mod float;
mod gl_renderer;
mod index_ptr;
mod input;
mod intersect;
mod light;
mod load;
mod material;
mod mesh;
mod obj_load;
mod pt_renderer;
mod sample;
mod scattering;
mod scene;
mod stats;
mod texture;
mod triangle;
mod util;
mod vertex;

use self::config::RenderConfig;
use self::gl_renderer::GLRenderer;
use self::input::InputState;
use self::pt_renderer::PTRenderer;

// TODO: add comparison mode
fn main() {
    match std::env::args().nth(1).as_ref().map(|s| s.as_str()) {
        Some("hq") => high_quality(),
        Some("b") => benchmark("bdpt", RenderConfig::bdpt_benchmark()),
        Some(_) => benchmark("", RenderConfig::benchmark()),
        None => online_render(),
    }
}

fn high_quality() {
    // TODO: Add command line switches to select scenes and config settings
    let scenes = [
        // "cornell-sphere",
        // "cornell-glossy",
        // "cornell-water",
        // "indirect",
        // "conference",
        "sponza",
    ];
    let tag = "hq";
    let config = RenderConfig::high_quality();
    let output_dir = PathBuf::from("results").join("hq");
    offline_render(&scenes, tag, &output_dir, config);
}

fn benchmark(tag: &str, config: RenderConfig) {
    let scenes = [
        "cornell-sphere",
        "cornell-glossy",
        "cornell-water",
        "indirect",
        "conference",
        "sponza",
    ];
    let output_dir = PathBuf::from("results");
    offline_render(&scenes, tag, &output_dir, config);
}

fn offline_render(scenes: &[&str], tag: &str, output_dir: &Path, config: RenderConfig) {
    let tag = if tag.is_empty() {
        tag.to_string()
    } else {
        format!("_{}", tag)
    };
    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let output_dir = root_dir.join(output_dir);
    std::fs::create_dir_all(output_dir.clone()).unwrap();
    let time_stamp = Local::now().format("%F_%H%M%S").to_string();

    // Initialize an OpenGL context that is needed for post-processing
    let events_loop = glium::glutin::EventsLoop::new();
    // Preferably this wouldn't need use a window at all but alas this is the closest I have gotten.
    // There exists HeadlessContext but that still pops up a window (atleast on Windows).
    // TODO: Maybe change this such that the window displays the current render?
    let window = glium::glutin::WindowBuilder::new()
        .with_dimensions(glium::glutin::dpi::LogicalSize::new(0.0, 0.0))
        .with_visibility(false)
        .with_decorations(false)
        .with_title("Rusty");
    let context = glium::glutin::ContextBuilder::new();
    let display = glium::Display::new(window, context, &events_loop).unwrap();

    for scene_name in scenes {
        stats::new_scene(scene_name);
        let _t = stats::time("Total");
        println!("{}...", scene_name);
        let (scene, camera) = load::cpu_scene_from_name(scene_name, &config);
        let pt_renderer = PTRenderer::offline_render(&display, &scene, &camera, &config);

        stats::time("Post-process");
        let scene_prefix = format!("{}{}", scene_name, tag);
        let scene_dir = output_dir.join(&scene_prefix);
        std::fs::create_dir_all(scene_dir.clone()).unwrap();
        let timestamped_image = scene_dir.join(format!("{}_{}.png", scene_prefix, time_stamp));
        pt_renderer.save_image(&display, &timestamped_image);
        // Make a copy to the main output directory
        let default_image = output_dir.join(scene_prefix).with_extension("png");
        std::fs::copy(timestamped_image, default_image).unwrap();
    }
    let stats_dir = output_dir.join(format!("stats{}", tag));
    std::fs::create_dir_all(stats_dir.clone()).unwrap();
    let stats_file = stats_dir.join(format!("stats{}_{}.txt", tag, time_stamp));
    stats::print_and_save(&stats_file);
}

fn online_render() {
    let mut config = RenderConfig::bdpt();
    let mut events_loop = glium::glutin::EventsLoop::new();
    let window = glium::glutin::WindowBuilder::new()
        .with_dimensions(config.dimensions())
        .with_resizable(false); // TODO: enable resizing
    let context = glium::glutin::ContextBuilder::new().with_depth_buffer(24);
    let display =
        glium::Display::new(window, context, &events_loop).expect("Failed to create display");

    let (mut scene, mut gpu_scene, mut camera) =
        load::gpu_scene_from_key(&display, VirtualKeyCode::Key1, &config).unwrap();
    let gl_renderer = GLRenderer::new(&display);
    let mut pt_renderer: Option<PTRenderer> = None;

    let mut input = InputState::new();
    let mut quit = false;
    let mut last_frame = Instant::now();

    loop {
        let mut target = display.draw();
        target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
        if let Some(renderer) = &mut pt_renderer {
            renderer.update_image();
            renderer.render_image(&display, &mut target);
        } else {
            gl_renderer.render(&mut target, &gpu_scene, &camera);
        }
        target.finish().unwrap();

        events_loop.poll_events(|event| {
            input.update(&event);
            match event {
                Event::WindowEvent {
                    event: WindowEvent::KeyboardInput { input, .. },
                    ..
                } => match input {
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(VirtualKeyCode::Space),
                        ..
                    } => {
                        if pt_renderer.is_some() {
                            pt_renderer = None;
                        } else {
                            pt_renderer =
                                Some(PTRenderer::start_render(&display, &scene, &camera, &config));
                        }
                    }
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(VirtualKeyCode::C),
                        ..
                    } => println!("camera: {:?}", camera.pos),
                    KeyboardInput {
                        state: ElementState::Pressed,
                        virtual_keycode: Some(keycode),
                        ..
                    } => {
                        if pt_renderer.is_none() {
                            if let Some(res) = load::gpu_scene_from_key(&display, keycode, &config)
                            {
                                scene = res.0;
                                gpu_scene = res.1;
                                camera = res.2;
                            }
                            config.handle_key(keycode);
                        }
                    }
                    _ => (),
                },
                Event::WindowEvent {
                    event: WindowEvent::CloseRequested,
                    ..
                } => quit = true,
                Event::WindowEvent {
                    event: WindowEvent::DroppedFile(path),
                    ..
                } => {
                    if pt_renderer.is_none() {
                        // TODO: don't crash on bad scenes
                        if let Some(res) = load::gpu_scene_from_path(&display, &path, &config) {
                            scene = res.0;
                            gpu_scene = res.1;
                            camera = res.2;
                            // TODO: would be nice if this grabbed the focus
                        }
                    }
                }
                _ => (),
            }
        });
        if pt_renderer.is_none() {
            camera.process_input(&input);
        }
        input.reset_deltas();
        if quit {
            return;
        }
        // Limit frame rate
        let frame_time = Duration::from_millis(5);
        let elapsed = last_frame.elapsed();
        if elapsed < frame_time {
            std::thread::park_timeout(frame_time - elapsed);
        }
        last_frame = Instant::now();
    }
}
<|endoftext|>


<commit_before>#![allow(unstable)]

extern crate getopts;
extern crate selecta;

use getopts::{optopt,getopts};
use selecta::configuration::Configuration;
use selecta::search::Search;
use selecta::tty::TTY;
use selecta::tty::IO;
use selecta::screen::Screen;

fn main() {
    let initial_query = extract_initial_query();
    let lines = read_lines();

    let config = Configuration::from_inputs(lines, initial_query, Some(20));
    let mut search = Search::blank(config);

    let mut tty = TTY::new();
    let mut screen = Screen::new();

    screen.print(&search);

    while !search.is_done() {
        let input = tty.read();
        match input {
            Some(n) => {
                search = screen.handle_keystroke(search, n.as_slice());
            },
            None => break,
        };
        screen.print(&search);
    }
    println!("{}\n", search.selection.unwrap_or("None".to_string()));
    screen.ansi.show_cursor();
}

fn extract_initial_query() -> Option<String> {
    let args = std::os::args();
    let opts = &[
        optopt("s", "search", "initial search query", ""),
    ];
    let matches = getopts(args.tail(), opts).unwrap();

    matches.opt_str("s")
}

fn read_lines() -> Vec<String> {
    std::io::stdio::stdin().lock().lines().map( |line| {
        line.unwrap().trim().to_string()
    }).collect()
}
<commit_msg>Remove redundant .trim().to_string()<commit_after>#![allow(unstable)]

extern crate getopts;
extern crate selecta;

use getopts::{optopt,getopts};
use selecta::configuration::Configuration;
use selecta::search::Search;
use selecta::tty::TTY;
use selecta::tty::IO;
use selecta::screen::Screen;

fn main() {
    let initial_query = extract_initial_query();
    let lines = read_lines();

    let config = Configuration::from_inputs(lines, initial_query, Some(20));
    let mut search = Search::blank(config);

    let mut tty = TTY::new();
    let mut screen = Screen::new();

    screen.print(&search);

    while !search.is_done() {
        let input = tty.read();
        match input {
            Some(n) => {
                search = screen.handle_keystroke(search, n.as_slice());
            },
            None => break,
        };
        screen.print(&search);
    }
    println!("{}\n", search.selection.unwrap_or("None".to_string()));
    screen.ansi.show_cursor();
}

fn extract_initial_query() -> Option<String> {
    let args = std::os::args();
    let opts = &[
        optopt("s", "search", "initial search query", ""),
    ];
    let matches = getopts(args.tail(), opts).unwrap();

    matches.opt_str("s")
}

fn read_lines() -> Vec<String> {
    std::io::stdio::stdin().lock().lines().map( |line| {
        line.unwrap()
    }).collect()
}
<|endoftext|>


<commit_before>#![feature(macro_rules)]

extern crate getopts;
use getopts::{optflag,getopts,OptGroup,usage};
use std::os;
use std::collections::HashMap;
use std::collections::hashmap::{Occupied, Vacant};
use std::io::{fs, File};
use std::io::fs::PathExtensions;

fn get_env() -> HashMap<String, String> {
    let mut env = HashMap::new();
    env.extend(os::env().into_iter().filter(|&(ref key, ref value)| {
        key.as_slice().starts_with("MATES_")
    }));
    env
}

fn from_env<'a>(env: &'a HashMap<String, String>, key: &str) -> &'a String {
    env.find_equiv(&key).expect(
        format!("The {} environment variable must be set.", key).as_slice()
    )
}

fn build_index(outfile: &Path, dir: &Path) {
    if !dir.is_dir() {
        fail!("MATES_DIR must be a directory.");
    };

    let mut outf = File::create(outfile);
    let entries = match fs::readdir(dir) {
        Ok(x) => x,
        Err(f) => { fail!(f.desc) }
    };
    for entry in entries.iter() {
        if !entry.is_file() {
            continue;
        }
        let itemstr = match File::open(entry).read_to_string() {
            Ok(x) => x,
            Err(f) => { fail!(format!("Failed to open {}: {}", entry.display(), f.desc)) }
        };
        let item = parse_item(&itemstr);
        let name = item.fullname();
        match item.emails() {
            Some(emails) => {
                for email in emails.iter() {
                    outf.write_str(format!("{}\t{}\n", email.value, name).as_slice());
                };
            },
            None => ()
        };
    };
}

struct PropertyValue {
    params: String,
    value: String,
}

struct Item {
    props: HashMap<String, Vec<PropertyValue>>
}

impl Item {
    fn single_value(&self, key: &String) -> Option<String> {
        match self.props.find(key) {
            Some(x) => { if x.len() > 0 { Some(x[0].value.clone()) } else { None } },
            None => { None }
        }
    }
    fn fullname(&self) -> String {
        match self.single_value(&"FN".into_string()) {
            Some(x) => { x }
            None => { "".into_string() }
        }
    }

    fn emails(&self) -> Option<&Vec<PropertyValue>> {
        self.props.find(&"EMAIL".into_string())
    }
}

fn parse_item(s: &String) -> Item {
    let mut linebuffer = String::new();
    let mut line: String;
    let mut is_continuation: bool;
    let mut rv = Item {
        props: HashMap::new()
    };

    for strline in s.as_slice().split('\n') {
        line = strline.into_string();
        is_continuation = false;
        while line.as_slice().char_at(0).is_whitespace() {
            is_continuation = true;
            line.remove(0);
        };

        if !is_continuation && linebuffer.len() > 0 {
            let (propkey, propvalue) = parse_line(&linebuffer);
            match rv.props.entry(propkey) {
                Occupied(values) => { values.into_mut().push(propvalue); },
                Vacant(values) => { values.set(vec![propvalue]); }
            };
            linebuffer.clear();
        };

        linebuffer.push_str(line.as_slice());
    };
    rv
}

macro_rules! next_or_this(
    ($iterator:expr, $this: expr) => (
        match $iterator.next() {
            Some(x) => x,
            None => $this
        }
    )
)

fn parse_line(s: &String) -> (String, PropertyValue) {
    let mut kv_splitresult = s.as_slice().splitn(1, ':');
    let key_and_params = kv_splitresult.next().expect("");
    let value = next_or_this!(kv_splitresult, "");

    let mut kp_splitresult = key_and_params.splitn(1, ';');
    let key = kp_splitresult.next().expect("");
    let params = next_or_this!(kp_splitresult, "");

    (key.into_string(), PropertyValue {
        value: value.into_string(),
        params: params.into_string()
    })
}

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

    let program = args[0].as_slice();
    let opts = [
        optflag("i", "index", "Create index."),
        optflag("h", "help", "Print help.")
    ];

    let matches = match getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    let env = get_env();

    let print_usage = || {
        println!("{}", usage(program, opts));
    };

    if matches.opt_present("h") {
        print_usage();
    } else if matches.opt_present("index") {
        let index_file = from_env(&env, "MATES_INDEX");
        let mates_dir = from_env(&env, "MATES_DIR");
        println!("Rebuilding index file \"{}\"...", index_file);
        build_index(
            &Path::new(index_file.as_slice()),
            &Path::new(mates_dir.as_slice())
        );
    } else {
        print_usage();
    };
}
<commit_msg>mutt query<commit_after>#![feature(macro_rules)]

extern crate getopts;
use getopts::{optflag,optopt,getopts,OptGroup,usage};
use std::os;
use std::collections::HashMap;
use std::collections::hashmap::{Occupied, Vacant};
use std::io;
use std::io::fs::PathExtensions;

fn get_env() -> HashMap<String, String> {
    let mut env = HashMap::new();
    env.extend(os::env().into_iter().filter(|&(ref key, ref value)| {
        key.as_slice().starts_with("MATES_")
    }));
    env
}

fn from_env<'a>(env: &'a HashMap<String, String>, key: &str) -> Option<&'a String> {
    env.find_equiv(&key)
}

fn expect_env<'a>(env: &'a HashMap<String, String>, key: &str) -> &'a String {
    from_env(env, key).expect(
        format!("The {} environment variable must be set.", key).as_slice()
    )
}

macro_rules! expect_or_this(
    ($iterator:expr, $this: expr) => (
        match $iterator {
            Some(x) => x,
            None => $this
        }
    )
)

fn build_index(outfile: &Path, dir: &Path) {
    if !dir.is_dir() {
        fail!("MATES_DIR must be a directory.");
    };

    let mut outf = io::File::create(outfile);
    let entries = match io::fs::readdir(dir) {
        Ok(x) => x,
        Err(f) => { fail!(f.desc) }
    };
    for entry in entries.iter() {
        if !entry.is_file() {
            continue;
        }
        let itemstr = match io::File::open(entry).read_to_string() {
            Ok(x) => x,
            Err(f) => { fail!(format!("Failed to open {}: {}", entry.display(), f.desc)) }
        };
        let item = parse_item(&itemstr);
        let name = item.fullname();
        match item.emails() {
            Some(emails) => {
                for email in emails.iter() {
                    outf.write_str(format!("{}\t{}\n", email.value, name).as_slice());
                };
            },
            None => ()
        };
    };
}

struct PropertyValue {
    params: String,
    value: String,
}

struct Item {
    props: HashMap<String, Vec<PropertyValue>>
}

impl Item {
    fn single_value(&self, key: &String) -> Option<String> {
        match self.props.find(key) {
            Some(x) => { if x.len() > 0 { Some(x[0].value.clone()) } else { None } },
            None => { None }
        }
    }
    fn fullname(&self) -> String {
        match self.single_value(&"FN".into_string()) {
            Some(x) => { x }
            None => { "".into_string() }
        }
    }

    fn emails(&self) -> Option<&Vec<PropertyValue>> {
        self.props.find(&"EMAIL".into_string())
    }
}

fn parse_item(s: &String) -> Item {
    let mut linebuffer = String::new();
    let mut line: String;
    let mut is_continuation: bool;
    let mut rv = Item {
        props: HashMap::new()
    };

    for strline in s.as_slice().split('\n') {
        line = strline.into_string();
        is_continuation = false;
        while line.as_slice().char_at(0).is_whitespace() {
            is_continuation = true;
            line.remove(0);
        };

        if !is_continuation && linebuffer.len() > 0 {
            let (propkey, propvalue) = parse_line(&linebuffer);
            match rv.props.entry(propkey) {
                Occupied(values) => { values.into_mut().push(propvalue); },
                Vacant(values) => { values.set(vec![propvalue]); }
            };
            linebuffer.clear();
        };

        linebuffer.push_str(line.as_slice());
    };
    rv
}

fn parse_line(s: &String) -> (String, PropertyValue) {
    let mut kv_splitresult = s.as_slice().splitn(1, ':');
    let key_and_params = kv_splitresult.next().expect("");
    let value = expect_or_this!(kv_splitresult.next(), "");

    let mut kp_splitresult = key_and_params.splitn(1, ';');
    let key = kp_splitresult.next().expect("");
    let params = expect_or_this!(kp_splitresult.next(), "");

    (key.into_string(), PropertyValue {
        value: value.into_string(),
        params: params.into_string()
    })
}

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

    let program = args[0].as_slice();
    let opts = [
        optflag("i", "index", "Create index."),
        optflag("h", "help", "Print help."),
        optopt("m", "mutt-search", "Search in index, for mutt search.", "")
    ];

    let matches = match getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    let env = get_env();

    let print_usage = || {
        println!("{}", usage(program, opts));
        println!("Environment variables:");
        println!("- MATES_INDEX: Path to index file, which is basically a cache of all");
        println!("               contacts.");
        println!("- MATES_DIR:   The vdir to use.");
        println!("- MATES_GREP:  The grep executable to use.");
    };

    if matches.opt_present("h") {
        print_usage();
    } else if matches.opt_present("index") {
        let index_file = expect_env(&env, "MATES_INDEX");
        let mates_dir = expect_env(&env, "MATES_DIR");
        println!("Rebuilding index file \"{}\"...", index_file);
        build_index(
            &Path::new(index_file.as_slice()),
            &Path::new(mates_dir.as_slice())
        );
    } else if matches.opt_present("mutt-search") {
        let index_file = expect_env(&env, "MATES_INDEX");
        let default_grep = "grep".into_string();
        let grep_cmd = expect_or_this!(from_env(&env, "MATES_GREP"), &default_grep);

        // FIXME: Better way to write this? We already checked for presence of mutt-search before
        let query = matches.opt_str("mutt-search").expect("This should never happen and yet it did.");
        let process = match match io::Command::new(grep_cmd.as_slice())
            .arg(query.as_slice())
            .arg(index_file.as_slice())
            .stdout(io::process::InheritFd(1))
            .stderr(io::process::InheritFd(2))
            .spawn() {
                Ok(child) => child,
                Err(e) => fail!("Failed to execute grep command: {}", e),
            }.wait() {
                Ok(code) => {
                    if !code.success() {
                        fail!("Grep command exited with code {}, aborting.", code);
                    };
                },
                Err(e) => fail!("Failed to execute grep command: {}", e),
            };
    } else {
        print_usage();
    };
}
<|endoftext|>


<commit_before>#![feature(box_syntax, io)]

extern crate iron;
extern crate rand;
extern crate "rustc-serialize" as rustc_serialize;

use iron::middleware::Handler;
use iron::prelude::*;
use iron::status;
use rand::{ OsRng, Rng };
use rand::distributions::{ IndependentSample, Range };
use rustc_serialize::json;
use std::collections::hash_map::HashMap;
use std::sync::{ Arc, Mutex };

type SrandResult = Result<QueryResult, &'static str>;

#[derive(RustcEncodable, RustcDecodable)]
struct QueryResult {
    high: u64,
    low: u64,
    avg: f64,
    rolls: Box<Vec<u64>>,
}

struct PrngHandler<R: Rng + Send + Sync + 'static> {
    map: HashMap<u64, Range<u64>>,
    rng: R,
}

impl<R: Rng + Send + Sync + 'static> PrngHandler<R> {
    fn build_query_result<'a>(&mut self, inputs: &[u64]) -> SrandResult {
        let values: Vec<_> = inputs.iter().map(|i| {
            if !self.map.contains_key(i) {
                self.map.insert(*i, Range::new(0, *i));
            }

            self.get_random(*i)
        }).collect();

        if values.len() > 0 {
            let average = {
                let (count, total) = values.iter().fold((0,0), |(count,total),b| (count + 1, total + *b));
                total as f64 / count as f64
            };

            Ok(QueryResult {
                high: *values.iter().max().unwrap_or(&0),
                low: *values.iter().min().unwrap_or(&0),
                avg: average,
                rolls: box values,
            })
        } else {
            Err("Invalid request.")
        }
    }

    fn get_random(&mut self, i: u64) -> u64 {
        self.map[i].ind_sample(&mut self.rng)
    }
}

impl<R: Rng + Send + Sync + 'static> Handler for Arc<Mutex<PrngHandler<R>>> {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let mut handler = match self.lock() {
            Ok(handler) => handler,
            Err(_) => return Ok(Response::with((status::Ok, "Unable to process request."))),
        };

        let inputs: Vec<u64> = req.url.path.iter().filter_map(|i| i.parse().ok()).collect();
        match handler.build_query_result(&inputs[..]) {
            Ok(result) => Ok(Response::with((status::Ok, json::encode(&result).unwrap()))),
            Err(e) => Ok(Response::with((status::Ok, e))),
        }
    }
}

fn main() {
    let prng = match OsRng::new() {
        Ok(rng) => Arc::new(Mutex::new(PrngHandler {
            map: HashMap::new(),
            rng: rng,
        })),
        Err(e) => {
            println!("{}", e.description());
            return;
        }
    };

    match Iron::new(prng).http("localhost:2020") {
        Ok(_) => (),
        Err(_) => println!("srand failed to start"),
    }
}
<commit_msg>Remove useless arc.<commit_after>#![feature(box_syntax, io)]

extern crate iron;
extern crate rand;
extern crate "rustc-serialize" as rustc_serialize;

use iron::middleware::Handler;
use iron::prelude::*;
use iron::status;
use rand::{ OsRng, Rng };
use rand::distributions::{ IndependentSample, Range };
use rustc_serialize::json;
use std::collections::hash_map::HashMap;
use std::sync::{ Mutex };

type SrandResult = Result<QueryResult, &'static str>;

#[derive(RustcEncodable, RustcDecodable)]
struct QueryResult {
    high: u64,
    low: u64,
    avg: f64,
    rolls: Box<Vec<u64>>,
}

struct PrngHandler<R: Rng + Send + Sync + 'static> {
    map: HashMap<u64, Range<u64>>,
    rng: R,
}

impl<R: Rng + Send + Sync + 'static> PrngHandler<R> {
    fn build_query_result<'a>(&mut self, inputs: &[u64]) -> SrandResult {
        let values: Vec<_> = inputs.iter().map(|i| {
            if !self.map.contains_key(i) {
                self.map.insert(*i, Range::new(0, *i));
            }

            self.get_random(*i)
        }).collect();

        if values.len() > 0 {
            let average = {
                let (count, total) = values.iter().fold((0,0), |(count,total),b| (count + 1, total + *b));
                total as f64 / count as f64
            };

            Ok(QueryResult {
                high: *values.iter().max().unwrap_or(&0),
                low: *values.iter().min().unwrap_or(&0),
                avg: average,
                rolls: box values,
            })
        } else {
            Err("Invalid request.")
        }
    }

    fn get_random(&mut self, i: u64) -> u64 {
        self.map[i].ind_sample(&mut self.rng)
    }
}

impl<R: Rng + Send + Sync + 'static> Handler for Mutex<PrngHandler<R>> {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let mut handler = match self.lock() {
            Ok(handler) => handler,
            Err(_) => return Ok(Response::with((status::Ok, "Unable to process request."))),
        };

        let inputs: Vec<u64> = req.url.path.iter().filter_map(|i| i.parse().ok()).collect();
        match handler.build_query_result(&inputs[..]) {
            Ok(result) => Ok(Response::with((status::Ok, json::encode(&result).unwrap()))),
            Err(e) => Ok(Response::with((status::Ok, e))),
        }
    }
}

fn main() {
    let prng = match OsRng::new() {
        Ok(rng) => Mutex::new(PrngHandler {
            map: HashMap::new(),
            rng: rng,
        }),
        Err(e) => {
            println!("{}", e.description());
            return;
        }
    };

    match Iron::new(prng).http("localhost:2020") {
        Ok(_) => (),
        Err(_) => println!("srand failed to start"),
    }
}
<|endoftext|>


<commit_before>extern crate time;

fn main() {
    let dow = ["Duinday", "Sitaday", "Wikiday", "Tuxday", "Gnuday", "Commonday"];
    let moy = ["Jabim", "Zodrak", "Trogool", "Yanar", "Shkumbe", "Habniah", "Skarl", "Mikon", "Pertunda", "Kib", "Broket"];
    let dpm = 33;
    let dpw = dow.len();
    let mpy = moy.len();
    let (year, month, day, yoff) = current_day(time::now());
    let wd = (yoff + month * 3 + day) % dpw;
    let mut moff = yoff;

    for mm in 0..mpy {
        let spaces = (17 - moy[mm].len()) / 2;
        for _ in 0..spaces {
            print!(" ");
        }
        let mut month_text = format!("{}\n", moy[mm]);

        for dd in 0..dpw {
            let mut abbrd = String::from(dow[dd]);
            abbrd.truncate(2);
            month_text = format!("{}{} ", month_text, abbrd);
        }

        let dd = if mm == month { day } else { 1000 };
        let days = print_days(moff, dpm, dpw, dd);
        month_text = format!("{}{}", month_text, days.0);
        moff = days.1;
        if mm == 2 {
            month_text = format!("{}--  Peer  Day  --", month_text);
        }
        else if mm == 6 {
            month_text = format!("{}--Torrent Feast--", month_text);
        }
        else if is_leap_year(year) && mm == 10 {
            month_text = format!("{}--Immersion Day--", month_text);
        }

        month_text = format!("{}\n", month_text);
        print!("{}", month_text);
    }

    let mut datestr = format!("{}, {:02} of {}", dow[wd], day, moy[month]);
    let mut daystr = format!("{:02}.{:02}", month + 1, day);
    if day == 34 {
        if month == 2 {
            datestr = format!("Peer Day");
            daystr = format!("P");
        }
        else if month == 6 {
            datestr = format!("Torrent Feast");
            daystr = format!("T");
        }
        else if month == 10 {
            datestr = format!("Immersion Feast");
            daystr = format!("T");
        }
    }
    let hol = format!("{}", what_holiday(month, day));
    println!("Today is {} {}{}.  [{}.{}]", datestr, year, hol, year, daystr);
}

fn print_days(moff: usize, dpm: usize, dpw: usize, highlight: usize) -> (String, usize) {
    let mut month = format!("");

    if moff != 0 {
        month = format!("{}\n", month);
    }

    for _ in 0 .. moff {
        month = format!("{}   ", month);
    }

    for day in 1 .. dpm + 1 {
        if (day + moff) % dpw == 1 {
            month = format!("{}\n", month);
        }

        let ch = if day == highlight { ">" } else if day + 1 == highlight { "<" } else { " " };
        month = format!("{}{:2}{}", month, day, ch);
    }

    month = format!("{}\n", month);
    return (month, if moff == 0 { 3 } else { 0 });
}

fn current_day(current_time: time::Tm) -> (usize, usize, usize, usize) {
    let epoch_days_offset = 1483889;
    let seconds_per_day = 86400;
    let now = current_time.to_timespec();
    let days_since_epoch = (epoch_days_offset + now.sec / seconds_per_day) as i32;
    let days_per_year = 365;
    let leap_year = 4;
    let not_leap_year = 33;
    let days_per_month = 33;

    let mut count = 1;
    let mut days_remaining = days_since_epoch;
    let mut year = 0;
    while days_remaining > days_per_year + 1 {
        days_remaining -= days_per_year;
        year += 1;
        if count > not_leap_year {
            count = 1;
        }
        if count % leap_year == 0 {
            days_remaining -= 1;
        }
        count += 1;
    }
    if days_remaining == days_per_year + 1 && count % leap_year == 0 {
        days_remaining = 0;
        year += 1;
    }

    let mut month = 0;
    while days_remaining > days_per_month {
        month += 1;
        days_remaining -= days_per_month;
        if month == 3 || month == 7 || month == 11 {
            days_remaining -= 1;
        }
    }

    let day = days_remaining;
    return (year, month as usize, day as usize + 1, (year % 2 * 3) as usize);
}

fn is_leap_year(year: usize) -> bool {
    let rem = year % 33;
    return rem != 0 && rem % 4 == 0;
}

fn what_holiday(month: usize, day: usize) -> String {
    let mut result = format!("");
    if month == 0 && day == 1 {
        result = format!(", New Year's Day");
    }
    else if month == 2 && day == 12 {
        result = format!(", Purification Day");
    }
    else if month == 2 && day == 24 {
        result = format!(", Document Freedom Day");
    }
    else if month == 3 && day == 33 {
        result = format!(", Hardware Freedom Day");
    }
    else if month == 5 && day == 4 {
        result = format!(", Harvest Gift");
    }
    else if month == 5 && day == 9 {
        result = format!(", Culture Freedom Day");
    }
    else if month == 7 && day == 29 {
        result = format!(", Familytide");
    }
    else if month == 8 && day == 33 {
        result = format!(", Software Freedom Day");
    }
    else if month == 9 && day == 30 {
        result = format!(", Open Access Day");
    }
    else if month == 10 && day == 21 {
        result = format!(", Freedom Day");
    }
    else if month == 0 && day == 27 {
        result = format!(", International Workers' Day");
    }
    return result;
}

<commit_msg>Print collected month information once<commit_after>extern crate time;

fn main() {
    let dow = ["Duinday", "Sitaday", "Wikiday", "Tuxday", "Gnuday", "Commonday"];
    let moy = ["Jabim", "Zodrak", "Trogool", "Yanar", "Shkumbe", "Habniah", "Skarl", "Mikon", "Pertunda", "Kib", "Broket"];
    let dpm = 33;
    let dpw = dow.len();
    let mpy = moy.len();
    let (year, month, day, yoff) = current_day(time::now());
    let wd = (yoff + month * 3 + day) % dpw;
    let mut moff = yoff;

    for mm in 0..mpy {
        let spaces = (17 - moy[mm].len()) / 2;
        let mut month_text = format!("");
        for _ in 0..spaces {
            month_text = format!("{} ", month_text);
        }

        month_text = format!("{}{}\n", month_text, moy[mm]);
        for dd in 0..dpw {
            let mut abbrd = String::from(dow[dd]);
            abbrd.truncate(2);
            month_text = format!("{}{} ", month_text, abbrd);
        }

        let dd = if mm == month { day } else { 1000 };
        let days = format_days(moff, dpm, dpw, dd);
        month_text = format!("{}{}", month_text, days.0);
        moff = days.1;
        if mm == 2 {
            month_text = format!("{}--  Peer  Day  --", month_text);
        }
        else if mm == 6 {
            month_text = format!("{}--Torrent Feast--", month_text);
        }
        else if is_leap_year(year) && mm == 10 {
            month_text = format!("{}--Immersion Day--", month_text);
        }

        month_text = format!("{}\n", month_text);
        print!("{}", month_text);
    }

    let mut datestr = format!("{}, {:02} of {}", dow[wd], day, moy[month]);
    let mut daystr = format!("{:02}.{:02}", month + 1, day);
    if day == 34 {
        if month == 2 {
            datestr = format!("Peer Day");
            daystr = format!("P");
        }
        else if month == 6 {
            datestr = format!("Torrent Feast");
            daystr = format!("T");
        }
        else if month == 10 {
            datestr = format!("Immersion Feast");
            daystr = format!("T");
        }
    }
    let hol = format!("{}", what_holiday(month, day));
    println!("Today is {} {}{}.  [{}.{}]", datestr, year, hol, year, daystr);
}

fn format_days(moff: usize, dpm: usize, dpw: usize, highlight: usize) -> (String, usize) {
    let mut month = format!("");

    if moff != 0 {
        month = format!("{}\n", month);
    }

    for _ in 0 .. moff {
        month = format!("{}   ", month);
    }

    for day in 1 .. dpm + 1 {
        if (day + moff) % dpw == 1 {
            month = format!("{}\n", month);
        }

        let ch = if day == highlight { ">" } else if day + 1 == highlight { "<" } else { " " };
        month = format!("{}{:2}{}", month, day, ch);
    }

    month = format!("{}\n", month);
    return (month, if moff == 0 { 3 } else { 0 });
}

fn current_day(current_time: time::Tm) -> (usize, usize, usize, usize) {
    let epoch_days_offset = 1483889;
    let seconds_per_day = 86400;
    let now = current_time.to_timespec();
    let days_since_epoch = (epoch_days_offset + now.sec / seconds_per_day) as i32;
    let days_per_year = 365;
    let leap_year = 4;
    let not_leap_year = 33;
    let days_per_month = 33;

    let mut count = 1;
    let mut days_remaining = days_since_epoch;
    let mut year = 0;
    while days_remaining > days_per_year + 1 {
        days_remaining -= days_per_year;
        year += 1;
        if count > not_leap_year {
            count = 1;
        }
        if count % leap_year == 0 {
            days_remaining -= 1;
        }
        count += 1;
    }
    if days_remaining == days_per_year + 1 && count % leap_year == 0 {
        days_remaining = 0;
        year += 1;
    }

    let mut month = 0;
    while days_remaining > days_per_month {
        month += 1;
        days_remaining -= days_per_month;
        if month == 3 || month == 7 || month == 11 {
            days_remaining -= 1;
        }
    }

    let day = days_remaining;
    return (year, month as usize, day as usize + 1, (year % 2 * 3) as usize);
}

fn is_leap_year(year: usize) -> bool {
    let rem = year % 33;
    return rem != 0 && rem % 4 == 0;
}

fn what_holiday(month: usize, day: usize) -> String {
    let mut result = format!("");
    if month == 0 && day == 1 {
        result = format!(", New Year's Day");
    }
    else if month == 2 && day == 12 {
        result = format!(", Purification Day");
    }
    else if month == 2 && day == 24 {
        result = format!(", Document Freedom Day");
    }
    else if month == 3 && day == 33 {
        result = format!(", Hardware Freedom Day");
    }
    else if month == 5 && day == 4 {
        result = format!(", Harvest Gift");
    }
    else if month == 5 && day == 9 {
        result = format!(", Culture Freedom Day");
    }
    else if month == 7 && day == 29 {
        result = format!(", Familytide");
    }
    else if month == 8 && day == 33 {
        result = format!(", Software Freedom Day");
    }
    else if month == 9 && day == 30 {
        result = format!(", Open Access Day");
    }
    else if month == 10 && day == 21 {
        result = format!(", Freedom Day");
    }
    else if month == 0 && day == 27 {
        result = format!(", International Workers' Day");
    }
    return result;
}

<|endoftext|>


<commit_before>//! # Entry point to the application.
//!
//! Gather and parse command line arguments and configuration files, pass
//! them into the application and do some post-exit clean up.
//!
//! ## Example Fastup Expressions
//!
//! ```fastup
//! hello <time> {world}
//! <gradient|50|ff0000|00ffff>
//! <cpu|00ff88|ff3322>
//! ```

#[macro_use]
extern crate clap;
#[macro_use]
extern crate fancy_status as fast;

mod app;

use std::process::exit;
use app::{Configuration, run};

fn main() {
    let config = parse_args();
    if let Err(err) = run(&config) {
        errln!("{}", err);
        exit(1);
    }
}

fn parse_args() -> Configuration {
    let args = clap_app! {
        @app (app_from_crate!())
        (@arg EXPRESSION: default_value("<time>") "Evaluates Fastup EXPRESSION if no <FILE> is given")
        (@arg FILE: -f --file +takes_value "Reads Fastup EXPRESSION from FILE and evaluates it")
        (@arg format: -o --output +takes_value default_value("ansi") possible_values(&["ansi", "tmux", "html", "dump"]) "Sets output format")
    }.get_matches();

    Configuration {
        expression: args.value_of("EXPRESSION").unwrap().into(), // TODO: read file
        renderer_name: args.value_of("format").unwrap().into(),
    }
}

<commit_msg>main: argument parser now reads file<commit_after>//! # Entry point to the application.
//!
//! Gather and parse command line arguments and configuration files, pass
//! them into the application and do some post-exit clean up.
//!
//! ## Example Fastup Expressions
//!
//! ```fastup
//! hello <time> {world}
//! <gradient|50|ff0000|00ffff>
//! <cpu|00ff88|ff3322>
//! ```

#[macro_use]
extern crate clap;
#[macro_use]
extern crate fancy_status as fast;

mod app;

use std::process::exit;
use std::path::Path;
use std::fs::File;
use std::io::Read;
use app::{Configuration, run};

type Result<T> = ::std::result::Result<T, String>;

fn main() {
    if let Err(err) = try_main() {
        errln!("{}", err);
        exit(1);
    }
}

fn try_main() -> Result<()> {
    let config = parse_args()?;
    run(&config)
}

fn parse_args() -> Result<Configuration> {
    let args = clap_app! {
        @app (app_from_crate!())
        (@arg EXPRESSION: default_value("<time>") "Evaluates Fastup EXPRESSION if no <FILE> is given")
        (@arg FILE: -f --file +takes_value "Reads Fastup EXPRESSION from FILE and evaluates it")
        (@arg format: -o --output +takes_value default_value("ansi") possible_values(&["ansi", "tmux", "html", "dump"]) "Sets output format")
    }.get_matches();

    Ok(Configuration {
        expression: match args.value_of("FILE") {
            Some(path) => read_whole_file(path)?,
            None => args.value_of("EXPRESSION").unwrap().into(),    // `EXPRESSION` has default value, just unwrap
        },
        renderer_name: args.value_of("format").unwrap().into(),     // `format` has default value, just unwrap
    })
}

fn read_whole_file<T>(path: T) -> Result<String>
    where T: AsRef<Path>
{
    let mut content = String::new();
    {
        let mut file = File::open(path).map_err(|e| e.to_string())?;
        file.read_to_string(&mut content).map_err(|e| e.to_string())?;
    }
    Ok(content)
}

<|endoftext|>


<commit_before>extern crate semver;
extern crate git2;
extern crate regex;
extern crate getopts;

use std::process;
use semver::Version;
use git2::Repository;
use git2::SORT_TOPOLOGICAL;
use git2::SORT_TIME;
use git2::SORT_REVERSE;
use regex::Regex;
use std::io::{self, Write};
use std::env;
use getopts::Options;

/// Extracts build number metadata from a version if available and increments it
fn get_metadata(version: &Version) -> u64 {
    let mut build: u64;

    if version.build.len() == 1 {
        let build_id = &version.build[0];
        build = build_id.to_string().parse::<u64>().unwrap();
        build = build + 1;
    } else {
        build = 1;
    }

    return build;
}

#[derive(Debug)]
struct Bumps {
    objects: u64,
    major: bool,
    minor: bool,
    patch: bool
}

/// Walk the repository from the given version up to HEAD and find all commit messages with bumps
fn detect_bumps(repo: &Repository, start_version: &Version, major_pattern: &str, minor_pattern: &str, patch_pattern: &str) -> Bumps {
    // Extract the version's object
    let object = repo.revparse_single(&start_version.to_string()).unwrap();
    let sha = object.id();

    // Set up the revwalker
    let mut revwalker = repo.revwalk().unwrap();
    // Sort chronically backwards
    revwalker.set_sorting(SORT_TOPOLOGICAL | SORT_TIME | SORT_REVERSE);
    // Start at HEAD
    revwalker.push_head().unwrap();

    // Hide the version's object and everything older
    revwalker.hide(sha).unwrap();

    // Walk through all of the objects and detect the bumps
    let mut bumps = Bumps {
        objects: 0,
        major: false,
        minor: false,
        patch: false
    };

    // Set up reg-expes
    let major_re = Regex::new(major_pattern).unwrap();
    let minor_re = Regex::new(minor_pattern).unwrap();
    let patch_re = Regex::new(patch_pattern).unwrap();

    for sha in revwalker {
        // Count objects
        bumps.objects += 1;
        // Find object
        let sha_str = sha.to_string();
        let object = repo.revparse_single(&sha_str).unwrap();
        // Read as commit if possible
        match object.as_commit() {
            Some(commit) => {
                // Read commit message
                let message = commit.message().unwrap();

                // Match against our regular expressions
                if major_re.is_match(message) {
                    bumps.major = true;
                } else if minor_re.is_match(message) {
                    bumps.minor = true;
                } else if patch_re.is_match(message) {
                    bumps.patch = true;
                }
            }
            None => {}
        }
    }

    return bumps;
}

/// Get the highest version from the repo tags
fn get_highest_version(repo: &Repository) -> Option<Version> {
    // Get the tags
    let tags = repo.tag_names(None).unwrap();
    // Prepare a versions vector
    let mut versions = Vec::new();

    // Iterate through the tags
    for tag in tags.iter() {
        let name = tag.unwrap();
        // If a version tag is detected, push it to the versions vector
        match Version::parse(name) {
            Ok(parsed) => versions.push(parsed),
            _ => {}
        }
    }

    // Were there any versions at all?
    if versions.len() > 0 {
        // Yep, sort them
        versions.sort_by(|a, b| a.cmp(b));
        // Get the highest
        let highest_version = versions.last().unwrap();
        // Return a clone of it
        Some(highest_version.clone())
    } else {
        // Nope
        None
    }
}

/// Help printout using getopts
fn print_usage(executable_path: &str, opts: Options) {
    let brief = format!("Usage: {} [REPOSITORY] [options]", executable_path);
    print!("{}", opts.usage(&brief));
}

fn main() {
    // Handle arguments
    let args: Vec<String> = env::args().collect();
    // Extract the executable's path
    let executable_path = args[0].clone();

    // Set up the options
    let mut opts = Options::new();
    opts.optopt("m", "major-regexp", "set major regexp pattern", "PATTERN");
    opts.optopt("f", "minor-regexp", "set minor regexp pattern", "PATTERN");
    opts.optopt("p", "patch-regexp", "set patch regexp pattern", "PATTERN");
    // Help flag
    opts.optflag("h", "help", "print this help menu");

    // Parse the arguments, crash on illegal arguments
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };

    // Print help
    if matches.opt_present("h") {
        print_usage(&executable_path, opts);
        return;
    }

    // Major regular expressions
    let major_regexp = match matches.opt_str("m") {
        Some(r) => r,
        None => r":major:".to_string()
    };
    // Minor regular expressions
    let minor_regexp = match matches.opt_str("m") {
        Some(r) => r,
        None => r":minor:".to_string()
    };
    // Patch regular expressions
    let patch_regexp = match matches.opt_str("m") {
        Some(r) => r,
        None => r":patch:".to_string()
    };

    // Repository directory
    let repo_dir = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        ".".to_string()
    };

    // Open the repo
    let repo = Repository::open(repo_dir).unwrap();
    // Retrieve the highest version
    let highest_version = match get_highest_version(&repo) {
        // Version found
        Some(version) => version,
        // Fallback to 0.0.1
        None => Version {
            major: 0,
            minor: 0,
            patch: 1,
            pre: vec!(),
            build: vec!()
        }
    };

    // Detect commit message bumps
    let bumps = detect_bumps(&repo, &highest_version, &major_regexp, &minor_regexp, &patch_regexp);
    // If no objects were walked, we don't want to tag the release at all
    if bumps.objects == 0 {
        process::exit(1);
    }

    // Clone to a new version
    let mut new_version = highest_version.clone();

    // Perform bumps
    if bumps.patch {
        new_version.increment_patch();
    }
    if bumps.minor {
        new_version.increment_minor();
    }
    if bumps.major {
        new_version.increment_major();
    }

    // Handle build metadata
    let build;
    if !bumps.patch && !bumps.minor && !bumps.major {
        build = get_metadata(&new_version);
    } else {
        build = 0;
    }

    // Construct a version name
    let output_version;
    if build == 0 {
        // If build metadata was 0, do not include it in the version name
        output_version = format!("{}.{}.{}", new_version.major, new_version.minor, new_version.patch);
    } else {
        output_version = format!("{}.{}.{}+{}", new_version.major, new_version.minor, new_version.patch, build);
    }

    // Send to stdout
    io::stdout().write(output_version.as_bytes()).unwrap();
}
<commit_msg>Fixed some regexp option typos and added support for force-bump flags<commit_after>extern crate semver;
extern crate git2;
extern crate regex;
extern crate getopts;

use std::process;
use semver::Version;
use git2::Repository;
use git2::SORT_TOPOLOGICAL;
use git2::SORT_TIME;
use git2::SORT_REVERSE;
use regex::Regex;
use std::io::{self, Write};
use std::env;
use getopts::Options;

/// Extracts build number metadata from a version if available and increments it
fn get_metadata(version: &Version) -> u64 {
    let mut build: u64;

    if version.build.len() == 1 {
        let build_id = &version.build[0];
        build = build_id.to_string().parse::<u64>().unwrap();
        build = build + 1;
    } else {
        build = 1;
    }

    return build;
}

#[derive(Debug)]
struct Bumps {
    objects: u64,
    major: bool,
    minor: bool,
    patch: bool
}

/// Walk the repository from the given version up to HEAD and find all commit messages with bumps
fn detect_bumps(repo: &Repository, start_version: &Version, major_pattern: &str, minor_pattern: &str, patch_pattern: &str) -> Bumps {
    // Extract the version's object
    let object = repo.revparse_single(&start_version.to_string()).unwrap();
    let sha = object.id();

    // Set up the revwalker
    let mut revwalker = repo.revwalk().unwrap();
    // Sort chronically backwards
    revwalker.set_sorting(SORT_TOPOLOGICAL | SORT_TIME | SORT_REVERSE);
    // Start at HEAD
    revwalker.push_head().unwrap();

    // Hide the version's object and everything older
    revwalker.hide(sha).unwrap();

    // Walk through all of the objects and detect the bumps
    let mut bumps = Bumps {
        objects: 0,
        major: false,
        minor: false,
        patch: false
    };

    // Set up reg-expes
    let major_re = Regex::new(major_pattern).unwrap();
    let minor_re = Regex::new(minor_pattern).unwrap();
    let patch_re = Regex::new(patch_pattern).unwrap();

    for sha in revwalker {
        // Count objects
        bumps.objects += 1;
        // Find object
        let sha_str = sha.to_string();
        let object = repo.revparse_single(&sha_str).unwrap();
        // Read as commit if possible
        match object.as_commit() {
            Some(commit) => {
                // Read commit message
                let message = commit.message().unwrap();

                // Match against our regular expressions
                if major_re.is_match(message) {
                    bumps.major = true;
                } else if minor_re.is_match(message) {
                    bumps.minor = true;
                } else if patch_re.is_match(message) {
                    bumps.patch = true;
                }
            }
            None => {}
        }
    }

    return bumps;
}

/// Get the highest version from the repo tags
fn get_highest_version(repo: &Repository) -> Option<Version> {
    // Get the tags
    let tags = repo.tag_names(None).unwrap();
    // Prepare a versions vector
    let mut versions = Vec::new();

    // Iterate through the tags
    for tag in tags.iter() {
        let name = tag.unwrap();
        // If a version tag is detected, push it to the versions vector
        match Version::parse(name) {
            Ok(parsed) => versions.push(parsed),
            _ => {}
        }
    }

    // Were there any versions at all?
    if versions.len() > 0 {
        // Yep, sort them
        versions.sort_by(|a, b| a.cmp(b));
        // Get the highest
        let highest_version = versions.last().unwrap();
        // Return a clone of it
        Some(highest_version.clone())
    } else {
        // Nope
        None
    }
}

/// Help printout using getopts
fn print_usage(executable_path: &str, opts: Options) {
    let brief = format!("Usage: {} [REPOSITORY] [options]", executable_path);
    print!("{}", opts.usage(&brief));
}

fn main() {
    // Handle arguments
    let args: Vec<String> = env::args().collect();
    // Extract the executable's path
    let executable_path = args[0].clone();

    // Set up the options
    let mut opts = Options::new();
    opts.optopt("m", "major-regexp", "set major regexp pattern", "PATTERN");
    opts.optopt("f", "minor-regexp", "set minor regexp pattern", "PATTERN");
    opts.optopt("p", "patch-regexp", "set patch regexp pattern", "PATTERN");
    // Help flag
    opts.optflag("h", "help", "print this help menu");
    // Force-bump options
    opts.optflag("M", "bump-major", "force bump major");
    opts.optflag("F", "bump-minor", "force bump minor");
    opts.optflag("P", "bump-patch", "force bump patch");

    // Parse the arguments, crash on illegal arguments
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };

    // Print help
    if matches.opt_present("h") {
        print_usage(&executable_path, opts);
        return;
    }

    // Major regular expressions
    let major_regexp = match matches.opt_str("m") {
        Some(r) => r,
        None => r":major:".to_string()
    };
    // Minor regular expressions
    let minor_regexp = match matches.opt_str("f") {
        Some(r) => r,
        None => r":minor:".to_string()
    };
    // Patch regular expressions
    let patch_regexp = match matches.opt_str("p") {
        Some(r) => r,
        None => r":patch:".to_string()
    };

    // Force-bumps
    let force_major = matches.opt_present("M");
    let force_minor = matches.opt_present("F");
    let force_patch = matches.opt_present("P");

    // Repository directory
    let repo_dir = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        ".".to_string()
    };

    // Open the repo
    let repo = Repository::open(repo_dir).unwrap();
    // Retrieve the highest version
    let highest_version = match get_highest_version(&repo) {
        // Version found
        Some(version) => version,
        // Fallback to 0.0.1
        None => Version {
            major: 0,
            minor: 0,
            patch: 1,
            pre: vec!(),
            build: vec!()
        }
    };

    // Detect commit message bumps
    let bumps = detect_bumps(&repo, &highest_version, &major_regexp, &minor_regexp, &patch_regexp);
    // If no objects were walked, we don't want to tag the release at all
    if bumps.objects == 0 {
        process::exit(1);
    }

    // Clone to a new version
    let mut new_version = highest_version.clone();

    // Perform bumps
    if bumps.patch || force_patch {
        new_version.increment_patch();
    }
    if bumps.minor || force_minor {
        new_version.increment_minor();
    }
    if bumps.major || force_major {
        new_version.increment_major();
    }

    // Handle build metadata
    let build;
    if !bumps.patch && !bumps.minor && !bumps.major {
        build = get_metadata(&new_version);
    } else {
        build = 0;
    }

    // Construct a version name
    let output_version;
    if build == 0 {
        // If build metadata was 0, do not include it in the version name
        output_version = format!("{}.{}.{}", new_version.major, new_version.minor, new_version.patch);
    } else {
        output_version = format!("{}.{}.{}+{}", new_version.major, new_version.minor, new_version.patch, build);
    }

    // Send to stdout
    io::stdout().write(output_version.as_bytes()).unwrap();
}
<|endoftext|>