<commit_before>//! Read and write Elasto Mania level files.

use std::io::{ Read };
use std::fs::File;
use byteorder::{ ByteOrder, ReadBytesExt, LittleEndian };
use super::{ Position, trim_string };

// Magic arbitrary number; signifies end-of-data. Followed by Top10 list(s).
const EOD: i32 = 0x0067103A;
// Magic arbitrary number; signifies end-of-file.
const EOF: i32 = 0x00845D52;

/// Game version.
#[derive(Debug, PartialEq)]
pub enum Version {
    Across,
    Elma
}

/// Type of object.
#[derive(Debug, PartialEq)]
pub enum ObjectType {
    Apple,
    Exit,
    Killer,
    Player
}

/// Object struct. Every level requires one `ObjectType::Player` Object and at least one `ObjectType::Exit` Object.
#[derive(Debug, PartialEq)]
pub struct Object {
    /// Position. See `Position` struct.
    pub position: Position<f64>,
    /// Type of Object, see `ObjectType`.
    pub object_type: ObjectType,
    /// Applies to `ObjectType::Apple` only.
    ///
    /// 0 = normal
    /// 1 = gravity up
    /// 2 = gravity down
    /// 3 = gravity left
    /// 4 = gravity right
    // TODO: enum with gravity
    pub gravity: i32,
    /// Applies to `ObjectType::Apple` only. Valid values are 1 to 9.
    pub animation: i32
}

/// Polygon struct.
#[derive(Debug, Default, PartialEq)]
pub struct Polygon {
    /// Grass polygon.
    pub grass: bool,
    /// Vector with all vertices, see Position struct.
    pub vertices: Vec<Position<f64>>
}

impl Polygon {
    pub fn new () -> Polygon {
        Polygon {
            grass: false,
            vertices: vec![]
        }
    }
}

/// Picture struct.
#[derive(Debug, PartialEq)]
pub struct Picture {
    /// Picture name.
    pub name: String,
    /// Texture name.
    pub texture: String,
    /// Mask name.
    pub mask: String,
    /// Position. See Position struct.
    pub position: Position<f64>,
    /// Z-distance
    pub distance: i32,
    /// Clipping.
    ///
    /// 0 = unclipped
    /// 1 = ground
    /// 2 = sky
    // TODO: make enum
    pub clip: i32
}

/// Top10 list entry struct.
#[derive(Debug)]
pub struct ListEntry {
    /// Player 1 name.
    pub name_1: String,
    /// Player 2 name.
    pub name_2: String,
    /// Time.
    pub time: i32
}

/// Level struct that contains all level information.
pub struct Level {
    /// Elma or Across level.
    pub version: Version,
    /// Raw binary data of a loaded or finalized constructed level.
    raw: Vec<u8>,
    /// Random number that links level file to replay files.
    pub link: i32,
    /// Contains four integrity checks (See calculate_integrity_sums()).
    pub integrity: [f64; 4],
    /// Level name.
    pub name: String,
    /// LGR file name.
    pub lgr: String,
    /// Ground texture name.
    pub ground: String,
    /// Sky texture name.
    pub sky: String,
    /// Vector with all polygons (See Polygon).
    pub polygons: Vec<Polygon>,
    /// Vector with all objects (See Object).
    pub objects: Vec<Object>,
    /// Vector with all pictures (See Picture).
    pub pictures: Vec<Picture>,
    /// Vector of Top10 single-player names and times.
    pub top10_single: Vec<ListEntry>,
    /// Vector of Top10 multi-player names and times.
    pub top10_multi: Vec<ListEntry>
}

impl Default for Level {
    fn default() -> Level { Level::new() }
}

impl Level {
    /// Returns a new Level struct.
    ///
    /// # Examples
    ///
    /// ```
    /// let level = elma::lev::Level::new();
    /// ```
    pub fn new () -> Level {
        Level {
            version: Version::Elma,
            raw: vec![],
            link: 0,
            integrity: [0.0f64; 4],
            name: String::new(),
            lgr: String::from("default"),
            ground: String::from("ground"),
            sky: String::from("sky"),
            polygons: vec![],
            objects: vec![],
            pictures: vec![],
            top10_single: vec![],
            top10_multi: vec![]
        }
    }

    /// Loads a level file and returns a Level struct.
    ///
    /// # Examples
    ///
    /// ```
    /// let level = elma::lev::Level::load_level("tests/test.lev");
    /// ```
    pub fn load_level (filename: &str) -> Level {
        let mut level = Level::new();
        let mut file = File::open(filename).unwrap();
        let mut buffer = vec![];
        file.read_to_end(&mut buffer).unwrap();
        level.raw = buffer;
        level.parse_level();
        level
    }

    /// Parses the raw binary data into Level struct fields.
    fn parse_level (&mut self) {
        let rem = self.raw.as_slice();

        // POT06 = Across, POT14 = Elma.
        let (version, rem) = rem.split_at(5);
        self.version = match version {
            [80, 79, 84, 49, 52] => Version::Elma,
            [80, 79, 84, 48, 54] => Version::Across,
            _ => panic!("Not a valid level file.")
        };

        // Link.
        let (_, mut rem) = rem.split_at(2); // Never used
        self.link = rem.read_i32::<LittleEndian>().unwrap();

        // Integrity checksums.
        for i in 0..4 {
            self.integrity[i] = rem.read_f64::<LittleEndian>().unwrap();
        }

        // Level name.
        let (name, rem) = rem.split_at(51);
        self.name = trim_string(name).unwrap();
        // LGR name.
        let (lgr, rem) = rem.split_at(16);
        self.lgr = trim_string(lgr).unwrap();
        // Ground texture name.
        let (ground, rem) = rem.split_at(10);
        self.ground = trim_string(ground).unwrap();
        // Sky texture name.
        let (sky, mut rem) = rem.split_at(10);
        self.sky = trim_string(sky).unwrap();

        // Polygons.
        let poly_count = (rem.read_f64::<LittleEndian>().unwrap() - 0.4643643).round() as usize;
        for _ in 0..poly_count {
            let grass = rem.read_i32::<LittleEndian>().unwrap() > 0;
            let vertex_count = rem.read_i32::<LittleEndian>().unwrap();
            let mut vertices: Vec<Position<f64>> = vec![];
            for _ in 0..vertex_count {
                let x = rem.read_f64::<LittleEndian>().unwrap();
                let y = rem.read_f64::<LittleEndian>().unwrap();
                vertices.push(Position {
                    x: x,
                    y: y
                });
            }
            self.polygons.push(Polygon {
                grass: grass,
                vertices: vertices
            });
        }

        // Objects.
        let object_count = (rem.read_f64::<LittleEndian>().unwrap() - 0.4643643).round() as usize;
        for _ in 0..object_count {
            let x = rem.read_f64::<LittleEndian>().unwrap();
            let y = rem.read_f64::<LittleEndian>().unwrap();
            let position = Position { x: x, y: y };
            let object_type = match rem.read_i32::<LittleEndian>().unwrap() {
                1 => ObjectType::Exit,
                2 => ObjectType::Apple,
                3 => ObjectType::Killer,
                4 => ObjectType::Player,
                _ => panic!("Not a valid object type")
            };
            let gravity = rem.read_i32::<LittleEndian>().unwrap();
            let animation = rem.read_i32::<LittleEndian>().unwrap() + 1;

            self.objects.push(Object {
                position: position,
                object_type: object_type,
                gravity: gravity,
                animation: animation
            });
        }

        // Pictures.
        let picture_count = (rem.read_f64::<LittleEndian>().unwrap() - 0.2345672).round() as usize;
        for _ in 0..picture_count {
            let (name, temp_rem) = rem.split_at(10);
            rem = temp_rem;
            let name = trim_string(name).unwrap();
            let (texture, temp_rem) = rem.split_at(10);
            rem = temp_rem;
            let texture = trim_string(texture).unwrap();
            let (mask, temp_rem) = rem.split_at(10);
            rem = temp_rem;
            let mask = trim_string(mask).unwrap();
            let x = rem.read_f64::<LittleEndian>().unwrap();
            let y = rem.read_f64::<LittleEndian>().unwrap();
            let distance = rem.read_i32::<LittleEndian>().unwrap();
            let clip = rem.read_i32::<LittleEndian>().unwrap();

            self.pictures.push(Picture {
                name: name,
                texture: texture,
                mask: mask,
                position: Position { x: x, y: y },
                distance: distance,
                clip: clip
            });
        }

        // EOD marker expected at this point.
        let expected = rem.read_i32::<LittleEndian>().unwrap();
        if expected != EOD { panic!("EOD marker mismatch: x0{:x} != x0{:x}", expected, EOD); }

        // First decrypt the top10 blocks.
        let (mut top10, mut rem) = rem.split_at(688);
        let decrypted_top10_data = crypt_top10(top10);

        // Single-player list.
        let single = &decrypted_top10_data[0..344];
        self.top10_single = parse_top10(single);

        // Multi-player list.
        let multi = &decrypted_top10_data[344..688];
        self.top10_multi = parse_top10(multi);

        // EOF marker expected at this point.
        let expected = rem.read_i32::<LittleEndian>().unwrap();
        if expected != EOF { panic!("EOF marker mismatch: x0{:x} != x0{:x}", expected, EOF); }
    }

    /// Combines the Level struct fields to generate the raw binary data,
    /// and calculate integrity sums.
    fn update (&self) {
        // TODO: convert
        self.calculate_integrity_sums();
    }

    fn calculate_integrity_sums (&self) {
        // TODO: do that
    }

    /// Converts all struct fields into raw binary form and returns it.
    pub fn get_raw (self) -> Vec<u8> {
        self.update();
        self.raw
    }

    /// Saves level as a file.
    pub fn save_lev (self, filename: &str) {
        self.update();
        // let file = File::create(&filename).unwrap();
        // TODO: write stuff.
    }
}

/// Decrypt and encrypt top10 list data. Same algorithm for both.
pub fn crypt_top10 (top10: &[u8]) -> &[u8] {
    // Who knows
    let mut ebp8: i16 = 0x15;
    let mut ebp10: i16 = 0x2637;

    for mut t in top10.iter_mut().take(688) {
        *t ^= (ebp8 & 0xFF) as u8;
        ebp10 = ebp10.wrapping_add((ebp8.wrapping_rem(0xD3D)).wrapping_mul(0xD3D));
        ebp8 = ebp10.wrapping_mul(0x1F).wrapping_add(0xD3D);
    }

    top10
}

/// Parse top10 lists and return a vector of `ListEntry`s
pub fn parse_top10 (top10: &[u8]) -> Vec<ListEntry> {
    let mut list: Vec<ListEntry> = vec![];
    let times = LittleEndian::read_i32(&top10[0..4]);
    for n in 0..times {
        let time_offset = (4 + n * 4) as usize;
        let time_end = time_offset + 4;
        let name_1_offset = (44 + n * 15) as usize;
        let name_1_end = name_1_offset + 15;
        let name_2_offset = (194 + n * 15) as usize;
        let name_2_end = name_2_offset + 15;
        // All of this pains me even though I don't understand it...
        let name_1 = &top10[name_1_offset..name_1_end];
        let name_2 = &top10[name_2_offset..name_2_end];
        list.push(ListEntry {
            time: LittleEndian::read_i32(&top10[time_offset..time_end]),
            name_1: trim_string(name_1).unwrap(),
            name_2: trim_string(name_2).unwrap()
        });
    }
    list
}
<commit_msg>Remove some unneeded variables<commit_after>//! Read and write Elasto Mania level files.

use std::io::{ Read };
use std::fs::File;
use byteorder::{ ByteOrder, ReadBytesExt, LittleEndian };
use super::{ Position, trim_string };

// Magic arbitrary number; signifies end-of-data. Followed by Top10 list(s).
const EOD: i32 = 0x0067103A;
// Magic arbitrary number; signifies end-of-file.
const EOF: i32 = 0x00845D52;

/// Game version.
#[derive(Debug, PartialEq)]
pub enum Version {
    Across,
    Elma
}

/// Type of object.
#[derive(Debug, PartialEq)]
pub enum ObjectType {
    Apple,
    Exit,
    Killer,
    Player
}

/// Object struct. Every level requires one `ObjectType::Player` Object and at least one `ObjectType::Exit` Object.
#[derive(Debug, PartialEq)]
pub struct Object {
    /// Position. See `Position` struct.
    pub position: Position<f64>,
    /// Type of Object, see `ObjectType`.
    pub object_type: ObjectType,
    /// Applies to `ObjectType::Apple` only.
    ///
    /// 0 = normal
    /// 1 = gravity up
    /// 2 = gravity down
    /// 3 = gravity left
    /// 4 = gravity right
    // TODO: enum with gravity
    pub gravity: i32,
    /// Applies to `ObjectType::Apple` only. Valid values are 1 to 9.
    pub animation: i32
}

/// Polygon struct.
#[derive(Debug, Default, PartialEq)]
pub struct Polygon {
    /// Grass polygon.
    pub grass: bool,
    /// Vector with all vertices, see Position struct.
    pub vertices: Vec<Position<f64>>
}

impl Polygon {
    pub fn new () -> Polygon {
        Polygon {
            grass: false,
            vertices: vec![]
        }
    }
}

/// Picture struct.
#[derive(Debug, PartialEq)]
pub struct Picture {
    /// Picture name.
    pub name: String,
    /// Texture name.
    pub texture: String,
    /// Mask name.
    pub mask: String,
    /// Position. See Position struct.
    pub position: Position<f64>,
    /// Z-distance
    pub distance: i32,
    /// Clipping.
    ///
    /// 0 = unclipped
    /// 1 = ground
    /// 2 = sky
    // TODO: make enum
    pub clip: i32
}

/// Top10 list entry struct.
#[derive(Debug)]
pub struct ListEntry {
    /// Player 1 name.
    pub name_1: String,
    /// Player 2 name.
    pub name_2: String,
    /// Time.
    pub time: i32
}

/// Level struct that contains all level information.
pub struct Level {
    /// Elma or Across level.
    pub version: Version,
    /// Raw binary data of a loaded or finalized constructed level.
    raw: Vec<u8>,
    /// Random number that links level file to replay files.
    pub link: i32,
    /// Contains four integrity checks (See calculate_integrity_sums()).
    pub integrity: [f64; 4],
    /// Level name.
    pub name: String,
    /// LGR file name.
    pub lgr: String,
    /// Ground texture name.
    pub ground: String,
    /// Sky texture name.
    pub sky: String,
    /// Vector with all polygons (See Polygon).
    pub polygons: Vec<Polygon>,
    /// Vector with all objects (See Object).
    pub objects: Vec<Object>,
    /// Vector with all pictures (See Picture).
    pub pictures: Vec<Picture>,
    /// Vector of Top10 single-player names and times.
    pub top10_single: Vec<ListEntry>,
    /// Vector of Top10 multi-player names and times.
    pub top10_multi: Vec<ListEntry>
}

impl Default for Level {
    fn default() -> Level { Level::new() }
}

impl Level {
    /// Returns a new Level struct.
    ///
    /// # Examples
    ///
    /// ```
    /// let level = elma::lev::Level::new();
    /// ```
    pub fn new () -> Level {
        Level {
            version: Version::Elma,
            raw: vec![],
            link: 0,
            integrity: [0.0f64; 4],
            name: String::new(),
            lgr: String::from("default"),
            ground: String::from("ground"),
            sky: String::from("sky"),
            polygons: vec![],
            objects: vec![],
            pictures: vec![],
            top10_single: vec![],
            top10_multi: vec![]
        }
    }

    /// Loads a level file and returns a Level struct.
    ///
    /// # Examples
    ///
    /// ```
    /// let level = elma::lev::Level::load_level("tests/test.lev");
    /// ```
    pub fn load_level (filename: &str) -> Level {
        let mut level = Level::new();
        let mut file = File::open(filename).unwrap();
        let mut buffer = vec![];
        file.read_to_end(&mut buffer).unwrap();
        level.raw = buffer;
        level.parse_level();
        level
    }

    /// Parses the raw binary data into Level struct fields.
    fn parse_level (&mut self) {
        let rem = self.raw.as_slice();

        // POT06 = Across, POT14 = Elma.
        let (version, rem) = rem.split_at(5);
        self.version = match version {
            [80, 79, 84, 49, 52] => Version::Elma,
            [80, 79, 84, 48, 54] => Version::Across,
            _ => panic!("Not a valid level file.")
        };

        // Link.
        let (_, mut rem) = rem.split_at(2); // Never used
        self.link = rem.read_i32::<LittleEndian>().unwrap();

        // Integrity checksums.
        for i in 0..4 {
            self.integrity[i] = rem.read_f64::<LittleEndian>().unwrap();
        }

        // Level name.
        let (name, rem) = rem.split_at(51);
        self.name = trim_string(name).unwrap();
        // LGR name.
        let (lgr, rem) = rem.split_at(16);
        self.lgr = trim_string(lgr).unwrap();
        // Ground texture name.
        let (ground, rem) = rem.split_at(10);
        self.ground = trim_string(ground).unwrap();
        // Sky texture name.
        let (sky, mut rem) = rem.split_at(10);
        self.sky = trim_string(sky).unwrap();

        // Polygons.
        let poly_count = (rem.read_f64::<LittleEndian>().unwrap() - 0.4643643).round() as usize;
        for _ in 0..poly_count {
            let grass = rem.read_i32::<LittleEndian>().unwrap() > 0;
            let vertex_count = rem.read_i32::<LittleEndian>().unwrap();
            let mut vertices: Vec<Position<f64>> = vec![];
            for _ in 0..vertex_count {
                let x = rem.read_f64::<LittleEndian>().unwrap();
                let y = rem.read_f64::<LittleEndian>().unwrap();
                vertices.push(Position {
                    x: x,
                    y: y
                });
            }
            self.polygons.push(Polygon {
                grass: grass,
                vertices: vertices
            });
        }

        // Objects.
        let object_count = (rem.read_f64::<LittleEndian>().unwrap() - 0.4643643).round() as usize;
        for _ in 0..object_count {
            let x = rem.read_f64::<LittleEndian>().unwrap();
            let y = rem.read_f64::<LittleEndian>().unwrap();
            let position = Position { x: x, y: y };
            let object_type = match rem.read_i32::<LittleEndian>().unwrap() {
                1 => ObjectType::Exit,
                2 => ObjectType::Apple,
                3 => ObjectType::Killer,
                4 => ObjectType::Player,
                _ => panic!("Not a valid object type")
            };
            let gravity = rem.read_i32::<LittleEndian>().unwrap();
            let animation = rem.read_i32::<LittleEndian>().unwrap() + 1;

            self.objects.push(Object {
                position: position,
                object_type: object_type,
                gravity: gravity,
                animation: animation
            });
        }

        // Pictures.
        let picture_count = (rem.read_f64::<LittleEndian>().unwrap() - 0.2345672).round() as usize;
        for _ in 0..picture_count {
            let (name, temp_rem) = rem.split_at(10);
            let name = trim_string(name).unwrap();
            let (texture, temp_rem) = temp_rem.split_at(10);
            let texture = trim_string(texture).unwrap();
            let (mask, temp_rem) = temp_rem.split_at(10);
            rem = temp_rem;
            let mask = trim_string(mask).unwrap();
            let x = rem.read_f64::<LittleEndian>().unwrap();
            let y = rem.read_f64::<LittleEndian>().unwrap();
            let distance = rem.read_i32::<LittleEndian>().unwrap();
            let clip = rem.read_i32::<LittleEndian>().unwrap();

            self.pictures.push(Picture {
                name: name,
                texture: texture,
                mask: mask,
                position: Position { x: x, y: y },
                distance: distance,
                clip: clip
            });
        }

        // EOD marker expected at this point.
        let expected = rem.read_i32::<LittleEndian>().unwrap();
        if expected != EOD { panic!("EOD marker mismatch: x0{:x} != x0{:x}", expected, EOD); }

        // First decrypt the top10 blocks.
        let (mut top10, mut rem) = rem.split_at(688);
        let decrypted_top10_data = crypt_top10(top10);

        // Single-player list.
        let single = &decrypted_top10_data[0..344];
        self.top10_single = parse_top10(single);

        // Multi-player list.
        let multi = &decrypted_top10_data[344..688];
        self.top10_multi = parse_top10(multi);

        // EOF marker expected at this point.
        let expected = rem.read_i32::<LittleEndian>().unwrap();
        if expected != EOF { panic!("EOF marker mismatch: x0{:x} != x0{:x}", expected, EOF); }
    }

    /// Combines the Level struct fields to generate the raw binary data,
    /// and calculate integrity sums.
    fn update (&self) {
        // TODO: convert
        self.calculate_integrity_sums();
    }

    fn calculate_integrity_sums (&self) {
        // TODO: do that
    }

    /// Converts all struct fields into raw binary form and returns it.
    pub fn get_raw (self) -> Vec<u8> {
        self.update();
        self.raw
    }

    /// Saves level as a file.
    pub fn save_lev (self, filename: &str) {
        self.update();
        // let file = File::create(&filename).unwrap();
        // TODO: write stuff.
    }
}

/// Decrypt and encrypt top10 list data. Same algorithm for both.
pub fn crypt_top10 (top10: &[u8]) -> &[u8] {
    // Who knows
    let mut ebp8: i16 = 0x15;
    let mut ebp10: i16 = 0x2637;

    for mut t in top10.iter_mut().take(688) {
        *t ^= (ebp8 & 0xFF) as u8;
        ebp10 = ebp10.wrapping_add((ebp8.wrapping_rem(0xD3D)).wrapping_mul(0xD3D));
        ebp8 = ebp10.wrapping_mul(0x1F).wrapping_add(0xD3D);
    }

    top10
}

/// Parse top10 lists and return a vector of `ListEntry`s
pub fn parse_top10 (top10: &[u8]) -> Vec<ListEntry> {
    let mut list: Vec<ListEntry> = vec![];
    let times = LittleEndian::read_i32(&top10[0..4]);
    for n in 0..times {
        let time_offset = (4 + n * 4) as usize;
        let time_end = time_offset + 4;
        let name_1_offset = (44 + n * 15) as usize;
        let name_1_end = name_1_offset + 15;
        let name_2_offset = (194 + n * 15) as usize;
        let name_2_end = name_2_offset + 15;
        // All of this pains me even though I don't understand it...
        let name_1 = &top10[name_1_offset..name_1_end];
        let name_2 = &top10[name_2_offset..name_2_end];
        list.push(ListEntry {
            time: LittleEndian::read_i32(&top10[time_offset..time_end]),
            name_1: trim_string(name_1).unwrap(),
            name_2: trim_string(name_2).unwrap()
        });
    }
    list
}
<|endoftext|>


<commit_before>//! This module provides the logic necessary to implement a license prompt into
//! the hab CLI. When a user runs hab for the first time, they will be prompted
//! to accept the Chef license agreement. If they choose not to, then hab will
//! exit and they will be unable to use the tool.
//!
//! Choosing to accept the license will write a file to either
//! /hab/accepted-licenses/habitat (if run as root) or
//! ~/.hab/accepted-licenses/habitat. The presence of this file will
//! prevent further license prompts. The file itself contains various metadata
//! that we collect, but it's important to note that the contents of this file
//! are not strictly important. The important thing is the presence of the file.
//!
//! If a user was to read our code and pre-emptively create one of the above
//! files and leave its contents completely empty, that is sufficient for the
//! acceptance of the license. We will not overwrite their empty file, nor
//! will we care that it is empty.
//!
//! Because of this, the metadata that we store is not strictly necessary and
//! is more of a best effort scenario. If we can't collect all of it for
//! whatever reason, it's fine.
//!
//! Additionally, there are 2 environment variables that are supported to
//! constitute license acceptance. Setting HAB_LICENSE to "accept" will
//! accept the license and persist the file and setting HAB_LICENSE to
//! "accept-no-persist" will accept the license but not persist the
//! acceptance.
//!
//! More detailed information on the license spec is available at
//! https://github.com/chef/license-acceptance

use crate::{common::ui::{self,
                         UIReader,
                         UIWriter,
                         UI},
            error::{Error,
                    Result},
            hcore::{fs::{am_i_root,
                         FS_ROOT_PATH},
                    users::get_current_username}};
use chrono::{DateTime,
             Utc};
use std::{env,
          fs::{self,
               File},
          io::Write,
          path::PathBuf};

const LICENSE_FILE_FORMAT_VERSION: &str = "1.0";
const LICENSE_ACCEPT_ENVVAR: &str = "HAB_LICENSE";

#[derive(Debug, Serialize, Deserialize)]
pub struct LicenseData {
    pub date_accepted: DateTime<Utc>,
    pub accepting_product: String,
    pub accepting_product_version: String,
    pub user: Option<String>,
    pub file_format: String,
}

impl LicenseData {
    pub fn new() -> Self {
        LicenseData { date_accepted: Utc::now(),
                      accepting_product: String::from("hab"),
                      accepting_product_version: super::VERSION.to_string(),
                      user: get_current_username(),
                      file_format: String::from(LICENSE_FILE_FORMAT_VERSION), }
    }
}

#[derive(Clone, Copy, PartialEq)]
pub enum LicenseAcceptance {
    Accepted,
    Denied,
    NotYetAccepted,
}

impl LicenseAcceptance {
    pub fn accepted(self) -> bool { self == Self::Accepted }
}

impl Default for LicenseAcceptance {
    fn default() -> Self { Self::NotYetAccepted }
}

pub fn check_for_license_acceptance() -> Result<LicenseAcceptance> {
    match (env_var_present()?, license_exists()) {
        // The environment variable takes precedence regardless of the existence of the license
        // file
        (l @ LicenseAcceptance::Accepted, _) | (l @ LicenseAcceptance::Denied, _) => Ok(l),
        (_, true) => Ok(LicenseAcceptance::Accepted),
        (..) => Ok(LicenseAcceptance::NotYetAccepted),
    }
}

pub fn check_for_license_acceptance_and_prompt(ui: &mut UI) -> Result<()> {
    match check_for_license_acceptance()? {
        LicenseAcceptance::Accepted => Ok(()),
        LicenseAcceptance::Denied => Err(Error::LicenseNotAccepted),
        LicenseAcceptance::NotYetAccepted => {
            ui.heading("+---------------------------------------------+")?;
            ui.heading("            Chef License Acceptance")?;
            ui.br()?;
            ui.info("Before you can continue, 1 product license must be accepted.")?;
            ui.info("View the license at https://www.chef.io/end-user-license-agreement")?;
            ui.br()?;
            ui.info("License that needs accepting:")?;
            ui.br()?;
            ui.info("  * Habitat")?;
            ui.br()?;

            if ui.prompt_yes_no("Do you accept the 1 product license?", Some(false))? {
                accept_license(ui)
            } else {
                ui.br()?;
                ui.info("If you do not accept this license you will not be able to use Chef \
                         products.")?;
                ui.br()?;

                if ui.prompt_yes_no("Do you accept the 1 product license?", Some(false))? {
                    accept_license(ui)
                } else {
                    ui.br()?;
                    ui.heading("+---------------------------------------------+")?;
                    Err(Error::LicenseNotAccepted)
                }
            }
        }
    }
}

pub fn accept_license(ui: &mut UI) -> Result<()> {
    if license_exists() {
        ui.info("You have already accepted the license.")?;
        return Ok(());
    }

    ui.br()?;
    ui.info("Accepting 1 product license...")?;

    write_license_file()?;

    ui.status(ui::Status::Custom(ui::Glyph::CheckMark, String::from("")),
              "1 product license accepted.")?;
    ui.br()?;
    ui.heading("+---------------------------------------------+")?;

    Ok(())
}

fn superuser_license_root() -> PathBuf { PathBuf::from(&*FS_ROOT_PATH).join("hab") }

fn user_license_root() -> PathBuf {
    if let Some(home) = dirs::home_dir() {
        home.join(".hab")
    } else {
        panic!("No home directory available. Unable to find a suitable place to write a license \
                file.");
    }
}

fn license_path(root_path: &PathBuf) -> PathBuf { root_path.join("accepted-licenses") }

fn license_file(license_path: &PathBuf) -> PathBuf { license_path.join("habitat") }

fn writeable_license_path() -> PathBuf {
    let root_dir = if am_i_root() {
        superuser_license_root()
    } else {
        user_license_root()
    };

    license_path(&root_dir)
}

fn env_var_present() -> Result<LicenseAcceptance> {
    match env::var(LICENSE_ACCEPT_ENVVAR) {
        Ok(val) => {
            if &val == "accept" {
                write_license_file()?;
                Ok(LicenseAcceptance::Accepted)
            } else if &val == "accept-no-persist" {
                Ok(LicenseAcceptance::Accepted)
            } else if &val == "deny" {
                Ok(LicenseAcceptance::Denied)
            } else {
                Ok(LicenseAcceptance::NotYetAccepted)
            }
        }
        Err(_) => Ok(LicenseAcceptance::NotYetAccepted),
    }
}

fn write_license_file() -> Result<()> {
    let license = LicenseData::new();
    let content = serde_yaml::to_string(&license)?;
    fs::create_dir_all(writeable_license_path())?;
    let mut file = File::create(license_file(&writeable_license_path()))?;
    file.write_all(content.as_bytes())?;
    Ok(())
}

pub fn license_exists() -> bool {
    license_file(&license_path(&user_license_root())).is_file()
    || license_file(&license_path(&superuser_license_root())).is_file()
}
<commit_msg>Add comment to LicenseAcceptance::Denied<commit_after>//! This module provides the logic necessary to implement a license prompt into
//! the hab CLI. When a user runs hab for the first time, they will be prompted
//! to accept the Chef license agreement. If they choose not to, then hab will
//! exit and they will be unable to use the tool.
//!
//! Choosing to accept the license will write a file to either
//! /hab/accepted-licenses/habitat (if run as root) or
//! ~/.hab/accepted-licenses/habitat. The presence of this file will
//! prevent further license prompts. The file itself contains various metadata
//! that we collect, but it's important to note that the contents of this file
//! are not strictly important. The important thing is the presence of the file.
//!
//! If a user was to read our code and pre-emptively create one of the above
//! files and leave its contents completely empty, that is sufficient for the
//! acceptance of the license. We will not overwrite their empty file, nor
//! will we care that it is empty.
//!
//! Because of this, the metadata that we store is not strictly necessary and
//! is more of a best effort scenario. If we can't collect all of it for
//! whatever reason, it's fine.
//!
//! Additionally, there are 2 environment variables that are supported to
//! constitute license acceptance. Setting HAB_LICENSE to "accept" will
//! accept the license and persist the file and setting HAB_LICENSE to
//! "accept-no-persist" will accept the license but not persist the
//! acceptance.
//!
//! More detailed information on the license spec is available at
//! https://github.com/chef/license-acceptance

use crate::{common::ui::{self,
                         UIReader,
                         UIWriter,
                         UI},
            error::{Error,
                    Result},
            hcore::{fs::{am_i_root,
                         FS_ROOT_PATH},
                    users::get_current_username}};
use chrono::{DateTime,
             Utc};
use std::{env,
          fs::{self,
               File},
          io::Write,
          path::PathBuf};

const LICENSE_FILE_FORMAT_VERSION: &str = "1.0";
const LICENSE_ACCEPT_ENVVAR: &str = "HAB_LICENSE";

#[derive(Debug, Serialize, Deserialize)]
pub struct LicenseData {
    pub date_accepted: DateTime<Utc>,
    pub accepting_product: String,
    pub accepting_product_version: String,
    pub user: Option<String>,
    pub file_format: String,
}

impl LicenseData {
    pub fn new() -> Self {
        LicenseData { date_accepted: Utc::now(),
                      accepting_product: String::from("hab"),
                      accepting_product_version: super::VERSION.to_string(),
                      user: get_current_username(),
                      file_format: String::from(LICENSE_FILE_FORMAT_VERSION), }
    }
}

#[derive(Clone, Copy, PartialEq)]
pub enum LicenseAcceptance {
    Accepted,
    /// Explicitly deny the license and do not prompt for license acceptance. This is useful for
    /// testing.
    Denied,
    NotYetAccepted,
}

impl LicenseAcceptance {
    pub fn accepted(self) -> bool { self == Self::Accepted }
}

impl Default for LicenseAcceptance {
    fn default() -> Self { Self::NotYetAccepted }
}

pub fn check_for_license_acceptance() -> Result<LicenseAcceptance> {
    match (env_var_present()?, license_exists()) {
        // The environment variable takes precedence regardless of the existence of the license
        // file
        (l @ LicenseAcceptance::Accepted, _) | (l @ LicenseAcceptance::Denied, _) => Ok(l),
        (_, true) => Ok(LicenseAcceptance::Accepted),
        (..) => Ok(LicenseAcceptance::NotYetAccepted),
    }
}

pub fn check_for_license_acceptance_and_prompt(ui: &mut UI) -> Result<()> {
    match check_for_license_acceptance()? {
        LicenseAcceptance::Accepted => Ok(()),
        LicenseAcceptance::Denied => Err(Error::LicenseNotAccepted),
        LicenseAcceptance::NotYetAccepted => {
            ui.heading("+---------------------------------------------+")?;
            ui.heading("            Chef License Acceptance")?;
            ui.br()?;
            ui.info("Before you can continue, 1 product license must be accepted.")?;
            ui.info("View the license at https://www.chef.io/end-user-license-agreement")?;
            ui.br()?;
            ui.info("License that needs accepting:")?;
            ui.br()?;
            ui.info("  * Habitat")?;
            ui.br()?;

            if ui.prompt_yes_no("Do you accept the 1 product license?", Some(false))? {
                accept_license(ui)
            } else {
                ui.br()?;
                ui.info("If you do not accept this license you will not be able to use Chef \
                         products.")?;
                ui.br()?;

                if ui.prompt_yes_no("Do you accept the 1 product license?", Some(false))? {
                    accept_license(ui)
                } else {
                    ui.br()?;
                    ui.heading("+---------------------------------------------+")?;
                    Err(Error::LicenseNotAccepted)
                }
            }
        }
    }
}

pub fn accept_license(ui: &mut UI) -> Result<()> {
    if license_exists() {
        ui.info("You have already accepted the license.")?;
        return Ok(());
    }

    ui.br()?;
    ui.info("Accepting 1 product license...")?;

    write_license_file()?;

    ui.status(ui::Status::Custom(ui::Glyph::CheckMark, String::from("")),
              "1 product license accepted.")?;
    ui.br()?;
    ui.heading("+---------------------------------------------+")?;

    Ok(())
}

fn superuser_license_root() -> PathBuf { PathBuf::from(&*FS_ROOT_PATH).join("hab") }

fn user_license_root() -> PathBuf {
    if let Some(home) = dirs::home_dir() {
        home.join(".hab")
    } else {
        panic!("No home directory available. Unable to find a suitable place to write a license \
                file.");
    }
}

fn license_path(root_path: &PathBuf) -> PathBuf { root_path.join("accepted-licenses") }

fn license_file(license_path: &PathBuf) -> PathBuf { license_path.join("habitat") }

fn writeable_license_path() -> PathBuf {
    let root_dir = if am_i_root() {
        superuser_license_root()
    } else {
        user_license_root()
    };

    license_path(&root_dir)
}

fn env_var_present() -> Result<LicenseAcceptance> {
    match env::var(LICENSE_ACCEPT_ENVVAR) {
        Ok(val) => {
            if &val == "accept" {
                write_license_file()?;
                Ok(LicenseAcceptance::Accepted)
            } else if &val == "accept-no-persist" {
                Ok(LicenseAcceptance::Accepted)
            } else if &val == "deny" {
                Ok(LicenseAcceptance::Denied)
            } else {
                Ok(LicenseAcceptance::NotYetAccepted)
            }
        }
        Err(_) => Ok(LicenseAcceptance::NotYetAccepted),
    }
}

fn write_license_file() -> Result<()> {
    let license = LicenseData::new();
    let content = serde_yaml::to_string(&license)?;
    fs::create_dir_all(writeable_license_path())?;
    let mut file = File::create(license_file(&writeable_license_path()))?;
    file.write_all(content.as_bytes())?;
    Ok(())
}

pub fn license_exists() -> bool {
    license_file(&license_path(&user_license_root())).is_file()
    || license_file(&license_path(&superuser_license_root())).is_file()
}
<|endoftext|>


<commit_before>extern crate base64;
extern crate byteorder;
extern crate hyper;
extern crate rayon;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

use byteorder::{BigEndian, WriteBytesExt};

use rayon::iter::{IntoParallelRefIterator, ParallelIterator};

use std::io::{Read, Write};

#[derive(Debug, Clone)]
pub struct Log {
    pub description: String,
    url: String,
    is_google: bool,
}

#[derive(Debug, Deserialize)]
pub struct SignedCertificateTimestamp {
    sct_version: u8,
    id: String,
    timestamp: u64,
    extensions: String,
    signature: String,
}

impl SignedCertificateTimestamp {
    pub fn to_raw_bytes(&self) -> Vec<u8> {
        let mut b = Vec::new();
        b.write_u8(self.sct_version).unwrap();

        let log_id = base64::decode(&self.id).unwrap();
        b.write(&log_id).unwrap();

        b.write_u64::<BigEndian>(self.timestamp).unwrap();

        let extensions = base64::decode(&self.extensions).unwrap();
        assert!(extensions.len() <= 65535);
        b.write_u16::<BigEndian>(extensions.len() as u16)
            .unwrap();
        b.write(&extensions).unwrap();

        let signature = base64::decode(&self.signature).unwrap();
        b.write(&signature).unwrap();

        return b;
    }
}

#[derive(Deserialize)]
struct LogsResponseLogs {
    description: String,
    url: String,
    operated_by: Vec<u32>,
    disqualified_at: Option<u64>,
}

#[derive(Deserialize)]
struct LogsResponseOperators {
    name: String,
    id: u32,
}

#[derive(Deserialize)]
struct LogsResponse {
    logs: Vec<LogsResponseLogs>,
    operators: Vec<LogsResponseOperators>,
}

const LOG_LIST_URL: &'static str = "https://www.gstatic.com/ct/log_list/log_list.json";

pub fn fetch_trusted_ct_logs(http_client: &hyper::Client) -> Vec<Log> {
    let response = http_client.get(LOG_LIST_URL).send().unwrap();
    // Limit the response to 10MB at most, to be resillient to DoS.
    let logs_response: LogsResponse = serde_json::from_reader(response.take(10 * 1024 * 1024))
        .unwrap();

    let google_id = logs_response
        .operators
        .iter()
        .find(|o| o.name == "Google")
        .map(|o| o.id);

    return logs_response
               .logs
               .into_iter()
               .filter(|log| log.disqualified_at.is_none())
               .map(|log| {
                        Log {
                            url: log.url,
                            description: log.description,
                            is_google: log.operated_by.contains(&google_id.unwrap()),
                        }
                    })
               .collect();
}

fn submit_to_log(http_client: &hyper::Client,
                 url: &str,
                 payload: &[u8])
                 -> Option<SignedCertificateTimestamp> {
    let mut url = "https://".to_string() + url;
    if !url.ends_with("/") {
        url += "/";
    }
    url += "ct/v1/add-chain";
    let response = http_client
        .post(&url)
        .body(hyper::client::Body::BufBody(payload, payload.len()))
        .header(hyper::header::ContentType::json())
        .send();
    let response = match response {
        Ok(r) => r,
        // TODO: maybe not all of these should be silently ignored.
        Err(_) => return None,
    };

    // 400, 403, and probably some others generally indicate a log doesn't accept certs from this
    // root, or that the log isn't accepting new submissions.
    if response.status.is_client_error() {
        return None;
    }

    // Limt the response to 10MB (well above what would ever be needed) to be resilient to DoS in
    // the face of a dumb or malicious log.
    return Some(serde_json::from_reader(response.take(10 * 1024 * 1024)).unwrap());
}

#[derive(Serialize, Deserialize)]
pub struct AddChainRequest {
    pub chain: Vec<String>,
}

pub fn submit_cert_to_logs<'a>(http_client: &hyper::Client,
                               logs: &'a [Log],
                               cert: Vec<Vec<u8>>)
                               -> Vec<(&'a Log, SignedCertificateTimestamp)> {
    let payload = serde_json::to_vec(&AddChainRequest {
                                          chain: cert.iter().map(|r| base64::encode(r)).collect(),
                                      })
            .unwrap();

    return logs.par_iter()
               .filter_map(|ref log| {
                               let sct = submit_to_log(http_client, &log.url, &payload);
                               return sct.map(|s| (*log, s));
                           })
               .collect();
}
<commit_msg>Shockingly simple...<commit_after>extern crate base64;
extern crate byteorder;
extern crate hyper;
extern crate rayon;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

use byteorder::{BigEndian, WriteBytesExt};

use rayon::iter::{IntoParallelRefIterator, ParallelIterator};

use std::io::{Read, Write};

#[derive(Debug, Clone)]
pub struct Log {
    pub description: String,
    url: String,
    is_google: bool,
}

#[derive(Debug, Deserialize)]
pub struct SignedCertificateTimestamp {
    sct_version: u8,
    id: String,
    timestamp: u64,
    extensions: String,
    signature: String,
}

impl SignedCertificateTimestamp {
    pub fn to_raw_bytes(&self) -> Vec<u8> {
        let mut b = Vec::new();
        b.write_u8(self.sct_version).unwrap();

        let log_id = base64::decode(&self.id).unwrap();
        b.write(&log_id).unwrap();

        b.write_u64::<BigEndian>(self.timestamp).unwrap();

        let extensions = base64::decode(&self.extensions).unwrap();
        assert!(extensions.len() <= 65535);
        b.write_u16::<BigEndian>(extensions.len() as u16)
            .unwrap();
        b.write(&extensions).unwrap();

        let signature = base64::decode(&self.signature).unwrap();
        b.write(&signature).unwrap();

        return b;
    }
}

#[derive(Deserialize)]
struct LogsResponseLogs {
    description: String,
    url: String,
    operated_by: Vec<u32>,
    disqualified_at: Option<u64>,
}

#[derive(Deserialize)]
struct LogsResponseOperators {
    name: String,
    id: u32,
}

#[derive(Deserialize)]
struct LogsResponse {
    logs: Vec<LogsResponseLogs>,
    operators: Vec<LogsResponseOperators>,
}

const LOG_LIST_URL: &'static str = "https://www.gstatic.com/ct/log_list/log_list.json";

pub fn fetch_trusted_ct_logs(http_client: &hyper::Client) -> Vec<Log> {
    let response = http_client.get(LOG_LIST_URL).send().unwrap();
    // Limit the response to 10MB at most, to be resillient to DoS.
    let logs_response: LogsResponse = serde_json::from_reader(response.take(10 * 1024 * 1024))
        .unwrap();

    let google_id = logs_response
        .operators
        .iter()
        .find(|o| o.name == "Google")
        .map(|o| o.id);

    return logs_response
               .logs
               .into_iter()
               .filter(|log| log.disqualified_at.is_none())
               .map(|log| {
                        Log {
                            url: log.url,
                            description: log.description,
                            is_google: log.operated_by.contains(&google_id.unwrap()),
                        }
                    })
               .collect();
}

fn submit_to_log(http_client: &hyper::Client,
                 url: &str,
                 payload: &[u8])
                 -> Option<SignedCertificateTimestamp> {
    let mut url = "https://".to_string() + url;
    if !url.ends_with("/") {
        url += "/";
    }
    url += "ct/v1/add-chain";
    let response = http_client
        .post(&url)
        .body(hyper::client::Body::BufBody(payload, payload.len()))
        .header(hyper::header::ContentType::json())
        .send();
    let response = match response {
        Ok(r) => r,
        // TODO: maybe not all of these should be silently ignored.
        Err(_) => return None,
    };

    // 400, 403, and probably some others generally indicate a log doesn't accept certs from this
    // root, or that the log isn't accepting new submissions.
    if response.status.is_client_error() {
        return None;
    }

    // Limt the response to 10MB (well above what would ever be needed) to be resilient to DoS in
    // the face of a dumb or malicious log.
    return Some(serde_json::from_reader(response.take(10 * 1024 * 1024)).unwrap());
}

#[derive(Serialize, Deserialize)]
pub struct AddChainRequest {
    pub chain: Vec<String>,
}

pub fn submit_cert_to_logs<'a>(http_client: &hyper::Client,
                               logs: &'a [Log],
                               cert: Vec<Vec<u8>>)
                               -> Vec<(&'a Log, SignedCertificateTimestamp)> {
    let payload = serde_json::to_vec(&AddChainRequest {
                                          chain: cert.iter().map(|r| base64::encode(r)).collect(),
                                      })
            .unwrap();

    return logs.par_iter()
               .filter_map(|log| {
                               let sct = submit_to_log(http_client, &log.url, &payload);
                               return sct.map(|s| (log, s));
                           })
               .collect();
}
<|endoftext|>


<commit_before>//! socketCAN support.
//!
//! The Linux kernel supports using CAN-devices through a network-like API
//! (see https://www.kernel.org/doc/Documentation/networking/can.txt). This
//! crate allows easy access to this functionality without having to wrestle
//! libc calls.

extern crate itertools;
extern crate libc;
extern crate nix;

use libc::{c_int, c_short, c_void, c_uint, socket, SOCK_RAW, close, bind,
           sockaddr, read, write, setsockopt, SOL_SOCKET, SO_RCVTIMEO,
           timeval, EINPROGRESS, SO_SNDTIMEO};
use itertools::Itertools;
use std::{error, fmt, io, time};
use std::mem::size_of;
use nix::net::if_::if_nametoindex;

/// Check an error return value for timeouts.
///
/// Due to the fact that timeouts are reported as errors, calling `read_frame`
/// on a socket with a timeout that does not receive a frame in time will
/// result in an error being returned. This trait adds a `should_retry` method
/// to `Error` and `Result` to check for this condition.
trait ShouldRetry {
    /// Check for timeout
    ///
    /// If `true`, the error is probably due to a timeout.
    fn should_retry(&self) -> bool;
}

impl ShouldRetry for io::Error {
    fn should_retry(&self) -> bool {
        match self.kind() {
            // EAGAIN, EINPROGRESS and EWOULDBLOCK are the three possible codes
            // returned when a timeout occurs. the stdlib already maps EAGAIN
            // and EWOULDBLOCK os WouldBlock
            io::ErrorKind::WouldBlock => true,
            // however, EINPROGRESS is also valid
            io::ErrorKind::Other => {
                if let Some(i) = self.raw_os_error() {
                    i == EINPROGRESS
                } else {
                    false
                }
            },
            _ => false,
        }
    }
}

impl<E: fmt::Debug> ShouldRetry for io::Result<E> {
    fn should_retry(&self) -> bool {
        if let &Err(ref e) = self {
            e.should_retry()
        } else {
            false
        }
    }
}

#[cfg(test)]
mod tests;

// constants stolen from C headers
const AF_CAN: c_int = 29;
const PF_CAN: c_int = 29;
const CAN_RAW: c_int = 1;

/// if set, indicate 29 bit extended format
pub const EFF_FLAG: u32 = 0x80000000;

/// remote transmission request flag
pub const RTR_FLAG: u32 = 0x40000000;

/// error flag
pub const ERR_FLAG: u32 = 0x20000000;

/// valid bits in standard frame id
pub const SFF_MASK: u32 = 0x000007ff;

/// valid bits in extended frame id
pub const EFF_MASK: u32 = 0x1fffffff;

/// valid bits in error frame
pub const ERR_MASK: u32 = 0x1fffffff;

fn c_timeval_new(t: time::Duration) -> timeval {
    timeval {
        tv_sec: t.as_secs() as i64,
        tv_usec: (t.subsec_nanos() / 1000) as i64,
    }
}

#[derive(Debug)]
#[repr(C)]
struct CANAddr {
    _af_can: c_short,
    if_index: c_int,  // address familiy,
    rx_id: u32,
    tx_id: u32,
}

#[derive(Debug)]
/// Errors opening socket
pub enum CANSocketOpenError {
    /// Device could not be found
    LookupError(nix::Error),

    /// System error while trying to look up device name
    IOError(io::Error),
}

impl fmt::Display for CANSocketOpenError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CANSocketOpenError::LookupError(ref e) =>
                write!(f, "CAN Device not found: {}", e),
            CANSocketOpenError::IOError(ref e) =>
                write!(f, "IO: {}", e),
        }
    }
}

impl error::Error for CANSocketOpenError {
    fn description(&self) -> &str {
        match *self {
            CANSocketOpenError::LookupError(_)
                => "can device not found",
            CANSocketOpenError::IOError(ref e)
                => e.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            CANSocketOpenError::LookupError(ref e) => Some(e),
            CANSocketOpenError::IOError(ref e) => Some(e),
        }
    }
}

#[derive(Debug, Copy, Clone)]
/// Error that occurs when creating CAN packets
pub enum ConstructionError {
    /// CAN ID was outside the range of valid IDs
    IDTooLarge,
    /// More than 8 Bytes of payload data were passed in
    TooMuchData,
}

impl fmt::Display for ConstructionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConstructionError::IDTooLarge
                => write!(f, "CAN ID too large"),
            ConstructionError::TooMuchData
                => write!(f, "Payload is larger than CAN maximum of 8 bytes")
        }
    }
}

impl error::Error for ConstructionError {
    fn description(&self) -> &str {
        match *self {
            ConstructionError::IDTooLarge => "can id too large",
            ConstructionError::TooMuchData => "too much data",
        }
    }
}

impl From<nix::Error> for CANSocketOpenError {
    fn from(e: nix::Error) -> CANSocketOpenError {
        CANSocketOpenError::LookupError(e)
    }
}

impl From<io::Error> for CANSocketOpenError {
    fn from(e: io::Error) -> CANSocketOpenError {
        CANSocketOpenError::IOError(e)
    }
}

/// A socket for a CAN device.
///
/// Will be closed upon deallocation. To close manually, use std::drop::Drop.
/// Internally this is just a wrapped file-descriptor.
pub struct CANSocket {
    fd: c_int,
}

impl CANSocket {
    /// Open a named CAN device.
    ///
    /// Usually the more common case, opens a socket can device by name, such
    /// as "vcan0" or "socan0".
    pub fn open(ifname: &str) -> Result<CANSocket, CANSocketOpenError> {
        let if_index = try!(if_nametoindex(ifname));
        CANSocket::open_if(if_index)
    }

    /// Open CAN device by interface number.
    ///
    /// Opens a CAN device by kernel interface number.
    pub fn open_if(if_index: c_uint) -> Result<CANSocket, CANSocketOpenError> {
        let addr = CANAddr{
            _af_can: AF_CAN as c_short,
            if_index: if_index as c_int,
            rx_id: 0, // ?
            tx_id: 0, // ?
        };

        // open socket
        let sock_fd;
        unsafe {
            sock_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
        }

        if sock_fd == -1 {
            return Err(CANSocketOpenError::from(io::Error::last_os_error()));
        }

        // bind it
        let bind_rv;
        unsafe {
            let sockaddr_ptr = &addr as *const CANAddr;
            bind_rv = bind(sock_fd, sockaddr_ptr as *const sockaddr,
                size_of::<CANAddr> () as u32);
        }

        if bind_rv == -1 {
            let e = io::Error::last_os_error();
            unsafe {
                close(sock_fd);
            }
            return Err(CANSocketOpenError::from(e))
        }

        Ok(CANSocket{fd: sock_fd})
    }

    fn close(&mut self) -> io::Result<()> {
        unsafe {
            let rv = close(self.fd);
            if rv != -1 {
                return Err(io::Error::last_os_error());
            }
        }
        Ok(())
    }

    /// Sets the read timeout on the socket
    ///
    /// For convenience, the result value can be checked using
    /// `ShouldRetry::should_retry` when a timeout is set.
    pub fn set_read_timeout(&self, duration: time::Duration) -> io::Result<()> {
        let rv = unsafe {
            let tv = c_timeval_new(duration);
            let tv_ptr: *const timeval = &tv as *const timeval;
            setsockopt(self.fd, SOL_SOCKET, SO_RCVTIMEO,
                       tv_ptr as *const c_void, size_of::<timeval>() as u32)
        };

        if rv != 0 {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }

    /// Sets the write timeout on the socket
    pub fn set_write_timeout(&self, duration: time::Duration) -> io::Result<()> {
        let rv = unsafe {
            let tv = c_timeval_new(duration);
            let tv_ptr: *const timeval = &tv as *const timeval;
            setsockopt(self.fd, SOL_SOCKET, SO_SNDTIMEO,
                       tv_ptr as *const c_void, size_of::<timeval>() as u32)
        };

        if rv != 0 {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }

    /// Blocking read a single can frame.
    pub fn read_frame(&self) -> io::Result<CANFrame> {
        let mut frame = CANFrame{
            _id: 0,
            _data_len: 0,
            _pad: 0,
            _res0: 0,
            _res1: 0,
            _data: [0; 8],
        };

        let read_rv = unsafe {
            let frame_ptr = &mut frame as *mut CANFrame;
            read(self.fd, frame_ptr as *mut c_void, size_of::<CANFrame>())
        };

        if read_rv as usize != size_of::<CANFrame>() {
            return Err(io::Error::last_os_error());
        }

        Ok(frame)
    }

    /// Blocking write a single can frame.
    pub fn write_frame(&self, frame: &CANFrame) -> io::Result<()> {
        // not a mutable reference needed (see std::net::UdpSocket) for
        // a comparison
        // debug!("Sending: {:?}", frame);

        let write_rv = unsafe {
            let frame_ptr = frame as *const CANFrame;
            write(self.fd, frame_ptr as *const c_void, size_of::<CANFrame>())
        };

        if write_rv as usize != size_of::<CANFrame>() {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }
}

impl Drop for CANSocket {
    fn drop(&mut self) {
        self.close().ok();  // ignore result
    }
}

/// CANFrame
///
/// Uses the same memory layout as the underlying kernel struct for performance
/// reasons.
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct CANFrame {
    /// 32 bit CAN_ID + EFF/RTR/ERR flags
    _id: u32,

    /// data length. Bytes beyond are not valid
    _data_len: u8,

    /// padding
    _pad: u8,

    /// reserved
    _res0: u8,

    /// reserved
    _res1: u8,

    /// buffer for data
    _data: [u8; 8],
}

impl CANFrame {
    pub fn new(id: u32, data: &[u8], rtr: bool, err: bool) -> Result<CANFrame,
    ConstructionError> {
        let mut _id = id;

        if data.len() > 8 {
            return Err(ConstructionError::TooMuchData)
        }

        if id > EFF_MASK {
            return Err(ConstructionError::IDTooLarge)
        }

        // set EFF_FLAG on large message
        if id > SFF_MASK {
            _id |= EFF_FLAG;
        }

        if rtr {
            _id |= RTR_FLAG;
        }

        if err {
            _id |= ERR_FLAG;
        }

        let mut full_data = [0; 8];

        // not cool =/
        for (n, c) in data.iter().enumerate() {
            full_data[n] = *c;
        }

        Ok(CANFrame{
            _id: _id,
            _data_len: data.len() as u8,
            _pad: 0,
            _res0: 0,
            _res1: 0,
            _data: full_data,
        })
    }

    /// Return the actual CAN ID (without EFF/RTR/ERR flags)
    pub fn id(&self) -> u32 {
        if self.is_extended() {
            self._id & EFF_MASK
        } else {
            self._id & SFF_MASK
        }
    }

    /// Return the error message
    pub fn err(&self) -> u32 {
        return self._id & ERR_MASK
    }

    /// Check if frame uses 29 bit extended frame format
    pub fn is_extended(&self) -> bool {
        self._id & EFF_FLAG != 0
    }

    /// Check if frame is an error message
    pub fn is_error(&self) -> bool {
        self._id & ERR_FLAG != 0
    }

    /// Check if frame is a remote transmission request
    pub fn is_rtr(&self) -> bool {
        self._id & RTR_FLAG != 0
    }

    /// A slice into the actual data. Slice will always be <= 8 bytes in length
    pub fn data(&self) -> &[u8] {
        &self._data[..(self._data_len as usize)]
    }
}

impl fmt::UpperHex for CANFrame {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(write!(f, "{:X}#", self.id()));

        let mut parts = self.data().iter().map(|v| format!("{:02X}", v));

        let sep = if f.alternate() { " " } else { " " };
        write!(f, "{}", parts.join(sep))
    }
}
<commit_msg>Remove obsolete mod tests;<commit_after>//! socketCAN support.
//!
//! The Linux kernel supports using CAN-devices through a network-like API
//! (see https://www.kernel.org/doc/Documentation/networking/can.txt). This
//! crate allows easy access to this functionality without having to wrestle
//! libc calls.

extern crate itertools;
extern crate libc;
extern crate nix;

use libc::{c_int, c_short, c_void, c_uint, socket, SOCK_RAW, close, bind,
           sockaddr, read, write, setsockopt, SOL_SOCKET, SO_RCVTIMEO,
           timeval, EINPROGRESS, SO_SNDTIMEO};
use itertools::Itertools;
use std::{error, fmt, io, time};
use std::mem::size_of;
use nix::net::if_::if_nametoindex;

/// Check an error return value for timeouts.
///
/// Due to the fact that timeouts are reported as errors, calling `read_frame`
/// on a socket with a timeout that does not receive a frame in time will
/// result in an error being returned. This trait adds a `should_retry` method
/// to `Error` and `Result` to check for this condition.
trait ShouldRetry {
    /// Check for timeout
    ///
    /// If `true`, the error is probably due to a timeout.
    fn should_retry(&self) -> bool;
}

impl ShouldRetry for io::Error {
    fn should_retry(&self) -> bool {
        match self.kind() {
            // EAGAIN, EINPROGRESS and EWOULDBLOCK are the three possible codes
            // returned when a timeout occurs. the stdlib already maps EAGAIN
            // and EWOULDBLOCK os WouldBlock
            io::ErrorKind::WouldBlock => true,
            // however, EINPROGRESS is also valid
            io::ErrorKind::Other => {
                if let Some(i) = self.raw_os_error() {
                    i == EINPROGRESS
                } else {
                    false
                }
            },
            _ => false,
        }
    }
}

impl<E: fmt::Debug> ShouldRetry for io::Result<E> {
    fn should_retry(&self) -> bool {
        if let &Err(ref e) = self {
            e.should_retry()
        } else {
            false
        }
    }
}

// constants stolen from C headers
const AF_CAN: c_int = 29;
const PF_CAN: c_int = 29;
const CAN_RAW: c_int = 1;

/// if set, indicate 29 bit extended format
pub const EFF_FLAG: u32 = 0x80000000;

/// remote transmission request flag
pub const RTR_FLAG: u32 = 0x40000000;

/// error flag
pub const ERR_FLAG: u32 = 0x20000000;

/// valid bits in standard frame id
pub const SFF_MASK: u32 = 0x000007ff;

/// valid bits in extended frame id
pub const EFF_MASK: u32 = 0x1fffffff;

/// valid bits in error frame
pub const ERR_MASK: u32 = 0x1fffffff;

fn c_timeval_new(t: time::Duration) -> timeval {
    timeval {
        tv_sec: t.as_secs() as i64,
        tv_usec: (t.subsec_nanos() / 1000) as i64,
    }
}

#[derive(Debug)]
#[repr(C)]
struct CANAddr {
    _af_can: c_short,
    if_index: c_int,  // address familiy,
    rx_id: u32,
    tx_id: u32,
}

#[derive(Debug)]
/// Errors opening socket
pub enum CANSocketOpenError {
    /// Device could not be found
    LookupError(nix::Error),

    /// System error while trying to look up device name
    IOError(io::Error),
}

impl fmt::Display for CANSocketOpenError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CANSocketOpenError::LookupError(ref e) =>
                write!(f, "CAN Device not found: {}", e),
            CANSocketOpenError::IOError(ref e) =>
                write!(f, "IO: {}", e),
        }
    }
}

impl error::Error for CANSocketOpenError {
    fn description(&self) -> &str {
        match *self {
            CANSocketOpenError::LookupError(_)
                => "can device not found",
            CANSocketOpenError::IOError(ref e)
                => e.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            CANSocketOpenError::LookupError(ref e) => Some(e),
            CANSocketOpenError::IOError(ref e) => Some(e),
        }
    }
}

#[derive(Debug, Copy, Clone)]
/// Error that occurs when creating CAN packets
pub enum ConstructionError {
    /// CAN ID was outside the range of valid IDs
    IDTooLarge,
    /// More than 8 Bytes of payload data were passed in
    TooMuchData,
}

impl fmt::Display for ConstructionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConstructionError::IDTooLarge
                => write!(f, "CAN ID too large"),
            ConstructionError::TooMuchData
                => write!(f, "Payload is larger than CAN maximum of 8 bytes")
        }
    }
}

impl error::Error for ConstructionError {
    fn description(&self) -> &str {
        match *self {
            ConstructionError::IDTooLarge => "can id too large",
            ConstructionError::TooMuchData => "too much data",
        }
    }
}

impl From<nix::Error> for CANSocketOpenError {
    fn from(e: nix::Error) -> CANSocketOpenError {
        CANSocketOpenError::LookupError(e)
    }
}

impl From<io::Error> for CANSocketOpenError {
    fn from(e: io::Error) -> CANSocketOpenError {
        CANSocketOpenError::IOError(e)
    }
}

/// A socket for a CAN device.
///
/// Will be closed upon deallocation. To close manually, use std::drop::Drop.
/// Internally this is just a wrapped file-descriptor.
pub struct CANSocket {
    fd: c_int,
}

impl CANSocket {
    /// Open a named CAN device.
    ///
    /// Usually the more common case, opens a socket can device by name, such
    /// as "vcan0" or "socan0".
    pub fn open(ifname: &str) -> Result<CANSocket, CANSocketOpenError> {
        let if_index = try!(if_nametoindex(ifname));
        CANSocket::open_if(if_index)
    }

    /// Open CAN device by interface number.
    ///
    /// Opens a CAN device by kernel interface number.
    pub fn open_if(if_index: c_uint) -> Result<CANSocket, CANSocketOpenError> {
        let addr = CANAddr{
            _af_can: AF_CAN as c_short,
            if_index: if_index as c_int,
            rx_id: 0, // ?
            tx_id: 0, // ?
        };

        // open socket
        let sock_fd;
        unsafe {
            sock_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
        }

        if sock_fd == -1 {
            return Err(CANSocketOpenError::from(io::Error::last_os_error()));
        }

        // bind it
        let bind_rv;
        unsafe {
            let sockaddr_ptr = &addr as *const CANAddr;
            bind_rv = bind(sock_fd, sockaddr_ptr as *const sockaddr,
                size_of::<CANAddr> () as u32);
        }

        if bind_rv == -1 {
            let e = io::Error::last_os_error();
            unsafe {
                close(sock_fd);
            }
            return Err(CANSocketOpenError::from(e))
        }

        Ok(CANSocket{fd: sock_fd})
    }

    fn close(&mut self) -> io::Result<()> {
        unsafe {
            let rv = close(self.fd);
            if rv != -1 {
                return Err(io::Error::last_os_error());
            }
        }
        Ok(())
    }

    /// Sets the read timeout on the socket
    ///
    /// For convenience, the result value can be checked using
    /// `ShouldRetry::should_retry` when a timeout is set.
    pub fn set_read_timeout(&self, duration: time::Duration) -> io::Result<()> {
        let rv = unsafe {
            let tv = c_timeval_new(duration);
            let tv_ptr: *const timeval = &tv as *const timeval;
            setsockopt(self.fd, SOL_SOCKET, SO_RCVTIMEO,
                       tv_ptr as *const c_void, size_of::<timeval>() as u32)
        };

        if rv != 0 {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }

    /// Sets the write timeout on the socket
    pub fn set_write_timeout(&self, duration: time::Duration) -> io::Result<()> {
        let rv = unsafe {
            let tv = c_timeval_new(duration);
            let tv_ptr: *const timeval = &tv as *const timeval;
            setsockopt(self.fd, SOL_SOCKET, SO_SNDTIMEO,
                       tv_ptr as *const c_void, size_of::<timeval>() as u32)
        };

        if rv != 0 {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }

    /// Blocking read a single can frame.
    pub fn read_frame(&self) -> io::Result<CANFrame> {
        let mut frame = CANFrame{
            _id: 0,
            _data_len: 0,
            _pad: 0,
            _res0: 0,
            _res1: 0,
            _data: [0; 8],
        };

        let read_rv = unsafe {
            let frame_ptr = &mut frame as *mut CANFrame;
            read(self.fd, frame_ptr as *mut c_void, size_of::<CANFrame>())
        };

        if read_rv as usize != size_of::<CANFrame>() {
            return Err(io::Error::last_os_error());
        }

        Ok(frame)
    }

    /// Blocking write a single can frame.
    pub fn write_frame(&self, frame: &CANFrame) -> io::Result<()> {
        // not a mutable reference needed (see std::net::UdpSocket) for
        // a comparison
        // debug!("Sending: {:?}", frame);

        let write_rv = unsafe {
            let frame_ptr = frame as *const CANFrame;
            write(self.fd, frame_ptr as *const c_void, size_of::<CANFrame>())
        };

        if write_rv as usize != size_of::<CANFrame>() {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }
}

impl Drop for CANSocket {
    fn drop(&mut self) {
        self.close().ok();  // ignore result
    }
}

/// CANFrame
///
/// Uses the same memory layout as the underlying kernel struct for performance
/// reasons.
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct CANFrame {
    /// 32 bit CAN_ID + EFF/RTR/ERR flags
    _id: u32,

    /// data length. Bytes beyond are not valid
    _data_len: u8,

    /// padding
    _pad: u8,

    /// reserved
    _res0: u8,

    /// reserved
    _res1: u8,

    /// buffer for data
    _data: [u8; 8],
}

impl CANFrame {
    pub fn new(id: u32, data: &[u8], rtr: bool, err: bool) -> Result<CANFrame,
    ConstructionError> {
        let mut _id = id;

        if data.len() > 8 {
            return Err(ConstructionError::TooMuchData)
        }

        if id > EFF_MASK {
            return Err(ConstructionError::IDTooLarge)
        }

        // set EFF_FLAG on large message
        if id > SFF_MASK {
            _id |= EFF_FLAG;
        }

        if rtr {
            _id |= RTR_FLAG;
        }

        if err {
            _id |= ERR_FLAG;
        }

        let mut full_data = [0; 8];

        // not cool =/
        for (n, c) in data.iter().enumerate() {
            full_data[n] = *c;
        }

        Ok(CANFrame{
            _id: _id,
            _data_len: data.len() as u8,
            _pad: 0,
            _res0: 0,
            _res1: 0,
            _data: full_data,
        })
    }

    /// Return the actual CAN ID (without EFF/RTR/ERR flags)
    pub fn id(&self) -> u32 {
        if self.is_extended() {
            self._id & EFF_MASK
        } else {
            self._id & SFF_MASK
        }
    }

    /// Return the error message
    pub fn err(&self) -> u32 {
        return self._id & ERR_MASK
    }

    /// Check if frame uses 29 bit extended frame format
    pub fn is_extended(&self) -> bool {
        self._id & EFF_FLAG != 0
    }

    /// Check if frame is an error message
    pub fn is_error(&self) -> bool {
        self._id & ERR_FLAG != 0
    }

    /// Check if frame is a remote transmission request
    pub fn is_rtr(&self) -> bool {
        self._id & RTR_FLAG != 0
    }

    /// A slice into the actual data. Slice will always be <= 8 bytes in length
    pub fn data(&self) -> &[u8] {
        &self._data[..(self._data_len as usize)]
    }
}

impl fmt::UpperHex for CANFrame {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(write!(f, "{:X}#", self.id()));

        let mut parts = self.data().iter().map(|v| format!("{:02X}", v));

        let sep = if f.alternate() { " " } else { " " };
        write!(f, "{}", parts.join(sep))
    }
}
<|endoftext|>


<commit_before>#![feature(core)]

//! A TCOD back-end for the Piston game engine.

extern crate input;
#[macro_use]
extern crate quack;
extern crate tcod;
extern crate window;

use input::{ keyboard, Button, MouseButton, Input, Motion };
use tcod::Console;
use window::{
    WindowSettings,
    ShouldClose, Size, PollEvent, SwapBuffers,
    CaptureCursor, DrawSize, Title, ExitOnEsc
};

pub struct TcodWindow {
    pub window: tcod::Console,
    title: String,
    should_close: bool,
    mouse_relative: Option<(f64, f64)>,
    mouse_state_prev: tcod::MouseState,
    exit_on_esc: bool,
}

impl TcodWindow {
    pub fn new(settings: WindowSettings) -> TcodWindow {
        let window = Console::init_root(
            settings.size[0] as i32,
            settings.size[1] as i32,
            &*settings.title,
            false
        );

        TcodWindow {
            window: window,
            title: settings.title,
            should_close: false,
            mouse_relative: None,
            // TODO: determine if there's a better way to initialize the
            // MouseState.
            mouse_state_prev: tcod::MouseState {
                x: 0is,
                y: 0is,
                dx: 0is,
                dy: 0is,
                cx: 0is,
                cy: 0is,
                dcx: 0is,
                dcy: 0is,
                lbutton: false,
                rbutton: false,
                mbutton: false,
                lbutton_pressed: false,
                rbutton_pressed: false,
                mbutton_pressed: false,
                wheel_up: false,
                wheel_down: false,
            },
            exit_on_esc: settings.exit_on_esc,
        }
    }

    fn poll_event(&mut self) -> Option<Input> {
        use tcod::KeyCode;
        use tcod::Key::Special;
        use tcod::system::Event;

        match self.mouse_relative {
            Some((x, y)) => {
                self.mouse_relative = None;
                return Some(Input::Move(Motion::MouseRelative(x, y)));
            }
            None => {}
        }
        let (flags, event) = tcod::system::check_for_event(tcod::ANY);
        match (flags, event) {
            (tcod::KEY_PRESS, Event::Key(ref key_state)) => {
                if self.exit_on_esc
                && key_state.key == Special(KeyCode::Escape) {
                    self.should_close = true;
                } else {
                    return Some(Input::Press(Button::Keyboard(tcod_map_key(key_state.key))));
                }
            },
            (tcod::KEY_RELEASE, Event::Key(ref key_state)) => {
                return Some(Input::Release(Button::Keyboard(tcod_map_key(key_state.key))));
            },
            (tcod::MOUSE_PRESS, Event::Mouse(ref mouse_state)) => {
                let button = tcod_map_mouse(self.mouse_state_prev, mouse_state);
                self.mouse_state_prev = *mouse_state.clone();
                return Some(Input::Press(Button::Mouse(button)));
            },
            (tcod::MOUSE_RELEASE, Event::Mouse(ref mouse_state)) => {
                let button = tcod_map_mouse(self.mouse_state_prev, mouse_state);
                self.mouse_state_prev = *mouse_state.clone();
                return Some(Input::Release(Button::Mouse(button)));
            },
            (tcod::MOUSE_MOVE, Event::Mouse(ref mouse_state)) => {
                // TODO: figure out why mouse_state.dx and mouse_state.dy
                // don't work.
                self.mouse_relative = Some((
                    (mouse_state.x - self.mouse_state_prev.x) as f64,
                    (mouse_state.y - self.mouse_state_prev.y) as f64
                ));
                self.mouse_state_prev = *mouse_state.clone();
                return Some(Input::Move(Motion::MouseCursor(mouse_state.x as f64, mouse_state.y as f64)));
            },
            (_, _) => {}
        }
        None
    }
}

quack! {
    _obj: TcodWindow[]
    get:
        fn () -> ShouldClose [] { ShouldClose(_obj.should_close) }
        fn () -> Size [] {
            let (w, h) = (_obj.window.width(), _obj.window.height());
            Size([w as u32, h as u32])
        }
        fn () -> DrawSize [] {
            //let (w, h) = _obj.window.get_drawable_size();
            let (w, h) = (_obj.window.width(), _obj.window.height());
            DrawSize([w as u32, h as u32])
        }
        // TODO: Remove the .clone()
        fn () -> Title [] { Title(_obj.title.clone()) }
        fn () -> ExitOnEsc [] { ExitOnEsc(_obj.exit_on_esc) }
    set:
        fn (val: CaptureCursor) [] {
            //sdl2::mouse::set_relative_mouse_mode(val.0)
        }
        fn (val: ShouldClose) [] { _obj.should_close = val.0 }
        fn (val: Title) [] {
            Console::set_window_title(&*val.0);
            _obj.title = val.0
        }
        fn (val: ExitOnEsc) [] { _obj.exit_on_esc = val.0 }
    action:
        fn (__: SwapBuffers) -> () [] {
            //_obj.window.gl_swap_window()
            Console::flush()
        }
        fn (__: PollEvent) -> Option<Input> [] { _obj.poll_event() }
}

pub fn tcod_map_key(key: tcod::Key) -> keyboard::Key {
    use input::keyboard::Key;
    use std::num::FromPrimitive;
    match key {
        tcod::Key::Printable(keycode) => FromPrimitive::from_u64(keycode as u64).unwrap(),
        tcod::Key::Special(keycode) => {
            match keycode {
                tcod::KeyCode::NoKey => Key::Unknown,
                tcod::KeyCode::Escape => Key::Escape,
                tcod::KeyCode::Backspace => Key::Backspace,
                tcod::KeyCode::Tab => Key::Tab,
                tcod::KeyCode::Enter => Key::Return,
                tcod::KeyCode::Shift => Key::LShift, // TODO: find RShift
                tcod::KeyCode::Control => Key::LCtrl,
                tcod::KeyCode::Alt => Key::LAlt,
                tcod::KeyCode::Pause => Key::Pause,
                tcod::KeyCode::Capslock => Key::CapsLock,
                tcod::KeyCode::Pageup => Key::PageUp,
                tcod::KeyCode::Pagedown => Key::PageDown,
                tcod::KeyCode::End => Key::End,
                tcod::KeyCode::Home => Key::Home,
                tcod::KeyCode::Up => Key::Up,
                tcod::KeyCode::Left => Key::Left,
                tcod::KeyCode::Right => Key::Right,
                tcod::KeyCode::Down => Key::Down,
                tcod::KeyCode::PrintScreen => Key::PrintScreen,
                tcod::KeyCode::Insert => Key::Insert,
                tcod::KeyCode::Delete => Key::Delete,
                tcod::KeyCode::LeftWin => Key::LGui,
                tcod::KeyCode::RightWin => Key::RGui,
                tcod::KeyCode::Apps => Key::Application,
                // The numbers on the alphanum section of the keyboard
                tcod::KeyCode::Number0 => Key::D0,
                tcod::KeyCode::Number1 => Key::D1,
                tcod::KeyCode::Number2 => Key::D2,
                tcod::KeyCode::Number3 => Key::D3,
                tcod::KeyCode::Number4 => Key::D4,
                tcod::KeyCode::Number5 => Key::D5,
                tcod::KeyCode::Number6 => Key::D6,
                tcod::KeyCode::Number7 => Key::D7,
                tcod::KeyCode::Number8 => Key::D8,
                tcod::KeyCode::Number9 => Key::D9,
                // The numbers on the numeric keypad
                tcod::KeyCode::NumPad0 => Key::NumPad0,
                tcod::KeyCode::NumPad1 => Key::NumPad1,
                tcod::KeyCode::NumPad2 => Key::NumPad2,
                tcod::KeyCode::NumPad3 => Key::NumPad3,
                tcod::KeyCode::NumPad4 => Key::NumPad4,
                tcod::KeyCode::NumPad5 => Key::NumPad5,
                tcod::KeyCode::NumPad6 => Key::NumPad6,
                tcod::KeyCode::NumPad7 => Key::NumPad7,
                tcod::KeyCode::NumPad8 => Key::NumPad8,
                tcod::KeyCode::NumPad9 => Key::NumPad9,
                tcod::KeyCode::NumPadAdd => Key::NumPadPlus,
                tcod::KeyCode::NumPadSubtract => Key::NumPadMinus,
                tcod::KeyCode::NumPadDivide => Key::NumPadDivide,
                tcod::KeyCode::NumPadMultiply => Key::NumPadMultiply,
                tcod::KeyCode::NumPadDecimal => Key::NumPadDecimal,
                tcod::KeyCode::NumPadEnter => Key::NumPadEnter,
                tcod::KeyCode::F1 => Key::F1,
                tcod::KeyCode::F2 => Key::F2,
                tcod::KeyCode::F3 => Key::F3,
                tcod::KeyCode::F4 => Key::F4,
                tcod::KeyCode::F5 => Key::F5,
                tcod::KeyCode::F6 => Key::F6,
                tcod::KeyCode::F7 => Key::F7,
                tcod::KeyCode::F8 => Key::F8,
                tcod::KeyCode::F9 => Key::F9,
                tcod::KeyCode::F10 => Key::F10,
                tcod::KeyCode::F11 => Key::F11,
                tcod::KeyCode::F12 => Key::F12,
                tcod::KeyCode::NUMLOCK => Key::NumLockClear,
                tcod::KeyCode::SCROLLLOCK => Key::ScrollLock,
                tcod::KeyCode::Spacebar => Key::Space,
                // TODO: check if this is correct.
                tcod::KeyCode::Char => Key::Unknown,
            }
        }
    }
}

pub fn tcod_map_mouse(prev_state: tcod::MouseState, state: &tcod::MouseState) -> MouseButton {
    if prev_state.lbutton != state.lbutton { return MouseButton::Left }
    else if prev_state.rbutton != state.rbutton { return MouseButton::Right }
    else if prev_state.mbutton != state.mbutton { return MouseButton::Middle }
    else { return MouseButton::Unknown }
}
<commit_msg>Fix is -> isize deprecation<commit_after>#![feature(core)]

//! A TCOD back-end for the Piston game engine.

extern crate input;
#[macro_use]
extern crate quack;
extern crate tcod;
extern crate window;

use input::{ keyboard, Button, MouseButton, Input, Motion };
use tcod::Console;
use window::{
    WindowSettings,
    ShouldClose, Size, PollEvent, SwapBuffers,
    CaptureCursor, DrawSize, Title, ExitOnEsc
};

pub struct TcodWindow {
    pub window: tcod::Console,
    title: String,
    should_close: bool,
    mouse_relative: Option<(f64, f64)>,
    mouse_state_prev: tcod::MouseState,
    exit_on_esc: bool,
}

impl TcodWindow {
    pub fn new(settings: WindowSettings) -> TcodWindow {
        let window = Console::init_root(
            settings.size[0] as i32,
            settings.size[1] as i32,
            &*settings.title,
            false
        );

        TcodWindow {
            window: window,
            title: settings.title,
            should_close: false,
            mouse_relative: None,
            // TODO: determine if there's a better way to initialize the
            // MouseState.
            mouse_state_prev: tcod::MouseState {
                x: 0isize,
                y: 0isize,
                dx: 0isize,
                dy: 0isize,
                cx: 0isize,
                cy: 0isize,
                dcx: 0isize,
                dcy: 0isize,
                lbutton: false,
                rbutton: false,
                mbutton: false,
                lbutton_pressed: false,
                rbutton_pressed: false,
                mbutton_pressed: false,
                wheel_up: false,
                wheel_down: false,
            },
            exit_on_esc: settings.exit_on_esc,
        }
    }

    fn poll_event(&mut self) -> Option<Input> {
        use tcod::KeyCode;
        use tcod::Key::Special;
        use tcod::system::Event;

        match self.mouse_relative {
            Some((x, y)) => {
                self.mouse_relative = None;
                return Some(Input::Move(Motion::MouseRelative(x, y)));
            }
            None => {}
        }
        let (flags, event) = tcod::system::check_for_event(tcod::ANY);
        match (flags, event) {
            (tcod::KEY_PRESS, Event::Key(ref key_state)) => {
                if self.exit_on_esc
                && key_state.key == Special(KeyCode::Escape) {
                    self.should_close = true;
                } else {
                    return Some(Input::Press(Button::Keyboard(tcod_map_key(key_state.key))));
                }
            },
            (tcod::KEY_RELEASE, Event::Key(ref key_state)) => {
                return Some(Input::Release(Button::Keyboard(tcod_map_key(key_state.key))));
            },
            (tcod::MOUSE_PRESS, Event::Mouse(ref mouse_state)) => {
                let button = tcod_map_mouse(self.mouse_state_prev, mouse_state);
                self.mouse_state_prev = *mouse_state.clone();
                return Some(Input::Press(Button::Mouse(button)));
            },
            (tcod::MOUSE_RELEASE, Event::Mouse(ref mouse_state)) => {
                let button = tcod_map_mouse(self.mouse_state_prev, mouse_state);
                self.mouse_state_prev = *mouse_state.clone();
                return Some(Input::Release(Button::Mouse(button)));
            },
            (tcod::MOUSE_MOVE, Event::Mouse(ref mouse_state)) => {
                // TODO: figure out why mouse_state.dx and mouse_state.dy
                // don't work.
                self.mouse_relative = Some((
                    (mouse_state.x - self.mouse_state_prev.x) as f64,
                    (mouse_state.y - self.mouse_state_prev.y) as f64
                ));
                self.mouse_state_prev = *mouse_state.clone();
                return Some(Input::Move(Motion::MouseCursor(mouse_state.x as f64, mouse_state.y as f64)));
            },
            (_, _) => {}
        }
        None
    }
}

quack! {
    _obj: TcodWindow[]
    get:
        fn () -> ShouldClose [] { ShouldClose(_obj.should_close) }
        fn () -> Size [] {
            let (w, h) = (_obj.window.width(), _obj.window.height());
            Size([w as u32, h as u32])
        }
        fn () -> DrawSize [] {
            //let (w, h) = _obj.window.get_drawable_size();
            let (w, h) = (_obj.window.width(), _obj.window.height());
            DrawSize([w as u32, h as u32])
        }
        // TODO: Remove the .clone()
        fn () -> Title [] { Title(_obj.title.clone()) }
        fn () -> ExitOnEsc [] { ExitOnEsc(_obj.exit_on_esc) }
    set:
        fn (val: CaptureCursor) [] {
            //sdl2::mouse::set_relative_mouse_mode(val.0)
        }
        fn (val: ShouldClose) [] { _obj.should_close = val.0 }
        fn (val: Title) [] {
            Console::set_window_title(&*val.0);
            _obj.title = val.0
        }
        fn (val: ExitOnEsc) [] { _obj.exit_on_esc = val.0 }
    action:
        fn (__: SwapBuffers) -> () [] {
            //_obj.window.gl_swap_window()
            Console::flush()
        }
        fn (__: PollEvent) -> Option<Input> [] { _obj.poll_event() }
}

pub fn tcod_map_key(key: tcod::Key) -> keyboard::Key {
    use input::keyboard::Key;
    use std::num::FromPrimitive;
    match key {
        tcod::Key::Printable(keycode) => FromPrimitive::from_u64(keycode as u64).unwrap(),
        tcod::Key::Special(keycode) => {
            match keycode {
                tcod::KeyCode::NoKey => Key::Unknown,
                tcod::KeyCode::Escape => Key::Escape,
                tcod::KeyCode::Backspace => Key::Backspace,
                tcod::KeyCode::Tab => Key::Tab,
                tcod::KeyCode::Enter => Key::Return,
                tcod::KeyCode::Shift => Key::LShift, // TODO: find RShift
                tcod::KeyCode::Control => Key::LCtrl,
                tcod::KeyCode::Alt => Key::LAlt,
                tcod::KeyCode::Pause => Key::Pause,
                tcod::KeyCode::Capslock => Key::CapsLock,
                tcod::KeyCode::Pageup => Key::PageUp,
                tcod::KeyCode::Pagedown => Key::PageDown,
                tcod::KeyCode::End => Key::End,
                tcod::KeyCode::Home => Key::Home,
                tcod::KeyCode::Up => Key::Up,
                tcod::KeyCode::Left => Key::Left,
                tcod::KeyCode::Right => Key::Right,
                tcod::KeyCode::Down => Key::Down,
                tcod::KeyCode::PrintScreen => Key::PrintScreen,
                tcod::KeyCode::Insert => Key::Insert,
                tcod::KeyCode::Delete => Key::Delete,
                tcod::KeyCode::LeftWin => Key::LGui,
                tcod::KeyCode::RightWin => Key::RGui,
                tcod::KeyCode::Apps => Key::Application,
                // The numbers on the alphanum section of the keyboard
                tcod::KeyCode::Number0 => Key::D0,
                tcod::KeyCode::Number1 => Key::D1,
                tcod::KeyCode::Number2 => Key::D2,
                tcod::KeyCode::Number3 => Key::D3,
                tcod::KeyCode::Number4 => Key::D4,
                tcod::KeyCode::Number5 => Key::D5,
                tcod::KeyCode::Number6 => Key::D6,
                tcod::KeyCode::Number7 => Key::D7,
                tcod::KeyCode::Number8 => Key::D8,
                tcod::KeyCode::Number9 => Key::D9,
                // The numbers on the numeric keypad
                tcod::KeyCode::NumPad0 => Key::NumPad0,
                tcod::KeyCode::NumPad1 => Key::NumPad1,
                tcod::KeyCode::NumPad2 => Key::NumPad2,
                tcod::KeyCode::NumPad3 => Key::NumPad3,
                tcod::KeyCode::NumPad4 => Key::NumPad4,
                tcod::KeyCode::NumPad5 => Key::NumPad5,
                tcod::KeyCode::NumPad6 => Key::NumPad6,
                tcod::KeyCode::NumPad7 => Key::NumPad7,
                tcod::KeyCode::NumPad8 => Key::NumPad8,
                tcod::KeyCode::NumPad9 => Key::NumPad9,
                tcod::KeyCode::NumPadAdd => Key::NumPadPlus,
                tcod::KeyCode::NumPadSubtract => Key::NumPadMinus,
                tcod::KeyCode::NumPadDivide => Key::NumPadDivide,
                tcod::KeyCode::NumPadMultiply => Key::NumPadMultiply,
                tcod::KeyCode::NumPadDecimal => Key::NumPadDecimal,
                tcod::KeyCode::NumPadEnter => Key::NumPadEnter,
                tcod::KeyCode::F1 => Key::F1,
                tcod::KeyCode::F2 => Key::F2,
                tcod::KeyCode::F3 => Key::F3,
                tcod::KeyCode::F4 => Key::F4,
                tcod::KeyCode::F5 => Key::F5,
                tcod::KeyCode::F6 => Key::F6,
                tcod::KeyCode::F7 => Key::F7,
                tcod::KeyCode::F8 => Key::F8,
                tcod::KeyCode::F9 => Key::F9,
                tcod::KeyCode::F10 => Key::F10,
                tcod::KeyCode::F11 => Key::F11,
                tcod::KeyCode::F12 => Key::F12,
                tcod::KeyCode::NUMLOCK => Key::NumLockClear,
                tcod::KeyCode::SCROLLLOCK => Key::ScrollLock,
                tcod::KeyCode::Spacebar => Key::Space,
                // TODO: check if this is correct.
                tcod::KeyCode::Char => Key::Unknown,
            }
        }
    }
}

pub fn tcod_map_mouse(prev_state: tcod::MouseState, state: &tcod::MouseState) -> MouseButton {
    if prev_state.lbutton != state.lbutton { return MouseButton::Left }
    else if prev_state.rbutton != state.rbutton { return MouseButton::Right }
    else if prev_state.mbutton != state.mbutton { return MouseButton::Middle }
    else { return MouseButton::Unknown }
}
<|endoftext|>