<commit_before>extern crate collections;
extern crate png;

use collections::Deque;
use collections::RingBuf;

use image::{Image, Color, Point};

mod image;

struct Cell {
    color: Color
}

fn main() {
    // Load example PNG image.
    let file = "examples/hex_square_tri.png";
    println!("Loading '{}'.", file);
    let image = Image::load_png(&Path::new(file));

    // Draw discovered cell boundaries into a new image.
    let mut cell_boundaries = Image::white(image.width, image.height);

    // Explore image breadth-first to break it
    // into cells of the same color.
    println!("Finding cells in image...");
    let pixels = (image.width * image.height) as uint;
    // TODO: reinstate separate `visited` map, too, for faster checking?
    let mut cell_map: Vec<Option<uint>> = Vec::from_elem(pixels, None);
    let mut cells: Vec<Cell> = Vec::with_capacity(100);
    let mut point_queue: RingBuf<Point> = RingBuf::with_capacity(pixels);
    // Create per-cell scratch space here so we don't need to allocate
    // again for every cell we visit.
    let mut cell_point_queue: RingBuf<Point> = RingBuf::with_capacity(pixels);
    point_queue.push(Point{ x: 0, y: 0 });
    while !point_queue.is_empty() {
        let point = match point_queue.pop_front() {
            None => fail!(),
            Some(p) => p,
        };
        // It might have already been consumed by another cell.
        if cell_map[image.linear_index(point)] == None {
            cells.push(Cell{ color: image.color_at(point) });
            let cell_index = cells.len() - 1;
            *cell_map.get_mut(image.linear_index(point)) = Some(cell_index);
            let cell = cells.get_mut(cell_index);

            // Need to explore a single cell exhaustively before moving on;
            // otherwise we might interpret a strangely shaped cell as two
            // different cells and then have to join them up somehow later.
            flood_cell(
                &mut cell_map,
                &mut point_queue,
                &mut cell_point_queue,
                point,
                cell,
                cell_index,
                &image,
                &mut cell_boundaries,
            );
        }
    }
    println!("Found {} cells.", cells.len());

    // Write out discovered cell boundaries.
    cell_boundaries.save_png(&Path::new("image_out/cell_boundaries.png"));
}

fn flood_cell(
    cell_map: &mut Vec<Option<uint>>,
    point_queue: &mut RingBuf<Point>,
    cell_point_queue: &mut RingBuf<Point>,
    starting_point: Point,
    cell: &mut Cell,
    cell_index: uint,
    image: &Image,
    cell_boundaries: &mut Image,
) {
    cell_point_queue.clear();
    cell_point_queue.push(starting_point);
    while !cell_point_queue.is_empty() {
        let point = match cell_point_queue.pop_front() {
            None => fail!(),
            Some(p) => p,
        };
        let neighbors = point_neighbors(point);
        for neighbor in neighbors.iter() {
            // Ignore if outside the image bounds.
            // TODO: support wrapping
            let oob =
                neighbor.x < 0 ||
                neighbor.x >= image.width as i32 ||
                neighbor.y < 0 ||
                neighbor.y >= image.height as i32;
            if oob {
                // Mark the current pixel (not the neighbor) as the edge of a cell.
                mark_cell_border(point, cell_boundaries);
                continue;
            }

            let neighbor_cell = cell_map.get_mut(image.linear_index(*neighbor));
            match *neighbor_cell {
                None => {
                    if image.color_at(*neighbor) == cell.color {
                        // Same color as this cell; add it to the cell and queue it
                        // up as a starting point for further explanation.
                        *neighbor_cell = Some(cell_index);
                        cell_point_queue.push(*neighbor);
                    } else {
                        // Doesn't belong to this cell; queue it up to maybe
                        // be the start of another cell.
                        point_queue.push(*neighbor);

                        // Neighbor is another color, so will eventually be part of another cell.
                        // Mark the current pixel (not the neighbor) as the edge of a cell.
                        mark_cell_border(point, cell_boundaries);
                    }
                },
                Some(int) => {
                    if image.color_at(*neighbor) != cell.color {
                        // Neighbor is already part of another cell.
                        // Mark the current pixel (not the neighbor) as the edge of a cell.
                        mark_cell_border(point, cell_boundaries);
                    }
                }
            }
        }
    }
}

fn mark_cell_border(point: Point, cell_boundaries: &mut Image) {
    cell_boundaries.set_color_at(point, Color{red: 127, green: 127, blue: 127});
}

fn point_neighbors(point: Point) -> [Point, ..8] {
    [
        Point{ x: point.x - 1, y: point.y - 1 },
        Point{ x: point.x,     y: point.y - 1 },
        Point{ x: point.x + 1, y: point.y - 1 },
        Point{ x: point.x - 1, y: point.y     },
        Point{ x: point.x + 1, y: point.y     },
        Point{ x: point.x - 1, y: point.y + 1 },
        Point{ x: point.x,     y: point.y + 1 },
        Point{ x: point.x + 1, y: point.y + 1 },
    ]
}<commit_msg>Support wrapping the image coordinate space.<commit_after>extern crate collections;
extern crate png;
extern crate getopts;

use std::os;
use getopts::{optopt, optflag, getopts, OptGroup};

use collections::Deque;
use collections::RingBuf;

use image::{Image, Color, Point};

mod image;

struct Cell {
    color: Color
}

fn main() {
    // Parse program arguments.
    let args: Vec<String> = os::args();
    let opts = [
        optflag("w", "wrap", "treat image space as toroidal")
    ];
    let matches = match getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_string()) }
    };
    let wrap = matches.opt_present("w");

    // Load example PNG image.
    let file = "examples/hex_square_tri.png";
    println!("Loading '{}'.", file);
    let image = Image::load_png(&Path::new(file));

    // Draw discovered cell boundaries into a new image.
    let mut cell_boundaries = Image::white(image.width, image.height);

    // Explore image breadth-first to break it
    // into cells of the same color.
    println!("Finding cells in image...");
    let pixels = (image.width * image.height) as uint;
    // TODO: reinstate separate `visited` map, too, for faster checking?
    let mut cell_map: Vec<Option<uint>> = Vec::from_elem(pixels, None);
    let mut cells: Vec<Cell> = Vec::with_capacity(100);
    let mut point_queue: RingBuf<Point> = RingBuf::with_capacity(pixels);
    // Create per-cell scratch space here so we don't need to allocate
    // again for every cell we visit.
    let mut cell_point_queue: RingBuf<Point> = RingBuf::with_capacity(pixels);
    point_queue.push(Point{ x: 0, y: 0 });
    while !point_queue.is_empty() {
        let point = match point_queue.pop_front() {
            None => fail!(),
            Some(p) => p,
        };
        // It might have already been consumed by another cell.
        if cell_map[image.linear_index(point)] == None {
            cells.push(Cell{ color: image.color_at(point) });
            let cell_index = cells.len() - 1;
            *cell_map.get_mut(image.linear_index(point)) = Some(cell_index);
            let cell = cells.get_mut(cell_index);

            // Need to explore a single cell exhaustively before moving on;
            // otherwise we might interpret a strangely shaped cell as two
            // different cells and then have to join them up somehow later.
            flood_cell(
                &mut cell_map,
                &mut point_queue,
                &mut cell_point_queue,
                point,
                cell,
                cell_index,
                &image,
                &mut cell_boundaries,
                wrap,
            );
        }
    }
    println!("Found {} cells.", cells.len());

    // Write out discovered cell boundaries.
    cell_boundaries.save_png(&Path::new("image_out/cell_boundaries.png"));
}

fn flood_cell(
    cell_map: &mut Vec<Option<uint>>,
    point_queue: &mut RingBuf<Point>,
    cell_point_queue: &mut RingBuf<Point>,
    starting_point: Point,
    cell: &mut Cell,
    cell_index: uint,
    image: &Image,
    cell_boundaries: &mut Image,
    wrap: bool,
) {
    cell_point_queue.clear();
    cell_point_queue.push(starting_point);
    while !cell_point_queue.is_empty() {
        let point = match cell_point_queue.pop_front() {
            None => fail!(),
            Some(p) => p,
        };
        let neighbors = point_neighbors(point);
        for neighbor in neighbors.iter() {
            let mut neighbor = *neighbor;

            // Wrap coordinates if requested.
            // Note that the result of % depends on the sign of the divisor,
            // so transpose everything north to avoid negative numbers entirely.
            if wrap {
                neighbor = Point{
                    x: (neighbor.x + image.width as i32) % image.width as i32,
                    y: (neighbor.y + image.height as i32) % image.height as i32,
                };
            }

            // Ignore if outside the image bounds.
            let oob = !wrap && (
                neighbor.x < 0 ||
                neighbor.x >= image.width as i32 ||
                neighbor.y < 0 ||
                neighbor.y >= image.height as i32
            );
            if oob {
                // Mark the current pixel (not the neighbor) as the edge of a cell.
                mark_cell_border(point, cell_boundaries);
                continue;
            }

            let neighbor_cell = cell_map.get_mut(image.linear_index(neighbor));
            match *neighbor_cell {
                None => {
                    if image.color_at(neighbor) == cell.color {
                        // Same color as this cell; add it to the cell and queue it
                        // up as a starting point for further explanation.
                        *neighbor_cell = Some(cell_index);
                        cell_point_queue.push(neighbor);
                    } else {
                        // Doesn't belong to this cell; queue it up to maybe
                        // be the start of another cell.
                        point_queue.push(neighbor);

                        // Neighbor is another color, so will eventually be part of another cell.
                        // Mark the current pixel (not the neighbor) as the edge of a cell.
                        mark_cell_border(point, cell_boundaries);
                    }
                },
                Some(int) => {
                    if image.color_at(neighbor) != cell.color {
                        // Neighbor is already part of another cell.
                        // Mark the current pixel (not the neighbor) as the edge of a cell.
                        mark_cell_border(point, cell_boundaries);
                    }
                }
            }
        }
    }
}

fn mark_cell_border(point: Point, cell_boundaries: &mut Image) {
    cell_boundaries.set_color_at(point, Color{red: 127, green: 127, blue: 127});
}

fn point_neighbors(point: Point) -> [Point, ..8] {
    [
        Point{ x: point.x - 1, y: point.y - 1 },
        Point{ x: point.x,     y: point.y - 1 },
        Point{ x: point.x + 1, y: point.y - 1 },
        Point{ x: point.x - 1, y: point.y     },
        Point{ x: point.x + 1, y: point.y     },
        Point{ x: point.x - 1, y: point.y + 1 },
        Point{ x: point.x,     y: point.y + 1 },
        Point{ x: point.x + 1, y: point.y + 1 },
    ]
}<|endoftext|>


<commit_before>extern crate ansi_term;

use ansi_term::Colour as Color;  // this is America

#[derive(Eq,PartialEq,Debug,Copy,Clone)]
struct Locale {
    rank: u8,
    file: u8
}

impl Locale {
    pub fn bit_index(&self) -> u32 {
        (8u32 * self.rank as u32) + self.file as u32
    }

    pub fn pinpoint(&self) -> Bitboard {
        Bitboard(2u64.pow(self.bit_index()))
    }
}

#[derive(Eq,PartialEq,Debug,Copy,Clone)]
struct Bitboard(u64);

impl Bitboard {
    pub fn new() -> Bitboard {
        Bitboard(0)
    }

    pub fn init(starters: &Vec<Locale>) -> Bitboard {
        let mut board = Bitboard::new();
        for &starter in starters.iter() {
            board = board.alight(starter);
        }
        board
    }

    pub fn union(&self, other: Bitboard) -> Bitboard {
        let Bitboard(our_bits) = *self;
        let Bitboard(their_bits) = other;
        Bitboard(our_bits | their_bits)
    }

    pub fn intersection(&self, other: Bitboard) -> Bitboard {
        let Bitboard(our_bits) = *self;
        let Bitboard(their_bits) = other;
        Bitboard(our_bits & their_bits)
    }

    pub fn invert(&self) -> Bitboard {
        let Bitboard(our_bits) = *self;
        Bitboard(!our_bits)
    }

    pub fn alight(&self, station: Locale) -> Bitboard {
        // classic `|= (1 << n)` would probably be more efficient, huh &c.
        self.union(station.pinpoint())
    }

    pub fn quench(&self, station: Locale) -> Bitboard {
        self.intersection(station.pinpoint().invert())
    }

    pub fn query(&self, station: Locale) -> bool {
        let Bitboard(our_bits) = *self;
        let Bitboard(beacon_bits) = station.pinpoint();
        (our_bits & beacon_bits) != 0
    }

    pub fn display(&self) {
        let Bitboard(debug) = *self;
        println!("{}", debug);
        for rank in 0..8 {
            for file in 0..8 {
                if self.query(Locale { rank: rank, file: file }) {
                    print!("• ");
                } else {
                    print!("_ ");
                }
            }
            println!("");
        }
    }
}

#[derive(Eq,PartialEq,Debug,Copy,Clone)]
enum Team { Orange, Blue }

impl Team {
    fn opponent(&self) -> Self {
        match self {
            &Team::Orange => Team::Blue,
            &Team::Blue => Team::Orange
        }
    }
}

#[derive(Eq,PartialEq,Debug,Copy,Clone)]
enum JobDescription {
    Servant,  // ♂
    Pony,  // ♀
    Scholar,  // ♀
    Cop,  // ♂
    Princess,  // ♀
    Figurehead  // ♂
}

#[derive(Eq,PartialEq,Debug,Copy,Clone)]
struct Agent {
    team: Team,
    job_description: JobDescription
}

impl Agent {
    // I wanted to call it `dramatis_personæ`, but "non-ascii idents
    // are not fully supported" 🙀
    pub fn dramatis_personae(team: Team) -> Vec<Agent> {
        vec![Agent{ team: team,
                    job_description: JobDescription::Servant },
             Agent{ team: team,
                    job_description: JobDescription::Pony },
             Agent{ team: team,
                    job_description: JobDescription::Scholar },
             Agent{ team: team,
                    job_description: JobDescription::Cop },
             Agent{ team: team,
                    job_description: JobDescription::Princess },
             Agent{ team: team,
                    job_description: JobDescription::Figurehead }]
    }

    pub fn render_caricature(&self) {
        let caricature = match self {
            &Agent { team: Team::Orange, .. } => {
                match self.job_description {
                    JobDescription::Servant => Color::Yellow.paint("♙"),
                    JobDescription::Pony => Color::Yellow.paint("♘"),
                    JobDescription::Scholar => Color::Yellow.paint("♗"),
                    JobDescription::Cop => Color::Yellow.paint("♖"),
                    JobDescription::Princess => Color::Yellow.paint("♕"),
                    JobDescription::Figurehead => Color::Yellow.paint("♔"),
                }
            },
            &Agent { team: Team::Blue, .. } => {
                match self.job_description {
                    JobDescription::Servant => Color::Cyan.paint("♟"),
                    JobDescription::Pony => Color::Cyan.paint("♞"),
                    JobDescription::Scholar => Color::Cyan.paint("♝"),
                    JobDescription::Cop => Color::Cyan.paint("♜"),
                    JobDescription::Princess => Color::Cyan.paint("♛"),
                    JobDescription::Figurehead => Color::Cyan.paint("♚"),
                }
            }
        };
        print!("{}", caricature);
    }

}

#[derive(Eq,PartialEq,Debug,Copy,Clone)]
struct GameState {
    to_move: Team,

    orange_servants: Bitboard,
    orange_ponies: Bitboard,
    orange_scholars: Bitboard,
    orange_cops: Bitboard,
    orange_princesses: Bitboard,
    orange_figurehead: Bitboard,

    blue_servants: Bitboard,
    blue_ponies: Bitboard,
    blue_scholars: Bitboard,
    blue_cops: Bitboard,
    blue_princesses: Bitboard,
    blue_figurehead: Bitboard,
}

impl GameState {
    pub fn new() -> GameState {
        let mut orange_servant_locales = Vec::new();
        let mut blue_servant_locales = Vec::new();
        for f in 0..8 {
            orange_servant_locales.push(Locale { rank: 1, file: f });
            blue_servant_locales.push(Locale { rank: 6, file: f });
        }
        GameState {
            to_move: Team::Orange,

            orange_servants: Bitboard::init(&orange_servant_locales),
            orange_ponies: Bitboard::init(
                &vec![Locale { rank: 0, file: 1 },
                      Locale { rank: 0, file: 6 }]
            ),
            orange_scholars: Bitboard::init(
                &vec![Locale { rank: 0, file: 2 },
                      Locale { rank: 0, file: 5 }]
            ),
            orange_cops: Bitboard::init(
                &vec![Locale { rank: 0, file: 0 },
                      Locale { rank: 0, file: 7 }]
            ),
            orange_princesses: Bitboard::init(
                &vec![Locale { rank: 0, file: 3 }]),
            orange_figurehead: Bitboard::init(
                &vec![Locale { rank: 0, file: 4 }]),
            blue_servants: Bitboard::init(&blue_servant_locales),
            blue_ponies: Bitboard::init(
                &vec![Locale { rank: 7, file: 1 },
                      Locale { rank: 7, file: 6 }]
            ),
            blue_scholars: Bitboard::init(
                &vec![Locale { rank: 7, file: 2 },
                      Locale { rank: 7, file: 5 }]
            ),
            blue_cops: Bitboard::init(
                &vec![Locale { rank: 7, file: 0 },
                      Locale { rank: 7, file: 7 }]
            ),
            blue_princesses: Bitboard::init(
                &vec![Locale { rank: 7, file: 3 }]),
            blue_figurehead: Bitboard::init(
                &vec![Locale { rank: 7, file: 4 }]),
        }
    }

    pub fn agent_to_bitboard_ref(&self, agent: Agent) -> &Bitboard {
        match agent {
            Agent{ team: Team::Orange,
                   job_description: JobDescription::Servant } =>
                &self.orange_servants,
            Agent{ team: Team::Orange,
                   job_description: JobDescription::Pony } =>
                &self.orange_ponies,
            Agent{ team: Team::Orange,
                   job_description: JobDescription::Scholar } =>
                &self.orange_scholars,
            Agent{ team: Team::Orange,
                   job_description: JobDescription::Cop } =>
                &self.orange_cops,
            Agent{ team: Team::Orange,
                   job_description: JobDescription::Princess } =>
                &self.orange_princesses,
            Agent{ team: Team::Orange,
                   job_description: JobDescription::Figurehead } =>
                &self.orange_figurehead,
            Agent{ team: Team::Blue,
                   job_description: JobDescription::Servant } =>
                &self.blue_servants,
            Agent{ team: Team::Blue,
                   job_description: JobDescription::Pony } =>
                &self.blue_ponies,
            Agent{ team: Team::Blue,
                   job_description: JobDescription::Scholar } =>
                &self.blue_scholars,
            Agent{ team: Team::Blue,
                   job_description: JobDescription::Cop } =>
                &self.blue_cops,
            Agent{ team: Team::Blue,
                   job_description: JobDescription::Princess } =>
                &self.blue_princesses,
            Agent{ team: Team::Blue,
                   job_description: JobDescription::Figurehead } =>
                &self.blue_figurehead,
        }
    }

    pub fn occupied(&self) -> Bitboard {
        // Sooo ... maybe this should be a loop
        // although this does have a certain visual appeal
        self.orange_servants.union(self.orange_ponies).union(
            self.orange_scholars).union(self.orange_cops).union(
                self.orange_princesses).union(self.orange_figurehead).union(
                    self.blue_servants).union(self.blue_ponies).union(
                        self.blue_scholars).union(self.blue_cops).union(
                            self.blue_princesses).union(
                                self.blue_figurehead)
    }

    pub fn unoccupied(&self) -> Bitboard {
        self.occupied().invert()
    }

    pub fn display(&self) {
        println!("  a b c d e f g h");
        for rank in 0..8 {
            print!("{} ", rank+1);
            for file in 0..8 {
                let locale = Locale { rank: rank, file: file };
                if self.occupied().invert().query(locale) {
                    print!("_ ");
                } else {
                    for &team in [Team::Orange, Team::Blue].iter() {
                        for &figurine_class in
                            Agent::dramatis_personae(team).iter() {
                                if self.agent_to_bitboard_ref(
                                    figurine_class).query(locale) {
                                        figurine_class.render_caricature();
                                        print!(" ");
                                }
                        }
                    }
                }
            }
            println!("");
        }
    }

    pub fn lookahead(&self) -> Vec<Self> {
        let possibilities = Vec::new();
        let moving_team = self.to_move;
        for agent_class in Agent::dramatis_personae(moving_team) {
            let positional_chart: &Bitboard = self.agent_to_bitboard_ref(
                agent_class);
            // for each agent class, compute possible moves ... somehow
        }
        // TODO work in progress
        possibilities
    }

}

fn main() {
    let arena = GameState::new();
    arena.display();
    println!("");
    arena.occupied().display();
    arena.occupied().invert().display();
}

#[cfg(test)]
mod test {
    use super::{Locale, Bitboard, Team, JobDescription, Agent, GameState};

    #[test]
    fn test_locale() {
        let a1 = Locale { rank: 0, file: 0 };
        assert_eq!(Bitboard(1u64), a1.pinpoint());

        let c4 = Locale { rank: 2, file: 3 };
        assert_eq!(Bitboard(524288u64), c4.pinpoint());
    }

    #[test]
    fn test_empty_board_is_empty() {
        let empty_board = Bitboard::new();
        for rank in 0..8 {
            for file in 0..8 {
                assert!(!empty_board.query(Locale { rank: rank, file: file }));
            }
        }
    }

    #[test]
    fn test_inverted_empty_board_is_full() {
        let full_board = Bitboard::new().invert();
        for rank in 0..8 {
            for file in 0..8 {
                assert!(full_board.query(Locale { rank: rank, file: file }));
            }
        }
    }

    #[test]
    fn test_alight_and_quench() {
        let mut stage = Bitboard(0);
        let b5 = Locale { rank: 1, file: 4 };
        stage = stage.alight(b5);
        assert!(stage.query(b5));
        stage = stage.quench(b5);
        assert!(!stage.query(b5));
    }

    #[test]
    fn test_init_bitboard() {
        let starters = vec![Locale{ rank: 1, file: 2 },
                            Locale{ rank: 3, file: 4 },
                            Locale{ rank: 5, file: 6 }];
        let stage = Bitboard::init(&starters);
        for &starter in starters.iter() {
            assert!(stage.query(starter));
        }
    }

    #[test]
    fn test_agent_to_bitboard_ref_on_new_gamestate() {
        let state = GameState::new();
        let agent = Agent { team: Team::Blue,
                            job_description: JobDescription::Princess };
        let blue_princess_realm = state.agent_to_bitboard_ref(agent);
        assert!(blue_princess_realm.query(Locale { rank: 7, file: 3 }));
    }

}
<commit_msg>considering displacement for the nth time in my life<commit_after>extern crate ansi_term;

use ansi_term::Colour as Color;  // this is America

#[derive(Eq,PartialEq,Debug,Copy,Clone)]
struct Locale {
    rank: u8,
    file: u8
}

impl Locale {
    pub fn bit_index(&self) -> u32 {
        (8u32 * self.rank as u32) + self.file as u32
    }

    pub fn pinpoint(&self) -> Bitboard {
        Bitboard(2u64.pow(self.bit_index()))
    }

    pub fn is_legal(&self) -> bool {
        self.rank >= 0 && self.rank < 8 && self.file >= 0 && self.file < 8
    }

    pub fn displace(&self, offset: (i8, i8)) -> Option<Self> {
        let (rank_offset, file_offset) = offset;
        let potential_locale = Locale {
            // why is this legal? What happens when you coerce a
            // negative number to unsigned?
            rank: (self.rank as i8 + rank_offset) as u8,
            file: (self.file as i8 + file_offset) as u8
        };
        if potential_locale.is_legal() {
            Some(potential_locale)
        } else {
            None
        }
    }
}

#[derive(Eq,PartialEq,Debug,Copy,Clone)]
struct Bitboard(u64);

impl Bitboard {
    pub fn new() -> Bitboard {
        Bitboard(0)
    }

    pub fn init(starters: &Vec<Locale>) -> Bitboard {
        let mut board = Bitboard::new();
        for &starter in starters.iter() {
            board = board.alight(starter);
        }
        board
    }

    pub fn union(&self, other: Bitboard) -> Bitboard {
        let Bitboard(our_bits) = *self;
        let Bitboard(their_bits) = other;
        Bitboard(our_bits | their_bits)
    }

    pub fn intersection(&self, other: Bitboard) -> Bitboard {
        let Bitboard(our_bits) = *self;
        let Bitboard(their_bits) = other;
        Bitboard(our_bits & their_bits)
    }

    pub fn invert(&self) -> Bitboard {
        let Bitboard(our_bits) = *self;
        Bitboard(!our_bits)
    }

    pub fn alight(&self, station: Locale) -> Bitboard {
        // classic `|= (1 << n)` would probably be more efficient, huh &c.
        self.union(station.pinpoint())
    }

    pub fn quench(&self, station: Locale) -> Bitboard {
        self.intersection(station.pinpoint().invert())
    }

    pub fn query(&self, station: Locale) -> bool {
        let Bitboard(our_bits) = *self;
        let Bitboard(beacon_bits) = station.pinpoint();
        (our_bits & beacon_bits) != 0
    }

    pub fn to_locales(&self) -> Vec<Locale> {
        let mut locales = Vec::new();
        for rank in 0..8 {
            for file in 0..8 {
                let potential_locale = Locale { rank: rank, file: file };
                if self.query(potential_locale) {
                    locales.push(potential_locale);
                }
            }
        }
        locales
    }

    pub fn display(&self) {
        let Bitboard(debug) = *self;
        println!("{}", debug);
        for rank in 0..8 {
            for file in 0..8 {
                if self.query(Locale { rank: rank, file: file }) {
                    print!("• ");
                } else {
                    print!("_ ");
                }
            }
            println!("");
        }
    }
}

#[derive(Eq,PartialEq,Debug,Copy,Clone)]
enum Team { Orange, Blue }

impl Team {
    fn opponent(&self) -> Self {
        match self {
            &Team::Orange => Team::Blue,
            &Team::Blue => Team::Orange
        }
    }
}

#[derive(Eq,PartialEq,Debug,Copy,Clone)]
enum JobDescription {
    Servant,  // ♂
    Pony,  // ♀
    Scholar,  // ♀
    Cop,  // ♂
    Princess,  // ♀
    Figurehead  // ♂
}

#[derive(Eq,PartialEq,Debug,Copy,Clone)]
struct Agent {
    team: Team,
    job_description: JobDescription
}

impl Agent {
    // I wanted to call it `dramatis_personæ`, but "non-ascii idents
    // are not fully supported" 🙀
    pub fn dramatis_personae(team: Team) -> Vec<Agent> {
        vec![Agent{ team: team,
                    job_description: JobDescription::Servant },
             Agent{ team: team,
                    job_description: JobDescription::Pony },
             Agent{ team: team,
                    job_description: JobDescription::Scholar },
             Agent{ team: team,
                    job_description: JobDescription::Cop },
             Agent{ team: team,
                    job_description: JobDescription::Princess },
             Agent{ team: team,
                    job_description: JobDescription::Figurehead }]
    }

    pub fn render_caricature(&self) {
        let caricature = match self {
            &Agent { team: Team::Orange, .. } => {
                match self.job_description {
                    JobDescription::Servant => Color::Yellow.paint("♙"),
                    JobDescription::Pony => Color::Yellow.paint("♘"),
                    JobDescription::Scholar => Color::Yellow.paint("♗"),
                    JobDescription::Cop => Color::Yellow.paint("♖"),
                    JobDescription::Princess => Color::Yellow.paint("♕"),
                    JobDescription::Figurehead => Color::Yellow.paint("♔"),
                }
            },
            &Agent { team: Team::Blue, .. } => {
                match self.job_description {
                    JobDescription::Servant => Color::Cyan.paint("♟"),
                    JobDescription::Pony => Color::Cyan.paint("♞"),
                    JobDescription::Scholar => Color::Cyan.paint("♝"),
                    JobDescription::Cop => Color::Cyan.paint("♜"),
                    JobDescription::Princess => Color::Cyan.paint("♛"),
                    JobDescription::Figurehead => Color::Cyan.paint("♚"),
                }
            }
        };
        print!("{}", caricature);
    }

}

#[derive(Eq,PartialEq,Debug,Copy,Clone)]
struct GameState {
    to_move: Team,

    orange_servants: Bitboard,
    orange_ponies: Bitboard,
    orange_scholars: Bitboard,
    orange_cops: Bitboard,
    orange_princesses: Bitboard,
    orange_figurehead: Bitboard,

    blue_servants: Bitboard,
    blue_ponies: Bitboard,
    blue_scholars: Bitboard,
    blue_cops: Bitboard,
    blue_princesses: Bitboard,
    blue_figurehead: Bitboard,
}

impl GameState {
    pub fn new() -> GameState {
        let mut orange_servant_locales = Vec::new();
        let mut blue_servant_locales = Vec::new();
        for f in 0..8 {
            orange_servant_locales.push(Locale { rank: 1, file: f });
            blue_servant_locales.push(Locale { rank: 6, file: f });
        }
        GameState {
            to_move: Team::Orange,

            orange_servants: Bitboard::init(&orange_servant_locales),
            orange_ponies: Bitboard::init(
                &vec![Locale { rank: 0, file: 1 },
                      Locale { rank: 0, file: 6 }]
            ),
            orange_scholars: Bitboard::init(
                &vec![Locale { rank: 0, file: 2 },
                      Locale { rank: 0, file: 5 }]
            ),
            orange_cops: Bitboard::init(
                &vec![Locale { rank: 0, file: 0 },
                      Locale { rank: 0, file: 7 }]
            ),
            orange_princesses: Bitboard::init(
                &vec![Locale { rank: 0, file: 3 }]),
            orange_figurehead: Bitboard::init(
                &vec![Locale { rank: 0, file: 4 }]),
            blue_servants: Bitboard::init(&blue_servant_locales),
            blue_ponies: Bitboard::init(
                &vec![Locale { rank: 7, file: 1 },
                      Locale { rank: 7, file: 6 }]
            ),
            blue_scholars: Bitboard::init(
                &vec![Locale { rank: 7, file: 2 },
                      Locale { rank: 7, file: 5 }]
            ),
            blue_cops: Bitboard::init(
                &vec![Locale { rank: 7, file: 0 },
                      Locale { rank: 7, file: 7 }]
            ),
            blue_princesses: Bitboard::init(
                &vec![Locale { rank: 7, file: 3 }]),
            blue_figurehead: Bitboard::init(
                &vec![Locale { rank: 7, file: 4 }]),
        }
    }

    pub fn agent_to_bitboard_ref(&self, agent: Agent) -> &Bitboard {
        match agent {
            Agent{ team: Team::Orange,
                   job_description: JobDescription::Servant } =>
                &self.orange_servants,
            Agent{ team: Team::Orange,
                   job_description: JobDescription::Pony } =>
                &self.orange_ponies,
            Agent{ team: Team::Orange,
                   job_description: JobDescription::Scholar } =>
                &self.orange_scholars,
            Agent{ team: Team::Orange,
                   job_description: JobDescription::Cop } =>
                &self.orange_cops,
            Agent{ team: Team::Orange,
                   job_description: JobDescription::Princess } =>
                &self.orange_princesses,
            Agent{ team: Team::Orange,
                   job_description: JobDescription::Figurehead } =>
                &self.orange_figurehead,
            Agent{ team: Team::Blue,
                   job_description: JobDescription::Servant } =>
                &self.blue_servants,
            Agent{ team: Team::Blue,
                   job_description: JobDescription::Pony } =>
                &self.blue_ponies,
            Agent{ team: Team::Blue,
                   job_description: JobDescription::Scholar } =>
                &self.blue_scholars,
            Agent{ team: Team::Blue,
                   job_description: JobDescription::Cop } =>
                &self.blue_cops,
            Agent{ team: Team::Blue,
                   job_description: JobDescription::Princess } =>
                &self.blue_princesses,
            Agent{ team: Team::Blue,
                   job_description: JobDescription::Figurehead } =>
                &self.blue_figurehead,
        }
    }

    pub fn occupied_by(&self, team: Team) -> Bitboard {
        match team {
            Team::Orange => self.orange_servants.union(
                self.orange_ponies).union(
                    self.orange_scholars).union(self.orange_cops).union(
                        self.orange_princesses).union(self.orange_figurehead),
            Team::Blue => self.blue_servants.union(self.blue_ponies).union(
                    self.blue_scholars).union(self.blue_cops).union(
                            self.blue_princesses).union(self.blue_figurehead)
        }
    }

    pub fn occupied(&self) -> Bitboard {
        self.occupied_by(Team::Orange).union(self.occupied_by(Team::Blue))
    }

    pub fn unoccupied(&self) -> Bitboard {
        self.occupied().invert()
    }

    fn servant_lookahead(&self, team: Team) -> Vec<GameState> {
        let initial_rank;
        let standard_offset;
        let boost_offset;
        let stun_offsets;
        match team {
            Team::Orange => {
                initial_rank = 1;
                standard_offset = (0, 1);
                boost_offset = (0, 2);
                stun_offsets = [(1, -1), (1, 1)];
            },
            Team::Blue => {
                initial_rank = 6;
                standard_offset = (0, -1);
                boost_offset = (0, -2);
                stun_offsets = [(-1, -1), (-1, 1)];
            }
        }
        let mut possibilities = Vec::new();
        let positional_chart: &Bitboard = self.agent_to_bitboard_ref(
            Agent { team: team, job_description: JobDescription::Servant });
        for start_locale in positional_chart.to_locales().iter() {
            // TODO can move one square
            if start_locale.rank == initial_rank {
                // TODO can move with the two-square "boost offset"
            }
            // TODO can move diagonally
        }
        possibilities
    }

    pub fn lookahead(&self) -> Vec<Self> {
        let possibilities = Vec::new();
        let moving_team = self.to_move;
        for agent_class in Agent::dramatis_personae(moving_team) {
            let positional_chart: &Bitboard = self.agent_to_bitboard_ref(
                agent_class);
            // for each agent class, compute possible moves ... somehow
        }
        // TODO work in progress
        possibilities
    }

    pub fn display(&self) {
        println!("  a b c d e f g h");
        for rank in 0..8 {
            print!("{} ", rank+1);
            for file in 0..8 {
                let locale = Locale { rank: rank, file: file };
                if self.occupied().invert().query(locale) {
                    print!("_ ");
                } else {
                    for &team in [Team::Orange, Team::Blue].iter() {
                        for &figurine_class in
                            Agent::dramatis_personae(team).iter() {
                                if self.agent_to_bitboard_ref(
                                    figurine_class).query(locale) {
                                        figurine_class.render_caricature();
                                        print!(" ");
                                }
                        }
                    }
                }
            }
            println!("");
        }
    }
}

fn main() {
    let arena = GameState::new();
    arena.display();
    println!("");
    arena.occupied().display();
    arena.occupied().invert().display();
}

#[cfg(test)]
mod test {
    use super::{Locale, Bitboard, Team, JobDescription, Agent, GameState};

    #[test]
    fn test_locale() {
        let a1 = Locale { rank: 0, file: 0 };
        assert_eq!(Bitboard(1u64), a1.pinpoint());

        let c4 = Locale { rank: 2, file: 3 };
        assert_eq!(Bitboard(524288u64), c4.pinpoint());
    }

    #[test]
    fn test_displace_locale() {
        assert_eq!(
            Some(Locale { rank: 3, file: 5 }),
            Locale { rank: 4, file: 4 }.displace((-1, 1))
        );
        assert_eq!(
            None,
            Locale { rank: 0, file: 0 }.displace((-1, 1))
        )
    }

    #[test]
    fn test_empty_board_is_empty() {
        let empty_board = Bitboard::new();
        for rank in 0..8 {
            for file in 0..8 {
                assert!(!empty_board.query(Locale { rank: rank, file: file }));
            }
        }
    }

    #[test]
    fn test_inverted_empty_board_is_full() {
        let full_board = Bitboard::new().invert();
        for rank in 0..8 {
            for file in 0..8 {
                assert!(full_board.query(Locale { rank: rank, file: file }));
            }
        }
    }

    #[test]
    fn test_alight_and_quench() {
        let mut stage = Bitboard(0);
        let b5 = Locale { rank: 1, file: 4 };
        stage = stage.alight(b5);
        assert!(stage.query(b5));
        stage = stage.quench(b5);
        assert!(!stage.query(b5));
    }

    #[test]
    fn test_init_bitboard() {
        let starters = vec![Locale{ rank: 1, file: 2 },
                            Locale{ rank: 3, file: 4 },
                            Locale{ rank: 5, file: 6 }];
        let stage = Bitboard::init(&starters);
        for &starter in starters.iter() {
            assert!(stage.query(starter));
        }
    }

    #[test]
    fn test_agent_to_bitboard_ref_on_new_gamestate() {
        let state = GameState::new();
        let agent = Agent { team: Team::Blue,
                            job_description: JobDescription::Princess };
        let blue_princess_realm = state.agent_to_bitboard_ref(agent);
        assert!(blue_princess_realm.query(Locale { rank: 7, file: 3 }));
    }

    #[test]
    fn test_orange_servants_to_locales_from_new_gamestate() {
        let state = GameState::new();
        let mut expected = Vec::new();
        for file in 0..8 {
            expected.push(Locale { rank: 1, file: file });
        }
        assert_eq!(expected, state.orange_servants.to_locales());
    }

}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper;
extern crate hubcaps;
extern crate afterparty;
extern crate serde_json;
extern crate serde;
extern crate rustc_serialize;
extern crate toml;
#[macro_use]
extern crate log;
extern crate env_logger;

use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::env;
use std::io::Read;

use hyper::{Client, Server, Url};
use hyper::header::{Headers, Authorization, Bearer, UserAgent};
use hubcaps::{Credentials, Github};
use afterparty::*;
use serde_json::Value;
use regex::Regex;
use toml::decode_str;

#[derive(RustcEncodable, RustcDecodable)]
struct RepoData {
    pub qa_user: String,
    pub qa_flags: Vec<String>,
}

#[derive(RustcEncodable, RustcDecodable)]
struct ConfigData {
    pub api_root: String,
    pub repos: HashMap<String, RepoData>,
}

macro_rules! panic_log {
    ($($arg:tt)*) => ( {
        error!($($arg)*);
        panic!()
    } );
}

trait ExpectLog {
    type Item;
    fn expect_log(self, &str) -> Self::Item;
}

impl<T> ExpectLog for Option<T> {
    type Item = T;
    fn expect_log(self, msg: &str) -> T {
        match self {
            None => panic_log!("{}", msg),
            Some(s) => s,
        }
    }
}

impl<T, E> ExpectLog for Result<T, E>
    where E: std::fmt::Display
{
    type Item = T;
    fn expect_log(self, msg: &str) -> T {
        match self {
            Err(err) => panic_log!("{} {}", msg, err),
            Ok(s) => s,
        }
    }
}

fn read_config_file(file_name: &str) -> ConfigData {
    let mut config_file = File::open(file_name)
        .expect_log(&format!("Couldn't open config file {}", file_name));
    let mut str = String::new();
    config_file.read_to_string(&mut str)
        .expect_log(&format!("Couldn't read config file {}", file_name));
    let toml: Option<ConfigData> = decode_str(&str);
    toml.expect_log(&format!("Couldn't decode config file {}", file_name))
}

#[test]
fn it_reads_toml() {
    env_logger::init().unwrap();
    let config_data = read_config_file("test-data/test.toml");
    assert_eq!(config_data.api_root, "github.ibm.com/api/v3");
    let steve = config_data.repos.get("lkgrele/steve").unwrap();
    assert_eq!(steve.qa_user, "leif");
    assert_eq!(steve.qa_flags, ["qa", "test"]);
    let raul = config_data.repos.get("raul/robot").unwrap();
    assert_eq!(raul.qa_user, "raul");
    assert_eq!(raul.qa_flags, ["qa"]);
}

fn main() {
    env_logger::init().unwrap();

    let mut hub = Hub::new();
    hub.handle("pull_request", |delivery: &Delivery| {
        match delivery.payload {
            Event::PullRequest { ref pull_request, ref repository, .. } => {
                if pull_request.merged {
                    handle_pr(&pull_request.commits_url, &repository.full_name)
                }
            }
            _ => info!("Recived a request that wasn't a pull-request webhook"),
        }
    });

    match Server::http("0.0.0.0:0") {
        Err(err) => error!("ERROR: failed to start server, the error was: {}", err),
        Ok(server) => {
            match server.handle(hub) {
                Err(err) => error!("ERROR starting handler, the error was: {}", err),
                Ok(_) => info!("Successfully started server"),
            }
        }
    }
}

fn handle_pr(commits_url: &str, repository: &str) {
    let config_data = read_config_file(".steve");
    let auth_token = env::var_os("STEVE_GITHUB_TOKEN")
        .expect_log("Missing github token, STEVE_GITHUB_TOKEN is not defined!")
        .into_string()
        .ok()
        .expect_log("Github token contains illegal characters!");
    match config_data.repos.get(&repository.to_owned()) {
        Some(config) => {
            update_issues(&commits_url,
                          &auth_token,
                          &config,
                          &config_data.api_root,
                          &repository)
        }
        None => info!("No config found for repo {}, skipping", repository),
    }
}

fn update_issues(commits_url: &str,
                 auth_token: &str,
                 config: &RepoData,
                 api_root: &str,
                 repository: &str) {
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let commits = client.get(commits_url).headers(headers).send().unwrap();
    assert_eq!(commits.status, hyper::Ok); //replace with actual error handling

    match serde_json::from_reader(commits) {
        Ok(json) => {
            match parse_commit_data(&json) {
                Ok(messages) => {
                    let mut issues = HashSet::new();
                    for message in messages {
                        get_issues(&mut issues, &message)
                    }

                    for issue in issues {
                        update_issue(&client, auth_token, issue, config, &repository, api_root);
                    }
                }
                Err(err) => error!("{:?}", err),
            }
        }
        Err(err) => error!("{:?}", err),
    }
}

#[test]
fn it_parses_commit_data() {
    let file = File::open("test-data/test-messages.json").expect("Couldn't open message file");
    let json = serde_json::from_reader(file);
    assert!(json.is_ok());
    let json = json.unwrap();
    let messages = parse_commit_data(&json);
    assert!(messages.is_ok());
    let messages = messages.unwrap();
    assert_eq!(messages[0], "message1");
    assert_eq!(messages[1], "swordfish");
}

fn parse_commit_data<'a>(json: &'a Value) -> Result<Vec<&'a str>, &str> {
    let arr = try!(json.as_array().ok_or("expected array as top level object"));
    arr.iter()
        .map(|obj| {
            obj.pointer("/commit/message")
                .ok_or("missing message field")
                .and_then(|m| m.as_string().ok_or("message field has wrong type"))
        })
        .collect()
}

fn update_issue(client: &Client,
                auth_token: &str,
                issue_num: u64,
                config: &RepoData,
                repository: &str,
                api_root: &str) {
    let github = Github::host(api_root,
                              "steve",
                              client,
                              Credentials::Token(auth_token.to_owned()));
    let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
        strs => (strs[0], strs[1]),
    };
    let _ = github.repo(repo_name, owner)
        .issue(issue_num)
        .labels()
        .add(config.qa_flags.iter().map(String::as_str).collect());
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let update_json = format!("{{\"assignee\":\"{}\"}}", config.qa_user);
    let url = Url::parse(&format!("{}/repos/{}/issues/{}", api_root, repository, issue_num))
        .expect("bad url");
    let res = client.patch(url)
        .body(&update_json)
        .headers(headers.to_owned())
        .send()
        .unwrap();
    assert_eq!(res.status, hyper::Ok);
}

#[test]
fn it_finds_issue_numbers() {
    let message = r"This is a commit message
        needs qa: #72 should get caught
        needs QA #333 should also get caught
        but #44 won't, and needs QA: #33 #45
        only gets #33. Duplicates are discarded:
        needs qa #72";
    let mut expected: HashSet<u64> = HashSet::new();
    expected.insert(72);
    expected.insert(333);
    expected.insert(33);
    let mut numbers: HashSet<u64> = HashSet::new();
    get_issues(&mut numbers, message);
    assert_eq!(numbers, expected);
}

fn get_issues(numbers: &mut HashSet<u64>, message: &str) {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"(?i)needs\sqa:?\s#(\d+)").expect_log("{}");
    }
    for num in RE.captures_iter(message).filter_map(|cap| cap.at(1)) {
        numbers.insert(num.parse::<u64>().expect_log("{}"));
    }
}
<commit_msg>Start refactoring update_issues.<commit_after>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper;
extern crate hubcaps;
extern crate afterparty;
extern crate serde_json;
extern crate serde;
extern crate rustc_serialize;
extern crate toml;
#[macro_use]
extern crate log;
extern crate env_logger;

use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::env;
use std::io::Read;

use hyper::{Client, Server, Url};
use hyper::header::{Headers, Authorization, Bearer, UserAgent};
use hubcaps::{Credentials, Github, IssueOptions};
use afterparty::*;
use serde_json::Value;
use regex::Regex;
use toml::decode_str;

#[derive(RustcEncodable, RustcDecodable)]
struct RepoData {
    pub qa_user: String,
    pub qa_flags: Vec<String>,
}

#[derive(RustcEncodable, RustcDecodable)]
struct ConfigData {
    pub api_root: String,
    pub repos: HashMap<String, RepoData>,
}

macro_rules! panic_log {
    ($($arg:tt)*) => ( {
        error!($($arg)*);
        panic!()
    } );
}

trait ExpectLog {
    type Item;
    fn expect_log(self, &str) -> Self::Item;
}

impl<T> ExpectLog for Option<T> {
    type Item = T;
    fn expect_log(self, msg: &str) -> T {
        match self {
            None => panic_log!("{}", msg),
            Some(s) => s,
        }
    }
}

impl<T, E> ExpectLog for Result<T, E>
    where E: std::fmt::Display
{
    type Item = T;
    fn expect_log(self, msg: &str) -> T {
        match self {
            Err(err) => panic_log!("{} {}", msg, err),
            Ok(s) => s,
        }
    }
}

fn read_config_file(file_name: &str) -> ConfigData {
    let mut config_file = File::open(file_name)
        .expect_log(&format!("Couldn't open config file {}", file_name));
    let mut str = String::new();
    config_file.read_to_string(&mut str)
        .expect_log(&format!("Couldn't read config file {}", file_name));
    let toml: Option<ConfigData> = decode_str(&str);
    toml.expect_log(&format!("Couldn't decode config file {}", file_name))
}

#[test]
fn it_reads_toml() {
    env_logger::init().unwrap();
    let config_data = read_config_file("test-data/test.toml");
    assert_eq!(config_data.api_root, "github.ibm.com/api/v3");
    let steve = config_data.repos.get("lkgrele/steve").unwrap();
    assert_eq!(steve.qa_user, "leif");
    assert_eq!(steve.qa_flags, ["qa", "test"]);
    let raul = config_data.repos.get("raul/robot").unwrap();
    assert_eq!(raul.qa_user, "raul");
    assert_eq!(raul.qa_flags, ["qa"]);
}

fn main() {
    env_logger::init().unwrap();

    let mut hub = Hub::new();
    hub.handle("pull_request", |delivery: &Delivery| {
        match delivery.payload {
            Event::PullRequest { ref pull_request, ref repository, .. } => {
                if pull_request.merged {
                    handle_pr(&pull_request.commits_url, &repository.full_name)
                }
            }
            _ => info!("Recived a request that wasn't a pull-request webhook"),
        }
    });

    match Server::http("0.0.0.0:0") {
        Err(err) => error!("ERROR: failed to start server, the error was: {}", err),
        Ok(server) => {
            match server.handle(hub) {
                Err(err) => error!("ERROR starting handler, the error was: {}", err),
                Ok(_) => info!("Successfully started server"),
            }
        }
    }
}

fn handle_pr(commits_url: &str, repository: &str) {
    let config_data = read_config_file(".steve");
    let auth_token = env::var_os("STEVE_GITHUB_TOKEN")
        .expect_log("Missing github token, STEVE_GITHUB_TOKEN is not defined!")
        .into_string()
        .ok()
        .expect_log("Github token contains illegal characters!");
    match config_data.repos.get(&repository.to_owned()) {
        Some(config) => {
            update_issues(&commits_url,
                          &auth_token,
                          &config,
                          &config_data.api_root,
                          &repository)
        }
        None => info!("No config found for repo {}, skipping", repository),
    }
}

fn update_issues(commits_url: &str,
                 auth_token: &str,
                 config: &RepoData,
                 api_root: &str,
                 repository: &str) {
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let commits = client.get(commits_url).headers(headers).send().unwrap();
    assert_eq!(commits.status, hyper::Ok); //replace with actual error handling

    match serde_json::from_reader(commits) {
        Ok(json) => {
            match parse_commit_data(&json) {
                Ok(messages) => {
                    let mut issues = HashSet::new();
                    for message in messages {
                        get_issues(&mut issues, &message)
                    }

                    if !issues.is_empty() {
                        let github = Github::host(api_root,
                                                  "steve",
                                                  &client,
                                                  Credentials::Token(auth_token.to_owned()));
                        let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
                            strs => (strs[0], strs[1]),
                        };
                        let repo = github.repo(repo_name, owner);
                        match repo.issues().list(&Default::default()) {
                            Err(err) => error!("{:?}", err),
                            Ok(issue_data) => {
                                for issue in issues {
                                    if issue_data.len() as u64 <= issue {
                                        info!("Wanted to update issue #{}, but {} doesn't have an \
                                               issue with that number",
                                              issue,
                                              repository)
                                    } else {
                                        let ref current_flags = issue_data[issue as usize].labels;
                                        let new_flags: Vec<String> = current_flags.into_iter()
                                            .map(|l| l.name.clone())
                                            .chain(config.qa_flags.clone().into_iter())
                                            .collect();
                                        let issue_options =
                                            IssueOptions::new::<String,
                                                                String,
                                                                String,
                                                                String>(None,
                                                                        None,
                                                                        Some(config.qa_user
                                                                            .clone()),
                                                                        None,
                                                                        Some(new_flags));
                                        if let Err(err) = repo.issue(issue).edit(&issue_options) {
                                            error!("{:?}", err)
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                Err(err) => error!("{:?}", err),
            }
        }
        Err(err) => error!("{:?}", err),
    }
}

#[test]
fn it_parses_commit_data() {
    let file = File::open("test-data/test-messages.json").expect("Couldn't open message file");
    let json = serde_json::from_reader(file);
    assert!(json.is_ok());
    let json = json.unwrap();
    let messages = parse_commit_data(&json);
    assert!(messages.is_ok());
    let messages = messages.unwrap();
    assert_eq!(messages[0], "message1");
    assert_eq!(messages[1], "swordfish");
}

fn parse_commit_data<'a>(json: &'a Value) -> Result<Vec<&'a str>, &str> {
    let arr = try!(json.as_array().ok_or("expected array as top level object"));
    arr.iter()
        .map(|obj| {
            obj.pointer("/commit/message")
                .ok_or("missing message field")
                .and_then(|m| m.as_string().ok_or("message field has wrong type"))
        })
        .collect()
}

fn update_issue(client: &Client,
                auth_token: &str,
                issue_num: u64,
                config: &RepoData,
                repository: &str,
                api_root: &str) {
    let github = Github::host(api_root,
                              "steve",
                              client,
                              Credentials::Token(auth_token.to_owned()));
    let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
        strs => (strs[0], strs[1]),
    };
    let _ = github.repo(repo_name, owner)
        .issue(issue_num)
        .labels()
        .add(config.qa_flags.iter().map(String::as_str).collect());
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let update_json = format!("{{\"assignee\":\"{}\"}}", config.qa_user);
    let url = Url::parse(&format!("{}/repos/{}/issues/{}", api_root, repository, issue_num))
        .expect("bad url");
    let res = client.patch(url)
        .body(&update_json)
        .headers(headers.to_owned())
        .send()
        .unwrap();
    assert_eq!(res.status, hyper::Ok);
}

#[test]
fn it_finds_issue_numbers() {
    let message = r"This is a commit message
        needs qa: #72 should get caught
        needs QA #333 should also get caught
        but #44 won't, and needs QA: #33 #45
        only gets #33. Duplicates are discarded:
        needs qa #72";
    let mut expected: HashSet<u64> = HashSet::new();
    expected.insert(72);
    expected.insert(333);
    expected.insert(33);
    let mut numbers: HashSet<u64> = HashSet::new();
    get_issues(&mut numbers, message);
    assert_eq!(numbers, expected);
}

fn get_issues(numbers: &mut HashSet<u64>, message: &str) {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"(?i)needs\sqa:?\s#(\d+)").expect_log("{}");
    }
    for num in RE.captures_iter(message).filter_map(|cap| cap.at(1)) {
        numbers.insert(num.parse::<u64>().expect_log("{}"));
    }
}
<|endoftext|>


<commit_before>extern crate byteorder;
extern crate slab;

#[macro_use]
extern crate futures;

#[macro_use]
extern crate tokio_core;

use std::env;
use std::net::SocketAddr;
use std::rc::Rc;
use std::cell::{Cell, RefCell};

use byteorder::{LittleEndian, ByteOrder};
use slab::Slab;
use futures::{Async, Poll, Future};
use futures::stream::Stream;
use tokio_core::io::{read_exact, write_all, Io};
use tokio_core::net::TcpListener;
use tokio_core::reactor::Core;

struct ReadStream<R> where R: ::std::io::Read {
    reader: R,
    buffer: Vec<u8>,
    pos: usize,
    frame_end: Option<u8>,
}

impl <R> ReadStream<R> where R: ::std::io::Read {
    fn new(reader: R) -> ReadStream<R> {
        ReadStream {
            reader: reader,
            buffer: Vec::new(),
            pos: 0,
            frame_end: None,
        }
    }
}

impl <R> Stream for ReadStream<R> where R: ::std::io::Read {
    type Item = Vec<u8>;
    type Error = ::std::io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        loop {
            if let Some(frame_end) = self.frame_end {
                let n = try_nb!(self.reader.read(&mut self.buffer[self.pos..frame_end as usize]));
                self.pos += n;
                if self.pos == frame_end as usize {
                    self.pos = 0;
                    let result = ::std::mem::replace(&mut self.buffer, Vec::new());
                    return Ok(Async::Ready(Some(result)))
                }
            } else {
                let mut buf = [0u8];
                let n = try_nb!(self.reader.read(&mut buf));
                if n == 0 { // EOF
                    return Ok(Async::Ready(None))
                }

                self.frame_end = Some(buf[0]);
                self.buffer = vec![0; buf[0] as usize];
            }
        }
    }
}

pub fn main() {
    let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
    let addr = addr.parse::<SocketAddr>().unwrap();

    let mut l = Core::new().unwrap();
    let handle = l.handle();

    // Create a TCP listener which will listen for incoming connections
    let socket = TcpListener::bind(&addr, &handle).unwrap();

    // Once we've got the TCP listener, inform that we have it
    println!("Listening on: {}", addr);

    let subscribers: Rc<RefCell<Slab<::write_queue::Sender, usize>>> =
        Rc::new(RefCell::new(Slab::with_capacity(1024)));

    let done = socket.incoming().for_each(move |(socket, _addr)| {
        // what's the spec?
        // first byte: 0 means publisher, 1 means subscriber.

        let subscribers = subscribers.clone();
        let header = [0u8; 1];
        let future = read_exact(socket, header).and_then(move |(socket, header)| -> Box<Future<Item=(),Error=::std::io::Error>> {
            println!("OK {:?}", header);
            match header[0] {
                0 => {
                    // publisher
                    let num_read = Rc::new(Cell::new(0u64));
                    let num_read1 = num_read.clone();
                    let done = futures::lazy(|| Ok(socket.split()))
                        .and_then(|(reader, writer)| {
                            ReadStream::new(reader).for_each(move |buf| {
                                num_read1.set(num_read1.get() + 1);
                                println!("buf {:?}", buf);
                                // TODO send this off to the subscribers.
                                Ok(())
                            }).and_then(move |()| {
                                let mut word = [0u8; 8];
                                <LittleEndian as ByteOrder>::write_u64(&mut word, num_read.get());
                                write_all(writer, word).map(|_| ())
                            })
                        });

                    Box::new(done)
                }
                1 => {
                    // subscriber

                    let (sender, queue) = ::write_queue::write_queue(socket);

                    let idx = match subscribers.borrow_mut().insert(sender) {
                        Ok(idx) => idx,
                        Err(_) => unimplemented!(), // should grow the slab.
                    };

                    Box::new(queue.then(move |_| {
                        subscribers.borrow_mut().remove(idx).unwrap();
                        Ok(())
                    }))
                }
                _ => {
                    // error
                    unimplemented!()
                }
            }
        }).map_err(|e| {
            println!("error: {}", e);
        });
        handle.spawn(future);

        // frame format: first byte is length of body. Then there is body.
        Ok(())
    });

    l.run(done).unwrap();
}

pub mod write_queue {
    use std::collections::VecDeque;
    use std::rc::Rc;
    use std::cell::RefCell;
    use futures::{self, task, Async, Future, Poll, Complete, Oneshot};
    use tokio_core::io::WriteAll;

    enum State<W> where W: ::std::io::Write {
        WritingHeader(W, Vec<u8>, Complete<Vec<u8>>),
        Writing(WriteAll<W, Vec<u8>>, Complete<Vec<u8>>),
        BetweenWrites(W),
        Empty,
    }

    /// A write of messages being written.
    pub struct WriteQueue<W> where W: ::std::io::Write {
        inner: Rc<RefCell<Inner>>,
        state: State<W>,
    }

    struct Inner {
        queue: VecDeque<(Vec<u8>, Complete<Vec<u8>>)>,
        sender_count: usize,
        task: Option<task::Task>,
    }

    pub struct Sender {
        inner: Rc<RefCell<Inner>>,
    }

    impl Clone for Sender {
        fn clone(&self) -> Sender {
            self.inner.borrow_mut().sender_count += 1;
            Sender { inner: self.inner.clone() }
        }
    }

    impl Drop for Sender {
        fn drop(&mut self) {
            self.inner.borrow_mut().sender_count -= 1;
        }
    }

    pub fn write_queue<W>(writer: W) -> (Sender, WriteQueue<W>)
        where W: ::std::io::Write
    {
        let inner = Rc::new(RefCell::new(Inner {
            queue: VecDeque::new(),
            task: None,
            sender_count: 1,
        }));

        let queue = WriteQueue {
            inner: inner.clone(),
            state: State::BetweenWrites(writer),
        };

        let sender = Sender { inner: inner };

        (sender, queue)
    }

    impl Sender {
        /// Enqueues a message to be written.
        pub fn send(&mut self, message: Vec<u8>) -> Oneshot<Vec<u8>> {
            let (complete, oneshot) = futures::oneshot();
            self.inner.borrow_mut().queue.push_back((message, complete));

            match self.inner.borrow_mut().task.take() {
                Some(t) => t.unpark(),
                None => (),
            }

            oneshot
        }

        /// Returns the number of messages queued to be written, not including any in-progress write.
        pub fn len(&mut self) -> usize {
            self.inner.borrow().queue.len()
        }
    }

    enum IntermediateState<W> where W: ::std::io::Write {
        WriteHeaderDone,
        WriteDone(Vec<u8>, W),
        StartWrite(Vec<u8>, Complete<Vec<u8>>),
        Resolve,
    }

    impl <W> Future for WriteQueue<W> where W: ::std::io::Write {
        type Item = W; // Resolves when all senders have been dropped and all messages written.
        type Error = ::std::io::Error;

        fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
            loop {
                let next = match self.state {
                    State::WritingHeader(ref mut write, ref buf, ref mut _complete) => {
                        let n = try_nb!(write.write(&[buf.len() as u8]));
                        match n {
                            0 => unimplemented!(), // TODO return error premature EOF
                            1 => IntermediateState::WriteHeaderDone,
                            _ => unreachable!(),
                        }
                    }
                    State::Writing(ref mut write, ref mut _complete) => {
                        let (w, m) = try_ready!(Future::poll(write));
                        IntermediateState::WriteDone(m, w)
                    }
                    State::BetweenWrites(ref mut _writer) => {
                        let front = self.inner.borrow_mut().queue.pop_front();
                        match front {
                            Some((m, complete)) => {
                                IntermediateState::StartWrite(m, complete)
                            }
                            None => {
                                let count = self.inner.borrow().sender_count;
                                if count == 0 {
                                    IntermediateState::Resolve
                                } else {
                                    self.inner.borrow_mut().task = Some(task::park());
                                    return Ok(Async::NotReady)
                                }
                            }
                        }
                    }
                    State::Empty => unreachable!(),
                };

                match next {
                    IntermediateState::WriteHeaderDone => {
                        let new_state = match ::std::mem::replace(&mut self.state, State::Empty) {
                            State::WritingHeader(writer, buf, complete) => {
                                State::Writing(::tokio_core::io::write_all(writer, buf), complete)
                            }
                            _ => unreachable!(),
                        };
                        self.state = new_state;
                    }
                    IntermediateState::WriteDone(m, w) => {
                        match ::std::mem::replace(&mut self.state, State::BetweenWrites(w)) {
                            State::Writing(_, complete) => {
                                complete.complete(m)
                            }
                            _ => unreachable!(),
                        }
                    }
                    IntermediateState::StartWrite(m, c) => {
                        let new_state = match ::std::mem::replace(&mut self.state, State::Empty) {
                            State::BetweenWrites(w) => {
                                State::WritingHeader(w, m, c)
                            }
                            _ => unreachable!(),
                        };
                        self.state = new_state;
                    }
                    IntermediateState::Resolve => {
                        match ::std::mem::replace(&mut self.state, State::Empty) {
                            State::BetweenWrites(w) => {
                                return Ok(Async::Ready(w))
                            }
                            _ => unreachable!(),
                        }
                    }
                }
            }
        }
    }
}
<commit_msg>send to write queue<commit_after>extern crate byteorder;
extern crate slab;

#[macro_use]
extern crate futures;

#[macro_use]
extern crate tokio_core;

use std::env;
use std::net::SocketAddr;
use std::rc::Rc;
use std::cell::{Cell, RefCell};

use byteorder::{LittleEndian, ByteOrder};
use slab::Slab;
use futures::{Async, Poll, Future};
use futures::stream::Stream;
use tokio_core::io::{read_exact, write_all, Io};
use tokio_core::net::TcpListener;
use tokio_core::reactor::Core;

struct ReadStream<R> where R: ::std::io::Read {
    reader: R,
    buffer: Vec<u8>,
    pos: usize,
    frame_end: Option<u8>,
}

impl <R> ReadStream<R> where R: ::std::io::Read {
    fn new(reader: R) -> ReadStream<R> {
        ReadStream {
            reader: reader,
            buffer: Vec::new(),
            pos: 0,
            frame_end: None,
        }
    }
}

impl <R> Stream for ReadStream<R> where R: ::std::io::Read {
    type Item = Vec<u8>;
    type Error = ::std::io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        loop {
            if let Some(frame_end) = self.frame_end {
                let n = try_nb!(self.reader.read(&mut self.buffer[self.pos..frame_end as usize]));
                self.pos += n;
                if self.pos == frame_end as usize {
                    self.pos = 0;
                    let result = ::std::mem::replace(&mut self.buffer, Vec::new());
                    return Ok(Async::Ready(Some(result)))
                }
            } else {
                let mut buf = [0u8];
                let n = try_nb!(self.reader.read(&mut buf));
                if n == 0 { // EOF
                    return Ok(Async::Ready(None))
                }

                self.frame_end = Some(buf[0]);
                self.buffer = vec![0; buf[0] as usize];
            }
        }
    }
}

pub fn main() {
    let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
    let addr = addr.parse::<SocketAddr>().unwrap();

    let mut l = Core::new().unwrap();
    let handle = l.handle();

    // Create a TCP listener which will listen for incoming connections
    let socket = TcpListener::bind(&addr, &handle).unwrap();

    // Once we've got the TCP listener, inform that we have it
    println!("Listening on: {}", addr);

    let subscribers: Rc<RefCell<Slab<::write_queue::Sender, usize>>> =
        Rc::new(RefCell::new(Slab::with_capacity(1024)));

    let done = socket.incoming().for_each(move |(socket, _addr)| {
        // what's the spec?
        // first byte: 0 means publisher, 1 means subscriber.

        let subscribers = subscribers.clone();
        let header = [0u8; 1];
        let future = read_exact(socket, header).and_then(move |(socket, header)| -> Box<Future<Item=(),Error=::std::io::Error>> {
            println!("OK {:?}", header);
            match header[0] {
                0 => {
                    // publisher
                    let num_read = Rc::new(Cell::new(0u64));
                    let num_read1 = num_read.clone();
                    let done = futures::lazy(|| Ok(socket.split()))
                        .and_then(|(reader, writer)| {
                            ReadStream::new(reader).for_each(move |buf| {
                                num_read1.set(num_read1.get() + 1);
                                for ref mut sender in subscribers.borrow_mut().iter_mut() {
                                    if sender.len() < 5 {
                                        drop(sender.send(buf.clone()));
                                    }
                                }
                                Ok(())
                            }).and_then(move |()| {
                                let mut word = [0u8; 8];
                                <LittleEndian as ByteOrder>::write_u64(&mut word, num_read.get());
                                write_all(writer, word).map(|_| ())
                            })
                        });

                    Box::new(done)
                }
                1 => {
                    // subscriber

                    let (sender, queue) = ::write_queue::write_queue(socket);

                    let idx = match subscribers.borrow_mut().insert(sender) {
                        Ok(idx) => idx,
                        Err(_) => unimplemented!(), // should grow the slab.
                    };

                    Box::new(queue.then(move |_| {
                        subscribers.borrow_mut().remove(idx).unwrap();
                        Ok(())
                    }))
                }
                _ => {
                    // error
                    unimplemented!()
                }
            }
        }).map_err(|e| {
            println!("error: {}", e);
        });
        handle.spawn(future);

        // frame format: first byte is length of body. Then there is body.
        Ok(())
    });

    l.run(done).unwrap();
}

pub mod write_queue {
    use std::collections::VecDeque;
    use std::rc::Rc;
    use std::cell::RefCell;
    use futures::{self, task, Async, Future, Poll, Complete, Oneshot};
    use tokio_core::io::WriteAll;

    enum State<W> where W: ::std::io::Write {
        WritingHeader(W, Vec<u8>, Complete<Vec<u8>>),
        Writing(WriteAll<W, Vec<u8>>, Complete<Vec<u8>>),
        BetweenWrites(W),
        Empty,
    }

    /// A write of messages being written.
    pub struct WriteQueue<W> where W: ::std::io::Write {
        inner: Rc<RefCell<Inner>>,
        state: State<W>,
    }

    struct Inner {
        queue: VecDeque<(Vec<u8>, Complete<Vec<u8>>)>,
        sender_count: usize,
        task: Option<task::Task>,
    }

    pub struct Sender {
        inner: Rc<RefCell<Inner>>,
    }

    impl Clone for Sender {
        fn clone(&self) -> Sender {
            self.inner.borrow_mut().sender_count += 1;
            Sender { inner: self.inner.clone() }
        }
    }

    impl Drop for Sender {
        fn drop(&mut self) {
            self.inner.borrow_mut().sender_count -= 1;
        }
    }

    pub fn write_queue<W>(writer: W) -> (Sender, WriteQueue<W>)
        where W: ::std::io::Write
    {
        let inner = Rc::new(RefCell::new(Inner {
            queue: VecDeque::new(),
            task: None,
            sender_count: 1,
        }));

        let queue = WriteQueue {
            inner: inner.clone(),
            state: State::BetweenWrites(writer),
        };

        let sender = Sender { inner: inner };

        (sender, queue)
    }

    impl Sender {
        /// Enqueues a message to be written.
        pub fn send(&mut self, message: Vec<u8>) -> Oneshot<Vec<u8>> {
            let (complete, oneshot) = futures::oneshot();
            self.inner.borrow_mut().queue.push_back((message, complete));

            match self.inner.borrow_mut().task.take() {
                Some(t) => t.unpark(),
                None => (),
            }

            oneshot
        }

        /// Returns the number of messages queued to be written, not including any in-progress write.
        pub fn len(&mut self) -> usize {
            self.inner.borrow().queue.len()
        }
    }

    enum IntermediateState<W> where W: ::std::io::Write {
        WriteHeaderDone,
        WriteDone(Vec<u8>, W),
        StartWrite(Vec<u8>, Complete<Vec<u8>>),
        Resolve,
    }

    impl <W> Future for WriteQueue<W> where W: ::std::io::Write {
        type Item = W; // Resolves when all senders have been dropped and all messages written.
        type Error = ::std::io::Error;

        fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
            loop {
                let next = match self.state {
                    State::WritingHeader(ref mut write, ref buf, ref mut _complete) => {
                        let n = try_nb!(write.write(&[buf.len() as u8]));
                        match n {
                            0 => unimplemented!(), // TODO return error premature EOF
                            1 => IntermediateState::WriteHeaderDone,
                            _ => unreachable!(),
                        }
                    }
                    State::Writing(ref mut write, ref mut _complete) => {
                        let (w, m) = try_ready!(Future::poll(write));
                        IntermediateState::WriteDone(m, w)
                    }
                    State::BetweenWrites(ref mut _writer) => {
                        let front = self.inner.borrow_mut().queue.pop_front();
                        match front {
                            Some((m, complete)) => {
                                IntermediateState::StartWrite(m, complete)
                            }
                            None => {
                                let count = self.inner.borrow().sender_count;
                                if count == 0 {
                                    IntermediateState::Resolve
                                } else {
                                    self.inner.borrow_mut().task = Some(task::park());
                                    return Ok(Async::NotReady)
                                }
                            }
                        }
                    }
                    State::Empty => unreachable!(),
                };

                match next {
                    IntermediateState::WriteHeaderDone => {
                        let new_state = match ::std::mem::replace(&mut self.state, State::Empty) {
                            State::WritingHeader(writer, buf, complete) => {
                                State::Writing(::tokio_core::io::write_all(writer, buf), complete)
                            }
                            _ => unreachable!(),
                        };
                        self.state = new_state;
                    }
                    IntermediateState::WriteDone(m, w) => {
                        match ::std::mem::replace(&mut self.state, State::BetweenWrites(w)) {
                            State::Writing(_, complete) => {
                                complete.complete(m)
                            }
                            _ => unreachable!(),
                        }
                    }
                    IntermediateState::StartWrite(m, c) => {
                        let new_state = match ::std::mem::replace(&mut self.state, State::Empty) {
                            State::BetweenWrites(w) => {
                                State::WritingHeader(w, m, c)
                            }
                            _ => unreachable!(),
                        };
                        self.state = new_state;
                    }
                    IntermediateState::Resolve => {
                        match ::std::mem::replace(&mut self.state, State::Empty) {
                            State::BetweenWrites(w) => {
                                return Ok(Async::Ready(w))
                            }
                            _ => unreachable!(),
                        }
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::num::Float;
use std::io;

struct Velocity {
    x: f32,
    y: f32,
    v: f32
}

fn main() {
    println!("DCC Tennis Ball Launcher Calculator");

    loop {
        let time: f32 = calculate_time();
        let velocity: Velocity = calculate_velocity(time);
        let angle: f32 = calculate_angle(&velocity);
        let force: f32 = calculate_force(&velocity);

        println!("The time the ball will travel is {}s", time);
        println!("The velocity of the ball will be {} m/s", velocity.v);
        println!("The angle of the launcher should be {}º", angle);
        println!("The force exerted by the launcher on the ball should be {} N", force);
    }
}

fn calculate_time() -> f32 {
    println!("Enter the height of the appeture");
    let input = io::stdin().read_line().ok().expect("Failed to read line");
    let input_num: Option<f32> = input.trim().parse();

    let distance = match input_num {
        Some(distance) => distance,
        None => 0.0
    };

    return Float::sqrt(2.0 * distance / 9.8) * 2.0;
}

fn calculate_velocity(time: f32) -> Velocity {
    println!("Enter the range of the appeture");
    let input = io::stdin().read_line().ok().expect("Failed to read line");
    let input_num: Option<f32> = input.trim().parse();

    let range = match input_num {
        Some(range) => range,
        None => 0.0
    };

    let velocity_x: f32 = range / time;
    let velocity_y: f32 = 9.8 * time;

    let velocity = Velocity { x: velocity_x, y: velocity_y, v: Float::sqrt(Float::powf(velocity_x, 2.0) + Float::powf(velocity_y, 2.0))};

    return velocity;
}

fn calculate_angle(velocity: &Velocity) -> f32 {
    return Float::atan(velocity.y / velocity.x).to_degrees();
}

fn calculate_force(velocity: &Velocity) -> f32 {
    println!("Enter the mass of the ball");
    let input_m = io::stdin().read_line().ok().expect("Failed to read line");
    let input_num_m: Option<f32> = input_m.trim().parse();

    let mass = match input_num_m {
        Some(mass) => mass,
        None => 0.0
    };

    println!("Enter the length of the pipe");
    let input_l = io::stdin().read_line().ok().expect("Failed to read line");
    let input_num_l: Option<f32> = input_l.trim().parse();

    let length = match input_num_l {
        Some(length) => length,
        None => 0.0
    };

    let acceleration: f32 = Float::powf(velocity.v, 2.0) / 2.0 * length;

    return mass * acceleration;
}
<commit_msg>Grammar fix<commit_after>use std::num::Float;
use std::io;

struct Velocity {
    x: f32,
    y: f32,
    v: f32
}

fn main() {
    println!("DCC Tennis Ball Launcher Calculator");

    loop {
        let time: f32 = calculate_time();
        let velocity: Velocity = calculate_velocity(time);
        let angle: f32 = calculate_angle(&velocity);
        let force: f32 = calculate_force(&velocity);

        println!("The time it'll take the ball to travel is {}s", time);
        println!("The velocity of the ball will be {} m/s", velocity.v);
        println!("The angle of the launcher should be {}º", angle);
        println!("The force exerted by the launcher on the ball should be {} N", force);
    }
}

fn calculate_time() -> f32 {
    println!("Enter the height of the appeture");
    let input = io::stdin().read_line().ok().expect("Failed to read line");
    let input_num: Option<f32> = input.trim().parse();

    let distance = match input_num {
        Some(distance) => distance,
        None => 0.0
    };

    return Float::sqrt(2.0 * distance / 9.8) * 2.0;
}

fn calculate_velocity(time: f32) -> Velocity {
    println!("Enter the range of the appeture");
    let input = io::stdin().read_line().ok().expect("Failed to read line");
    let input_num: Option<f32> = input.trim().parse();

    let range = match input_num {
        Some(range) => range,
        None => 0.0
    };

    let velocity_x: f32 = range / time;
    let velocity_y: f32 = 9.8 * time;

    let velocity = Velocity { x: velocity_x, y: velocity_y, v: Float::sqrt(Float::powf(velocity_x, 2.0) + Float::powf(velocity_y, 2.0))};

    return velocity;
}

fn calculate_angle(velocity: &Velocity) -> f32 {
    return Float::atan(velocity.y / velocity.x).to_degrees();
}

fn calculate_force(velocity: &Velocity) -> f32 {
    println!("Enter the mass of the ball");
    let input_m = io::stdin().read_line().ok().expect("Failed to read line");
    let input_num_m: Option<f32> = input_m.trim().parse();

    let mass = match input_num_m {
        Some(mass) => mass,
        None => 0.0
    };

    println!("Enter the length of the pipe");
    let input_l = io::stdin().read_line().ok().expect("Failed to read line");
    let input_num_l: Option<f32> = input_l.trim().parse();

    let length = match input_num_l {
        Some(length) => length,
        None => 0.0
    };

    let acceleration: f32 = Float::powf(velocity.v, 2.0) / 2.0 * length;

    return mass * acceleration;
}
<|endoftext|>


<commit_before>extern crate glob;
#[macro_use]
extern crate lazy_static;
extern crate log;
#[macro_use]
extern crate serde;
extern crate serde_json;
extern crate structopt;
extern crate syslog;
extern crate toml;

mod applog;
mod connection;
mod error;
mod message;
mod statics;
mod structs;

use log::debug;
use message::*;
use std::env;
use std::io::{Error, ErrorKind};
use structopt::StructOpt;
use structs::Config;

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
enum Command {
    /// Gets user public key
    Key {
        #[structopt(parse(from_str))]
        user: String,
    },
    /// Executes pam check
    Pam,
    /// Check configuration
    Check {
        #[structopt(parse(from_os_str))]
        confpath: std::path::PathBuf,
    },
    /// Cleans caches up
    #[structopt(alias = "cleanup")]
    CleanUp,
    /// Get rate limit for github api
    #[structopt(alias = "ratelimit")]
    RateLimit,
    /// Displays version details
    Version,
    /// Displays completion
    Completion {
        #[structopt(subcommand)]
        shell: Shell,
    },
}

#[allow(clippy::enum_variant_names)]
#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
enum Shell {
    Bash,
    Fish,
    Zsh,
    PowerShell,
    Elvish,
}

fn main() -> Result<(), Error> {
    let command = Command::from_args();
    let conn = match connection::Connection::new(&format!("{:?}", command)) {
        Ok(conn) => conn,
        Err(err) => return Err(Error::new(ErrorKind::Other, format!("{:?}", err))),
    };
    debug!("connected to socket: {:?}", conn);

    match command {
        Command::Check { confpath } => match Config::from_path(&confpath) {
            Ok(_) => return Ok(()),
            Err(_) => return Err(Error::new(ErrorKind::Other, "check failed")),
        },
        Command::Key { user } => match conn.communicate(ClientMessage::Key { user }) {
            Ok(DaemonMessage::Key { keys }) => {
                println!("{}", keys);
                return Ok(());
            }
            _ => return Err(Error::new(ErrorKind::PermissionDenied, "key check failed")),
        },
        Command::Pam => match env::var("PAM_USER") {
            Ok(user) => match conn.communicate(ClientMessage::Pam { user }) {
                Ok(DaemonMessage::Pam { result }) => {
                    if result {
                        return Ok(());
                    } else {
                        return Err(Error::new(ErrorKind::NotFound, "user not found"));
                    }
                }
                _ => return Err(Error::new(ErrorKind::Other, "faild")),
            },
            Err(_) => return Err(Error::new(ErrorKind::ConnectionRefused, "failed")),
        },
        Command::CleanUp => match conn.communicate(ClientMessage::CleanUp) {
            Ok(_) => return Ok(()),
            Err(_) => return Err(Error::new(ErrorKind::Other, "failed")),
        },
        Command::RateLimit => match conn.communicate(ClientMessage::RateLimit) {
            Ok(DaemonMessage::RateLimit { limit,
                                          remaining,
                                          reset, }) => {
                println!("remaining: {}/{}, reset:{}", remaining, limit, reset);
            }
            _ => return Err(Error::new(ErrorKind::Other, "failed")),
        },
        Command::Version => {
            println!("{}",
                     concat!(env!("CARGO_PKG_VERSION"),
                             include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))));
        }
        Command::Completion { shell } => {
            let shell = match shell {
                Shell::Bash => structopt::clap::Shell::Bash,
                Shell::Fish => structopt::clap::Shell::Fish,
                Shell::Zsh => structopt::clap::Shell::Zsh,
                Shell::PowerShell => structopt::clap::Shell::PowerShell,
                Shell::Elvish => structopt::clap::Shell::Elvish,
            };
            Command::clap().gen_completions_to("sectora", shell, &mut std::io::stdout());
        }
    };
    Ok(())
}
<commit_msg>Refactor<commit_after>extern crate glob;
#[macro_use]
extern crate lazy_static;
extern crate log;
#[macro_use]
extern crate serde;
extern crate serde_json;
extern crate structopt;
extern crate syslog;
extern crate toml;

mod applog;
mod connection;
mod error;
mod message;
mod statics;
mod structs;

use log::debug;
use message::*;
use std::env;
use std::io::{Error, ErrorKind};
use structopt::StructOpt;
use structs::Config;

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
enum Command {
    /// Gets user public key
    Key {
        #[structopt(parse(from_str))]
        user: String,
    },
    /// Executes pam check
    Pam,
    /// Check configuration
    Check {
        #[structopt(parse(from_os_str))]
        confpath: std::path::PathBuf,
    },
    /// Cleans caches up
    #[structopt(alias = "cleanup")]
    CleanUp,
    /// Get rate limit for github api
    #[structopt(alias = "ratelimit")]
    RateLimit,
    /// Displays version details
    Version,
    /// Displays completion
    Completion {
        #[structopt(subcommand)]
        shell: Shell,
    },
}

#[allow(clippy::enum_variant_names)]
#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case")]
enum Shell {
    Bash,
    Fish,
    Zsh,
    PowerShell,
    Elvish,
}

fn show_keys(conn: &connection::Connection, user: &str) -> Result<(), Error> {
    match conn.communicate(ClientMessage::Key { user: user.to_owned() }) {
        Ok(DaemonMessage::Key { keys }) => {
            println!("{}", keys);
            return Ok(());
        }
        _ => return Err(Error::new(ErrorKind::PermissionDenied, "key check failed")),
    }
}

fn main() -> Result<(), Error> {
    let command = Command::from_args();
    let conn = match connection::Connection::new(&format!("{:?}", command)) {
        Ok(conn) => conn,
        Err(err) => return Err(Error::new(ErrorKind::Other, format!("{:?}", err))),
    };
    debug!("connected to socket: {:?}", conn);

    match command {
        Command::Check { confpath } => match Config::from_path(&confpath) {
            Ok(_) => return Ok(()),
            Err(_) => return Err(Error::new(ErrorKind::Other, "check failed")),
        },
        Command::Key { user } => show_keys(&conn, &user)?,
        Command::Pam => match env::var("PAM_USER") {
            Ok(user) => match conn.communicate(ClientMessage::Pam { user }) {
                Ok(DaemonMessage::Pam { result }) => {
                    if result {
                        return Ok(());
                    } else {
                        return Err(Error::new(ErrorKind::NotFound, "user not found"));
                    }
                }
                _ => return Err(Error::new(ErrorKind::Other, "faild")),
            },
            Err(_) => return Err(Error::new(ErrorKind::ConnectionRefused, "failed")),
        },
        Command::CleanUp => match conn.communicate(ClientMessage::CleanUp) {
            Ok(_) => return Ok(()),
            Err(_) => return Err(Error::new(ErrorKind::Other, "failed")),
        },
        Command::RateLimit => match conn.communicate(ClientMessage::RateLimit) {
            Ok(DaemonMessage::RateLimit { limit,
                                          remaining,
                                          reset, }) => {
                println!("remaining: {}/{}, reset:{}", remaining, limit, reset);
            }
            _ => return Err(Error::new(ErrorKind::Other, "failed")),
        },
        Command::Version => {
            println!("{}",
                     concat!(env!("CARGO_PKG_VERSION"),
                             include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))));
        }
        Command::Completion { shell } => {
            let shell = match shell {
                Shell::Bash => structopt::clap::Shell::Bash,
                Shell::Fish => structopt::clap::Shell::Fish,
                Shell::Zsh => structopt::clap::Shell::Zsh,
                Shell::PowerShell => structopt::clap::Shell::PowerShell,
                Shell::Elvish => structopt::clap::Shell::Elvish,
            };
            Command::clap().gen_completions_to("sectora", shell, &mut std::io::stdout());
        }
    };
    Ok(())
}
<|endoftext|>