<commit_before>use libc::*;
use std::borrow::{Cow, IntoCow};
use std::mem;
use std::mem::transmute as cast;
use std::ffi::CStr;
use std::collections::VecDeque;
use Joystick;

extern {
    fn emscripten_get_num_gamepads() -> c_int;
    fn emscripten_get_gamepad_status(index: c_int, state: *mut NativeEvent) -> c_int;
}

#[repr(C)]
pub struct NativeEvent {
    timestamp: f64,
    num_axes: c_int,
    num_buttons: c_int,
    axis: [f64; 64],
    analog_button: [f64; 64],
    digital_button: [bool; 64],
    connected: bool,
    index: c_long,
    id: [c_char; 64],
    mapping: [c_char; 64]
}

pub struct NativeJoystick {
    last: NativeEvent,
	events: VecDeque<::Event>
}

fn os_error(code: c_int) -> &'static str {
    match code {
        1 => "Deferred",
        -1 => "Not supported",
        -2 => "Failed not deferred",
        -3 => "Invalid target",
        -4 => "Unknown target",
        -5 => "Invalid param",
        -6 => "Failed",
        -7 => "No data",
        _ => "No error"
    }
}

impl ::Joystick for NativeJoystick {
    type WithState = NativeJoystick;
    type NativeEvent = ::Event;
    fn new(index: u8) -> Result<NativeJoystick, &'static str> {
        unsafe {
            let mut state = mem::uninitialized();
            let code = emscripten_get_gamepad_status(index as c_int, &mut state);
            if code != 0 {
                Err(os_error(code))
            } else {
                Ok(NativeJoystick {
                    last: state,
                    events: VecDeque::with_capacity(16)
                })
            }
        }
    }
    fn is_connected(&self) -> bool {
        self.last.connected
    }
    fn get_id(&self) -> Cow<str> {
        unsafe {
            use std::str;
            let bytes = CStr::from_ptr(self.last.id.as_ptr()).to_bytes();
            str::from_utf8_unchecked(bytes).into_cow()
        }
    }
    fn get_battery(&self) -> Option<f32> {
        None
    }
    fn get_index(&self) -> u8 {
        self.last.index as u8
    }
    fn get_num_axes(&self) -> u8 {
        self.last.num_axes as u8
    }
    fn get_num_buttons(&self) -> u8 {
        self.last.num_buttons as u8
    }
    fn with_state(self) -> NativeJoystick {
        self
    }
	fn poll_native(&mut self) -> Option<::Event> {
		use ::StatefulJoystick;
		self.update();
		self.events.pop_back()
	}
}
impl ::StatefulJoystick for NativeJoystick {
    fn get_axis(&self, index: ::Axis) -> Option<i16> {
        self.get_normalised_axis(index).map(|axis| (axis * ::MAX_JOYSTICK_VALUE as f32) as i16)
    }
    fn get_normalised_axis(&self, index: ::Axis) -> Option<f32> {
        let index = unsafe { cast(index) };
        if index < self.last.num_axes as u8 && index < 64 {
            Some(self.last.axis[index as usize] as f32)
        } else {
            None
        }
    }
    fn get_button(&self, index: ::Button) -> Option<bool> {
        let index = unsafe { cast(index) };
        if index < self.last.num_buttons as u8 && index < 64 {
          Some(self.last.digital_button[index as usize])
        } else {
          None
        }
    }
    fn update(&mut self) {
        unsafe {
            let mut state = mem::uninitialized();
            emscripten_get_gamepad_status(self.last.index as i32, &mut state);
            {
                let now = &state;
                let last = &self.last;
                for button in (0..now.num_buttons) {
                    let now_btn = now.digital_button[button as usize];
                    let last_btn = last.digital_button[button as usize];
                    if now_btn && !last_btn {
                        let btn = cast(button as u8);
            			self.events.push_back(::Event::ButtonPressed(btn))
                    } else if !now_btn && last_btn {
                        let btn = cast(button as u8);
            			self.events.push_back(::Event::ButtonReleased(btn))
                    }
                }
                for axis in (0..now.num_axes) {
                    let now_axis = now.axis[axis as usize];
                    let last_axis = last.axis[axis as usize];
                    if now_axis != last_axis {
                        let axis = cast(axis as u8);
                		self.events.push_back(::Event::AxisMoved(axis, (now_axis as f32 * ::MAX_JOYSTICK_VALUE as f32) as i16))
                    }
                }
            }
            self.last = state;
        }
    }
}

/// Scan for joysticks
pub fn scan() -> Vec<NativeJoystick> {
    let count = unsafe { emscripten_get_num_gamepads() };
    let mut joysticks = Vec::with_capacity(count as usize);
    for i in (0..count) {
        joysticks.push(Joystick::new(i as u8).unwrap())
    }
    joysticks
}
<commit_msg>Update emscripten target<commit_after>use libc::*;
use std::borrow::{Cow, IntoCow};
use std::mem;
use std::mem::transmute as cast;
use std::ffi::CStr;
use std::collections::VecDeque;
use Joystick;

extern {
    fn emscripten_get_num_gamepads() -> c_int;
    fn emscripten_get_gamepad_status(index: c_int, state: *mut NativeEvent) -> c_int;
}

#[repr(C)]
pub struct NativeEvent {
    timestamp: f64,
    num_axes: c_int,
    num_buttons: c_int,
    axis: [f64; 64],
    analog_button: [f64; 64],
    digital_button: [bool; 64],
    connected: bool,
    index: c_long,
    id: [c_char; 64],
    mapping: [c_char; 64]
}

pub struct NativeJoystick {
    last: NativeEvent,
	events: VecDeque<::Event>
}

fn os_error(code: c_int) -> &'static str {
    match code {
        1 => "Deferred",
        -1 => "Not supported",
        -2 => "Failed not deferred",
        -3 => "Invalid target",
        -4 => "Unknown target",
        -5 => "Invalid param",
        -6 => "Failed",
        -7 => "No data",
        _ => "No error"
    }
}

impl ::Joystick for NativeJoystick {
    type WithState = NativeJoystick;
    type NativeEvent = ::Event;
    fn new(index: u8) -> Result<NativeJoystick, &'static str> {
        unsafe {
            let mut state = mem::uninitialized();
            let code = emscripten_get_gamepad_status(index as c_int, &mut state);
            if code != 0 {
                Err(os_error(code))
            } else {
                Ok(NativeJoystick {
                    last: state,
                    events: VecDeque::with_capacity(16)
                })
            }
        }
    }
    fn is_connected(&self) -> bool {
        self.last.connected
    }
    fn get_id(&self) -> Cow<str> {
        unsafe {
            use std::str;
            let bytes = CStr::from_ptr(self.last.id.as_ptr()).to_bytes();
            str::from_utf8_unchecked(bytes).into_cow()
        }
    }
    fn get_battery(&self) -> Option<f32> {
        None
    }
    fn get_index(&self) -> u8 {
        self.last.index as u8
    }
    fn get_num_axes(&self) -> u8 {
        self.last.num_axes as u8
    }
    fn get_num_buttons(&self) -> u8 {
        self.last.num_buttons as u8
    }
    fn with_state(self) -> NativeJoystick {
        self
    }
	fn poll_native(&mut self) -> Option<::Event> {
		use ::StatefulJoystick;
		self.update();
		self.events.pop_back()
	}
}
impl ::StatefulJoystick for NativeJoystick {
    fn get_axis(&self, index: ::Axis) -> Option<i16> {
        self.get_normalised_axis(index).map(|axis| (axis * ::MAX_AXIS_VALUE as f32) as i16)
    }
    fn get_normalised_axis(&self, index: ::Axis) -> Option<f32> {
        let index = unsafe { cast(index) };
        if index < self.last.num_axes as u8 && index < 64 {
            Some(self.last.axis[index as usize] as f32)
        } else {
            None
        }
    }
    fn get_button(&self, index: ::Button) -> Option<bool> {
        let index = unsafe { cast(index) };
        if index < self.last.num_buttons as u8 && index < 64 {
          Some(self.last.digital_button[index as usize])
        } else {
          None
        }
    }
    fn update(&mut self) {
        unsafe {
            let mut state = mem::uninitialized();
            emscripten_get_gamepad_status(self.last.index as i32, &mut state);
            {
                let now = &state;
                let last = &self.last;
                for button in (0..now.num_buttons) {
                    let now_btn = now.digital_button[button as usize];
                    let last_btn = last.digital_button[button as usize];
                    if now_btn && !last_btn {
                        let btn = cast(button as u8);
            			self.events.push_back(::Event::ButtonPressed(btn))
                    } else if !now_btn && last_btn {
                        let btn = cast(button as u8);
            			self.events.push_back(::Event::ButtonReleased(btn))
                    }
                }
                for axis in (0..now.num_axes) {
                    let now_axis = now.axis[axis as usize];
                    let last_axis = last.axis[axis as usize];
                    if now_axis != last_axis {
                        let axis = cast(axis as u8);
                		self.events.push_back(::Event::AxisMoved(axis, (now_axis as f32 * ::MAX_AXIS_VALUE as f32) as i16))
                    }
                }
            }
            self.last = state;
        }
    }
}

/// Scan for joysticks
pub fn scan() -> Vec<NativeJoystick> {
    let count = unsafe { emscripten_get_num_gamepads() };
    let mut joysticks = Vec::with_capacity(count as usize);
    for i in (0..count) {
        joysticks.push(Joystick::new(i as u8).unwrap())
    }
    joysticks
}
<|endoftext|>


<commit_before>pub mod search;

use std::thread;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::TranspositionTable;
use rand;
use rand::distributions::{Sample, Range};

const VERSION: &'static str = "0.1";

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    multi_pv: usize,
    is_thinking: bool,
    is_pondering: bool,
    replies: Vec<EngineReply>,
    infinite: bool,
    best_move: String,
    move_stack: Vec<Move>,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Progress>,
    results: Receiver<search::Done>,
    searcher: thread::JoinHandle<()>,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let (results_tx, results_rx) = channel();
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            multi_pv: 1,
            is_thinking: false,
            is_pondering: false,
            replies: vec![],
            infinite: false,
            best_move: "0000".to_string(),
            move_stack: Vec::new(),
            tt: tt.clone(),
            commands: commands_tx,
            reports: reports_rx,
            results: results_rx,
            searcher: thread::spawn(move || {
                search::run(&tt, commands_rx, reports_tx, results_tx);
            }),
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // We do not support re-sizing of the transposition table
            // once the engine had started, so we do nothing.
            "Hash" => (),

            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // Tells the engine to output multiple best lines.
            // the default value is `1`.)
            "MultiPV" => {
                self.multi_pv = value.parse::<usize>().ok().unwrap_or(1);
            }

            // An invalid option.
            _ => {
                self.replies.push(EngineReply::Info(vec![("string".to_string(),
                                                          format!("Invalid option \"{}\"", name))]));
            }
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

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

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if self.is_thinking {
            return;
        }

        // Get some legal move
        let s = &mut self.move_stack;
        let p = &mut self.position;
        p.generate_moves(s);
        let mut rng = rand::thread_rng();
        let mut between = Range::new(0, 10);
        let mut best_score = -20000;
        while let Some(m) = s.pop() {
            if p.do_move(m) {
                let score = -p.evaluate_quiescence(-20000, 20000).0 + between.sample(&mut rng);
                if score >= best_score {
                    best_score = score;
                    // self.replies.push(EngineReply::Info(vec![("info".to_string(),
                    //                                           format!("{} -> {}",
                    //                                                   m.notation(),
                    //                                                   score))]));
                    self.best_move = m.notation();
                }
                p.undo_move();
            }
        }

        // Start a new search
        self.commands
            .send(search::Command::Search(search::Parameters {
                id: 0,
                position: p.clone(),
                depth: 5,
                lower_bound: -20000,
                upper_bound: 20000,
            }))
            .unwrap();
        self.is_thinking = true;
        self.is_pondering = ponder;
        self.infinite = infinite;
    }

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

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            self.is_pondering = false;  // TODO: search the TT here instead.
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        if !self.is_pondering {
            if let Ok(_) = self.results.try_recv() {
                self.try_to_get_best_move_from_tt();
                self.replies.push(EngineReply::BestMove {
                    best_move: self.best_move.clone(),
                    ponder_move: None,
                });
                self.best_move = "0000".to_string();
                self.is_thinking = false;
            }
        }
        self.replies.pop()
    }
}

impl Engine {
    fn try_to_get_best_move_from_tt(&mut self) {
        if let Some(entry) = self.tt.probe(self.position.hash()) {
            let m = entry.move16();
            if m != 0 {
                let move_type = ((m & 0b1100000000000000) >> 14) as MoveType;
                let orig_square = ((m & 0b0011111100000000) >> 8) as Square;
                let dest_square = ((m & 0b0000000011111100) >> 2) as Square;
                let promoted_piece = match m & 0b11 {
                    0 => "q",
                    1 => "r",
                    2 => "b",
                    3 => "n",
                    _ => panic!("invalid promoted piece code"),
                };
                self.best_move = format!("{}{}{}",
                                         notation(orig_square),
                                         notation(dest_square),
                                         if move_type == MOVE_PROMOTION {
                                             promoted_piece
                                         } else {
                                             ""
                                         })
            }
        }
    }
}

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

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

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

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

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}
<commit_msg>more work on engine<commit_after>pub mod search;

use std::thread;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::TranspositionTable;

const VERSION: &'static str = "0.1";

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    multi_pv: usize,
    is_thinking: bool,
    is_pondering: bool,
    replies: Vec<EngineReply>,
    infinite: bool,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Progress>,
    results: Receiver<search::Done>,
    searcher: thread::JoinHandle<()>,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let (results_tx, results_rx) = channel();
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            multi_pv: 1,
            is_thinking: false,
            is_pondering: false,
            replies: vec![],
            infinite: false,
            tt: tt.clone(),
            commands: commands_tx,
            reports: reports_rx,
            results: results_rx,
            searcher: thread::spawn(move || {
                search::run(&tt, commands_rx, reports_tx, results_tx);
            }),
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // We do not support re-sizing of the transposition table
            // once the engine had started, so we do nothing.
            "Hash" => (),

            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // Tells the engine to output multiple best lines.
            // the default value is `1`.)
            "MultiPV" => {
                self.multi_pv = value.parse::<usize>().ok().unwrap_or(1);
            }

            // An invalid option.
            _ => {
                self.replies.push(EngineReply::Info(vec![("string".to_string(),
                                                          format!("Invalid option \"{}\"", name))]));
            }
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

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

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if self.is_thinking {
            return;
        }

        // use rand;
        // use rand::distributions::{Sample, Range};
        // let mut rng = rand::thread_rng();
        // let mut between = Range::new(0, 10);
        // let x = between.sample(&mut rng);

        // Start a new search
        self.commands
            .send(search::Command::Search(search::Parameters {
                id: 0,
                position: self.position.clone(),
                depth: 5,
                lower_bound: -20000,
                upper_bound: 20000,
            }))
            .unwrap();
        self.is_thinking = true;
        self.is_pondering = ponder;
        self.infinite = infinite;
    }

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

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: do interactive deepening instead.
        if !self.is_pondering {
            if let Ok(_) = self.results.try_recv() {
                self.stop();
            }
        }

        self.replies.pop()
    }
}

impl Engine {
    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::from_u32(0);
            let mut v = Vec::with_capacity(128);
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.move16();
        }
        if m != 0 {
            let move_type = ((m & 0b1100000000000000) >> 14) as MoveType;
            let orig_square = ((m & 0b0011111100000000) >> 8) as Square;
            let dest_square = ((m & 0b0000000011111100) >> 2) as Square;
            let promoted_piece = match m & 0b11 {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

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

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

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

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

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


<commit_before>use std::path::PathBuf;
use std::fs::File;
use std::error::Error;
use std::fs;
use std::fmt;
use std::io::{Seek, Read, SeekFrom};
use std::collections::HashMap;
use flate2::read::GzDecoder;

#[derive(Debug)]
pub enum FsError {
    FileNotFound,
    InvalidDirectory,
    NoFileHandle,
    MalformedDataSequence,
    CorruptedData,
}
impl Error for FsError {
    fn description(&self) -> &str {
        match *self {
            FsError::FileNotFound => "the folder does not exist or cannot be read from",
            FsError::InvalidDirectory => "the specified directory is not a valid directory",
            FsError::NoFileHandle => "the filesystem did not load a file yet",
            FsError::MalformedDataSequence => "the data sequence did not complete correctly",
            FsError::CorruptedData => "the data was corrupt",
        }
    }
}
impl fmt::Display for FsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            FsError::FileNotFound => write!(f, "the folder specified could not be found or read from"),
            FsError::InvalidDirectory => write!(f, "the specified directory is not a valid directory"),
            FsError::NoFileHandle => write!(f, "the filesystem did not load a file yet"),
            FsError::MalformedDataSequence => write!(f, "the data sequence did not complete correctly"),
            FsError::CorruptedData => write!(f, "the data was corrupt"),
        }
    }
}

#[derive(Debug,Clone)]
pub enum CompressionType {
    /// The archive is not compressed and the raw data is the real data.
    None,
    /// The archive is compressed with the Bzip2 compression algorithm.
    Bzip2,
    /// The archive is compressed with (a slightly modified, headerless) Gzip codec.
    Gzip,
    /// The archive is compressed with a modified LZMA2 variant (without size field).
    Lzma
}

#[derive(Debug)]
pub struct FileSystem {
    path: PathBuf,
    mainfile: MainFile,
    indices: HashMap<u32, IndexFile>
}

#[derive(Debug)]
pub struct MainFile {
    file: Option<File>
}

#[derive(Debug)]
pub struct IndexFile {
    id: u32,
    file: File
}

#[derive(Debug,Clone)]
pub struct IndexEntry {
    index: u8,
    id: u32,
    size: u32,
    offset: u64
}

#[derive(Debug,Clone)]
pub struct EntryHeader {
    raw_size: u32,
    real_size: u32,
    compression: CompressionType
}

impl EntryHeader {

    pub fn from_bytes(bytes: [u8; 9]) -> Result<EntryHeader, std::io::Error> {
        // Parse the 9 bytes of important info
        let compression_type = bytes[0];
        let raw_size: u32 = ((bytes[1] as u32) << 24) | ((bytes[2] as u32) << 16) | ((bytes[3] as u32) << 8) | (bytes[4] as u32);
        let real_size: u32 = ((bytes[5] as u32) << 24) | ((bytes[6] as u32) << 16) | ((bytes[7] as u32) << 8) | (bytes[8] as u32);

        // Return the new entry header
        return Ok(EntryHeader {
            raw_size: raw_size,
            real_size: real_size,
            compression: CompressionType::from_code(compression_type)
        });
    }

}

#[derive(Debug,Clone)]
pub struct BlockHeader {
    big: bool,
    entry_id: u32,
    index_id: u8,

    next_seq: i32,
    next_block: u32
}

impl CompressionType {
    /// Fetches the appropriate type of compression based on the header field
    /// in the archive header.
    pub fn from_code(code: u8) -> CompressionType {
        match code {
            1 => CompressionType::Bzip2,
            2 => CompressionType::Gzip,
            3 => CompressionType::Lzma,
            _ => CompressionType::None
        }
    }
}

impl BlockHeader {
    pub fn from_block(big: bool, data: [u8; 520]) -> BlockHeader {
        match big {
            true => {
                BlockHeader {
                    big: true,
                    entry_id: ((data[0] as u32) << 24) | ((data[1] as u32) << 16) | ((data[2] as u32) << 8) | (data[3] as u32),
                    next_seq: (((data[4] as u32) << 8) | (data[5] as u32)) as i32,
                    next_block: ((data[6] as u32) << 16) | ((data[7] as u32) << 8) | (data[8] as u32),
                    index_id: data[9] as u8
                }
            },
            false => {
                BlockHeader {
                    big: false,
                    entry_id: ((data[0] as u32) << 8) | (data[1] as u32),
                    next_seq: (((data[2] as u32) << 8) | (data[3] as u32)) as i32,
                    next_block: ((data[4] as u32) << 16) | ((data[5] as u32) << 8) | (data[6] as u32),
                    index_id: data[7] as u8
                }
            }
        }
    }
}

impl IndexEntry {
    pub fn index(&self) -> u8 {
        self.index
    }

    /// Gets the id of this block.
    pub fn id(&self) -> u32 {
        self.id
    }

    /// Gets the absolute size of the entry data, not counting the 8-10
    /// byte header in the blocks.
    pub fn size(&self) -> u32 {
        self.size
    }

    /// Gets the absolute offset of the very first 520-byte block of this
    /// entry in the main data file.
    pub fn offset(&self) -> u64 {
        self.offset
    }

    pub fn block(&self) -> u32 {
        (self.offset / 520u64) as u32
    }
}

impl IndexFile {
    pub fn last_entry(&self) -> u64 {
         self.file.metadata().unwrap().len() / 6u64
    }

    pub fn entry(&mut self, id: u32) -> Option<IndexEntry> {
        let ref mut file = self.file;
        let mut tmp: [u8; 6] = [0; 6];

        // Seek to the proper position and read into the temp buffer
        let seek_offset = id as u64 * 6u64;
        let res1 = file.seek(SeekFrom::Start(seek_offset));
        let res2 = file.read(&mut tmp);

        // Check if the seek and read operation succeeded
        if res1.is_err() || res2.is_err() {
            return None;
        }

        // Check if the operations returned the expected results
        if res1.unwrap() != seek_offset || res2.unwrap() != 6 {
            return None;
        }

        // Decode the size and offset from the temp buffer
        let size: u32 = ((tmp[0] as u32) << 16) | ((tmp[1] as u32) << 8) | (tmp[2] as u32);
        let offset: u64 = ((tmp[3] as u64) << 16) | ((tmp[4] as u64) << 8) | (tmp[5] as u64);

        Some(IndexEntry {index: self.id as u8, id: id, size: size, offset: offset * 520u64})
    }
}

impl FileSystem {
    pub fn new(string: &'static str) -> Result<FileSystem, FsError> {
        // Declare some nice variables!!!
        let path = PathBuf::from(string);
        let metadata = fs::metadata(&path);

        // Make sure the folder exists
        if !metadata.is_ok() {
            return Err(FsError::FileNotFound);
        }

        // Make sure it is a directory
        if !metadata.unwrap().is_dir() {
            return Err(FsError::InvalidDirectory);
        }

        // Create mainfile path
        let mut mainfile_path = PathBuf::from(string);
        mainfile_path.push("main_file_cache.dat2");

        // Find all valid index files
        let mut indices: HashMap<u32, IndexFile> = HashMap::new();
        let entries = fs::read_dir(&path).unwrap();
        for entry in entries {
            let e = entry.unwrap();
            let fname = e.file_name().into_string().unwrap();

            // Is this an index?
            if fname.starts_with("main_file_cache.idx") {
                // Parse the index id into an integer
                let idx = fname[19..].parse::<u32>().unwrap();

                // Add the index file to our map with indices
                indices.insert(idx, IndexFile {id: idx, file: File::open(e.path()).unwrap()});
            }
        }

        // Create the filesystem object and return it
        let file = File::open(mainfile_path).ok();
        let mainfile = MainFile{file: file};

        Ok(FileSystem {path: path, mainfile: mainfile, indices: indices})
    }

    /// Gets the mainfile, that is, the main_file_cache.dat2 entry in the folder
    /// that holds the actual binary data of the filesystem entries.
    pub fn mainfile(&mut self) -> &mut MainFile {
        &mut self.mainfile
    }

    /// Gets an index with a specific id if it exists. The index can only exist if the file exists
    /// on the file system.
    pub fn index(&mut self, index: u32) -> Option<&mut IndexFile> {
        self.indices.get_mut(&index)
    }
}

impl MainFile {
    /// Checks if the file exists.
    pub fn exists(&self) -> bool {
        self.file.is_some()
    }

    /// Gets the backing file, if existant. Returns a new instance with a fresh seek pointer.
    pub fn file(&mut self) -> Option<&mut File> {
        self.file.as_mut()
    }

    /// Calculates the number of data blocks in the mainfile (if existant). This is done by
    /// taking the file size and dividing that by 520 (rouding up), because each block
    /// takes up 520 bytes of data.
    pub fn num_blocks(&self) -> Option<u64> {
        match self.file {
            Some(ref x) => Some((x.metadata().unwrap().len() + 519u64) / 520u64),
            None => None
        }
    }

    /// Reads a block of data, specified by the block id. The data is read at 520 * block_id
    /// and is exactly 520 bytes big. It is not guaranteed all 520 bytes are occupied if the
    /// block is the last one, thus possible to be trimmed.
    pub fn read_block(&mut self, block: u32) -> Option<[u8; 520]> {
        // Do we have a valid file?
        if self.file.is_none() {
            return None;
        }

        let mut data: [u8; 520] = [0; 520];
        let file = self.file().unwrap();

        // Seek to the right position and read the data
        file.seek(SeekFrom::Start(block as u64 * 520u64)).unwrap();
        file.read(&mut data).unwrap();

        return Some(data);
    }

    pub fn read_header(&mut self, entry: IndexEntry) -> Option<EntryHeader> {
        // Do we have a valid file?
        if self.file.is_none() {
            return None;
        }

        let mut hdr: [u8; 9] = [0; 9];
        let file = self.file().unwrap();

        // Seek to the right position and read the data, skipping the block header at start
        let block_header_len = if entry.id() > 0xFFFF { 10 } else { 8 };
        file.seek(SeekFrom::Start(entry.offset() + block_header_len)).unwrap();
        file.read(&mut hdr).unwrap();

        return Some(EntryHeader::from_bytes(hdr).unwrap());
    }

    pub fn read_entry(&mut self, entry: IndexEntry) -> Result<Vec<u8>, FsError> {
        // Do we have a valid file?
        if self.file.is_none() {
            return Err(FsError::NoFileHandle);
        }

        // Create a vec with what we assume is the size. If not, the vec will
        // perfectly resize itself, so it's only an estimation to help us speed up.
        let mut data: Vec<u8> = Vec::with_capacity(entry.size() as usize);

        let mut current_block = entry.block();
        let mut remaining = entry.size();
        let mut current_seq = 0; // We expect a next part to be '1'

        while remaining > 0 {
            let block_data = self.read_block(current_block).unwrap();
            let block_info = BlockHeader::from_block(entry.id() > 65535, block_data);

            let header_size = if block_info.big {10} else {8};
            let available_data = 520 - header_size;
            let consumable = if remaining > available_data {available_data} else {remaining};

            // Do some checks to validate this block.
            if remaining > 0 {
                if block_info.index_id != entry.index() || block_info.next_seq != current_seq {
                    return Err(FsError::MalformedDataSequence);
                } else {
                    // TODO this is so inefficient I should probably feel bad. Really bad. Terribly bad.
                    if block_info.big {
                        for i in block_data[10..520].iter() {
                            data.push(*i);
                        }
                    } else {
                        for i in block_data[8..520].iter() {
                            data.push(*i);
                        }
                    }

                    current_block += 1;
                    current_seq += 1;
                }
            }

            remaining -= consumable;
        }

        Ok(data)
    }

    pub fn read_decompressed(&mut self, entry: IndexEntry) -> Result<Vec<u8>, FsError> {
        let data = self.read_entry(entry.clone())?;
        let header = self.read_header(entry).unwrap();

        match header.compression {
            CompressionType::None => {
                panic!("Compressionless unsupported")
            }
            CompressionType::Gzip => {
                let mut cursor = std::io::Cursor::new(data);
                cursor.seek(SeekFrom::Current(9)).unwrap();

                let mut decoder = GzDecoder::new(cursor);
                let mut out = Vec::<u8>::new();
                out.resize(header.real_size as usize, 0);

                match decoder.read_exact(out.as_mut_slice()) {
                    Err(_) => return Err(FsError::CorruptedData),
                    Ok(_) => return Ok(out),
                }
            }
            CompressionType::Lzma => {
                panic!("Lzma compression unsupported")
            }
            CompressionType::Bzip2 => {
                panic!("Bzip2 compression unsupported")
            }
        }
    }

}
<commit_msg>Add From trait for FsError<commit_after>use std::{io::ErrorKind, path::PathBuf};
use std::fs::File;
use std::error::Error;
use std::fs;
use std::fmt;
use std::io::{Seek, Read, SeekFrom};
use std::collections::HashMap;
use flate2::read::GzDecoder;

#[derive(Debug)]
pub enum FsError {
    FileNotFound,
    InvalidDirectory,
    NoFileHandle,
    MalformedDataSequence,
    CorruptedData,
}
impl Error for FsError {
    fn description(&self) -> &str {
        match *self {
            FsError::FileNotFound => "the folder does not exist or cannot be read from",
            FsError::InvalidDirectory => "the specified directory is not a valid directory",
            FsError::NoFileHandle => "the filesystem did not load a file yet",
            FsError::MalformedDataSequence => "the data sequence did not complete correctly",
            FsError::CorruptedData => "the data was corrupt",
        }
    }
}
impl fmt::Display for FsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            FsError::FileNotFound => write!(f, "the folder specified could not be found or read from"),
            FsError::InvalidDirectory => write!(f, "the specified directory is not a valid directory"),
            FsError::NoFileHandle => write!(f, "the filesystem did not load a file yet"),
            FsError::MalformedDataSequence => write!(f, "the data sequence did not complete correctly"),
            FsError::CorruptedData => write!(f, "the data was corrupt"),
        }
    }
}
impl From<FsError> for std::io::Error {
    fn from(other: FsError) -> std::io::Error {
        std::io::Error::new(ErrorKind::Other, other)
    }
}

#[derive(Debug,Clone)]
pub enum CompressionType {
    /// The archive is not compressed and the raw data is the real data.
    None,
    /// The archive is compressed with the Bzip2 compression algorithm.
    Bzip2,
    /// The archive is compressed with (a slightly modified, headerless) Gzip codec.
    Gzip,
    /// The archive is compressed with a modified LZMA2 variant (without size field).
    Lzma
}

#[derive(Debug)]
pub struct FileSystem {
    path: PathBuf,
    mainfile: MainFile,
    indices: HashMap<u32, IndexFile>
}

#[derive(Debug)]
pub struct MainFile {
    file: Option<File>
}

#[derive(Debug)]
pub struct IndexFile {
    id: u32,
    file: File
}

#[derive(Debug,Clone)]
pub struct IndexEntry {
    index: u8,
    id: u32,
    size: u32,
    offset: u64
}

#[derive(Debug,Clone)]
pub struct EntryHeader {
    raw_size: u32,
    real_size: u32,
    compression: CompressionType
}

impl EntryHeader {

    pub fn from_bytes(bytes: [u8; 9]) -> Result<EntryHeader, std::io::Error> {
        // Parse the 9 bytes of important info
        let compression_type = bytes[0];
        let raw_size: u32 = ((bytes[1] as u32) << 24) | ((bytes[2] as u32) << 16) | ((bytes[3] as u32) << 8) | (bytes[4] as u32);
        let real_size: u32 = ((bytes[5] as u32) << 24) | ((bytes[6] as u32) << 16) | ((bytes[7] as u32) << 8) | (bytes[8] as u32);

        // Return the new entry header
        return Ok(EntryHeader {
            raw_size: raw_size,
            real_size: real_size,
            compression: CompressionType::from_code(compression_type)
        });
    }

}

#[derive(Debug,Clone)]
pub struct BlockHeader {
    big: bool,
    entry_id: u32,
    index_id: u8,

    next_seq: i32,
    next_block: u32
}

impl CompressionType {
    /// Fetches the appropriate type of compression based on the header field
    /// in the archive header.
    pub fn from_code(code: u8) -> CompressionType {
        match code {
            1 => CompressionType::Bzip2,
            2 => CompressionType::Gzip,
            3 => CompressionType::Lzma,
            _ => CompressionType::None
        }
    }
}

impl BlockHeader {
    pub fn from_block(big: bool, data: [u8; 520]) -> BlockHeader {
        match big {
            true => {
                BlockHeader {
                    big: true,
                    entry_id: ((data[0] as u32) << 24) | ((data[1] as u32) << 16) | ((data[2] as u32) << 8) | (data[3] as u32),
                    next_seq: (((data[4] as u32) << 8) | (data[5] as u32)) as i32,
                    next_block: ((data[6] as u32) << 16) | ((data[7] as u32) << 8) | (data[8] as u32),
                    index_id: data[9] as u8
                }
            },
            false => {
                BlockHeader {
                    big: false,
                    entry_id: ((data[0] as u32) << 8) | (data[1] as u32),
                    next_seq: (((data[2] as u32) << 8) | (data[3] as u32)) as i32,
                    next_block: ((data[4] as u32) << 16) | ((data[5] as u32) << 8) | (data[6] as u32),
                    index_id: data[7] as u8
                }
            }
        }
    }
}

impl IndexEntry {
    pub fn index(&self) -> u8 {
        self.index
    }

    /// Gets the id of this block.
    pub fn id(&self) -> u32 {
        self.id
    }

    /// Gets the absolute size of the entry data, not counting the 8-10
    /// byte header in the blocks.
    pub fn size(&self) -> u32 {
        self.size
    }

    /// Gets the absolute offset of the very first 520-byte block of this
    /// entry in the main data file.
    pub fn offset(&self) -> u64 {
        self.offset
    }

    pub fn block(&self) -> u32 {
        (self.offset / 520u64) as u32
    }
}

impl IndexFile {
    pub fn last_entry(&self) -> u64 {
         self.file.metadata().unwrap().len() / 6u64
    }

    pub fn entry(&mut self, id: u32) -> Option<IndexEntry> {
        let ref mut file = self.file;
        let mut tmp: [u8; 6] = [0; 6];

        // Seek to the proper position and read into the temp buffer
        let seek_offset = id as u64 * 6u64;
        let res1 = file.seek(SeekFrom::Start(seek_offset));
        let res2 = file.read(&mut tmp);

        // Check if the seek and read operation succeeded
        if res1.is_err() || res2.is_err() {
            return None;
        }

        // Check if the operations returned the expected results
        if res1.unwrap() != seek_offset || res2.unwrap() != 6 {
            return None;
        }

        // Decode the size and offset from the temp buffer
        let size: u32 = ((tmp[0] as u32) << 16) | ((tmp[1] as u32) << 8) | (tmp[2] as u32);
        let offset: u64 = ((tmp[3] as u64) << 16) | ((tmp[4] as u64) << 8) | (tmp[5] as u64);

        Some(IndexEntry {index: self.id as u8, id: id, size: size, offset: offset * 520u64})
    }
}

impl FileSystem {
    pub fn new(string: &'static str) -> Result<FileSystem, FsError> {
        // Declare some nice variables!!!
        let path = PathBuf::from(string);
        let metadata = fs::metadata(&path);

        // Make sure the folder exists
        if !metadata.is_ok() {
            return Err(FsError::FileNotFound);
        }

        // Make sure it is a directory
        if !metadata.unwrap().is_dir() {
            return Err(FsError::InvalidDirectory);
        }

        // Create mainfile path
        let mut mainfile_path = PathBuf::from(string);
        mainfile_path.push("main_file_cache.dat2");

        // Find all valid index files
        let mut indices: HashMap<u32, IndexFile> = HashMap::new();
        let entries = fs::read_dir(&path).unwrap();
        for entry in entries {
            let e = entry.unwrap();
            let fname = e.file_name().into_string().unwrap();

            // Is this an index?
            if fname.starts_with("main_file_cache.idx") {
                // Parse the index id into an integer
                let idx = fname[19..].parse::<u32>().unwrap();

                // Add the index file to our map with indices
                indices.insert(idx, IndexFile {id: idx, file: File::open(e.path()).unwrap()});
            }
        }

        // Create the filesystem object and return it
        let file = File::open(mainfile_path).ok();
        let mainfile = MainFile{file: file};

        Ok(FileSystem {path: path, mainfile: mainfile, indices: indices})
    }

    /// Gets the mainfile, that is, the main_file_cache.dat2 entry in the folder
    /// that holds the actual binary data of the filesystem entries.
    pub fn mainfile(&mut self) -> &mut MainFile {
        &mut self.mainfile
    }

    /// Gets an index with a specific id if it exists. The index can only exist if the file exists
    /// on the file system.
    pub fn index(&mut self, index: u32) -> Option<&mut IndexFile> {
        self.indices.get_mut(&index)
    }
}

impl MainFile {
    /// Checks if the file exists.
    pub fn exists(&self) -> bool {
        self.file.is_some()
    }

    /// Gets the backing file, if existant. Returns a new instance with a fresh seek pointer.
    pub fn file(&mut self) -> Option<&mut File> {
        self.file.as_mut()
    }

    /// Calculates the number of data blocks in the mainfile (if existant). This is done by
    /// taking the file size and dividing that by 520 (rouding up), because each block
    /// takes up 520 bytes of data.
    pub fn num_blocks(&self) -> Option<u64> {
        match self.file {
            Some(ref x) => Some((x.metadata().unwrap().len() + 519u64) / 520u64),
            None => None
        }
    }

    /// Reads a block of data, specified by the block id. The data is read at 520 * block_id
    /// and is exactly 520 bytes big. It is not guaranteed all 520 bytes are occupied if the
    /// block is the last one, thus possible to be trimmed.
    pub fn read_block(&mut self, block: u32) -> Option<[u8; 520]> {
        // Do we have a valid file?
        if self.file.is_none() {
            return None;
        }

        let mut data: [u8; 520] = [0; 520];
        let file = self.file().unwrap();

        // Seek to the right position and read the data
        file.seek(SeekFrom::Start(block as u64 * 520u64)).unwrap();
        file.read(&mut data).unwrap();

        return Some(data);
    }

    pub fn read_header(&mut self, entry: IndexEntry) -> Option<EntryHeader> {
        // Do we have a valid file?
        if self.file.is_none() {
            return None;
        }

        let mut hdr: [u8; 9] = [0; 9];
        let file = self.file().unwrap();

        // Seek to the right position and read the data, skipping the block header at start
        let block_header_len = if entry.id() > 0xFFFF { 10 } else { 8 };
        file.seek(SeekFrom::Start(entry.offset() + block_header_len)).unwrap();
        file.read(&mut hdr).unwrap();

        return Some(EntryHeader::from_bytes(hdr).unwrap());
    }

    pub fn read_entry(&mut self, entry: IndexEntry) -> Result<Vec<u8>, FsError> {
        // Do we have a valid file?
        if self.file.is_none() {
            return Err(FsError::NoFileHandle);
        }

        // Create a vec with what we assume is the size. If not, the vec will
        // perfectly resize itself, so it's only an estimation to help us speed up.
        let mut data: Vec<u8> = Vec::with_capacity(entry.size() as usize);

        let mut current_block = entry.block();
        let mut remaining = entry.size();
        let mut current_seq = 0; // We expect a next part to be '1'

        while remaining > 0 {
            let block_data = self.read_block(current_block).unwrap();
            let block_info = BlockHeader::from_block(entry.id() > 65535, block_data);

            let header_size = if block_info.big {10} else {8};
            let available_data = 520 - header_size;
            let consumable = if remaining > available_data {available_data} else {remaining};

            // Do some checks to validate this block.
            if remaining > 0 {
                if block_info.index_id != entry.index() || block_info.next_seq != current_seq {
                    return Err(FsError::MalformedDataSequence);
                } else {
                    // TODO this is so inefficient I should probably feel bad. Really bad. Terribly bad.
                    if block_info.big {
                        for i in block_data[10..520].iter() {
                            data.push(*i);
                        }
                    } else {
                        for i in block_data[8..520].iter() {
                            data.push(*i);
                        }
                    }

                    current_block += 1;
                    current_seq += 1;
                }
            }

            remaining -= consumable;
        }

        Ok(data)
    }

    pub fn read_decompressed(&mut self, entry: IndexEntry) -> Result<Vec<u8>, FsError> {
        let data = self.read_entry(entry.clone())?;
        let header = self.read_header(entry).unwrap();

        match header.compression {
            CompressionType::None => {
                panic!("Compressionless unsupported")
            }
            CompressionType::Gzip => {
                let mut cursor = std::io::Cursor::new(data);
                cursor.seek(SeekFrom::Current(9)).unwrap();

                let mut decoder = GzDecoder::new(cursor);
                let mut out = Vec::<u8>::new();
                out.resize(header.real_size as usize, 0);

                match decoder.read_exact(out.as_mut_slice()) {
                    Err(_) => return Err(FsError::CorruptedData),
                    Ok(_) => return Ok(out),
                }
            }
            CompressionType::Lzma => {
                panic!("Lzma compression unsupported")
            }
            CompressionType::Bzip2 => {
                panic!("Bzip2 compression unsupported")
            }
        }
    }

}
<|endoftext|>


<commit_before>use scene::{Scene, BoxedScene, SceneCommand};
use opengl_graphics::Gl;
use event::RenderArgs;
use input::{Button, Key};
use graphics;
use graphics::RelativeTransform;

#[derive(Clone)]
enum CellState {
    Empty,
    Dirt,
    Stone
}

struct World {
    cells: Vec<CellState>,
    width: u32,
    height: u32
}

impl World {
    pub fn new(width: u32, height: u32) -> World {
        World {
            cells: vec![CellState::Dirt; (width * height) as usize],
            width: width,
            height: height
        }
    }
}

pub struct GameScene {
    quit: bool,
    world: World,
    camera_pos: (f64, f64)
}

impl GameScene {
    pub fn new() -> BoxedScene {
        Box::new(GameScene {
            quit: false,
            world: World::new(100, 100),
            camera_pos: (0.0, 0.0)
        })
    }
}

impl Scene for GameScene {
    fn render(&self, gl: &mut Gl, args: &RenderArgs) {
        const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
        const BROWN: [f32; 4] = [0.2, 0.2, 0.0, 1.0];
        const GREY: [f32; 4] = [0.2, 0.2, 0.2, 1.0];
        const CELL_SIZE: u32 = 32;

        let (camera_x, camera_y) = self.camera_pos;

        let context = &graphics::Context::abs(args.width as f64, args.height as f64)
            .trans(((args.width / 2) - (CELL_SIZE / 2)) as f64, ((args.height / 2) - (CELL_SIZE / 2)) as f64) //Camera is in the centre of the screen
            .trans(-camera_x, -camera_y);

        gl.draw([0, 0, args.width as i32, args.height as i32], |_, gl| {
            graphics::clear(BLACK, gl);
            for (i, cell) in self.world.cells.iter().enumerate() {
                let x = (i as u32 % self.world.width) * CELL_SIZE;
                let y = (i as u32 / self.world.height) * CELL_SIZE;

                match *cell {
                    CellState::Dirt => {
                        graphics::rectangle(BROWN, graphics::rectangle::square(x as f64, y as f64, CELL_SIZE as f64), context.transform, gl);
                    },
                    CellState::Stone => {
                        graphics::rectangle(GREY, graphics::rectangle::square(x as f64, y as f64, CELL_SIZE as f64), context.transform, gl);
                    },
                    _ => ()
                }
            }
        });

    }

    fn think(&mut self) -> Option<SceneCommand> {

        if self.quit {
            Some(SceneCommand::PopScene)
        } else {
            None
        }
    }

    fn press(&mut self, button: &Button) {
        match button {
            &Button::Keyboard(Key::Up) => {
                let (x, y) = self.camera_pos;
                self.camera_pos = (x, y - 1.0);
            },
            &Button::Keyboard(Key::Down) => {
                let (x, y) = self.camera_pos;
                self.camera_pos = (x, y + 1.0);
            },
            &Button::Keyboard(Key::Left) => {
                let (x, y) = self.camera_pos;
                self.camera_pos = (x - 1.0, y);
            },
            &Button::Keyboard(Key::Right) => {
                let (x, y) = self.camera_pos;
                self.camera_pos = (x + 1.0, y);
            },
            _ => ()
        }
        //self.quit = true;
    }

    fn release(&mut self, button: &Button) {
    }
}
<commit_msg>KeyDown handling<commit_after>use std::collections::HashSet;
use scene::{Scene, BoxedScene, SceneCommand};
use opengl_graphics::Gl;
use event::RenderArgs;
use input::{Button, Key};
use graphics;
use graphics::RelativeTransform;

#[derive(Clone)]
enum CellState {
    Empty,
    Dirt,
    Stone
}

struct World {
    cells: Vec<CellState>,
    width: u32,
    height: u32
}

impl World {
    pub fn new(width: u32, height: u32) -> World {
        World {
            cells: vec![CellState::Dirt; (width * height) as usize],
            width: width,
            height: height
        }
    }
}

pub struct GameScene {
    quit: bool,
    world: World,
    keys: HashSet<Key>,
    camera_pos: (f64, f64)
}

impl GameScene {
    pub fn new() -> BoxedScene {
        Box::new(GameScene {
            quit: false,
            world: World::new(100, 100),
            keys: HashSet::new(),
            camera_pos: (0.0, 0.0)
        })
    }
}

impl Scene for GameScene {
    fn render(&self, gl: &mut Gl, args: &RenderArgs) {
        const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
        const BROWN: [f32; 4] = [0.2, 0.2, 0.0, 1.0];
        const GREY: [f32; 4] = [0.2, 0.2, 0.2, 1.0];
        const CELL_SIZE: u32 = 32;

        let (camera_x, camera_y) = self.camera_pos;

        let context = &graphics::Context::abs(args.width as f64, args.height as f64)
            .trans(((args.width / 2) - (CELL_SIZE / 2)) as f64, ((args.height / 2) - (CELL_SIZE / 2)) as f64) //Camera is in the centre of the screen
            .trans(-camera_x, -camera_y);

        gl.draw([0, 0, args.width as i32, args.height as i32], |_, gl| {
            graphics::clear(BLACK, gl);
            for (i, cell) in self.world.cells.iter().enumerate() {
                let x = (i as u32 % self.world.width) * CELL_SIZE;
                let y = (i as u32 / self.world.height) * CELL_SIZE;

                match *cell {
                    CellState::Dirt => {
                        graphics::rectangle(BROWN, graphics::rectangle::square(x as f64, y as f64, CELL_SIZE as f64), context.transform, gl);
                    },
                    CellState::Stone => {
                        graphics::rectangle(GREY, graphics::rectangle::square(x as f64, y as f64, CELL_SIZE as f64), context.transform, gl);
                    },
                    _ => ()
                }
            }
        });

    }

    fn think(&mut self) -> Option<SceneCommand> {

        for &key in self.keys.iter() {
            match key {
                Key::Up => {
                    let (x, y) = self.camera_pos;
                    self.camera_pos = (x, y - 1.0);
                },
                Key::Down => {
                    let (x, y) = self.camera_pos;
                    self.camera_pos = (x, y + 1.0);
                },
                Key::Left => {
                    let (x, y) = self.camera_pos;
                    self.camera_pos = (x - 1.0, y);
                },
                Key::Right => {
                    let (x, y) = self.camera_pos;
                    self.camera_pos = (x + 1.0, y);
                },
                _ => ()
            }
        };

        if self.quit {
            Some(SceneCommand::PopScene)
        } else {
            None
        }
    }

    fn press(&mut self, button: &Button) {
        match button {
            &Button::Keyboard(key) => {
                self.keys.insert(key)
            },
            _ => false
        };
        //self.quit = true;
    }

    fn release(&mut self, button: &Button) {
        match button {
            &Button::Keyboard(key) => {
                self.keys.remove(&key)
            },
            _ => false
        };
    }
}
<|endoftext|>


<commit_before>//
// base64n.rs - new base64 module
//
// The Base64 Alphabet
//
//  Value Encoding  Value Encoding  Value Encoding  Value Encoding
//      0 A            17 R            34 i            51 z
//      1 B            18 S            35 j            52 0
//      2 C            19 T            36 k            53 1
//      3 D            20 U            37 l            54 2
//      4 E            21 V            38 m            55 3
//      5 F            22 W            39 n            56 4
//      6 G            23 X            40 o            57 5
//      7 H            24 Y            41 p            58 6
//      8 I            25 Z            42 q            59 7
//      9 J            26 a            43 r            60 8
//     10 K            27 b            44 s            61 9
//     11 L            28 c            45 t            62 +
//     12 M            29 d            46 u            63 /
//     13 N            30 e            47 v
//     14 O            31 f            48 w         (pad) =
//     15 P            32 g            49 x
//     16 Q            33 h            50 y
//
// The "URL and Filename safe" Base 64 Alphabet
//
// Value Encoding  Value Encoding  Value Encoding  Value Encoding
//     0 A            17 R            34 i            51 z
//     1 B            18 S            35 j            52 0
//     2 C            19 T            36 k            53 1
//     3 D            20 U            37 l            54 2
//     4 E            21 V            38 m            55 3
//     5 F            22 W            39 n            56 4
//     6 G            23 X            40 o            57 5
//     7 H            24 Y            41 p            58 6
//     8 I            25 Z            42 q            59 7
//     9 J            26 a            43 r            60 8
//    10 K            27 b            44 s            61 9
//    11 L            28 c            45 t            62 - (minus)
//    12 M            29 d            46 u            63 _ (underline)
//    13 N            30 e            47 v
//    14 O            31 f            48 w
//    15 P            32 g            49 x
//    16 Q            33 h            50 y         (pad) =
//

import vec::len;

const PAD: u8 = 61u8;

iface enc {
    fn encode(dst: [mutable u8], src: [u8]);
    fn encode_byte(src: [u8]) -> [u8];
    // FIXME `decode` should return desired length of `dst`
    fn decode(dst: [mutable u8], src: [u8]);
    fn decode_byte(src: [u8]) -> [u8];
}

fn mk() -> enc {
    type _enc = {table: [u8]};

    impl of enc for _enc {
        fn encode(dst: [mutable u8], src: [u8]) {
            b64encode(self.table, dst, src);
        }
        fn decode(dst: [mutable u8], src: [u8]) {
            // FIXME need self.decode_map instead of self.table.
            b64decode(self.table, dst, src);
        }
        fn encode_byte(src: [u8]) -> [u8] {
            let dst_length = encoded_len(len(src));
            let dst = vec::to_mut(vec::from_elem(dst_length, 0u8));
            self.encode(dst, src);
            vec::from_mut(dst)
        }
        fn decode_byte(src: [u8]) -> [u8] { [] }
    }

    let table = vec::to_mut(vec::from_elem(62u, 0u8)), i = 0u8;
    u8::range(65u8, 91u8)  { |j| table[i] = j; i += 1u8; }
    u8::range(97u8, 123u8) { |j| table[i] = j; i += 1u8; }
    u8::range(48u8, 58u8)  { |j| table[i] = j; i += 1u8; }

    {table: vec::from_mut(table)} as enc
}

fn encoded_len(src_length: uint) -> uint {
    (src_length + 2u) / 3u * 4u
}

fn decoded_len(src_length: uint) -> uint {
    src_length / 4u * 3u
}

fn b64encode(table: [u8], dst: [mutable u8], src: [u8]) {
    if len(src) == 0u {
        ret;
    }

    let src_length = len(src);
    let dst_length = len(dst);
    let dst_curr = 0u;
    let src_curr = 0u;

    if dst_length % 4u != 0u {
        fail "dst's length should be divisible by 4";
    }

    while src_length > 0u {
        dst[dst_curr + 0u] = 0u8;
        dst[dst_curr + 1u] = 0u8;
        dst[dst_curr + 2u] = 0u8;
        dst[dst_curr + 3u] = 0u8;

        if src_length == 1u {
            dst[dst_curr + 0u] |= (src[src_curr + 0u]) >> 2u8;
            dst[dst_curr + 1u] |= (src[src_curr + 0u] << 4u8) & 0x3f_u8;
        } else if src_length == 2u {
            dst[dst_curr + 0u] |= (src[src_curr + 0u]) >> 2u8;
            dst[dst_curr + 1u] |= (src[src_curr + 0u] << 4u8) & 0x3f_u8;
            dst[dst_curr + 1u] |= (src[src_curr + 1u] >> 4u8);
            dst[dst_curr + 2u] |= (src[src_curr + 1u] << 2u8) & 0x3f_u8;
        } else {
            dst[dst_curr + 0u] |= (src[src_curr + 0u]) >> 2u8;
            dst[dst_curr + 1u] |= (src[src_curr + 0u] << 4u8) & 0x3f_u8;
            dst[dst_curr + 1u] |= (src[src_curr + 1u] >> 4u8);
            dst[dst_curr + 2u] |= (src[src_curr + 1u] << 2u8) & 0x3f_u8;
            dst[dst_curr + 2u] |= (src[src_curr + 2u] >> 6u8);
            dst[dst_curr + 3u] |= (src[src_curr + 3u]) & 0x3f_u8;
        }

        dst[dst_curr + 0u] = table[dst[dst_curr + 0u]];
        dst[dst_curr + 1u] = table[dst[dst_curr + 1u]];
        dst[dst_curr + 2u] = table[dst[dst_curr + 2u]];
        dst[dst_curr + 3u] = table[dst[dst_curr + 3u]];

        if src_length < 3u {
            dst[dst_curr + 3u] = PAD;
            if src_length < 2u {
                dst[dst_curr + 2u] = PAD;
            }
            break;
        }

        src_length -= 3u;
        src_curr += 3u;
        dst_curr += 4u;
    }
}

fn b64decode(table: [u8], dst: [mutable u8], src: [u8]) {
    // FIXME write
}
<commit_msg>Add test for base64n::enc.encode_byte<commit_after>//
// base64n.rs - new base64 module
//
// The Base64 Alphabet
//
//  Value Encoding  Value Encoding  Value Encoding  Value Encoding
//      0 A            17 R            34 i            51 z
//      1 B            18 S            35 j            52 0
//      2 C            19 T            36 k            53 1
//      3 D            20 U            37 l            54 2
//      4 E            21 V            38 m            55 3
//      5 F            22 W            39 n            56 4
//      6 G            23 X            40 o            57 5
//      7 H            24 Y            41 p            58 6
//      8 I            25 Z            42 q            59 7
//      9 J            26 a            43 r            60 8
//     10 K            27 b            44 s            61 9
//     11 L            28 c            45 t            62 +
//     12 M            29 d            46 u            63 /
//     13 N            30 e            47 v
//     14 O            31 f            48 w         (pad) =
//     15 P            32 g            49 x
//     16 Q            33 h            50 y
//
// The "URL and Filename safe" Base 64 Alphabet
//
// Value Encoding  Value Encoding  Value Encoding  Value Encoding
//     0 A            17 R            34 i            51 z
//     1 B            18 S            35 j            52 0
//     2 C            19 T            36 k            53 1
//     3 D            20 U            37 l            54 2
//     4 E            21 V            38 m            55 3
//     5 F            22 W            39 n            56 4
//     6 G            23 X            40 o            57 5
//     7 H            24 Y            41 p            58 6
//     8 I            25 Z            42 q            59 7
//     9 J            26 a            43 r            60 8
//    10 K            27 b            44 s            61 9
//    11 L            28 c            45 t            62 - (minus)
//    12 M            29 d            46 u            63 _ (underline)
//    13 N            30 e            47 v
//    14 O            31 f            48 w
//    15 P            32 g            49 x
//    16 Q            33 h            50 y         (pad) =
//

use std;

import vec::len;

const PAD: u8 = 61u8;

iface enc {
    fn encode(dst: [mutable u8], src: [u8]);
    fn encode_byte(src: [u8]) -> [u8];
    // FIXME `decode` should return desired length of `dst`
    fn decode(dst: [mutable u8], src: [u8]);
    fn decode_byte(src: [u8]) -> [u8];
}

fn mk() -> enc {
    type _enc = {table: [u8]};

    impl of enc for _enc {
        fn encode(dst: [mutable u8], src: [u8]) {
            b64encode(self.table, dst, src);
        }
        fn decode(dst: [mutable u8], src: [u8]) {
            // FIXME need self.decode_map instead of self.table.
            b64decode(self.table, dst, src);
        }
        fn encode_byte(src: [u8]) -> [u8] {
            let dst_length = encoded_len(len(src));
            let dst = vec::to_mut(vec::from_elem(dst_length, 0u8));
            self.encode(dst, src);
            vec::from_mut(dst)
        }
        fn decode_byte(src: [u8]) -> [u8] { [] }
    }

    let table = vec::to_mut(vec::from_elem(62u, 0u8)), i = 0u8;
    u8::range(65u8, 91u8)  { |j| table[i] = j; i += 1u8; }
    u8::range(97u8, 123u8) { |j| table[i] = j; i += 1u8; }
    u8::range(48u8, 58u8)  { |j| table[i] = j; i += 1u8; }

    {table: vec::from_mut(table)} as enc
}

fn encoded_len(src_length: uint) -> uint {
    (src_length + 2u) / 3u * 4u
}

fn decoded_len(src_length: uint) -> uint {
    src_length / 4u * 3u
}

fn b64encode(table: [u8], dst: [mutable u8], src: [u8]) {
    if len(src) == 0u {
        ret;
    }

    let src_length = len(src);
    let dst_length = len(dst);
    let dst_curr = 0u;
    let src_curr = 0u;

    if dst_length % 4u != 0u {
        fail "dst's length should be divisible by 4";
    }

    while src_length > 0u {
        dst[dst_curr + 0u] = 0u8;
        dst[dst_curr + 1u] = 0u8;
        dst[dst_curr + 2u] = 0u8;
        dst[dst_curr + 3u] = 0u8;

        if src_length == 1u {
            dst[dst_curr + 0u] |= (src[src_curr + 0u]) >> 2u8;
            dst[dst_curr + 1u] |= (src[src_curr + 0u] << 4u8) & 0x3f_u8;
        } else if src_length == 2u {
            dst[dst_curr + 0u] |= (src[src_curr + 0u]) >> 2u8;
            dst[dst_curr + 1u] |= (src[src_curr + 0u] << 4u8) & 0x3f_u8;
            dst[dst_curr + 1u] |= (src[src_curr + 1u] >> 4u8);
            dst[dst_curr + 2u] |= (src[src_curr + 1u] << 2u8) & 0x3f_u8;
        } else {
            dst[dst_curr + 0u] |= (src[src_curr + 0u]) >> 2u8;
            dst[dst_curr + 1u] |= (src[src_curr + 0u] << 4u8) & 0x3f_u8;
            dst[dst_curr + 1u] |= (src[src_curr + 1u] >> 4u8);
            dst[dst_curr + 2u] |= (src[src_curr + 1u] << 2u8) & 0x3f_u8;
            dst[dst_curr + 2u] |= (src[src_curr + 2u] >> 6u8);
            dst[dst_curr + 3u] |= (src[src_curr + 2u]) & 0x3f_u8;
        }

        dst[dst_curr + 0u] = table[dst[dst_curr + 0u]];
        dst[dst_curr + 1u] = table[dst[dst_curr + 1u]];
        dst[dst_curr + 2u] = table[dst[dst_curr + 2u]];
        dst[dst_curr + 3u] = table[dst[dst_curr + 3u]];

        if src_length < 3u {
            dst[dst_curr + 3u] = PAD;
            if src_length < 2u {
                dst[dst_curr + 2u] = PAD;
            }
            break;
        }

        src_length -= 3u;
        src_curr += 3u;
        dst_curr += 4u;
    }
}

fn b64decode(table: [u8], dst: [mutable u8], src: [u8]) {
    // FIXME write
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_encode_byte() {
        let src = [102u8, 111u8, 111u8, 98u8, 97u8, 114u8];
        let enc = mk();
        let res = enc.encode_byte(src);
        assert res == [90u8, 109u8, 57u8, 118u8, 89u8, 109u8, 70u8, 121u8];
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 `multipart` Crate Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use mock::{ClientRequest, HttpBuffer};

use server::{MultipartField, MultipartData, ReadEntry, FieldHeaders};

use mime::{self, Mime};

use rand::{self, Rng};

use std::collections::{HashMap, HashSet};
use std::collections::hash_map::{Entry, OccupiedEntry};
use std::fmt;
use std::io::prelude::*;
use std::io::Cursor;
use std::iter::{self, FromIterator};

const MIN_FIELDS: usize = 1;
const MAX_FIELDS: usize = 3;

const MIN_LEN: usize = 2;
const MAX_LEN: usize = 5;
const MAX_DASHES: usize = 2;

fn collect_rand<C: FromIterator<T>, T, F: FnMut() -> T>(mut gen: F) -> C {
    (0 .. rand::thread_rng().gen_range(MIN_FIELDS, MAX_FIELDS))
        .map(|_| gen()).collect()
}

macro_rules! expect_fmt (
    ($val:expr, $($args:tt)*) => (
        match $val {
            Some(val) => val,
            None => panic!($($args)*),
        }
    );
);

/// The error is provided as the `err` format argument
macro_rules! expect_ok_fmt (
    ($val:expr, $($args:tt)*) => (
        match $val {
            Ok(val) => val,
            Err(e) => panic!($($args)*, err=e),
        }
    );
);

fn get_field<'m, V>(field: &FieldHeaders, fields: &'m mut HashMap<String, V>) -> Option<OccupiedEntry<'m, String, V>> {
    match fields.entry(field.name.to_string()) {
        Entry::Occupied(occupied) => Some(occupied),
        Entry::Vacant(_) => None,
    }
}

#[derive(Debug)]
struct TestFields {
    texts: HashMap<String, HashSet<String>>,
    files: HashMap<String, HashSet<FileEntry>>,
}

impl TestFields {
    fn gen() -> Self {
        TestFields {
            texts: collect_rand(|| (gen_string(), collect_rand(gen_string))),
            files: collect_rand(|| (gen_string(), FileEntry::gen_many())),
        }
    }

    fn check_field<M: ReadEntry>(&mut self, mut field: MultipartField<M>) -> M {
        if field.is_text() {
            let mut text_entries = expect_fmt!(get_field(&field.headers, &mut self.texts),
                                        "Got text field that wasn't in original dataset: {:?}",
                                        field.headers);

            let mut text = String::new();
            expect_ok_fmt!(
                field.data.read_to_string(&mut text),
                "error failed to read text data to string: {:?}\n{err}", field.headers
            );

            assert!(
                text_entries.get_mut().remove(&text),
                "Got field text data that wasn't in original data set: {:?}\n{:?}\n{:?}",
                field.headers,
                text,
                text_entries.get(),
            );

            if text_entries.get().is_empty() {
                text_entries.remove_entry();
            }

            return field.data.into_inner();
        }

        let mut file_entries = expect_fmt!(get_field(&field.headers, &mut self.files),
                                        "Got file field that wasn't in original dataset: {:?}",
                                        field.headers);

        let field_name = field.headers.name.clone();
        let (test_entry, inner) = FileEntry::from_field(field);

        assert!(
            file_entries.get_mut().remove(&test_entry),
            "Got field entry that wasn't in original dataset: name: {:?}\n{:?}\nEntries: {:?}",
            field_name,
            test_entry,
            file_entries.get()
        );

        if file_entries.get().is_empty() {
            file_entries.remove_entry();
        }

        return inner;
    }

    fn assert_is_empty(&self) {
        assert!(self.texts.is_empty(), "Text Fields were not exhausted! {:?}", self.texts);
        assert!(self.files.is_empty(), "File Fields were not exhausted! {:?}", self.files);
    }
}

#[derive(Debug, Hash, PartialEq, Eq)]
struct FileEntry {
    content_type: Mime,
    filename: Option<String>,
    data: PrintHex,
}

impl FileEntry {
    fn from_field<M: ReadEntry>(mut field: MultipartField<M>) -> (FileEntry, M) {
        let mut data = Vec::new();
        expect_ok_fmt!(
            field.data.read_to_end(&mut data),
            "Error reading file field: {:?}\n{err}", field.headers
        );

        (
            FileEntry {
                content_type: field.headers.content_type.unwrap_or(mime::APPLICATION_OCTET_STREAM),
                filename: field.headers.filename,
                data: PrintHex(data),
            },
            field.data.into_inner()
        )
    }

    fn gen_many() -> HashSet<FileEntry> {
        collect_rand(Self::gen)
    }

    fn gen() -> Self {
        let filename = match gen_bool() {
            true => Some(gen_string()),
            false => None,
        };

        let data = PrintHex(match gen_bool() {
            true => gen_string().into_bytes(),
            false => gen_bytes(),
        });

        FileEntry {
            content_type: rand_mime(),
            filename,
            data,
        }
    }

    fn filename(&self) -> Option<&str> {
        self.filename.as_ref().map(|s| &**s)
    }
}

#[derive(PartialEq, Eq, Hash)]
struct PrintHex(Vec<u8>);

impl fmt::Debug for PrintHex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[")?;

        let mut written = false;

        for byte in &self.0 {
            write!(f, "{:X}", byte)?;

            if written {
                write!(f, ", ")?;
            }

            written = true;
        }

        write!(f, "]")
    }
}

macro_rules! do_test (
    ($client_test:ident, $server_test:ident) => (
        let _ = ::env_logger::init();

        info!("Client Test: {:?} Server Test: {:?}", stringify!($client_test),
              stringify!($server_test));

        let mut test_fields = TestFields::gen();

        trace!("Fields for test: {:?}", test_fields);

        let buf = $client_test(&test_fields);

        trace!(
            "\n==Test Buffer Begin==\n{}\n==Test Buffer End==",
            String::from_utf8_lossy(&buf.buf)
        );

        $server_test(buf, &mut test_fields);

        test_fields.assert_is_empty();
    );
);

#[test]
fn reg_client_reg_server() {
    do_test!(test_client, test_server);
}

#[test]
fn reg_client_entry_server() {
    do_test!(test_client, test_server_entry_api);
}

#[test]
fn lazy_client_reg_server() {
    do_test!(test_client_lazy, test_server);
}

#[test]
fn lazy_client_entry_server() {
    do_test!(test_client_lazy, test_server_entry_api);
}

mod extended {
    use super::{test_client, test_server, test_server_entry_api, test_client_lazy, TestFields};

    use std::time::Instant;

    const TIME_LIMIT_SECS: u64 = 300;

    #[test]
    #[ignore]
    fn reg_client_reg_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client, test_server);
        }
    }

    #[test]
    #[ignore]
    fn reg_client_entry_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client, test_server_entry_api);
        }
    }

    #[test]
    #[ignore]
    fn lazy_client_reg_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client_lazy, test_server);
        }
    }

    #[test]
    #[ignore]
    fn lazy_client_entry_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client_lazy, test_server_entry_api);
        }
    }
}

fn gen_bool() -> bool {
    rand::thread_rng().gen()
}

fn gen_string() -> String {
    let mut rng_1 = rand::thread_rng();
    let mut rng_2 = rand::thread_rng();

    let str_len_1 = rng_1.gen_range(MIN_LEN, MAX_LEN + 1);
    let str_len_2 = rng_2.gen_range(MIN_LEN, MAX_LEN + 1);
    let num_dashes = rng_1.gen_range(0, MAX_DASHES + 1);

    rng_1.gen_ascii_chars().take(str_len_1)
        .chain(iter::repeat('-').take(num_dashes))
        .chain(rng_2.gen_ascii_chars().take(str_len_2))
        .collect()
}

fn gen_bytes() -> Vec<u8> {
    gen_string().into_bytes()
}

fn test_client(test_fields: &TestFields) -> HttpBuffer {
    use client::Multipart;

    let request = ClientRequest::default();

    let mut test_files = test_fields.files.iter().flat_map(
        |(name, files)| files.iter().map(move |file| (name, file))
    );

    let mut test_texts = test_fields.texts.iter().flat_map(
        |(name, texts)| texts.iter().map(move |text| (name, text))
    );

    let mut multipart = Multipart::from_request(request).unwrap();

    // Intersperse file fields amongst text fields
    for (name, text) in test_texts {
        if let Some((file_name, file)) = test_files.next() {
            multipart.write_stream(file_name, &mut &*file.data.0, file.filename(),
                                   Some(file.content_type.clone())).unwrap();
        }

        multipart.write_text(name, text).unwrap();
    }

    // Write remaining files
    for (file_name, file) in test_files {
        multipart.write_stream(file_name, &mut &*file.data.0, file.filename(),
                               Some(file.content_type.clone())).unwrap();
    }

    multipart.send().unwrap()
}

fn test_client_lazy(test_fields: &TestFields) -> HttpBuffer {
    use client::lazy::Multipart;

    let mut multipart = Multipart::new();

    let mut test_files = test_fields.files.iter().flat_map(
        |(name, files)| files.iter().map(move |file| (name, file))
    );

    let mut test_texts = test_fields.texts.iter().flat_map(
        |(name, texts)| texts.iter().map(move |text| (name, text))
    );

    for (name, text) in test_texts {
        if let Some((file_name, file)) = test_files.next() {
                multipart.add_stream(&**file_name, Cursor::new(&file.data.0), file.filename(),
                                     Some(file.content_type.clone()));
        }

        multipart.add_text(&**name, &**text);
    }

    for (file_name, file) in test_files {
        multipart.add_stream(&**file_name, Cursor::new(&file.data.0), file.filename(),
                             Some(file.content_type.clone()));
    }

    let mut prepared = multipart.prepare().unwrap();

    let mut buf = Vec::new();

    let boundary = prepared.boundary().to_owned();
    let content_len = prepared.content_len();

    prepared.read_to_end(&mut buf).unwrap();

    HttpBuffer::with_buf(buf, boundary, content_len)
}

fn test_server(buf: HttpBuffer, fields: &mut TestFields) {
    use server::Multipart;

    let server_buf = buf.for_server();

    if let Some(content_len) = server_buf.content_len {
        assert!(content_len == server_buf.data.len() as u64, "Supplied content_len different from actual");
    }

    let mut multipart = Multipart::from_request(server_buf)
        .unwrap_or_else(|_| panic!("Buffer should be multipart!"));

    while let Some(mut field) = multipart.read_entry_mut().unwrap_opt() {
        fields.check_field(field);
    }
}

fn test_server_entry_api(buf: HttpBuffer, fields: &mut TestFields) {
    use server::Multipart;

    let server_buf = buf.for_server();

    if let Some(content_len) = server_buf.content_len {
        assert!(content_len == server_buf.data.len() as u64, "Supplied content_len different from actual");
    }

    let mut multipart = Multipart::from_request(server_buf)
        .unwrap_or_else(|_| panic!("Buffer should be multipart!"));

    let entry = multipart.into_entry().expect_alt("Expected entry, got none", "Error reading entry");
    multipart = fields.check_field(entry);

    while let Some(entry) = multipart.into_entry().unwrap_opt() {
        multipart = fields.check_field(entry);
    }
}

fn rand_mime() -> Mime {
    rand::thread_rng().choose(&[
        // TODO: fill this out, preferably with variants that may be hard to parse
        // i.e. containing hyphens, mainly
        mime::APPLICATION_OCTET_STREAM,
        mime::TEXT_PLAIN,
        mime::IMAGE_PNG,
    ]).unwrap().clone()
}
<commit_msg>fix `local_test`<commit_after>// Copyright 2016 `multipart` Crate Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use mock::{ClientRequest, HttpBuffer};

use server::{MultipartField, MultipartData, ReadEntry, FieldHeaders};

use mime::{self, Mime};

use rand::{self, Rng};

use std::collections::{HashMap, HashSet};
use std::collections::hash_map::{Entry, OccupiedEntry};
use std::fmt;
use std::io::prelude::*;
use std::io::Cursor;
use std::iter::{self, FromIterator};

const MIN_FIELDS: usize = 1;
const MAX_FIELDS: usize = 3;

const MIN_LEN: usize = 2;
const MAX_LEN: usize = 5;
const MAX_DASHES: usize = 2;

fn collect_rand<C: FromIterator<T>, T, F: FnMut() -> T>(mut gen: F) -> C {
    (0 .. rand::thread_rng().gen_range(MIN_FIELDS, MAX_FIELDS))
        .map(|_| gen()).collect()
}

macro_rules! expect_fmt (
    ($val:expr, $($args:tt)*) => (
        match $val {
            Some(val) => val,
            None => panic!($($args)*),
        }
    );
);

/// The error is provided as the `err` format argument
macro_rules! expect_ok_fmt (
    ($val:expr, $($args:tt)*) => (
        match $val {
            Ok(val) => val,
            Err(e) => panic!($($args)*, err=e),
        }
    );
);

fn get_field<'m, V>(field: &FieldHeaders, fields: &'m mut HashMap<String, V>) -> Option<OccupiedEntry<'m, String, V>> {
    match fields.entry(field.name.to_string()) {
        Entry::Occupied(occupied) => Some(occupied),
        Entry::Vacant(_) => None,
    }
}

#[derive(Debug)]
struct TestFields {
    texts: HashMap<String, HashSet<String>>,
    files: HashMap<String, HashSet<FileEntry>>,
}

impl TestFields {
    fn gen() -> Self {
        TestFields {
            texts: collect_rand(|| (gen_string(), collect_rand(gen_string))),
            files: collect_rand(|| (gen_string(), FileEntry::gen_many())),
        }
    }

    fn check_field<M: ReadEntry>(&mut self, mut field: MultipartField<M>) -> M {
        // text/plain fields would be considered a file by `TestFields`
        if field.headers.content_type.is_none() {
            let mut text_entries = expect_fmt!(get_field(&field.headers, &mut self.texts),
                                        "Got text field that wasn't in original dataset: {:?}",
                                        field.headers);

            let mut text = String::new();
            expect_ok_fmt!(
                field.data.read_to_string(&mut text),
                "error failed to read text data to string: {:?}\n{err}", field.headers
            );

            assert!(
                text_entries.get_mut().remove(&text),
                "Got field text data that wasn't in original data set: {:?}\n{:?}\n{:?}",
                field.headers,
                text,
                text_entries.get(),
            );

            if text_entries.get().is_empty() {
                text_entries.remove_entry();
            }

            return field.data.into_inner();
        }

        let mut file_entries = expect_fmt!(get_field(&field.headers, &mut self.files),
                                        "Got file field that wasn't in original dataset: {:?}",
                                        field.headers);

        let field_name = field.headers.name.clone();
        let (test_entry, inner) = FileEntry::from_field(field);

        assert!(
            file_entries.get_mut().remove(&test_entry),
            "Got field entry that wasn't in original dataset: name: {:?}\n{:?}\nEntries: {:?}",
            field_name,
            test_entry,
            file_entries.get()
        );

        if file_entries.get().is_empty() {
            file_entries.remove_entry();
        }

        return inner;
    }

    fn assert_is_empty(&self) {
        assert!(self.texts.is_empty(), "Text Fields were not exhausted! {:?}", self.texts);
        assert!(self.files.is_empty(), "File Fields were not exhausted! {:?}", self.files);
    }
}

#[derive(Debug, Hash, PartialEq, Eq)]
struct FileEntry {
    content_type: Mime,
    filename: Option<String>,
    data: PrintHex,
}

impl FileEntry {
    fn from_field<M: ReadEntry>(mut field: MultipartField<M>) -> (FileEntry, M) {
        let mut data = Vec::new();
        expect_ok_fmt!(
            field.data.read_to_end(&mut data),
            "Error reading file field: {:?}\n{err}", field.headers
        );

        (
            FileEntry {
                content_type: field.headers.content_type.unwrap_or(mime::APPLICATION_OCTET_STREAM),
                filename: field.headers.filename,
                data: PrintHex(data),
            },
            field.data.into_inner()
        )
    }

    fn gen_many() -> HashSet<FileEntry> {
        collect_rand(Self::gen)
    }

    fn gen() -> Self {
        let filename = match gen_bool() {
            true => Some(gen_string()),
            false => None,
        };

        let data = PrintHex(match gen_bool() {
            true => gen_string().into_bytes(),
            false => gen_bytes(),
        });

        FileEntry {
            content_type: rand_mime(),
            filename,
            data,
        }
    }

    fn filename(&self) -> Option<&str> {
        self.filename.as_ref().map(|s| &**s)
    }
}

#[derive(PartialEq, Eq, Hash)]
struct PrintHex(Vec<u8>);

impl fmt::Debug for PrintHex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[")?;

        let mut written = false;

        for byte in &self.0 {
            write!(f, "{:X}", byte)?;

            if written {
                write!(f, ", ")?;
            }

            written = true;
        }

        write!(f, "]")
    }
}

macro_rules! do_test (
    ($client_test:ident, $server_test:ident) => (
        let _ = ::env_logger::init();

        info!("Client Test: {:?} Server Test: {:?}", stringify!($client_test),
              stringify!($server_test));

        let mut test_fields = TestFields::gen();

        trace!("Fields for test: {:?}", test_fields);

        let buf = $client_test(&test_fields);

        trace!(
            "\n==Test Buffer Begin==\n{}\n==Test Buffer End==",
            String::from_utf8_lossy(&buf.buf)
        );

        $server_test(buf, &mut test_fields);

        test_fields.assert_is_empty();
    );
);

#[test]
fn reg_client_reg_server() {
    do_test!(test_client, test_server);
}

#[test]
fn reg_client_entry_server() {
    do_test!(test_client, test_server_entry_api);
}

#[test]
fn lazy_client_reg_server() {
    do_test!(test_client_lazy, test_server);
}

#[test]
fn lazy_client_entry_server() {
    do_test!(test_client_lazy, test_server_entry_api);
}

mod extended {
    use super::{test_client, test_server, test_server_entry_api, test_client_lazy, TestFields};

    use std::time::Instant;

    const TIME_LIMIT_SECS: u64 = 300;

    #[test]
    #[ignore]
    fn reg_client_reg_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client, test_server);
        }
    }

    #[test]
    #[ignore]
    fn reg_client_entry_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client, test_server_entry_api);
        }
    }

    #[test]
    #[ignore]
    fn lazy_client_reg_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client_lazy, test_server);
        }
    }

    #[test]
    #[ignore]
    fn lazy_client_entry_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client_lazy, test_server_entry_api);
        }
    }
}

fn gen_bool() -> bool {
    rand::thread_rng().gen()
}

fn gen_string() -> String {
    let mut rng_1 = rand::thread_rng();
    let mut rng_2 = rand::thread_rng();

    let str_len_1 = rng_1.gen_range(MIN_LEN, MAX_LEN + 1);
    let str_len_2 = rng_2.gen_range(MIN_LEN, MAX_LEN + 1);
    let num_dashes = rng_1.gen_range(0, MAX_DASHES + 1);

    rng_1.gen_ascii_chars().take(str_len_1)
        .chain(iter::repeat('-').take(num_dashes))
        .chain(rng_2.gen_ascii_chars().take(str_len_2))
        .collect()
}

fn gen_bytes() -> Vec<u8> {
    gen_string().into_bytes()
}

fn test_client(test_fields: &TestFields) -> HttpBuffer {
    use client::Multipart;

    let request = ClientRequest::default();

    let mut test_files = test_fields.files.iter().flat_map(
        |(name, files)| files.iter().map(move |file| (name, file))
    );

    let mut test_texts = test_fields.texts.iter().flat_map(
        |(name, texts)| texts.iter().map(move |text| (name, text))
    );

    let mut multipart = Multipart::from_request(request).unwrap();

    // Intersperse file fields amongst text fields
    for (name, text) in test_texts {
        if let Some((file_name, file)) = test_files.next() {
            multipart.write_stream(file_name, &mut &*file.data.0, file.filename(),
                                   Some(file.content_type.clone())).unwrap();
        }

        multipart.write_text(name, text).unwrap();
    }

    // Write remaining files
    for (file_name, file) in test_files {
        multipart.write_stream(file_name, &mut &*file.data.0, file.filename(),
                               Some(file.content_type.clone())).unwrap();
    }

    multipart.send().unwrap()
}

fn test_client_lazy(test_fields: &TestFields) -> HttpBuffer {
    use client::lazy::Multipart;

    let mut multipart = Multipart::new();

    let mut test_files = test_fields.files.iter().flat_map(
        |(name, files)| files.iter().map(move |file| (name, file))
    );

    let mut test_texts = test_fields.texts.iter().flat_map(
        |(name, texts)| texts.iter().map(move |text| (name, text))
    );

    for (name, text) in test_texts {
        if let Some((file_name, file)) = test_files.next() {
                multipart.add_stream(&**file_name, Cursor::new(&file.data.0), file.filename(),
                                     Some(file.content_type.clone()));
        }

        multipart.add_text(&**name, &**text);
    }

    for (file_name, file) in test_files {
        multipart.add_stream(&**file_name, Cursor::new(&file.data.0), file.filename(),
                             Some(file.content_type.clone()));
    }

    let mut prepared = multipart.prepare().unwrap();

    let mut buf = Vec::new();

    let boundary = prepared.boundary().to_owned();
    let content_len = prepared.content_len();

    prepared.read_to_end(&mut buf).unwrap();

    HttpBuffer::with_buf(buf, boundary, content_len)
}

fn test_server(buf: HttpBuffer, fields: &mut TestFields) {
    use server::Multipart;

    let server_buf = buf.for_server();

    if let Some(content_len) = server_buf.content_len {
        assert!(content_len == server_buf.data.len() as u64, "Supplied content_len different from actual");
    }

    let mut multipart = Multipart::from_request(server_buf)
        .unwrap_or_else(|_| panic!("Buffer should be multipart!"));

    while let Some(mut field) = multipart.read_entry_mut().unwrap_opt() {
        fields.check_field(field);
    }
}

fn test_server_entry_api(buf: HttpBuffer, fields: &mut TestFields) {
    use server::Multipart;

    let server_buf = buf.for_server();

    if let Some(content_len) = server_buf.content_len {
        assert!(content_len == server_buf.data.len() as u64, "Supplied content_len different from actual");
    }

    let mut multipart = Multipart::from_request(server_buf)
        .unwrap_or_else(|_| panic!("Buffer should be multipart!"));

    let entry = multipart.into_entry().expect_alt("Expected entry, got none", "Error reading entry");
    multipart = fields.check_field(entry);

    while let Some(entry) = multipart.into_entry().unwrap_opt() {
        multipart = fields.check_field(entry);
    }
}

fn rand_mime() -> Mime {
    rand::thread_rng().choose(&[
        // TODO: fill this out, preferably with variants that may be hard to parse
        // i.e. containing hyphens, mainly
        mime::APPLICATION_OCTET_STREAM,
        mime::TEXT_PLAIN,
        mime::IMAGE_PNG,
    ]).unwrap().clone()
}
<|endoftext|>