<commit_before>use space::*;
use space::Direction::*;

#[derive(PartialEq, Debug)]
enum EntityType {
    Bot,
    Block,
    Abyss,
}

enum PushResult {
    Success,
    TooHeavy,
    Abyss,
}

enum Entity {
    Bot {
        position: Position,
        ticks_until_action: u8,
    },
    Block {
        position: Position,
    },
    Abyss {
        position: Position,
    },
}

pub struct Area {
    contents: Vec<Entity>,
    bounds: Rectangle,
    ticks_elapsed: u16,
}
impl Area {
    fn new(bounds: Rectangle) -> Area {
        Area {
            contents: Vec::new(),
            bounds: bounds,
            ticks_elapsed: 0,
        }
    }

    fn make(&mut self, position: Position, entType: EntityType) {
        let entity = match entType {
            EntityType::Bot => Entity::Bot { position: position, ticks_until_action: 0 },
            EntityType::Block => Entity::Block { position: position },
            EntityType::Abyss => Entity::Abyss { position: position },
        };
        self.contents.push(entity);
    }

    fn remove(&mut self, position: Position) {
        self.contents.retain(|element| {
            position != match *element {
                Entity::Bot { position, .. } => position,
                Entity::Block { position } => position,
                Entity::Abyss { position } => position,
            }
        });
    }

    fn shift(&mut self, source: Position, destination: Position) {
        for element in self.contents.iter_mut() {
            match *element {
                Entity::Bot { position, ticks_until_action } if position == source => {
                    *element = Entity::Bot {
                        position: destination,
                        ticks_until_action: ticks_until_action,
                    };
                    return;
                },
                Entity::Block { position } if position == source => {
                    *element = Entity::Block { position: destination };
                    return;
                },
                _ => (),
            }
        }
    }

    fn push(&mut self, focus: Position, direction: Direction) -> PushResult {
        let destination = focus + direction;

        match self.type_at(focus) {
            None => PushResult::Success,
            Some(EntityType::Abyss) => PushResult::Abyss,
            Some(EntityType::Bot) => {
                self.shift(focus, destination);
                PushResult::Success
            },
            Some(EntityType::Block) => {
                if self.type_at(destination) == Some(EntityType::Block) {
                    PushResult::TooHeavy
                }
                else {
                    self.shift(focus, destination);
                    PushResult::Success
                }
            },
        }
    }

    fn bot_go(&mut self, source: Position, direction: Direction) {
        let destination = source + direction;
        match self.push(destination, direction) {
            PushResult::Success => self.shift(source, destination),
            PushResult::Abyss => self.remove(source),
            PushResult::TooHeavy => (),
        }
    }

    fn bot_drill(&mut self, position: Position, direction: Direction) {

    }

    fn type_at(&self, dest: Position) -> Option<EntityType> {
        if !self.bounds.contains(dest) {
            return Some(EntityType::Abyss)
        }
        for element in &self.contents {
            match *element {
                Entity::Bot { position, .. } if dest == position => return Some(EntityType::Bot),
                Entity::Block { position } if dest == position => return Some(EntityType::Block),
                Entity::Abyss { position } if dest == position => return Some(EntityType::Abyss),
                _ => (),
            }
        }
        None
    }

    fn occupied(&self, position: Position) -> bool {
        match self.type_at(position) {
            Some(_) => true,
            None => false,
        }
    }

    fn tick(&mut self) {
        self.ticks_elapsed += 1;
    }
}

fn test_data() -> (Position, Position, Position, Area) {
    (
        Position::new(0, 0),
        Position::new(2, 0),
        Position::new(5, 3),
        Area::new(Rectangle::wh(10 * East + 10 * South)),
    )
}

#[test]
fn occupation() {
    let (a, b, c, mut area) = test_data();

    area.make(a, EntityType::Bot);
    assert!(area.occupied(a));
    assert_eq!(area.type_at(a), Some(EntityType::Bot));
    assert!(!area.occupied(b));
    assert!(!area.occupied(c));

    area.make(b, EntityType::Block);
    assert!(area.occupied(a));
    assert_eq!(area.type_at(a), Some(EntityType::Bot));
    assert!(area.occupied(b));
    assert_eq!(area.type_at(b), Some(EntityType::Block));
    assert!(!area.occupied(c));
}

#[test]
fn abyss_around_arena() {
    let (_, _, _, area) = test_data();

    assert_eq!(area.type_at(Position::new(0, 0)), None);
    assert_eq!(area.type_at(Position::new(-1, 0)), Some(EntityType::Abyss));
    assert_eq!(area.type_at(Position::new(0, -1)), Some(EntityType::Abyss));
    assert_eq!(area.type_at(Position::new(10, 0)), Some(EntityType::Abyss));
    assert_eq!(area.type_at(Position::new(0, 10)), Some(EntityType::Abyss));
    assert_eq!(area.type_at(Position::new(10, 10)), Some(EntityType::Abyss));
}

#[test]
fn travelment() {
    for dir in vec![North, East, South, West] {
        let (_, _, c, mut area) = test_data();

        area.make(c, EntityType::Bot);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), None);
        assert_eq!(area.type_at(c + dir), Some(EntityType::Bot));
    }
}

#[test]
fn pushing() {
    for dir in vec![North, East, South, West] {
        let (_, _, c, mut area) = test_data();

        area.make(c, EntityType::Bot);
        area.make(c + dir, EntityType::Block);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), None);
        assert_eq!(area.type_at(c + dir), Some(EntityType::Bot));
        assert_eq!(area.type_at(c + dir + dir), Some(EntityType::Block));
    }
}

#[test]
fn pushing_too_heavy() {
    for dir in vec![North, East, South, West] {
        let (_, _, c, mut area) = test_data();

        area.make(c, EntityType::Bot);
        area.make(c + dir, EntityType::Block);
        area.make(c + dir + dir, EntityType::Block);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), Some(EntityType::Bot));
        assert_eq!(area.type_at(c + dir), Some(EntityType::Block));
        assert_eq!(area.type_at(c + dir + dir), Some(EntityType::Block));
    }
}

#[test]
fn skydiving() {
    for dir in vec![North, East, South, West] {
        let (_, _, c, mut area) = test_data();

        area.make(c, EntityType::Bot);
        area.make(c + dir, EntityType::Abyss);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), None);
        assert_eq!(area.type_at(c + dir), Some(EntityType::Abyss));
    }

    for dir in vec![North, East, South, West] {
        let mut area = Area::new(Rectangle::corners(Position::zero(), Position::zero()));
        assert_eq!(area.type_at(Position::zero()), None);

        area.make(Position::zero(), EntityType::Bot);
        area.bot_go(Position::zero(), dir);
        assert_eq!(area.type_at(Position::zero()), None);
        assert_eq!(area.type_at(Position::zero() + dir), Some(EntityType::Abyss));
    }
}

#[test]
fn shove_into_abyss() {
    for dir in vec![North, East, South, West] {
        let (_, _, c, mut area) = test_data();

        area.make(c, EntityType::Bot);
        area.make(c + dir, EntityType::Abyss);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), None);
        assert_eq!(area.type_at(c + dir), Some(EntityType::Abyss));
    }
}

#[test]
fn elapsation() {
    let (_, _, c, mut area) = test_data();
    assert_eq!(area.ticks_elapsed, 0);

    area.tick();
    assert_eq!(area.ticks_elapsed, 1);

    area.tick();
    assert_eq!(area.ticks_elapsed, 2);
}
<commit_msg>Bots are squished when pushed against things<commit_after>use space::*;
use space::Direction::*;

#[derive(PartialEq, Debug)]
enum EntityType {
    Bot,
    Block,
    Abyss,
}

enum PushResult {
    Success,
    TooHeavy,
    Abyss,
}

enum Entity {
    Bot {
        position: Position,
        ticks_until_action: u8,
    },
    Block {
        position: Position,
    },
    Abyss {
        position: Position,
    },
}

pub struct Area {
    contents: Vec<Entity>,
    bounds: Rectangle,
    ticks_elapsed: u16,
}
impl Area {
    fn new(bounds: Rectangle) -> Area {
        Area {
            contents: Vec::new(),
            bounds: bounds,
            ticks_elapsed: 0,
        }
    }

    fn make(&mut self, position: Position, entType: EntityType) {
        let entity = match entType {
            EntityType::Bot => Entity::Bot { position: position, ticks_until_action: 0 },
            EntityType::Block => Entity::Block { position: position },
            EntityType::Abyss => Entity::Abyss { position: position },
        };
        self.contents.push(entity);
    }

    fn remove(&mut self, position: Position) {
        self.contents.retain(|element| {
            position != match *element {
                Entity::Bot { position, .. } => position,
                Entity::Block { position } => position,
                Entity::Abyss { position } => position,
            }
        });
    }

    fn shift(&mut self, source: Position, destination: Position) {
        for element in self.contents.iter_mut() {
            match *element {
                Entity::Bot { position, ticks_until_action } if position == source => {
                    *element = Entity::Bot {
                        position: destination,
                        ticks_until_action: ticks_until_action,
                    };
                    return;
                },
                Entity::Block { position } if position == source => {
                    *element = Entity::Block { position: destination };
                    return;
                },
                _ => (),
            }
        }
    }

    fn push(&mut self, focus: Position, direction: Direction) -> PushResult {
        let destination = focus + direction;

        match self.type_at(focus) {
            None => PushResult::Success,
            Some(EntityType::Abyss) => PushResult::Abyss,
            Some(EntityType::Bot) => {
                match self.type_at(destination) {
                    None => {
                        self.shift(focus, destination);
                        PushResult::Success
                    },
                    Some(_) => {
                        self.remove(focus);
                        PushResult::Success
                    }
                }
            },
            Some(EntityType::Block) => {
                match self.type_at(destination) {
                    None => {
                        self.shift(focus, destination);
                        PushResult::Success
                    },
                    Some(EntityType::Block) => PushResult::TooHeavy,
                    Some(EntityType::Abyss) => {
                        self.remove(focus);
                        PushResult::Success
                    },
                    Some(EntityType::Bot) => {
                        let destination2 = destination + direction;
                        match self.type_at(destination2) {
                            None => {
                                self.shift(destination, destination2);
                                self.shift(focus, destination);
                                PushResult::Success
                            },
                            Some(_) => {
                                self.remove(destination);
                                self.shift(focus, destination);
                                PushResult::Success
                            },
                        }
                    },
                }
            },
        }
    }

    fn bot_go(&mut self, source: Position, direction: Direction) {
        let destination = source + direction;
        match self.push(destination, direction) {
            PushResult::Success => self.shift(source, destination),
            PushResult::Abyss => self.remove(source),
            PushResult::TooHeavy => (),
        }
    }

    fn bot_drill(&mut self, position: Position, direction: Direction) {

    }

    fn type_at(&self, dest: Position) -> Option<EntityType> {
        if !self.bounds.contains(dest) {
            return Some(EntityType::Abyss)
        }
        for element in &self.contents {
            match *element {
                Entity::Bot { position, .. } if dest == position => return Some(EntityType::Bot),
                Entity::Block { position } if dest == position => return Some(EntityType::Block),
                Entity::Abyss { position } if dest == position => return Some(EntityType::Abyss),
                _ => (),
            }
        }
        None
    }

    fn occupied(&self, position: Position) -> bool {
        match self.type_at(position) {
            Some(_) => true,
            None => false,
        }
    }

    fn tick(&mut self) {
        self.ticks_elapsed += 1;
    }
}

fn test_data() -> (Position, Position, Position, Area) {
    (
        Position::new(0, 0),
        Position::new(2, 0),
        Position::new(5, 4),
        Area::new(Rectangle::wh(10 * East + 10 * South)),
    )
}

#[test]
fn occupation() {
    let (a, b, c, mut area) = test_data();

    area.make(a, EntityType::Bot);
    assert!(area.occupied(a));
    assert_eq!(area.type_at(a), Some(EntityType::Bot));
    assert!(!area.occupied(b));
    assert!(!area.occupied(c));

    area.make(b, EntityType::Block);
    assert!(area.occupied(a));
    assert_eq!(area.type_at(a), Some(EntityType::Bot));
    assert!(area.occupied(b));
    assert_eq!(area.type_at(b), Some(EntityType::Block));
    assert!(!area.occupied(c));
}

#[test]
fn abyss_around_arena() {
    let (_, _, _, area) = test_data();

    assert_eq!(area.type_at(Position::new(0, 0)), None);
    assert_eq!(area.type_at(Position::new(-1, 0)), Some(EntityType::Abyss));
    assert_eq!(area.type_at(Position::new(0, -1)), Some(EntityType::Abyss));
    assert_eq!(area.type_at(Position::new(10, 0)), Some(EntityType::Abyss));
    assert_eq!(area.type_at(Position::new(0, 10)), Some(EntityType::Abyss));
    assert_eq!(area.type_at(Position::new(10, 10)), Some(EntityType::Abyss));
}

#[test]
fn travelment() {
    for dir in vec![North, East, South, West] {
        let (_, _, c, mut area) = test_data();

        area.make(c, EntityType::Bot);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), None);
        assert_eq!(area.type_at(c + dir), Some(EntityType::Bot));
    }
}

#[test]
fn pushing() {
    for dir in vec![North, East, South, West] {
        let (_, _, c, mut area) = test_data();

        area.make(c, EntityType::Bot);
        area.make(c + dir, EntityType::Block);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), None);
        assert_eq!(area.type_at(c + dir), Some(EntityType::Bot));
        assert_eq!(area.type_at(c + dir + dir), Some(EntityType::Block));
    }
}

#[test]
fn pushing_too_heavy() {
    for dir in vec![North, East, South, West] {
        let (_, _, c, mut area) = test_data();

        area.make(c, EntityType::Bot);
        area.make(c + dir, EntityType::Block);
        area.make(c + dir + dir, EntityType::Block);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), Some(EntityType::Bot));
        assert_eq!(area.type_at(c + dir), Some(EntityType::Block));
        assert_eq!(area.type_at(c + dir + dir), Some(EntityType::Block));
    }
}

#[test]
fn skydiving() {
    for dir in vec![North, East, South, West] {
        let (_, _, c, mut area) = test_data();

        area.make(c, EntityType::Bot);
        area.make(c + dir, EntityType::Abyss);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), None);
        assert_eq!(area.type_at(c + dir), Some(EntityType::Abyss));
    }

    for dir in vec![North, East, South, West] {
        let mut area = Area::new(Rectangle::corners(Position::zero(), Position::zero()));
        assert_eq!(area.type_at(Position::zero()), None);

        area.make(Position::zero(), EntityType::Bot);
        area.bot_go(Position::zero(), dir);
        assert_eq!(area.type_at(Position::zero()), None);
        assert_eq!(area.type_at(Position::zero() + dir), Some(EntityType::Abyss));
    }
}

#[test]
fn shove_into_abyss() {
    for dir in vec![North, East, South, West] {
        let (_, _, c, mut area) = test_data();
        area.make(c, EntityType::Bot);
        area.make(c + dir, EntityType::Bot);
        area.make(c + dir + dir, EntityType::Abyss);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), None);
        assert_eq!(area.type_at(c + dir), Some(EntityType::Bot));
        assert_eq!(area.type_at(c + dir + dir), Some(EntityType::Abyss));

        let (_, _, c, mut area) = test_data();
        area.make(c, EntityType::Bot);
        area.make(c + dir, EntityType::Block);
        area.make(c + dir + dir, EntityType::Abyss);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), None);
        assert_eq!(area.type_at(c + dir), Some(EntityType::Bot));
        assert_eq!(area.type_at(c + dir + dir), Some(EntityType::Abyss));

        let (_, _, c, mut area) = test_data();
        area.make(c, EntityType::Bot);
        area.make(c + dir, EntityType::Block);
        area.make(c + dir * 2, EntityType::Bot);
        area.make(c + dir * 3, EntityType::Abyss);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), None);
        assert_eq!(area.type_at(c + dir), Some(EntityType::Bot));
        assert_eq!(area.type_at(c + dir * 2), Some(EntityType::Block));
        assert_eq!(area.type_at(c + dir * 3), Some(EntityType::Abyss));

        let (_, _, c, mut area) = test_data();
        area.make(c, EntityType::Bot);
        area.make(c + dir, EntityType::Bot);
        area.make(c + dir * 2, EntityType::Bot);
        area.make(c + dir * 3, EntityType::Abyss);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), None);
        assert_eq!(area.type_at(c + dir), Some(EntityType::Bot));
        assert_eq!(area.type_at(c + dir * 2), Some(EntityType::Bot));
        assert_eq!(area.type_at(c + dir * 3), Some(EntityType::Abyss));
    }
}

#[test]
fn squish_directly() {
    for dir in vec![North, East, South, West] {
        let (_, _, c, mut area) = test_data();

        area.make(c, EntityType::Bot);
        area.make(c + dir, EntityType::Bot);
        area.make(c + dir + dir, EntityType::Block);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), None);
        assert_eq!(area.type_at(c + dir), Some(EntityType::Bot));
        assert_eq!(area.type_at(c + dir + dir), Some(EntityType::Block));
    }
}

#[test]
fn squish_indirectly() {
    for dir in vec![North, East, South, West] {
        let (_, _, c, mut area) = test_data();
        area.make(c, EntityType::Bot);
        area.make(c + dir, EntityType::Block);
        area.make(c + dir + dir, EntityType::Bot);
        area.make(c + dir + dir + dir, EntityType::Block);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), None);
        assert_eq!(area.type_at(c + dir), Some(EntityType::Bot));
        assert_eq!(area.type_at(c + dir + dir), Some(EntityType::Block));
        assert_eq!(area.type_at(c + dir + dir + dir), Some(EntityType::Block));

        let (_, _, c, mut area) = test_data();
        area.make(c, EntityType::Bot);
        area.make(c + dir, EntityType::Block);
        area.make(c + dir + dir, EntityType::Bot);
        area.make(c + dir + dir + dir, EntityType::Bot);
        area.bot_go(c, dir);
        assert_eq!(area.type_at(c), None);
        assert_eq!(area.type_at(c + dir), Some(EntityType::Bot));
        assert_eq!(area.type_at(c + dir + dir), Some(EntityType::Block));
        assert_eq!(area.type_at(c + dir + dir + dir), Some(EntityType::Bot));
    }
}

#[test]
fn elapsation() {
    let (_, _, c, mut area) = test_data();
    assert_eq!(area.ticks_elapsed, 0);

    area.tick();
    assert_eq!(area.ticks_elapsed, 1);

    area.tick();
    assert_eq!(area.ticks_elapsed, 2);
}
<|endoftext|>


<commit_before>//! Implementation of support for the Enttec USB DMX Pro dongle.

use serde::{Deserialize, Serialize};
use std::io::Write;
use std::time::Duration;
use std::{cmp::min, fmt};

use crate::PortListing;

use super::{DmxPort, Error};
use serialport::{available_ports, new, SerialPort, SerialPortInfo, SerialPortType, UsbPortInfo};

// Some constants used for enttec message framing.
const START_VAL: u8 = 0x7E;
const END_VAL: u8 = 0xE7;

// Universe size constraints.
const MIN_UNIVERSE_SIZE: usize = 24;
const MAX_UNIVERSE_SIZE: usize = 512;

// Port action flags.
const SET_PARAMETERS: u8 = 4;
//const RECEIVE_DMX_PACKET: u8 = 5;
const SEND_DMX_PACKET: u8 = 6;

/// Format a byte buffer as an enttec message into the provided writer.
/// Maximum valid size for payload is 600; no check is made here that the payload is within this range.
fn write_packet<W: Write>(
    message_type: u8,
    payload: &[u8],
    add_payload_pad_byte: bool,
    mut w: W,
) -> Result<(), Error> {
    // Enttec messages are the size of the payload plus 5 bytes for type, length, and framing.
    let payload_size = payload.len() + add_payload_pad_byte as usize;
    let (len_lsb, len_msb) = (payload_size as u8, (payload_size >> 8) as u8);
    let header = [START_VAL, message_type, len_lsb, len_msb];
    w.write_all(&header)?;
    if add_payload_pad_byte {
        w.write_all(&[0][..])?;
    }
    w.write_all(payload)?;
    w.write_all(&[END_VAL][..])?;
    Ok(())
}

#[derive(Debug, Serialize, Deserialize)]
pub struct EnttecParams {
    /// DMX output break time in 10.67 microsecond units. Valid range is 9 to 127.
    break_time: u8,
    /// DMX output Mark After Break time in 10.67 microsecond units. Valid range is 1 to 127.
    mark_after_break_time: u8,
    /// DMX output rate in packets per second. Valid range is 1 to 40, or 0 for fastest rate
    /// possible (this will make the most difference when the output universe size is smallest).
    output_rate: u8,
}

impl Default for EnttecParams {
    /// Default parameters for the enttec port.
    /// In summary: minimum break and mark times, fastest fixed framerate.
    fn default() -> Self {
        EnttecParams {
            break_time: 9,
            mark_after_break_time: 1,
            output_rate: 40,
        }
    }
}

impl EnttecParams {
    fn write_into<W: Write>(&self, w: W) -> Result<(), Error> {
        let payload = [
            0, // user size lsb?
            0, // user size msb?
            self.break_time,
            self.mark_after_break_time,
            self.output_rate,
        ];
        write_packet(SET_PARAMETERS, &payload, false, w)
    }
}

#[derive(Serialize, Deserialize)]
pub struct EnttecDmxPort {
    params: EnttecParams,
    #[serde(skip)]
    port: Option<Box<dyn SerialPort>>,
    #[serde(with = "SerialPortInfoDef")]
    info: SerialPortInfo,
}

impl EnttecDmxPort {
    /// Create an enttec port.
    /// The port is not opened yet.
    pub fn new(info: SerialPortInfo) -> Self {
        let params = EnttecParams::default();

        Self {
            params: params,
            port: None,
            info,
        }
    }

    /// Create an enttec port and open it.
    pub fn opened(info: SerialPortInfo) -> Result<Self, Error> {
        let mut port = Self::new(info);
        port.open()?;
        Ok(port)
    }

    /// Write the current parameters out to the port.
    fn write_params(&mut self) -> Result<(), Error> {
        self.params
            .write_into(self.port.as_mut().ok_or(Error::PortClosed)?)
    }
}

#[typetag::serde]
impl DmxPort for EnttecDmxPort {
    /// Return the available enttec ports connected to this system.
    /// TODO: provide a mechanism to specialize this implementation depending on platform.
    fn available_ports() -> Result<PortListing, Error> {
        Ok(available_ports()?
            .into_iter()
            .filter(|info| {
                if let SerialPortType::UsbPort(usb_port_info) = &info.port_type {
                    if let Some(product) = &usb_port_info.product {
                        // this should handle the unix case
                        #[cfg(unix)]
                        return product == "DMX USB PRO";
                        // the windows case, unfortunately, we do not have
                        // enough details from the COM port interface to ensure that
                        // we only include enttecs.  The best we can do is filter
                        // down to FTDI ports.
                        #[cfg(windows)]
                        return product == "FTDI";
                    }
                }
                false
            })
            .map(|info| Box::new(EnttecDmxPort::new(info)) as Box<dyn DmxPort>)
            .collect())
    }

    /// Open the port.
    fn open(&mut self) -> Result<(), Error> {
        if self.port.is_some() {
            return Ok(());
        }

        // baud rate is not used on FTDI
        let port = new(&self.info.port_name, 57600)
            .timeout(Duration::from_millis(1))
            .open()?;

        self.port = Some(port);

        // send the default parameters to the port
        if let Err(e) = self.write_params() {
            self.port = None;
            return Err(e);
        }
        Ok(())
    }

    fn close(&mut self) {
        self.port = None;
    }

    fn write(&mut self, frame: &[u8]) -> Result<(), Error> {
        let port = self.port.as_mut().ok_or(Error::PortClosed)?;
        let size = frame.len();
        if size < MIN_UNIVERSE_SIZE {
            let mut padded_frame = Vec::with_capacity(MIN_UNIVERSE_SIZE);
            padded_frame.extend_from_slice(frame);
            padded_frame.resize(MIN_UNIVERSE_SIZE, 0);
            write_packet(SEND_DMX_PACKET, &padded_frame, true, port)
        } else {
            write_packet(
                SEND_DMX_PACKET,
                &frame[0..min(size, MAX_UNIVERSE_SIZE)],
                true,
                port,
            )
        }
    }
}

impl fmt::Display for EnttecDmxPort {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.info.port_type {
            SerialPortType::UsbPort(p) => {
                if let Some(sn) = &p.serial_number {
                    return write!(f, "Enttec DMX USB PRO {}", sn);
                }
            }
            _ => (),
        }
        write!(f, "Enttec DMX USB PRO {}", self.info.port_name)
    }
}

// Derive serde for serial port info.

#[derive(Serialize, Deserialize)]
#[serde(remote = "SerialPortInfo")]
struct SerialPortInfoDef {
    pub port_name: String,
    #[serde(with = "SerialPortTypeDef")]
    pub port_type: SerialPortType,
}

#[derive(Serialize, Deserialize)]
#[serde(remote = "SerialPortType")]
pub enum SerialPortTypeDef {
    #[serde(with = "UsbPortInfoDef")]
    UsbPort(UsbPortInfo),
    PciPort,
    BluetoothPort,
    Unknown,
}

#[derive(Serialize, Deserialize)]
#[serde(remote = "UsbPortInfo")]
pub struct UsbPortInfoDef {
    pub vid: u16,
    pub pid: u16,
    pub serial_number: Option<String>,
    pub manufacturer: Option<String>,
    pub product: Option<String>,
}

#[cfg(test)]
mod test {
    use std::{thread::sleep, time::Duration};

    use super::*;
    use std::error::Error;

    #[test]
    fn test() -> Result<(), Box<dyn Error>> {
        let mut port = EnttecDmxPort::available_ports()?.pop().unwrap();
        println!("{}", port);
        port.open()?;
        for val in 0..255 {
            port.write(&[val][..])?;
            sleep(Duration::from_millis(25));
        }
        port.write(&[0][..])?;
        Ok(())
    }
}
<commit_msg>Fix enttec filter function for windows.<commit_after>//! Implementation of support for the Enttec USB DMX Pro dongle.

use serde::{Deserialize, Serialize};
use std::io::Write;
use std::time::Duration;
use std::{cmp::min, fmt};

use crate::PortListing;

use super::{DmxPort, Error};
use serialport::{available_ports, new, SerialPort, SerialPortInfo, SerialPortType, UsbPortInfo};

// Some constants used for enttec message framing.
const START_VAL: u8 = 0x7E;
const END_VAL: u8 = 0xE7;

// Universe size constraints.
const MIN_UNIVERSE_SIZE: usize = 24;
const MAX_UNIVERSE_SIZE: usize = 512;

// Port action flags.
const SET_PARAMETERS: u8 = 4;
//const RECEIVE_DMX_PACKET: u8 = 5;
const SEND_DMX_PACKET: u8 = 6;

/// Format a byte buffer as an enttec message into the provided writer.
/// Maximum valid size for payload is 600; no check is made here that the payload is within this range.
fn write_packet<W: Write>(
    message_type: u8,
    payload: &[u8],
    add_payload_pad_byte: bool,
    mut w: W,
) -> Result<(), Error> {
    // Enttec messages are the size of the payload plus 5 bytes for type, length, and framing.
    let payload_size = payload.len() + add_payload_pad_byte as usize;
    let (len_lsb, len_msb) = (payload_size as u8, (payload_size >> 8) as u8);
    let header = [START_VAL, message_type, len_lsb, len_msb];
    w.write_all(&header)?;
    if add_payload_pad_byte {
        w.write_all(&[0][..])?;
    }
    w.write_all(payload)?;
    w.write_all(&[END_VAL][..])?;
    Ok(())
}

#[derive(Debug, Serialize, Deserialize)]
pub struct EnttecParams {
    /// DMX output break time in 10.67 microsecond units. Valid range is 9 to 127.
    break_time: u8,
    /// DMX output Mark After Break time in 10.67 microsecond units. Valid range is 1 to 127.
    mark_after_break_time: u8,
    /// DMX output rate in packets per second. Valid range is 1 to 40, or 0 for fastest rate
    /// possible (this will make the most difference when the output universe size is smallest).
    output_rate: u8,
}

impl Default for EnttecParams {
    /// Default parameters for the enttec port.
    /// In summary: minimum break and mark times, fastest fixed framerate.
    fn default() -> Self {
        EnttecParams {
            break_time: 9,
            mark_after_break_time: 1,
            output_rate: 40,
        }
    }
}

impl EnttecParams {
    fn write_into<W: Write>(&self, w: W) -> Result<(), Error> {
        let payload = [
            0, // user size lsb?
            0, // user size msb?
            self.break_time,
            self.mark_after_break_time,
            self.output_rate,
        ];
        write_packet(SET_PARAMETERS, &payload, false, w)
    }
}

#[derive(Serialize, Deserialize)]
pub struct EnttecDmxPort {
    params: EnttecParams,
    #[serde(skip)]
    port: Option<Box<dyn SerialPort>>,
    #[serde(with = "SerialPortInfoDef")]
    info: SerialPortInfo,
}

impl EnttecDmxPort {
    /// Create an enttec port.
    /// The port is not opened yet.
    pub fn new(info: SerialPortInfo) -> Self {
        let params = EnttecParams::default();

        Self {
            params: params,
            port: None,
            info,
        }
    }

    /// Create an enttec port and open it.
    pub fn opened(info: SerialPortInfo) -> Result<Self, Error> {
        let mut port = Self::new(info);
        port.open()?;
        Ok(port)
    }

    /// Write the current parameters out to the port.
    fn write_params(&mut self) -> Result<(), Error> {
        self.params
            .write_into(self.port.as_mut().ok_or(Error::PortClosed)?)
    }
}

#[typetag::serde]
impl DmxPort for EnttecDmxPort {
    /// Return the available enttec ports connected to this system.
    /// TODO: provide a mechanism to specialize this implementation depending on platform.
    fn available_ports() -> Result<PortListing, Error> {
        Ok(available_ports()?
            .into_iter()
            .filter(|info| {
                if let SerialPortType::UsbPort(usb_port_info) = &info.port_type {
                    return is_enttec(usb_port_info);
                }
                false
            })
            .map(|info| Box::new(EnttecDmxPort::new(info)) as Box<dyn DmxPort>)
            .collect())
    }

    /// Open the port.
    fn open(&mut self) -> Result<(), Error> {
        if self.port.is_some() {
            return Ok(());
        }

        // baud rate is not used on FTDI
        let port = new(&self.info.port_name, 57600)
            .timeout(Duration::from_millis(1))
            .open()?;

        self.port = Some(port);

        // send the default parameters to the port
        if let Err(e) = self.write_params() {
            self.port = None;
            return Err(e);
        }
        Ok(())
    }

    fn close(&mut self) {
        self.port = None;
    }

    fn write(&mut self, frame: &[u8]) -> Result<(), Error> {
        let port = self.port.as_mut().ok_or(Error::PortClosed)?;
        let size = frame.len();
        if size < MIN_UNIVERSE_SIZE {
            let mut padded_frame = Vec::with_capacity(MIN_UNIVERSE_SIZE);
            padded_frame.extend_from_slice(frame);
            padded_frame.resize(MIN_UNIVERSE_SIZE, 0);
            write_packet(SEND_DMX_PACKET, &padded_frame, true, port)
        } else {
            write_packet(
                SEND_DMX_PACKET,
                &frame[0..min(size, MAX_UNIVERSE_SIZE)],
                true,
                port,
            )
        }
    }
}

impl fmt::Display for EnttecDmxPort {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.info.port_type {
            SerialPortType::UsbPort(p) => {
                if let Some(sn) = &p.serial_number {
                    return write!(f, "Enttec DMX USB PRO {}", sn);
                }
            }
            _ => (),
        }
        write!(f, "Enttec DMX USB PRO {}", self.info.port_name)
    }
}

#[cfg(unix)]
fn is_enttec(info: &UsbPortInfo) -> bool {
    if let Some(product) = &info.product {
        return product == "DMX USB PRO";
    }
    false
}

#[cfg(windows)]
fn is_enttec(info: &UsbPortInfo) -> bool {
    if let Some(manufacturer) = &info.manufacturer {
        return manufacturer == "FTDI";
    }
    false
}

// Derive serde for serial port info.

#[derive(Serialize, Deserialize)]
#[serde(remote = "SerialPortInfo")]
struct SerialPortInfoDef {
    pub port_name: String,
    #[serde(with = "SerialPortTypeDef")]
    pub port_type: SerialPortType,
}

#[derive(Serialize, Deserialize)]
#[serde(remote = "SerialPortType")]
pub enum SerialPortTypeDef {
    #[serde(with = "UsbPortInfoDef")]
    UsbPort(UsbPortInfo),
    PciPort,
    BluetoothPort,
    Unknown,
}

#[derive(Serialize, Deserialize)]
#[serde(remote = "UsbPortInfo")]
pub struct UsbPortInfoDef {
    pub vid: u16,
    pub pid: u16,
    pub serial_number: Option<String>,
    pub manufacturer: Option<String>,
    pub product: Option<String>,
}

#[cfg(test)]
mod test {
    use std::{thread::sleep, time::Duration};

    use super::*;
    use std::error::Error;

    #[test]
    fn test() -> Result<(), Box<dyn Error>> {
        let mut port = EnttecDmxPort::available_ports()?.pop().unwrap();
        println!("{}", port);
        port.open()?;
        for val in 0..255 {
            port.write(&[val][..])?;
            sleep(Duration::from_millis(25));
        }
        port.write(&[0][..])?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate r2d2;

quick_error! {
    /// The most generic error message in ReQL
    #[derive(Debug)]
    pub enum Error {
        Compile(descr: &'static str) { }
        Runtime(err: RuntimeError) { from() }
        Driver(err: DriverError) { from() }
    }
}

quick_error! {
    /// The parent class of all runtime errors
    ///
    /// All errors on the server unrelated to compilation. Programs may use this to catch any runtime
    /// error, but the server will always return a more specific error class.
    #[derive(Debug)]
    pub enum RuntimeError {
        QueryLogic(err: QueryLogicError) { from() }
        ResourceLimit(descr: &'static str) {}
        User(descr: &'static str) {}
        Internal(descr: &'static str) {}
        Timeout(descr: &'static str) {}
        Availability(err: AvailabilityError) { from() }
        Permissions(descr: &'static str) {}
    }
}

quick_error! {
    /// The query contains a logical impossibility, such as adding a number to a string.
    #[derive(Debug)]
    pub enum QueryLogicError {
        NonExistence(descr: &'static str) {}
    }
}

quick_error! {
    /// A server in the cluster is unavailable
    ///
    /// The parent class of `OpFailedError` and `OpIndeterminateError`. Programs may use this
    /// to catch any availability error, but the server will always return one of this class’s
    /// children.
    #[derive(Debug)]
    pub enum AvailabilityError {
        OpFailed(descr: &'static str) {}
        OpIndeterminate(descr: &'static str) {}
    }
}

quick_error! {
    /// An error has occurred within the driver
    ///
    /// This may be a driver bug, or it may be an unfulfillable command, such as an unserializable
    /// query.
    #[derive(Debug)]
    pub enum DriverError {
        Auth(descr: &'static str) {}
        Initialization(err: r2d2::InitializationError) { from() }
    }
}

/// Converts external error to ours
impl From<r2d2::InitializationError> for Error {
    fn from(err: r2d2::InitializationError) -> Error {
        From::from(DriverError::Initialization(err))
    }
}
<commit_msg>Add pool error types<commit_after>extern crate r2d2;

quick_error! {
    /// The most generic error message in ReQL
    #[derive(Debug)]
    pub enum Error {
        Compile(descr: &'static str) { }
        Runtime(err: RuntimeError) { from() }
        Driver(err: DriverError) { from() }
    }
}

quick_error! {
    /// The parent class of all runtime errors
    ///
    /// All errors on the server unrelated to compilation. Programs may use this to catch any runtime
    /// error, but the server will always return a more specific error class.
    #[derive(Debug)]
    pub enum RuntimeError {
        QueryLogic(err: QueryLogicError) { from() }
        ResourceLimit(descr: &'static str) {}
        User(descr: &'static str) {}
        Internal(descr: &'static str) {}
        Timeout(descr: &'static str) {}
        Availability(err: AvailabilityError) { from() }
        Permissions(descr: &'static str) {}
    }
}

quick_error! {
    /// The query contains a logical impossibility, such as adding a number to a string.
    #[derive(Debug)]
    pub enum QueryLogicError {
        NonExistence(descr: &'static str) {}
    }
}

quick_error! {
    /// A server in the cluster is unavailable
    ///
    /// The parent class of `OpFailedError` and `OpIndeterminateError`. Programs may use this
    /// to catch any availability error, but the server will always return one of this class’s
    /// children.
    #[derive(Debug)]
    pub enum AvailabilityError {
        OpFailed(descr: &'static str) {}
        OpIndeterminate(descr: &'static str) {}
    }
}

quick_error! {
    /// An error has occurred within the driver
    ///
    /// This may be a driver bug, or it may be an unfulfillable command, such as an unserializable
    /// query.
    #[derive(Debug)]
    pub enum DriverError {
        Auth(descr: &'static str) {}
        Initialization(err: r2d2::InitializationError) { from() }
        Connection(err: ConnectionError) { from() }
    }
}

quick_error! {
    /// Connection related errors
    #[derive(Debug)]
    pub enum ConnectionError {
        PoolWrite(descr: &'static str) {}
        PoolRead(descr: &'static str) {}
    }
}

/// Converts external error to ours
impl From<r2d2::InitializationError> for Error {
    fn from(err: r2d2::InitializationError) -> Error {
        From::from(DriverError::Initialization(err))
    }
}
<|endoftext|>


<commit_before>use error::Error;
use glob::glob;
use hyper::client::HttpConnector;
use hyper::rt::{self, Future, Stream};
use hyper::{header, Body, Chunk, Client, Request, Response};
use hyper_tls::HttpsConnector;
use serde_json;
use statics::TEMP_DIRNAME;
use std;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::sync::mpsc;
use structs::{Config, Member, PublicKey, RateLimit, Repo, Sector, SectorGroup, Team};

pub struct GithubClient {
    pub conf: Config,
}

impl GithubClient {
    pub fn new(config: &Config) -> GithubClient {
        if std::env::var("SSL_CERT_FILE").is_err() {
            std::env::set_var("SSL_CERT_FILE", &config.cert_path);
        }
        GithubClient { conf: config.clone() }
    }

    fn get_cache_path(url: &str) -> std::path::PathBuf {
        let mut path = std::env::temp_dir();
        path.push(TEMP_DIRNAME);
        path.push(url);
        path
    }

    fn load_contents_from_cache(&self, url: &str) -> Result<(std::fs::Metadata, String), Error> {
        let path = Self::get_cache_path(url);
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut contents = String::new();
        f.read_to_string(&mut contents)?;
        Ok((metadata, contents))
    }

    fn store_contents_to_cache(&self, url: &str, contents: &str) -> Result<(), Error> {
        let path = Self::get_cache_path(url);
        std::fs::create_dir_all(path.parent().unwrap_or(std::path::Path::new("/")))?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write_all(contents.as_bytes())?;
        Ok(())
    }

    fn get_contents_from_url(&self, url: &str) -> Result<String, Error> {
        let mut all_contents: Vec<serde_json::value::Value> = Vec::new();
        let mut page = 1;
        loop {
            let mut new_array = self.get_contents_from_url_page(url, page)?;
            if new_array.is_empty() {
                break;
            }
            all_contents.append(&mut new_array);
            page += 1;
        }
        let contents = serde_json::ser::to_string(&all_contents)?;
        self.store_contents_to_cache(url, &contents)?;
        Ok(contents)
    }

    fn build_request(&self, url: &str) -> Result<Request<Body>, Error> {
        let token = String::from("token ") + &self.conf.token;
        Request::get(url).header(header::AUTHORIZATION, token.as_str())
                         .header(header::USER_AGENT, "sectora")
                         .body(Body::empty())
                         .map_err(Error::from)
    }

    fn build_page_request(&self, url: &str, page: u64) -> Result<Request<Body>, Error> {
        let sep = if url.contains('?') { '&' } else { '?' };
        let url_p = format!("{}{}page={}", url, sep, page);
        self.build_request(&url_p)
    }

    fn build_https_client() -> Result<Client<HttpsConnector<HttpConnector>>, Error> {
        Ok(Client::builder().build(HttpsConnector::new(4)?))
    }

    fn run_request(&self, req: Request<Body>) -> Result<Chunk, Error> {
        let concat_body = |res: Response<Body>| res.into_body().concat2();
        let (tx, rx) = mpsc::sync_channel(1);
        let ex = tx.clone();
        let send_res = move |r| tx.send(Ok(r)).expect("send response");
        let send_err = move |e| ex.send(Err(Error::from(e))).expect("send err");
        let hc = Self::build_https_client()?;
        rt::run(rt::lazy(move || hc.request(req).and_then(concat_body).map(send_res).map_err(send_err)));
        rx.recv().expect("recv response")
    }

    fn get_contents_from_url_page(&self, url: &str, page: u64) -> Result<Vec<serde_json::Value>, Error> {
        let req = self.build_page_request(url, page)?;
        self.run_request(req)
            .and_then(|body| serde_json::from_slice(&body).map_err(Error::from))
    }

    fn get_contents(&self, url: &str) -> Result<String, Error> {
        match self.load_contents_from_cache(url) {
            Ok((metadata, cache_contents)) => match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                Ok(caching_duration) => {
                    if caching_duration.as_secs() > self.conf.cache_duration {
                        match self.get_contents_from_url(url) {
                            Ok(contents_from_url) => Ok(contents_from_url),
                            Err(_) => Ok(cache_contents),
                        }
                    } else {
                        Ok(cache_contents)
                    }
                }
                Err(_) => Ok(cache_contents),
            },
            Err(_) => self.get_contents_from_url(url),
        }
    }

    #[allow(dead_code)]
    pub fn print_user_public_key(&self, user: &str) -> Result<(), Error> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user: &str) -> Result<String, Error> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let contents = self.get_contents(&url)?;
        let keys = serde_json::from_str::<Vec<PublicKey>>(&contents)?;
        Ok(keys.iter().map(|k| k.key.clone()).collect::<Vec<String>>().join("\n"))
    }

    #[allow(dead_code)]
    pub fn check_pam(&self, user: &str) -> Result<bool, Error> {
        let sectors = self.get_sectors()?;
        Ok(sectors.iter().any(|team| team.members.contains_key(user)))
    }

    pub fn get_sectors(&self) -> Result<Vec<SectorGroup>, Error> {
        let mut sectors: Vec<SectorGroup> = self.get_teams_result()?;
        sectors.append(&mut self.get_repos_result()?);
        Ok(sectors)
    }

    fn get_teams_result(&self) -> Result<Vec<SectorGroup>, Error> {
        let gh_teams = self.get_team_map()?;
        let mut teams = Vec::new();
        for team_conf in &self.conf.team {
            if let Some(gh_team) = gh_teams.get(&team_conf.name) {
                teams.push(SectorGroup { sector: Sector::from(gh_team.clone()),
                                         gid: team_conf.gid,
                                         group: team_conf.group.clone(),
                                         members: self.get_team_members(gh_team.id)? });
            }
        }
        Ok(teams)
    }

    fn get_team_map(&self) -> Result<HashMap<String, Team>, Error> {
        let url = format!("{}/orgs/{}/teams", self.conf.endpoint, self.conf.org);
        let contents = self.get_contents(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(&contents)?;
        Ok(teams.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_team_members(&self, mid: u64) -> Result<HashMap<String, Member>, Error> {
        let url = format!("{}/teams/{}/members", self.conf.endpoint, mid);
        let contents = self.get_contents(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    fn get_repos_result(&self) -> Result<Vec<SectorGroup>, Error> {
        let gh_repos = self.get_repo_map()?;
        let mut repos = Vec::new();
        for repo_conf in &self.conf.repo {
            if let Some(gh_repo) = gh_repos.get(&repo_conf.name) {
                repos.push(SectorGroup { sector: Sector::from(gh_repo.clone()),
                                         gid: repo_conf.gid,
                                         group: repo_conf.group.clone(),
                                         members: self.get_repo_collaborators(&gh_repo.name)? });
            }
        }
        Ok(repos)
    }

    fn get_repo_map(&self) -> Result<HashMap<String, Repo>, Error> {
        let url = format!("{}/orgs/{}/repos", self.conf.endpoint, self.conf.org);
        let contents = self.get_contents(&url)?;
        let repos = serde_json::from_str::<Vec<Repo>>(&contents)?;
        Ok(repos.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_repo_collaborators(&self, repo_name: &str) -> Result<HashMap<String, Member>, Error> {
        let url = format!("{}/repos/{}/{}/collaborators?affiliation=outside",
                          self.conf.endpoint, self.conf.org, repo_name);
        let contents = self.get_contents(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    fn get_rate_limit(&self) -> Result<RateLimit, Error> {
        let url = format!("{}/rate_limit", self.conf.endpoint);
        let req = self.build_request(&url)?;
        self.run_request(req)
            .and_then(|body| serde_json::from_slice(&body).map_err(Error::from))
    }

    #[allow(dead_code)]
    pub fn print_rate_limit(&self) -> Result<(), Error> {
        let rate_limit = self.get_rate_limit()?;
        println!("{:?}", rate_limit);
        Ok(())
    }

    #[allow(dead_code)]
    pub fn clear_all_caches(&self) -> Result<(), Error> {
        let mut path = std::env::temp_dir();
        path.push(TEMP_DIRNAME);
        path.push("**/*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {
                    if path.is_file() {
                        std::fs::remove_file(path)?
                    }
                }
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }
}
<commit_msg>Refactor<commit_after>use error::Error;
use glob::glob;
use hyper::client::HttpConnector;
use hyper::rt::{self, Future, Stream};
use hyper::{header, Body, Chunk, Client, Request, Response};
use hyper_tls::HttpsConnector;
use serde_json;
use statics::TEMP_DIRNAME;
use std;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::sync::mpsc;
use structs::{Config, Member, PublicKey, RateLimit, Repo, Sector, SectorGroup, Team};

pub struct GithubClient {
    pub conf: Config,
}

impl GithubClient {
    pub fn new(config: &Config) -> GithubClient {
        if std::env::var("SSL_CERT_FILE").is_err() {
            std::env::set_var("SSL_CERT_FILE", &config.cert_path);
        }
        GithubClient { conf: config.clone() }
    }

    fn get_cache_path(url: &str) -> std::path::PathBuf {
        let mut path = std::env::temp_dir();
        path.push(TEMP_DIRNAME);
        path.push(url);
        path
    }

    fn load_contents_from_cache(&self, url: &str) -> Result<(std::fs::Metadata, String), Error> {
        let path = Self::get_cache_path(url);
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut contents = String::new();
        f.read_to_string(&mut contents)?;
        Ok((metadata, contents))
    }

    fn store_contents_to_cache(&self, url: &str, contents: &str) -> Result<(), Error> {
        let path = Self::get_cache_path(url);
        std::fs::create_dir_all(path.parent().unwrap_or(std::path::Path::new("/")))?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write_all(contents.as_bytes())?;
        Ok(())
    }

    fn get_contents_from_url(&self, url: &str) -> Result<String, Error> {
        let mut all_contents: Vec<serde_json::value::Value> = Vec::new();
        let mut page = 1;
        loop {
            let mut new_array = self.get_contents_from_url_page(url, page)?;
            if new_array.is_empty() {
                break;
            }
            all_contents.append(&mut new_array);
            page += 1;
        }
        let contents = serde_json::ser::to_string(&all_contents)?;
        self.store_contents_to_cache(url, &contents)?;
        Ok(contents)
    }

    fn build_request(&self, url: &str) -> Result<Request<Body>, Error> {
        let token = String::from("token ") + &self.conf.token;
        Request::get(url).header(header::AUTHORIZATION, token.as_str())
                         .header(header::USER_AGENT, "sectora")
                         .body(Body::empty())
                         .map_err(Error::from)
    }

    fn build_page_request(&self, url: &str, page: u64) -> Result<Request<Body>, Error> {
        let sep = if url.contains('?') { '&' } else { '?' };
        let url_p = format!("{}{}page={}", url, sep, page);
        self.build_request(&url_p)
    }

    fn build_https_client() -> Result<Client<HttpsConnector<HttpConnector>>, Error> {
        Ok(Client::builder().build(HttpsConnector::new(4)?))
    }

    fn run_request(&self, req: Request<Body>) -> Result<Chunk, Error> {
        let concat_body = |res: Response<Body>| res.into_body().concat2();
        let (tx, rx) = mpsc::sync_channel(1);
        let ex = tx.clone();
        let send_res = move |r| tx.send(Ok(r)).expect("send response");
        let send_err = move |e| ex.send(Err(Error::from(e))).expect("send err");
        let hc = Self::build_https_client()?;
        rt::run(rt::lazy(move || hc.request(req).and_then(concat_body).map(send_res).map_err(send_err)));
        rx.recv().expect("recv response")
    }

    fn get_contents_from_url_page(&self, url: &str, page: u64) -> Result<Vec<serde_json::Value>, Error> {
        let req = self.build_page_request(url, page)?;
        self.run_request(req)
            .and_then(|body| serde_json::from_slice(&body).map_err(Error::from))
    }

    fn get_contents(&self, url: &str) -> Result<String, Error> {
        match self.load_contents_from_cache(url) {
            Ok((metadata, cache_contents)) => match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                Ok(caching_duration) => {
                    if caching_duration.as_secs() > self.conf.cache_duration {
                        match self.get_contents_from_url(url) {
                            Ok(contents_from_url) => Ok(contents_from_url),
                            Err(_) => Ok(cache_contents),
                        }
                    } else {
                        Ok(cache_contents)
                    }
                }
                Err(_) => Ok(cache_contents),
            },
            Err(_) => self.get_contents_from_url(url),
        }
    }

    #[allow(dead_code)]
    pub fn print_user_public_key(&self, user: &str) -> Result<(), Error> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user: &str) -> Result<String, Error> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let contents = self.get_contents(&url)?;
        let keys = serde_json::from_str::<Vec<PublicKey>>(&contents)?;
        Ok(keys.iter().map(|k| k.key.clone()).collect::<Vec<String>>().join("\n"))
    }

    #[allow(dead_code)]
    pub fn check_pam(&self, user: &str) -> Result<bool, Error> {
        let sectors = self.get_sectors()?;
        Ok(sectors.iter().any(|team| team.members.contains_key(user)))
    }

    pub fn get_sectors(&self) -> Result<Vec<SectorGroup>, Error> {
        let mut sectors: Vec<SectorGroup> = self.get_teams_result()?;
        sectors.append(&mut self.get_repos_result()?);
        Ok(sectors)
    }

    fn get_teams_result(&self) -> Result<Vec<SectorGroup>, Error> {
        let gh_teams = self.get_team_map(&self.conf.org)?;
        let mut teams = Vec::new();
        for team_conf in &self.conf.team {
            if let Some(gh_team) = gh_teams.get(&team_conf.name) {
                teams.push(SectorGroup { sector: Sector::from(gh_team.clone()),
                                         gid: team_conf.gid,
                                         group: team_conf.group.clone(),
                                         members: self.get_team_members(gh_team.id)? });
            }
        }
        Ok(teams)
    }

    fn get_team_map(&self, org: &str) -> Result<HashMap<String, Team>, Error> {
        let url = format!("{}/orgs/{}/teams", self.conf.endpoint, org);
        let contents = self.get_contents(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(&contents)?;
        Ok(teams.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_team_members(&self, mid: u64) -> Result<HashMap<String, Member>, Error> {
        let url = format!("{}/teams/{}/members", self.conf.endpoint, mid);
        let contents = self.get_contents(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    fn get_repos_result(&self) -> Result<Vec<SectorGroup>, Error> {
        let gh_repos = self.get_repo_map(&self.conf.org)?;
        let mut repos = Vec::new();
        for repo_conf in &self.conf.repo {
            if let Some(gh_repo) = gh_repos.get(&repo_conf.name) {
                repos.push(SectorGroup { sector: Sector::from(gh_repo.clone()),
                                         gid: repo_conf.gid,
                                         group: repo_conf.group.clone(),
                                         members: self.get_repo_collaborators(&gh_repo.name)? });
            }
        }
        Ok(repos)
    }

    fn get_repo_map(&self, org: &str) -> Result<HashMap<String, Repo>, Error> {
        let url = format!("{}/orgs/{}/repos", self.conf.endpoint, org);
        let contents = self.get_contents(&url)?;
        let repos = serde_json::from_str::<Vec<Repo>>(&contents)?;
        Ok(repos.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    fn get_repo_collaborators(&self, repo_name: &str) -> Result<HashMap<String, Member>, Error> {
        let url = format!("{}/repos/{}/{}/collaborators?affiliation=outside",
                          self.conf.endpoint, self.conf.org, repo_name);
        let contents = self.get_contents(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    fn get_rate_limit(&self) -> Result<RateLimit, Error> {
        let url = format!("{}/rate_limit", self.conf.endpoint);
        let req = self.build_request(&url)?;
        self.run_request(req)
            .and_then(|body| serde_json::from_slice(&body).map_err(Error::from))
    }

    #[allow(dead_code)]
    pub fn print_rate_limit(&self) -> Result<(), Error> {
        let rate_limit = self.get_rate_limit()?;
        println!("{:?}", rate_limit);
        Ok(())
    }

    #[allow(dead_code)]
    pub fn clear_all_caches(&self) -> Result<(), Error> {
        let mut path = std::env::temp_dir();
        path.push(TEMP_DIRNAME);
        path.push("**/*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {
                    if path.is_file() {
                        std::fs::remove_file(path)?
                    }
                }
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std;
pub fn prime_filter(iter_size: usize) -> std::vec::Vec<bool>{
    //Sieve of Atkin
    if iter_size < 100 {
        slow_prime_filter(iter_size)
    }else{
        let mut prime_filter = vec![false; iter_size];
        prime_filter[2] = true;
        prime_filter[3] = true;
        prime_filter[5] = true;
        let mut y_sq = 1;
        let mut to_next_y_sq = 3;
        while y_sq<iter_size {
            if y_sq%2 == 1 {
                //n_1 = 4x^2 + y^2 === 1 (mod 4)
                let (mut n_1, mut to_next_n_1) = (y_sq, 4);
                loop{
                    n_1 += to_next_n_1;
                    to_next_n_1 += 8;
                    match n_1%60{
                        _ if n_1 >= iter_size => break,
                        1 | 13 | 17 | 29 | 37 | 41 | 49 | 53 => (),
                        _ => continue,
                    };

                    // println!("1: {}", n_1);
                    prime_filter[n_1] ^= true;
                };
            };
            if y_sq%3 == 1 {
                //n_2 = 3x^2 + y^2 === 1 (mod 6)
                let (mut n_2, mut to_next_n_2) = (y_sq, 3);
                loop {
                    n_2 += to_next_n_2;
                    to_next_n_2 += 6;
                    match n_2%60{
                        _ if n_2 >= iter_size => break,
                        7 | 19 | 31 | 43 => (),
                        _ => continue,
                    };
                    // println!("2: {}", n_2);
                    prime_filter[n_2] ^= true;
                };
                //n_3 = 3x^2 - y^2 === 11 (mod 12)
                let (mut n_3, mut to_next_n_3) = (2*y_sq, 3*to_next_y_sq);
                loop {
                    n_3 += to_next_n_3;
                    to_next_n_3 += 6;
                    match n_3%60{
                        _ if n_3 >= iter_size => break,
                        11 | 23 | 47 | 59 => (),
                        _ => continue,
                    };
                    // println!("3: {}", n_3);
                    prime_filter[n_3] ^= true;
                };
            };
            while{ //Do-while
                y_sq += to_next_y_sq;
                to_next_y_sq += 2;
                y_sq%6 == 0
            } {};
        };
        //Eliminate non-squarefree numbers
        let mut n_sq = 49; // 7^2
        let mut next_n_sq = 32; //9^2 - 7^2, skip even numbers.
        while n_sq < iter_size {
            let mut non_sq_free = n_sq;
            while non_sq_free < iter_size {
                prime_filter[non_sq_free] = false;
                while{ //Do-while
                    non_sq_free += n_sq + n_sq;
                    (non_sq_free%3==0) | (non_sq_free%5==0)
                } {};
            };
            while{ //Do-while
                n_sq += next_n_sq;
                next_n_sq += 8;
                (n_sq%3==0) | (n_sq%5 == 0)
            } {};
        }
        prime_filter
    }
}

#[cfg(test)]
pub fn old_prime_filter(iter_size: usize) -> std::vec::Vec<bool>{
    slow_prime_filter(iter_size)
}

fn slow_prime_filter(iter_size: usize) -> std::vec::Vec<bool>{
    if iter_size < 5 {
         let mut ret = vec![false, false, true, true];
         ret.truncate(iter_size);
         return ret
     }
    let mut prime_filter = vec![true; iter_size];
    prime_filter[0] = false;
    prime_filter[1] = false;
    let mut cur_num = 2;
    'outer: loop{
        for i in (cur_num+1)..iter_size{
            if 0 == i%cur_num { prime_filter[i] = false; }
        }
        cur_num += 1;
        while !prime_filter[cur_num]{
            if cur_num*cur_num > iter_size {
                break 'outer
            }
            cur_num += 1;
        }
    };
    prime_filter
}
<commit_msg>Reorganized prime_filter for future stuff<commit_after>use std;
pub fn prime_filter(iter_size: usize) -> Vec<bool>{
    if iter_size<100{
        slow_prime_filter(iter_size)
    }else{
        prime_filter_section(0, iter_size)
    }
}
fn prime_filter_section(min:usize, max: usize) -> Vec<bool>{
    //Sieve of Atkin
    assert!(min<max);
    let mut prime_filter = vec![false; max-min];
    if (min <= 2) & (max > 2) {prime_filter[2-min] = true;}
    if (min <= 3) & (max > 3) {prime_filter[3-min] = true;}
    if (min <= 5) & (max > 5) {prime_filter[5-min] = true;}

    let (mut y_sq, mut to_next_y_sq) = match min {
        0|1 => (1, 3),
        _ => (min*min, 2*min + 1),
    };
    while y_sq<max {
        if y_sq%2 == 1 {
            //n_1 = 4x^2 + y^2 === 1 (mod 4)
            let (mut n_1, mut to_next_n_1) = (y_sq, 4);
            loop{
                n_1 += to_next_n_1;
                to_next_n_1 += 8;
                match n_1%60{
                    _ if n_1 >= max => break,
                    1 | 13 | 17 | 29 | 37 | 41 | 49 | 53 => (),
                    _ => continue,
                };

                // println!("1: {}", n_1);
                prime_filter[n_1 - min] ^= true;
            };
        };
        if y_sq%3 == 1 {
            //n_2 = 3x^2 + y^2 === 1 (mod 6)
            let (mut n_2, mut to_next_n_2) = (y_sq, 3);
            loop {
                n_2 += to_next_n_2;
                to_next_n_2 += 6;
                match n_2%60{
                    _ if n_2 >= max => break,
                    7 | 19 | 31 | 43 => (),
                    _ => continue,
                };
                // println!("2: {}", n_2);
                prime_filter[n_2 - min] ^= true;
            };
            //n_3 = 3x^2 - y^2 === 11 (mod 12)
            let (mut n_3, mut to_next_n_3) = (2*y_sq, 3*to_next_y_sq);
            loop {
                n_3 += to_next_n_3;
                to_next_n_3 += 6;
                match n_3%60{
                    _ if n_3 >= max => break,
                    11 | 23 | 47 | 59 => (),
                    _ => continue,
                };
                // println!("3: {}", n_3);
                prime_filter[n_3 - min] ^= true;
            };
        };
        while{ //Do-while
            y_sq += to_next_y_sq;
            to_next_y_sq += 2;
            y_sq%6 == 0
        } {};
    };
    //Eliminate non-squarefree numbers
    let mut n_sq = 49; // 7^2
    let mut next_n_sq = 32; //9^2 - 7^2, skip even numbers.
    while n_sq < max {
        let mut non_sq_free = n_sq;
        while non_sq_free < max {
            prime_filter[non_sq_free] = false;
            while{ //Do-while
                non_sq_free += n_sq + n_sq;
                (non_sq_free%3==0) | (non_sq_free%5==0)
            } {};
        };
        while{ //Do-while
            n_sq += next_n_sq;
            next_n_sq += 8;
            (n_sq%3==0) | (n_sq%5 == 0)
        } {};
    }
    prime_filter
}

#[cfg(test)]
pub fn old_prime_filter(iter_size: usize) -> std::vec::Vec<bool>{
    slow_prime_filter(iter_size)
}

fn slow_prime_filter(iter_size: usize) -> std::vec::Vec<bool>{
    if iter_size < 5 {
         let mut ret = vec![false, false, true, true];
         ret.truncate(iter_size);
         return ret
     }
    let mut prime_filter = vec![true; iter_size];
    prime_filter[0] = false;
    prime_filter[1] = false;
    let mut cur_num = 2;
    'outer: loop{
        for i in (cur_num+1)..iter_size{
            if 0 == i%cur_num { prime_filter[i] = false; }
        }
        cur_num += 1;
        while !prime_filter[cur_num]{
            if cur_num*cur_num > iter_size {
                break 'outer
            }
            cur_num += 1;
        }
    };
    prime_filter
}
<|endoftext|>


<commit_before>use std::io::fs;

pub struct Folder {
    pub name: String,
    pub cur: Vec<Path>,
    pub new: Vec<Path>,
    pub tmp: Vec<Path>
}

macro_rules! make_vec_path(
    ($path:ident, $inp:ident) => {
        match fs::readdir(&($path.join("$inp"))) {
            Err(_) => { return None; }
            Ok(res) => { $inp = res; }
        }
    };
)

impl Folder {
    pub fn new(name: String, path_str: String) -> Option<Folder> {
        let path = Path::new(path_str);
        let mut cur = Vec::new();
        make_vec_path!(path, cur);
        let mut new = Vec::new();
        make_vec_path!(path, new);
        let mut tmp = Vec::new();
        make_vec_path!(path, tmp);
        return Some(Folder {
            name: name,
            cur: cur,
            new: new,
            tmp: tmp
        });
    }
}
<commit_msg>Macro abuse to make rustc STFU.<commit_after>use std::io::fs;

pub struct Folder {
    pub name: String,
    pub cur: Vec<Path>,
    pub new: Vec<Path>,
    pub tmp: Vec<Path>
}

macro_rules! make_vec_path(
    ($path:ident, $inp:ident, $next:expr) => ({
        match fs::readdir(&($path.join("$inp"))) {
            Err(_) => { return None; }
            Ok(res) => {
                let $inp = res;
                $next
            }
        }
    });
)

impl Folder {
    pub fn new(name: String, path_str: String) -> Option<Folder> {
        let path = Path::new(path_str);
        make_vec_path!(path, cur,
            make_vec_path!(path, new,
                make_vec_path!(path, tmp,
                    return Some(Folder {
                        name: name,
                        cur: cur,
                        new: new,
                        tmp: tmp
                    })
                )
            )
        );
    }
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2016, Boris Popov <popov@whitekefir.ru>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//

pub fn hello() {

}
<commit_msg>add mod forker<commit_after>//
// Copyright (c) 2016, Boris Popov <popov@whitekefir.ru>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//

extern crate libc;

use self::libc::fork;
use worker;

pub struct Forker {
}

impl Forker {
    pub fn new() -> Forker {
        Forker{}
    }

    pub fn spawn(&self) {
        let pid: i32;
        unsafe {
            pid = fork();
        }

        if pid < 0 {
            panic!("Can't fork!");
        }

        if pid != 0 {
            return;
        }

        self.main();
    }

    fn main(&self) {
        worker::main();
    }
}
<|endoftext|>


<commit_before>//! The module implements the client side of the HTTP/2 protocol and exposes
//! an API for using it.
use std::io;
use std::convert::From;
use std::error::Error;

use hpack::decoder::DecoderError;

pub mod frame;
pub mod transport;
pub mod connection;
pub mod session;

/// An alias for the type that represents the ID of an HTTP/2 stream
pub type StreamId = u32;
/// An alias for the type that represents HTTP/2 haders. For now we only alias
/// the tuple of byte vectors instead of going with a full struct representation.
pub type Header = (Vec<u8>, Vec<u8>);

/// An enum representing errors that can arise when performing operations
/// involving an HTTP/2 connection.
#[derive(Debug)]
pub enum HttpError {
    IoError(io::Error),
    UnknownFrameType,
    InvalidFrame,
    CompressionError(DecoderError),
    UnknownStreamId,
    UnableToConnect,
    MalformedResponse,
}

/// Implement the trait that allows us to automatically convert `io::Error`s
/// into an `HttpError` by wrapping the given `io::Error` into an `HttpError::IoError` variant.
impl From<io::Error> for HttpError {
    fn from(err: io::Error) -> HttpError {
        HttpError::IoError(err)
    }
}

/// Implementation of the `PartialEq` trait as a convenience for tests.
#[cfg(test)]
impl PartialEq for HttpError {
    fn eq(&self, other: &HttpError) -> bool {
        match (self, other) {
            (&HttpError::IoError(ref e1), &HttpError::IoError(ref e2)) => {
                e1.kind() == e2.kind() && e1.description() == e2.description()
            },
            (&HttpError::UnknownFrameType, &HttpError::UnknownFrameType) => true,
            (&HttpError::InvalidFrame, &HttpError::InvalidFrame) => true,
            (&HttpError::CompressionError(ref e1), &HttpError::CompressionError(ref e2)) => {
                e1 == e2
            },
            (&HttpError::UnknownStreamId, &HttpError::UnknownStreamId) => true,
            (&HttpError::UnableToConnect, &HttpError::UnableToConnect) => true,
            (&HttpError::MalformedResponse, &HttpError::MalformedResponse) => true,
            _ => false,
        }
    }
}

/// A convenience `Result` type that has the `HttpError` type as the error
/// type and a generic Ok result type.
pub type HttpResult<T> = Result<T, HttpError>;

/// An enum representing the two possible HTTP schemes.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum HttpScheme {
    /// The variant corresponding to `http://`
    Http,
    /// The variant corresponding to `https://`
    Https,
}

impl HttpScheme {
    /// Returns a byte string representing the scheme.
    #[inline]
    pub fn as_bytes(&self) -> &'static [u8] {
        match *self {
            HttpScheme::Http => b"http",
            HttpScheme::Https => b"https",
        }
    }
}

/// A struct representing the full raw response received on an HTTP/2 connection.
///
/// The full body of the response is included, regardless how large it may be.
/// The headers contain both the meta-headers, as well as the actual headers.
#[derive(Clone)]
pub struct Response {
    /// The ID of the stream to which the response is associated. HTTP/1.1 does
    /// not really have an equivalent to this.
    pub stream_id: StreamId,
    /// Exposes *all* the raw response headers, including the meta-headers.
    /// (For now the only meta header allowed in HTTP/2 responses is the
    /// `:status`.)
    pub headers: Vec<Header>,
    /// The full body of the response as an uninterpreted sequence of bytes.
    pub body: Vec<u8>,
}

impl Response {
    /// Creates a new `Response` with all the components already provided.
    pub fn new(stream_id: StreamId, headers: Vec<Header>, body: Vec<u8>)
            -> Response {
        Response {
            stream_id: stream_id,
            headers: headers,
            body: body,
        }
    }

    /// Gets the response status code from the pseudo-header. If the response
    /// does not contain the response as the first pseuo-header, an error is
    /// returned as such a response is malformed.
    pub fn status_code(&self) -> HttpResult<u16> {
        // Since pseudo-headers MUST be found before any regular header fields
        // and the *only* pseudo-header defined for responses is the `:status`
        // field, the `:status` MUST be the first header; otherwise, the
        // response is malformed.
        if self.headers.len() < 1 {
            return Err(HttpError::MalformedResponse)
        }
        if &self.headers[0].0 != &b":status" {
            Err(HttpError::MalformedResponse)
        } else {
            Ok(try!(Response::parse_status_code(&self.headers[0].1)))
        }
    }

    /// A helper function that parses a given buffer as a status code and
    /// returns it as a `u16`, if it is valid.
    fn parse_status_code(buf: &[u8]) -> HttpResult<u16> {
        // "The status-code element is a three-digit integer code [...]"
        if buf.len() != 3 {
            return Err(HttpError::MalformedResponse);
        }

        // "There are five values for the first digit"
        if buf[0] < b'1' || buf[0] > b'5' {
            return Err(HttpError::MalformedResponse);
        }

        // The rest of them just have to be digits
        if buf[1] < b'0' || buf[1] > b'9' || buf[2] < b'0' || buf[2] > b'9' {
            return Err(HttpError::MalformedResponse);
        }

        // Finally, we can merge them into an integer
        Ok(100 * ((buf[0] - b'0') as u16) +
           10 * ((buf[1] - b'0') as u16) +
           1 * ((buf[2] - b'0') as u16))
    }
}

/// A struct representing a full HTTP/2 request, along with the full body, as a
/// sequence of bytes.
#[derive(Clone)]
pub struct Request {
    pub stream_id: u32,
    pub headers: Vec<Header>,
    pub body: Vec<u8>,
}

#[cfg(test)]
mod tests {
    use super::{Response, HttpError, HttpScheme};

    /// Tests that the `Response` struct correctly parses a status code from
    /// its headers list.
    #[test]
    fn test_parse_status_code_response() {
        {
            // Only status => Ok
            let resp = Response::new(
                1,
                vec![(b":status".to_vec(), b"200".to_vec())],
                vec![]);
            assert_eq!(resp.status_code().ok().unwrap(), 200);
        }
        {
            // Extra headers => still works
            let resp = Response::new(
                1,
                vec![(b":status".to_vec(), b"200".to_vec()),
                     (b"key".to_vec(), b"val".to_vec())],
                vec![]);
            assert_eq!(resp.status_code().ok().unwrap(), 200);
        }
        {
            // Status is not the first header => malformed
            let resp = Response::new(
                1,
                vec![(b"key".to_vec(), b"val".to_vec()),
                     (b":status".to_vec(), b"200".to_vec())],
                vec![]);
            assert_eq!(resp.status_code().err().unwrap(),
                       HttpError::MalformedResponse);
        }
        {
            // No headers at all => Malformed
            let resp = Response::new(1, vec![], vec![]);
            assert_eq!(resp.status_code().err().unwrap(),
                       HttpError::MalformedResponse);
        }
    }

    /// Tests that the `HttpScheme` enum returns the correct scheme strings for
    /// the two variants.
    #[test]
    fn test_scheme_string() {
        assert_eq!(HttpScheme::Http.as_bytes(), b"http");
        assert_eq!(HttpScheme::Https.as_bytes(), b"https");
    }
}
<commit_msg>Add a list of ALPN protocol names for HTTP/2<commit_after>//! The module implements the client side of the HTTP/2 protocol and exposes
//! an API for using it.
use std::io;
use std::convert::From;
use std::error::Error;

use hpack::decoder::DecoderError;

pub mod frame;
pub mod transport;
pub mod connection;
pub mod session;

/// An alias for the type that represents the ID of an HTTP/2 stream
pub type StreamId = u32;
/// An alias for the type that represents HTTP/2 haders. For now we only alias
/// the tuple of byte vectors instead of going with a full struct representation.
pub type Header = (Vec<u8>, Vec<u8>);

/// A set of protocol names that the library should use to indicate that HTTP/2
/// is supported during protocol negotiation (NPN or ALPN).
/// We include some of the drafts' protocol names, since there is basically no
/// difference for all intents and purposes (and some servers out there still
/// only officially advertise draft support).
/// TODO: Eventually only use "h2".
pub const ALPN_PROTOCOLS: &'static [&'static [u8]] = &[
    b"h2",
    b"h2-16",
    b"h2-15",
    b"h2-14",
];

/// An enum representing errors that can arise when performing operations
/// involving an HTTP/2 connection.
#[derive(Debug)]
pub enum HttpError {
    IoError(io::Error),
    UnknownFrameType,
    InvalidFrame,
    CompressionError(DecoderError),
    UnknownStreamId,
    UnableToConnect,
    MalformedResponse,
}

/// Implement the trait that allows us to automatically convert `io::Error`s
/// into an `HttpError` by wrapping the given `io::Error` into an `HttpError::IoError` variant.
impl From<io::Error> for HttpError {
    fn from(err: io::Error) -> HttpError {
        HttpError::IoError(err)
    }
}

/// Implementation of the `PartialEq` trait as a convenience for tests.
#[cfg(test)]
impl PartialEq for HttpError {
    fn eq(&self, other: &HttpError) -> bool {
        match (self, other) {
            (&HttpError::IoError(ref e1), &HttpError::IoError(ref e2)) => {
                e1.kind() == e2.kind() && e1.description() == e2.description()
            },
            (&HttpError::UnknownFrameType, &HttpError::UnknownFrameType) => true,
            (&HttpError::InvalidFrame, &HttpError::InvalidFrame) => true,
            (&HttpError::CompressionError(ref e1), &HttpError::CompressionError(ref e2)) => {
                e1 == e2
            },
            (&HttpError::UnknownStreamId, &HttpError::UnknownStreamId) => true,
            (&HttpError::UnableToConnect, &HttpError::UnableToConnect) => true,
            (&HttpError::MalformedResponse, &HttpError::MalformedResponse) => true,
            _ => false,
        }
    }
}

/// A convenience `Result` type that has the `HttpError` type as the error
/// type and a generic Ok result type.
pub type HttpResult<T> = Result<T, HttpError>;

/// An enum representing the two possible HTTP schemes.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum HttpScheme {
    /// The variant corresponding to `http://`
    Http,
    /// The variant corresponding to `https://`
    Https,
}

impl HttpScheme {
    /// Returns a byte string representing the scheme.
    #[inline]
    pub fn as_bytes(&self) -> &'static [u8] {
        match *self {
            HttpScheme::Http => b"http",
            HttpScheme::Https => b"https",
        }
    }
}

/// A struct representing the full raw response received on an HTTP/2 connection.
///
/// The full body of the response is included, regardless how large it may be.
/// The headers contain both the meta-headers, as well as the actual headers.
#[derive(Clone)]
pub struct Response {
    /// The ID of the stream to which the response is associated. HTTP/1.1 does
    /// not really have an equivalent to this.
    pub stream_id: StreamId,
    /// Exposes *all* the raw response headers, including the meta-headers.
    /// (For now the only meta header allowed in HTTP/2 responses is the
    /// `:status`.)
    pub headers: Vec<Header>,
    /// The full body of the response as an uninterpreted sequence of bytes.
    pub body: Vec<u8>,
}

impl Response {
    /// Creates a new `Response` with all the components already provided.
    pub fn new(stream_id: StreamId, headers: Vec<Header>, body: Vec<u8>)
            -> Response {
        Response {
            stream_id: stream_id,
            headers: headers,
            body: body,
        }
    }

    /// Gets the response status code from the pseudo-header. If the response
    /// does not contain the response as the first pseuo-header, an error is
    /// returned as such a response is malformed.
    pub fn status_code(&self) -> HttpResult<u16> {
        // Since pseudo-headers MUST be found before any regular header fields
        // and the *only* pseudo-header defined for responses is the `:status`
        // field, the `:status` MUST be the first header; otherwise, the
        // response is malformed.
        if self.headers.len() < 1 {
            return Err(HttpError::MalformedResponse)
        }
        if &self.headers[0].0 != &b":status" {
            Err(HttpError::MalformedResponse)
        } else {
            Ok(try!(Response::parse_status_code(&self.headers[0].1)))
        }
    }

    /// A helper function that parses a given buffer as a status code and
    /// returns it as a `u16`, if it is valid.
    fn parse_status_code(buf: &[u8]) -> HttpResult<u16> {
        // "The status-code element is a three-digit integer code [...]"
        if buf.len() != 3 {
            return Err(HttpError::MalformedResponse);
        }

        // "There are five values for the first digit"
        if buf[0] < b'1' || buf[0] > b'5' {
            return Err(HttpError::MalformedResponse);
        }

        // The rest of them just have to be digits
        if buf[1] < b'0' || buf[1] > b'9' || buf[2] < b'0' || buf[2] > b'9' {
            return Err(HttpError::MalformedResponse);
        }

        // Finally, we can merge them into an integer
        Ok(100 * ((buf[0] - b'0') as u16) +
           10 * ((buf[1] - b'0') as u16) +
           1 * ((buf[2] - b'0') as u16))
    }
}

/// A struct representing a full HTTP/2 request, along with the full body, as a
/// sequence of bytes.
#[derive(Clone)]
pub struct Request {
    pub stream_id: u32,
    pub headers: Vec<Header>,
    pub body: Vec<u8>,
}

#[cfg(test)]
mod tests {
    use super::{Response, HttpError, HttpScheme};

    /// Tests that the `Response` struct correctly parses a status code from
    /// its headers list.
    #[test]
    fn test_parse_status_code_response() {
        {
            // Only status => Ok
            let resp = Response::new(
                1,
                vec![(b":status".to_vec(), b"200".to_vec())],
                vec![]);
            assert_eq!(resp.status_code().ok().unwrap(), 200);
        }
        {
            // Extra headers => still works
            let resp = Response::new(
                1,
                vec![(b":status".to_vec(), b"200".to_vec()),
                     (b"key".to_vec(), b"val".to_vec())],
                vec![]);
            assert_eq!(resp.status_code().ok().unwrap(), 200);
        }
        {
            // Status is not the first header => malformed
            let resp = Response::new(
                1,
                vec![(b"key".to_vec(), b"val".to_vec()),
                     (b":status".to_vec(), b"200".to_vec())],
                vec![]);
            assert_eq!(resp.status_code().err().unwrap(),
                       HttpError::MalformedResponse);
        }
        {
            // No headers at all => Malformed
            let resp = Response::new(1, vec![], vec![]);
            assert_eq!(resp.status_code().err().unwrap(),
                       HttpError::MalformedResponse);
        }
    }

    /// Tests that the `HttpScheme` enum returns the correct scheme strings for
    /// the two variants.
    #[test]
    fn test_scheme_string() {
        assert_eq!(HttpScheme::Http.as_bytes(), b"http");
        assert_eq!(HttpScheme::Https.as_bytes(), b"https");
    }
}
<|endoftext|>


<commit_before>//! This module contains the pieces for parsing Multiboot headers.

use core::convert::TryInto;

pub const MULTIBOOT_HEADER_MAGIC: u32 = 0x1BADB002;

/// Multiboot struct bootloaders mainly interact with
// TODO: the derived Debug implementation is not good
#[derive(Debug, Copy, Clone)]
pub struct Header {
    header: MultibootHeader,
    /// the index at which the header starts
    pub header_start: u32,
}

/// Representation of a Multiboot header according to specification.
///
/// <rawtext>
///          +-------------------+
/// 0        | magic             |    (required)
/// 4        | flags             |    (required)
/// 8        | checksum          |    (required)
///          +-------------------+
/// 12       | header_addr       |    (present if flags[16] is set)
/// 16       | load_addr         |    (present if flags[16] is set)
/// 20       | load_end_addr     |    (present if flags[16] is set)
/// 24       | bss_end_addr      |    (present if flags[16] is set)
/// 28       | entry_addr        |    (present if flags[16] is set)
///          +-------------------+
/// 32       | mode_type         |    (present if flags[2] is set)
/// 36       | width             |    (present if flags[2] is set)
/// 40       | height            |    (present if flags[2] is set)
/// 44       | depth             |    (present if flags[2] is set)
///          +-------------------+
/// </rawtext>
#[repr(C, packed)]
#[derive(Debug, Copy, Clone)]
struct MultibootHeader {
    magic: u32,
    flags: u32,
    checksum: u32,
    addresses: MultibootAddresses,
    video_mode: MultibootVideoMode,
}

impl Header {
    /// Get the header by parsing it from a slice.
    ///
    /// The needed portion of the slice is copied.
    pub fn from_slice(buffer: &[u8]) -> Option<Self> {
        // first, find the header
        let (header, header_start) = Self::find_header(buffer)?;
        // then check that it's valid
        assert_eq!(header.magic, MULTIBOOT_HEADER_MAGIC);
        assert_eq!(header.magic.wrapping_add(header.flags).wrapping_add(header.checksum), 0);
        // finally, return it
        Some(Self { header, header_start })
    }

    /// Find the header and copy it from a given slice.
    fn find_header(buffer: &[u8]) -> Option<(MultibootHeader, u32)> {
        // the magic is 32 bit aligned and inside the first 8192 bytes
        assert!(buffer.len() >= 8192);
        let magic_index = match buffer[0..8192]
        .chunks_exact(4).position(|vals|
            u32::from_le_bytes(vals.try_into().unwrap()) // yes, there's 4 bytes here
            == MULTIBOOT_HEADER_MAGIC
        ) {
            Some(idx) => idx * 4,
            None => return None
        };
        const HEADER_SIZE: usize = core::mem::size_of::<MultibootHeader>();
        // TryInto only works for lengths <= 32, so let's copy the stuff :(
        let mut header_bytes: [u8; HEADER_SIZE] = [0; HEADER_SIZE];
        buffer.iter().skip(magic_index).zip(header_bytes.iter_mut()).for_each(|(&buf, arr)| {
            *arr = buf;
        });
        let header = unsafe {core::mem::transmute::<[u8; HEADER_SIZE], MultibootHeader>(header_bytes)};
        Some((header, magic_index as u32))
    }

    check_flag!(
        doc = "If true, then the modules have to be page aligned.",
        wants_modules_page_aligned,
        0
    );
    check_flag!(
        doc = "If true, memory information must be passed.",
        wants_memory_information,
        1
    );
    check_flag!(
        doc = "If true, then the `mode_type`, `width`, `height` and `depth` fields are valid and video information has to be passed.",
        has_video_mode,
        2
    );
    check_flag!(
        doc = "If true, then the `header_addr`, `load_addr`, `load_end_addr`, `bss_end_addr` and `entry_addr` fields are valid.",
        has_multiboot_addresses,
        16
    );

    /// Get the load addresses specified in the Multiboot header.
    ///
    /// If this function returns `None` the binary has to be loaded as an ELF instead.
    pub fn get_addresses(&self) -> Option<MultibootAddresses> {
        if self.has_multiboot_addresses() {
            assert!(self.header.addresses.load_address <= self.header.addresses.header_address);
            Some(self.header.addresses)
        } else {
            None
        }
    }

    /// Get the preferred video mode specified in the Multiboot header.
    ///
    /// If this function returns `None` the binary has to be loaded as an ELF instead.
    pub fn get_preferred_video_mode(&self) -> Option<MultibootVideoMode> {
        if self.has_video_mode() {
            Some(self.header.video_mode)
        } else {
            None
        }
    }
}

/// Addresses specified in the Multiboot header
#[derive(Debug, Copy, Clone)]
#[repr(C, packed)]
pub struct MultibootAddresses {
    pub header_address: u32,
    pub load_address: u32,
    pub load_end_address: u32,
    pub bss_end_address: u32,
    pub entry_address: u32,
}

impl MultibootAddresses {
    /// Compute the offset of the load address into the binary.
    ///
    /// Multiboot 0.6.96: section "3.1.3 The address fields of Multiboot header" says
    /// this is the "offset at which the header was found, minus (header_addr - load_addr)".
    pub fn compute_load_offset(&self, header_start: u32) -> u32 {
        header_start - (self.header_address - self.load_address)
    }
}

/// Preferred video mode specified in the Multiboot header
#[derive(Debug, Copy, Clone)]
#[repr(C, packed)]
pub struct MultibootVideoMode {
    mode_type: u32,
    pub width: u32,
    pub height: u32,
    depth: u32,
}

impl MultibootVideoMode {
    /// Get the preferred video mode type
    pub fn mode_type(&self) -> Option<VideoModeType> {
        match self.mode_type {
            0 => Some(VideoModeType::LinearGraphics),
            1 => Some(VideoModeType::TextMode),
            _ => None,
        }
    }

    /// Get the preferred depth, if possible.
    pub fn depth(&self) -> Option<u32> {
        // Only a pixel-based mode has a depth.
        if self.mode_type() == Some(VideoModeType::LinearGraphics) {
            Some(self.depth)
        } else {
            None
        }
    }
}

/// Preferred video mode type
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum VideoModeType {
    LinearGraphics,
    TextMode,
}
<commit_msg>Add proper Debug implementations for the Header<commit_after>//! This module contains the pieces for parsing Multiboot headers.

use core::convert::TryInto;
use core::fmt;

pub const MULTIBOOT_HEADER_MAGIC: u32 = 0x1BADB002;

/// Multiboot struct bootloaders mainly interact with
#[derive(Copy, Clone)]
pub struct Header {
    header: MultibootHeader,
    /// the index at which the header starts
    pub header_start: u32,
}

/// Representation of a Multiboot header according to specification.
///
/// <rawtext>
///          +-------------------+
/// 0        | magic             |    (required)
/// 4        | flags             |    (required)
/// 8        | checksum          |    (required)
///          +-------------------+
/// 12       | header_addr       |    (present if flags[16] is set)
/// 16       | load_addr         |    (present if flags[16] is set)
/// 20       | load_end_addr     |    (present if flags[16] is set)
/// 24       | bss_end_addr      |    (present if flags[16] is set)
/// 28       | entry_addr        |    (present if flags[16] is set)
///          +-------------------+
/// 32       | mode_type         |    (present if flags[2] is set)
/// 36       | width             |    (present if flags[2] is set)
/// 40       | height            |    (present if flags[2] is set)
/// 44       | depth             |    (present if flags[2] is set)
///          +-------------------+
/// </rawtext>
#[repr(C, packed)]
#[derive(Debug, Copy, Clone)]
struct MultibootHeader {
    magic: u32,
    flags: u32,
    checksum: u32,
    addresses: MultibootAddresses,
    video_mode: MultibootVideoMode,
}

impl Header {
    /// Get the header by parsing it from a slice.
    ///
    /// The needed portion of the slice is copied.
    pub fn from_slice(buffer: &[u8]) -> Option<Self> {
        // first, find the header
        let (header, header_start) = Self::find_header(buffer)?;
        // then check that it's valid
        assert_eq!(header.magic, MULTIBOOT_HEADER_MAGIC);
        assert_eq!(header.magic.wrapping_add(header.flags).wrapping_add(header.checksum), 0);
        // finally, return it
        Some(Self { header, header_start })
    }

    /// Find the header and copy it from a given slice.
    fn find_header(buffer: &[u8]) -> Option<(MultibootHeader, u32)> {
        // the magic is 32 bit aligned and inside the first 8192 bytes
        assert!(buffer.len() >= 8192);
        let magic_index = match buffer[0..8192]
        .chunks_exact(4).position(|vals|
            u32::from_le_bytes(vals.try_into().unwrap()) // yes, there's 4 bytes here
            == MULTIBOOT_HEADER_MAGIC
        ) {
            Some(idx) => idx * 4,
            None => return None
        };
        const HEADER_SIZE: usize = core::mem::size_of::<MultibootHeader>();
        // TryInto only works for lengths <= 32, so let's copy the stuff :(
        let mut header_bytes: [u8; HEADER_SIZE] = [0; HEADER_SIZE];
        buffer.iter().skip(magic_index).zip(header_bytes.iter_mut()).for_each(|(&buf, arr)| {
            *arr = buf;
        });
        let header = unsafe {core::mem::transmute::<[u8; HEADER_SIZE], MultibootHeader>(header_bytes)};
        Some((header, magic_index as u32))
    }

    check_flag!(
        doc = "If true, then the modules have to be page aligned.",
        wants_modules_page_aligned,
        0
    );
    check_flag!(
        doc = "If true, memory information must be passed.",
        wants_memory_information,
        1
    );
    check_flag!(
        doc = "If true, then the `mode_type`, `width`, `height` and `depth` fields are valid and video information has to be passed.",
        has_video_mode,
        2
    );
    check_flag!(
        doc = "If true, then the `header_addr`, `load_addr`, `load_end_addr`, `bss_end_addr` and `entry_addr` fields are valid.",
        has_multiboot_addresses,
        16
    );

    /// Get the load addresses specified in the Multiboot header.
    ///
    /// If this function returns `None` the binary has to be loaded as an ELF instead.
    pub fn get_addresses(&self) -> Option<MultibootAddresses> {
        if self.has_multiboot_addresses() {
            assert!(self.header.addresses.load_address <= self.header.addresses.header_address);
            Some(self.header.addresses)
        } else {
            None
        }
    }

    /// Get the preferred video mode specified in the Multiboot header.
    ///
    /// If this function returns `None` the binary has to be loaded as an ELF instead.
    pub fn get_preferred_video_mode(&self) -> Option<MultibootVideoMode> {
        if self.has_video_mode() {
            Some(self.header.video_mode)
        } else {
            None
        }
    }
}

impl fmt::Debug for Header {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Header")
            .field("wants_modules_page_aligned", &self.wants_modules_page_aligned())
            .field("wants_memory_information", &self.wants_memory_information())
            .field("addresses", &self.get_addresses())
            .field("video_mode", &self.get_preferred_video_mode())
            .finish()
    }
}

/// Addresses specified in the Multiboot header
#[derive(Debug, Copy, Clone)]
#[repr(C, packed)]
pub struct MultibootAddresses {
    pub header_address: u32,
    pub load_address: u32,
    pub load_end_address: u32,
    pub bss_end_address: u32,
    pub entry_address: u32,
}

impl MultibootAddresses {
    /// Compute the offset of the load address into the binary.
    ///
    /// Multiboot 0.6.96: section "3.1.3 The address fields of Multiboot header" says
    /// this is the "offset at which the header was found, minus (header_addr - load_addr)".
    pub fn compute_load_offset(&self, header_start: u32) -> u32 {
        header_start - (self.header_address - self.load_address)
    }
}

/// Preferred video mode specified in the Multiboot header
#[derive(Copy, Clone)]
#[repr(C, packed)]
pub struct MultibootVideoMode {
    mode_type: u32,
    pub width: u32,
    pub height: u32,
    depth: u32,
}

impl MultibootVideoMode {
    /// Get the preferred video mode type
    pub fn mode_type(&self) -> Option<VideoModeType> {
        match self.mode_type {
            0 => Some(VideoModeType::LinearGraphics),
            1 => Some(VideoModeType::TextMode),
            _ => None,
        }
    }

    /// Get the preferred depth, if possible.
    pub fn depth(&self) -> Option<u32> {
        // Only a pixel-based mode has a depth.
        if self.mode_type() == Some(VideoModeType::LinearGraphics) {
            Some(self.depth)
        } else {
            None
        }
    }
}

impl fmt::Debug for MultibootVideoMode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("MultibootVideoMode")
            .field("mode_type", &self.mode_type())
            .field("width", &self.width)
            .field("height", &self.height)
            .field("depth", &self.depth())
            .finish()
    }
}

/// Preferred video mode type
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum VideoModeType {
    LinearGraphics,
    TextMode,
}
<|endoftext|>