<commit_before>// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors,
// refer to the Cargo.toml file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Angle units for type-safe, self-documenting code.

use std::fmt;
use std::f64;
use std::ops::*;

use rand::{Rand, Rng};
use rand::distributions::range::SampleRange;

use rust_num::{Float, Zero};
use rust_num::traits::cast;

use approx::ApproxEq;
use num::BaseFloat;

/// An angle, in radians
#[derive(Copy, Clone, PartialEq, PartialOrd, RustcEncodable, RustcDecodable)]
pub struct Rad<S> { pub s: S }
/// An angle, in degrees
#[derive(Copy, Clone, PartialEq, PartialOrd, RustcEncodable, RustcDecodable)]
pub struct Deg<S> { pub s: S }

/// Create a new angle, in radians
#[inline] pub fn rad<S: BaseFloat>(s: S) -> Rad<S> { Rad { s: s } }
/// Create a new angle, in degrees
#[inline] pub fn deg<S: BaseFloat>(s: S) -> Deg<S> { Deg { s: s } }

impl<S> From<Rad<S>> for Deg<S> where S: BaseFloat {
    #[inline]
    fn from(r: Rad<S>) -> Deg<S> {
        deg(r.s * cast(180.0 / f64::consts::PI).unwrap())
    }
}

impl<S> From<Deg<S>> for Rad<S> where S: BaseFloat {
    #[inline]
    fn from(d: Deg<S>) -> Rad<S> {
        rad(d.s * cast(f64::consts::PI / 180.0).unwrap())
    }
}

/// Private utility functions for converting to/from scalars
trait ScalarConv<S> {
    fn from(s: S) -> Self;
    fn s<'a>(&'a self) -> &'a S;
    fn mut_s<'a>(&'a mut self) -> &'a mut S;
}

impl<S: BaseFloat> ScalarConv<S> for Rad<S> {
    #[inline] fn from(s: S) -> Rad<S> { rad(s) }
    #[inline] fn s<'a>(&'a self) -> &'a S { &self.s }
    #[inline] fn mut_s<'a>(&'a mut self) -> &'a mut S { &mut self.s }
}

impl<S: BaseFloat> ScalarConv<S> for Deg<S> {
    #[inline] fn from(s: S) -> Deg<S> { deg(s) }
    #[inline] fn s<'a>(&'a self) -> &'a S { &self.s }
    #[inline] fn mut_s<'a>(&'a mut self) -> &'a mut S { &mut self.s }
}

/// Operations on angles.
pub trait Angle where
    Self: Copy + Clone,
    Self: PartialEq + PartialOrd,
    // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
    Self: ApproxEq<Epsilon = <Self as Angle>::Unitless>,
    Self: Into<Rad<<Self as Angle>::Unitless>>,
    Self: Into<Deg<<Self as Angle>::Unitless>>,
    Self: ScalarConv<<Self as Angle>::Unitless>,
    Self: fmt::Debug,

    Self: Neg<Output = Self>,

    Self: Add<Self, Output = Self>,
    Self: Sub<Self, Output = Self>,
    Self: Div<Self, Output = <Self as Angle>::Unitless>,
    Self: Rem<Self, Output = <Self as Angle>::Unitless>,

    Self: Mul<<Self as Angle>::Unitless, Output = Self>,
    Self: Div<<Self as Angle>::Unitless, Output = Self>,
    Self: Rem<<Self as Angle>::Unitless, Output = Self>,
{
    type Unitless: BaseFloat;

    /// Create a new angle from any other valid angle.
    fn from<A: Angle<Unitless = Self::Unitless>>(theta: A) -> Self;

    /// Return the angle, normalized to the range `[0, full_turn)`.
    fn normalize(self) -> Self;

    /// Return the angle rotated by half a turn
    #[inline]
    fn opposite(self) -> Self {
        Self::normalize(self + Self::turn_div_2())
    }

    /// Returns the interior bisector of the two angles
    #[inline]
    fn bisect(self, other: Self) -> Self {
        let half = cast(0.5f64).unwrap();
        Self::normalize((self - other) * half + self)
    }

    fn zero() -> Self;
    fn full_turn() -> Self;
    fn turn_div_2() -> Self;
    fn turn_div_3() -> Self;
    fn turn_div_4() -> Self;
    fn turn_div_6() -> Self;

    #[inline]
    fn equiv(&self, other: &Self) -> bool {
        self.normalize() == other.normalize()
    }
}

#[inline] pub fn bisect<A: Angle>(a: A, b: A) -> A { a.bisect(b) }

#[inline] pub fn sin<S: BaseFloat, R: Into<Rad<S>>>(theta: R) -> S { theta.into().s.sin() }
#[inline] pub fn cos<S: BaseFloat, R: Into<Rad<S>>>(theta: R) -> S { theta.into().s.cos() }
#[inline] pub fn tan<S: BaseFloat, R: Into<Rad<S>>>(theta: R) -> S { theta.into().s.tan() }
#[inline] pub fn sin_cos<S: BaseFloat, R: Into<Rad<S>>>(theta: R) -> (S, S) { theta.into().s.sin_cos() }

#[inline] pub fn cot<S: BaseFloat, R: Into<Rad<S>>>(theta: R) -> S { tan(theta.into()).recip() }
#[inline] pub fn sec<S: BaseFloat, R: Into<Rad<S>>>(theta: R) -> S { cos(theta.into()).recip() }
#[inline] pub fn csc<S: BaseFloat, R: Into<Rad<S>>>(theta: R) -> S { sin(theta.into()).recip() }

#[inline] pub fn asin<S: BaseFloat, R: From<Rad<S>>>(s: S) -> R { rad(s.asin()).into() }
#[inline] pub fn acos<S: BaseFloat, R: From<Rad<S>>>(s: S) -> R { rad(s.acos()).into() }
#[inline] pub fn atan<S: BaseFloat, R: From<Rad<S>>>(s: S) -> R { rad(s.atan()).into() }
#[inline] pub fn atan2<S: BaseFloat, R: From<Rad<S>>>(a: S, b: S) -> R { rad(a.atan2(b)).into() }

macro_rules! impl_angle {
    ($Angle:ident, $fmt:expr, $full_turn:expr, $hi:expr) => {
        impl<S: BaseFloat> Angle for $Angle<S> {
            type Unitless = S;

            #[inline]
            fn zero() -> $Angle<S> { ScalarConv::from(S::zero()) }

            #[inline]
            fn from<A: Angle<Unitless = S>>(theta: A) -> $Angle<S> { theta.into() }

            #[inline]
            fn normalize(self) -> Self {
                let tmp = self % Self::full_turn().s;
                if tmp < Self::zero() { tmp + Self::full_turn() } else { tmp }
            }

            #[inline] fn full_turn() -> $Angle<S> { ScalarConv::from(cast($full_turn).unwrap()) }
            #[inline] fn turn_div_2() -> $Angle<S> { let factor: S = cast(2).unwrap(); $Angle::full_turn() / factor }
            #[inline] fn turn_div_3() -> $Angle<S> { let factor: S = cast(3).unwrap(); $Angle::full_turn() / factor }
            #[inline] fn turn_div_4() -> $Angle<S> { let factor: S = cast(4).unwrap(); $Angle::full_turn() / factor }
            #[inline] fn turn_div_6() -> $Angle<S> { let factor: S = cast(6).unwrap(); $Angle::full_turn() / factor }
        }

        impl<S: BaseFloat> Neg for $Angle<S> {
            type Output = $Angle<S>;

            #[inline]
            fn neg(self) -> $Angle<S> { ScalarConv::from(-self.s) }
        }

        impl<'a, S: BaseFloat> Neg for &'a $Angle<S> {
            type Output = $Angle<S>;

            #[inline]
            fn neg(self) -> $Angle<S> { ScalarConv::from(-self.s) }
        }

        impl_binary_operator!(<S: BaseFloat> Add<$Angle<S> > for $Angle<S> {
            fn add(lhs, rhs) -> $Angle<S> { ScalarConv::from(lhs.s + rhs.s) }
        });
        impl_binary_operator!(<S: BaseFloat> Sub<$Angle<S> > for $Angle<S> {
            fn sub(lhs, rhs) -> $Angle<S> { ScalarConv::from(lhs.s - rhs.s) }
        });
        impl_binary_operator!(<S: BaseFloat> Div<$Angle<S> > for $Angle<S> {
            fn div(lhs, rhs) -> S { lhs.s / rhs.s }
        });
        impl_binary_operator!(<S: BaseFloat> Rem<$Angle<S> > for $Angle<S> {
            fn rem(lhs, rhs) -> S { lhs.s % rhs.s }
        });

        impl_binary_operator!(<S: BaseFloat> Mul<S> for $Angle<S> {
            fn mul(lhs, scalar) -> $Angle<S> { ScalarConv::from(lhs.s * scalar) }
        });
        impl_binary_operator!(<S: BaseFloat> Div<S> for $Angle<S> {
            fn div(lhs, scalar) -> $Angle<S> { ScalarConv::from(lhs.s / scalar) }
        });
        impl_binary_operator!(<S: BaseFloat> Rem<S> for $Angle<S> {
            fn rem(lhs, scalar) -> $Angle<S> { ScalarConv::from(lhs.s % scalar) }
        });

        impl<S: BaseFloat> ApproxEq for $Angle<S> {
            type Epsilon = S;

            #[inline]
            fn approx_eq_eps(&self, other: &$Angle<S>, epsilon: &S) -> bool {
                self.s.approx_eq_eps(&other.s, epsilon)
            }
        }

        impl<S: BaseFloat + SampleRange> Rand for $Angle<S> {
            #[inline]
            fn rand<R: Rng>(rng: &mut R) -> $Angle<S> {
                ScalarConv::from(rng.gen_range(cast(-$hi).unwrap(), cast($hi).unwrap()))
            }
        }

        impl<S: BaseFloat> fmt::Debug for $Angle<S> {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, $fmt, self.s)
            }
        }
    }
}

impl_angle!(Rad, "{:?} rad", f64::consts::PI * 2.0, f64::consts::PI);
impl_angle!(Deg, "{:?}°", 360, 180);
<commit_msg>Remove ScalarConv trait and Angle::from<commit_after>// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors,
// refer to the Cargo.toml file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Angle units for type-safe, self-documenting code.

use std::fmt;
use std::f64;
use std::ops::*;

use rand::{Rand, Rng};
use rand::distributions::range::SampleRange;

use rust_num::{Float, Zero};
use rust_num::traits::cast;

use approx::ApproxEq;
use num::BaseFloat;

/// An angle, in radians
#[derive(Copy, Clone, PartialEq, PartialOrd, RustcEncodable, RustcDecodable)]
pub struct Rad<S> { pub s: S }
/// An angle, in degrees
#[derive(Copy, Clone, PartialEq, PartialOrd, RustcEncodable, RustcDecodable)]
pub struct Deg<S> { pub s: S }

/// Create a new angle, in radians
#[inline] pub fn rad<S: BaseFloat>(s: S) -> Rad<S> { Rad { s: s } }
/// Create a new angle, in degrees
#[inline] pub fn deg<S: BaseFloat>(s: S) -> Deg<S> { Deg { s: s } }

impl<S> From<Rad<S>> for Deg<S> where S: BaseFloat {
    #[inline]
    fn from(r: Rad<S>) -> Deg<S> {
        Deg::new(r.s * cast(180.0 / f64::consts::PI).unwrap())
    }
}

impl<S> From<Deg<S>> for Rad<S> where S: BaseFloat {
    #[inline]
    fn from(d: Deg<S>) -> Rad<S> {
        Rad::new(d.s * cast(f64::consts::PI / 180.0).unwrap())
    }
}

/// Operations on angles.
pub trait Angle where
    Self: Copy + Clone,
    Self: PartialEq + PartialOrd,
    // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
    Self: ApproxEq<Epsilon = <Self as Angle>::Unitless>,
    Self: fmt::Debug,

    Self: Neg<Output = Self>,

    Self: Add<Self, Output = Self>,
    Self: Sub<Self, Output = Self>,
    Self: Div<Self, Output = <Self as Angle>::Unitless>,
    Self: Rem<Self, Output = <Self as Angle>::Unitless>,

    Self: Mul<<Self as Angle>::Unitless, Output = Self>,
    Self: Div<<Self as Angle>::Unitless, Output = Self>,
    Self: Rem<<Self as Angle>::Unitless, Output = Self>,
{
    type Unitless: BaseFloat;

    /// Create an angle from a unitless value.
    fn new(value: Self::Unitless) -> Self;

    /// Return the angle, normalized to the range `[0, full_turn)`.
    fn normalize(self) -> Self;

    /// Return the angle rotated by half a turn
    #[inline]
    fn opposite(self) -> Self {
        Self::normalize(self + Self::turn_div_2())
    }

    /// Returns the interior bisector of the two angles
    #[inline]
    fn bisect(self, other: Self) -> Self {
        let half = cast(0.5f64).unwrap();
        Self::normalize((self - other) * half + self)
    }

    fn zero() -> Self;
    fn full_turn() -> Self;
    fn turn_div_2() -> Self;
    fn turn_div_3() -> Self;
    fn turn_div_4() -> Self;
    fn turn_div_6() -> Self;

    #[inline]
    fn equiv(&self, other: &Self) -> bool {
        self.normalize() == other.normalize()
    }
}

#[inline] pub fn bisect<A: Angle>(a: A, b: A) -> A { a.bisect(b) }

#[inline] pub fn sin<S: BaseFloat, R: Into<Rad<S>>>(theta: R) -> S { theta.into().s.sin() }
#[inline] pub fn cos<S: BaseFloat, R: Into<Rad<S>>>(theta: R) -> S { theta.into().s.cos() }
#[inline] pub fn tan<S: BaseFloat, R: Into<Rad<S>>>(theta: R) -> S { theta.into().s.tan() }
#[inline] pub fn sin_cos<S: BaseFloat, R: Into<Rad<S>>>(theta: R) -> (S, S) { theta.into().s.sin_cos() }

#[inline] pub fn cot<S: BaseFloat, R: Into<Rad<S>>>(theta: R) -> S { tan(theta.into()).recip() }
#[inline] pub fn sec<S: BaseFloat, R: Into<Rad<S>>>(theta: R) -> S { cos(theta.into()).recip() }
#[inline] pub fn csc<S: BaseFloat, R: Into<Rad<S>>>(theta: R) -> S { sin(theta.into()).recip() }

#[inline] pub fn asin<S: BaseFloat, R: From<Rad<S>>>(s: S) -> R { rad(s.asin()).into() }
#[inline] pub fn acos<S: BaseFloat, R: From<Rad<S>>>(s: S) -> R { rad(s.acos()).into() }
#[inline] pub fn atan<S: BaseFloat, R: From<Rad<S>>>(s: S) -> R { rad(s.atan()).into() }
#[inline] pub fn atan2<S: BaseFloat, R: From<Rad<S>>>(a: S, b: S) -> R { rad(a.atan2(b)).into() }

macro_rules! impl_angle {
    ($Angle:ident, $fmt:expr, $full_turn:expr, $hi:expr) => {
        impl<S: BaseFloat> Angle for $Angle<S> {
            type Unitless = S;

            #[inline]
            fn new(value: S) -> $Angle<S> {
                $Angle { s: value }
            }

            #[inline]
            fn zero() -> $Angle<S> {
                $Angle::new(S::zero())
            }

            #[inline]
            fn normalize(self) -> Self {
                let tmp = self % Self::full_turn().s;
                if tmp < Self::zero() { tmp + Self::full_turn() } else { tmp }
            }

            #[inline] fn full_turn() -> $Angle<S> { $Angle::new(cast($full_turn).unwrap()) }
            #[inline] fn turn_div_2() -> $Angle<S> { let factor: S = cast(2).unwrap(); $Angle::full_turn() / factor }
            #[inline] fn turn_div_3() -> $Angle<S> { let factor: S = cast(3).unwrap(); $Angle::full_turn() / factor }
            #[inline] fn turn_div_4() -> $Angle<S> { let factor: S = cast(4).unwrap(); $Angle::full_turn() / factor }
            #[inline] fn turn_div_6() -> $Angle<S> { let factor: S = cast(6).unwrap(); $Angle::full_turn() / factor }
        }

        impl<S: BaseFloat> Neg for $Angle<S> {
            type Output = $Angle<S>;

            #[inline]
            fn neg(self) -> $Angle<S> { $Angle::new(-self.s) }
        }

        impl<'a, S: BaseFloat> Neg for &'a $Angle<S> {
            type Output = $Angle<S>;

            #[inline]
            fn neg(self) -> $Angle<S> { $Angle::new(-self.s) }
        }

        impl_binary_operator!(<S: BaseFloat> Add<$Angle<S> > for $Angle<S> {
            fn add(lhs, rhs) -> $Angle<S> { $Angle::new(lhs.s + rhs.s) }
        });
        impl_binary_operator!(<S: BaseFloat> Sub<$Angle<S> > for $Angle<S> {
            fn sub(lhs, rhs) -> $Angle<S> { $Angle::new(lhs.s - rhs.s) }
        });
        impl_binary_operator!(<S: BaseFloat> Div<$Angle<S> > for $Angle<S> {
            fn div(lhs, rhs) -> S { lhs.s / rhs.s }
        });
        impl_binary_operator!(<S: BaseFloat> Rem<$Angle<S> > for $Angle<S> {
            fn rem(lhs, rhs) -> S { lhs.s % rhs.s }
        });

        impl_binary_operator!(<S: BaseFloat> Mul<S> for $Angle<S> {
            fn mul(lhs, scalar) -> $Angle<S> { $Angle::new(lhs.s * scalar) }
        });
        impl_binary_operator!(<S: BaseFloat> Div<S> for $Angle<S> {
            fn div(lhs, scalar) -> $Angle<S> { $Angle::new(lhs.s / scalar) }
        });
        impl_binary_operator!(<S: BaseFloat> Rem<S> for $Angle<S> {
            fn rem(lhs, scalar) -> $Angle<S> { $Angle::new(lhs.s % scalar) }
        });

        impl<S: BaseFloat> ApproxEq for $Angle<S> {
            type Epsilon = S;

            #[inline]
            fn approx_eq_eps(&self, other: &$Angle<S>, epsilon: &S) -> bool {
                self.s.approx_eq_eps(&other.s, epsilon)
            }
        }

        impl<S: BaseFloat + SampleRange> Rand for $Angle<S> {
            #[inline]
            fn rand<R: Rng>(rng: &mut R) -> $Angle<S> {
                $Angle::new(rng.gen_range(cast(-$hi).unwrap(), cast($hi).unwrap()))
            }
        }

        impl<S: BaseFloat> fmt::Debug for $Angle<S> {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, $fmt, self.s)
            }
        }
    }
}

impl_angle!(Rad, "{:?} rad", f64::consts::PI * 2.0, f64::consts::PI);
impl_angle!(Deg, "{:?}°", 360, 180);
<|endoftext|>


<commit_before>extern crate rand;

use self::rand::{thread_rng, Rng};

use super::srs;
use super::srs::Direction;
use super::tetromino::{
    Point,
    Rotation,
    Tetromino,
    TetrominoType,
    TYPES
};

pub const WIDTH: usize = 10;
pub const HEIGHT: usize = 22;

const SPAWN: Point = Point { x: 3, y: 0 };

const LEFT: Point = Point { x: -1, y: 0 };
const RIGHT: Point = Point { x: 1, y: 0 };
const UP: Point = Point { x: 0, y: -1 };
const DOWN: Point = Point { x: 0, y: 1 };

pub type Field = [[Option<TetrominoType>; WIDTH]; HEIGHT];

/// A struct representing a 10x22 Tetris board
pub struct Board {
    field: Field,
    curr: Tetromino,
    next: Vec<TetrominoType>,
    is_topped_out: bool,
    score: usize,
    level: usize,
    cleared: usize,
}

impl Board {

    /// Initializes a new Board struct
    pub fn new() -> Self {

        // Create a sequence of pieces and shuffle them
        let mut next = vec![
            TetrominoType::I,
            TetrominoType::J,
            TetrominoType::L,
            TetrominoType::O,
            TetrominoType::S,
            TetrominoType::T,
            TetrominoType::Z,
        ];

        let mut rng = thread_rng();
        rng.shuffle(&mut next);

        let mut board = Board {
            field: [[None; WIDTH]; HEIGHT],
            curr: Tetromino::new(SPAWN, next.pop().unwrap(), Rotation::Spawn),
            next: next,
            is_topped_out: false,
            score: 0,
            cleared: 0,
            level: 0,
        };

        board.add_current();
        board
    }

    /// Applies gravity and clears any lines
    pub fn tick(&mut self) {
        self.apply_gravity();
    }

    /// Applies gravity to the field
    fn apply_gravity(&mut self) {
        let offset = DOWN;

        if self.is_moveable(offset) {
            self.do_move(offset);
        }

        else {
            self.clear_lines();
            self.spawn();
        }
    }

    /// Clears any lines from the field completed by the current Tetromino
    fn clear_lines(&mut self) {
        let mut cleared = vec![];

        for &mino in self.curr.minos().iter() {
            let row = (self.curr.origin().y + mino.y) as usize;

            if self.is_line(row) {
                for col in 0..WIDTH {
                    self.field[row][col] = None;
                }

                cleared.push(row);
            }
        }

        // Drop the rows if any line clears were made
        if cleared.first().is_some() {
            self.drop_rows(*cleared.first().unwrap(), cleared.len());

            // Update the player's status
            self.cleared += cleared.len();

            if self.cleared % 10 == 0 {
                self.level += 1;
            }

            self.score_clear(cleared.len());
        }
    }

    /// Determines if a specific row in the field is a complete line
    fn is_line(&self, row: usize) -> bool {
        for col in 0..WIDTH {
            if self.field[row][col].is_none() {
                return false;
            }
        }

        true
    }

    /// Drops all rows above the given start row by drop
    fn drop_rows(&mut self, start: usize, drop: usize) {

        // We must use rev() because Rust doesn't support backwards iteration
        for row in (0..start).rev() {
            for col in 0..WIDTH {
                if self.field[row][col].is_some() {
                    self.field[row + drop][col] = self.field[row][col];
                    self.field[row][col] = None;
                }
            }
        }
    }

    /// Increases the score based on the number of lines cleared
    fn score_clear(&mut self, lines: usize) {

        // The scoring formula used is identical to the NES version
        self.score += match lines {
            1 =>   40 * (self.level + 1),
            2 =>  100 * (self.level + 1),
            3 =>  300 * (self.level + 1),
            4 => 1200 * (self.level + 1),

            // 4+ lines cleared is impossible in the current system.
            _ => 0,
        }
    }

    /// Moves the current Tetromino to the left
    pub fn left(&mut self) {
        self.move_tetromino(LEFT);
    }

    /// Moves the current Tetromino to the right
    pub fn right(&mut self) {
        self.move_tetromino(RIGHT);
    }

    /// Moves the current Tetromino up
    pub fn up(&mut self) {
        self.move_tetromino(UP);
    }

    /// Moves the current Tetromino down
    pub fn down(&mut self) {
        self.move_tetromino(DOWN);
    }

    /// Continually drops the current Tetromino until it locks
    pub fn drop(&mut self) {
        while self.is_moveable(DOWN) {
            self.do_move(DOWN);
        }

        self.clear_lines();
        self.spawn();
    }

    /// Moves the current Tetromino by an (x, y) offset
    fn move_tetromino(&mut self, offset: Point) {
        if self.is_moveable(offset) {
            self.do_move(offset);
        }
    }

    /// Determines if an offset to the current Tetromino is possible or not
    fn is_moveable(&self, offset: Point) -> bool {
        for &mino in self.curr.minos().iter() {
            let pos = self.curr.origin() + mino + offset;

            // Determine if the field's boundaries are respected
            if pos.x < 0 || pos.y < 0 || (pos.x as usize) >= WIDTH || (pos.y as usize) >= HEIGHT {
                return false;
            }

             // Determine if the current Tetromino overlaps only itself if moved
            if self.field[pos.y as usize][pos.x as usize].is_some() && !self.overlaps_active(pos) {
                return false;
            }
        }

        true
    }

    /// Determines if an offset to the current Tetromino would cause it to
    /// only overlap itself or not
    fn overlaps_active(&self, new: Point) -> bool {
        for &mino in self.curr.minos().iter() {
            let pos = self.curr.origin() + mino;

            if new.x == pos.x && new.y == pos.y {
                return true;
            }
        }

        false
    }

    /// Performs the actual movement
    fn do_move(&mut self, offset: Point) {
        let mut minos = self.curr.minos().clone();

        // Moving down or right requires us to handle the blocks in
        // reverse order to prevent blocks from being erased
        match offset {
            RIGHT | DOWN => minos.reverse(),
            _ => { },
        }

        // Perform the move
        for &mino in minos.iter() {
            let org = self.curr.origin() + mino;
            let pos = org + offset;

            self.field[pos.y as usize][pos.x as usize] = self.field[org.y as usize][org.x as usize];
            self.field[org.y as usize][org.x as usize] = None;
        }

        let origin = self.curr.origin() + offset;
        self.curr.set_origin(origin);
    }

    /// Rotates the current Tetromino in a specified direction
    pub fn rotate(&mut self, dir: Direction) {
        match srs::rotate(&self.field, &self.curr, dir) {
            Some((field, rotated)) => {
                self.field = field;
                self.curr = rotated;
            },

            None => { },
        }
    }

    /// Peeks at the next Tetromino
    pub fn peek_next(&self) -> Tetromino {
        Tetromino::new(SPAWN, *self.next.last().unwrap(), Rotation::Spawn)
    }

    /// Spawns the next Tetromino in the sequence
    fn spawn(&mut self) {
        self.curr = Tetromino::new(SPAWN, self.next.pop().unwrap(), Rotation::Spawn);
        self.add_current();

        // All of the pieces have been picked, so reshuffle them
        if self.next.is_empty() {
            self.next = vec![
                TetrominoType::I,
                TetrominoType::J,
                TetrominoType::L,
                TetrominoType::O,
                TetrominoType::S,
                TetrominoType::T,
                TetrominoType::Z,
            ];

            let mut rng = thread_rng();
            rng.shuffle(&mut self.next);
        }
    }

    /// Adds the current Tetromino to the field
    fn add_current(&mut self) {

        // Add the new blocks to the board
        for &mino in self.curr.minos().iter() {
            let pos = self.curr.origin() + mino;

            // The field has been topped out and the game is over
            if self.field[pos.y as usize][pos.x as usize].is_some() {
                self.is_topped_out = true;
                break;
            }

            self.field[pos.y as usize][pos.x as usize] = Some(self.curr.tetromino_type());
        }
    }

    // GETTERS / SETTERS

    pub fn field(&self) -> Field {
        self.field
    }

    pub fn is_topped_out(&self) -> bool {
        self.is_topped_out
    }

    pub fn score(&self) -> usize {
        self.score
    }

    pub fn level(&self) -> usize {
        self.level
    }

    pub fn cleared(&self) -> usize {
        self.cleared
    }
}<commit_msg>Add scoring for hard and soft drops<commit_after>extern crate rand;

use self::rand::{thread_rng, Rng};

use super::srs;
use super::srs::Direction;
use super::tetromino::{
    Point,
    Rotation,
    Tetromino,
    TetrominoType,
    TYPES
};

pub const WIDTH: usize = 10;
pub const HEIGHT: usize = 22;

const SPAWN: Point = Point { x: 3, y: 0 };

const LEFT: Point = Point { x: -1, y: 0 };
const RIGHT: Point = Point { x: 1, y: 0 };
const UP: Point = Point { x: 0, y: -1 };
const DOWN: Point = Point { x: 0, y: 1 };

const SOFT_DROP: usize = 1;
const HARD_DROP: usize = 2;

pub type Field = [[Option<TetrominoType>; WIDTH]; HEIGHT];

/// A struct representing a 10x22 Tetris board
pub struct Board {
    field: Field,
    curr: Tetromino,
    next: Vec<TetrominoType>,
    is_topped_out: bool,
    score: usize,
    level: usize,
    cleared: usize,
}

impl Board {

    /// Initializes a new Board struct
    pub fn new() -> Self {

        // Create a sequence of pieces and shuffle them
        let mut next = vec![
            TetrominoType::I,
            TetrominoType::J,
            TetrominoType::L,
            TetrominoType::O,
            TetrominoType::S,
            TetrominoType::T,
            TetrominoType::Z,
        ];

        let mut rng = thread_rng();
        rng.shuffle(&mut next);

        let mut board = Board {
            field: [[None; WIDTH]; HEIGHT],
            curr: Tetromino::new(SPAWN, next.pop().unwrap(), Rotation::Spawn),
            next: next,
            is_topped_out: false,
            score: 0,
            cleared: 0,
            level: 0,
        };

        board.add_current();
        board
    }

    /// Applies gravity and clears any lines
    pub fn tick(&mut self) {
        self.apply_gravity();
    }

    /// Applies gravity to the field
    fn apply_gravity(&mut self) {
        let offset = DOWN;

        if self.is_moveable(offset) {
            self.do_move(offset);
            self.score += SOFT_DROP;
        }

        else {
            self.clear_lines();
            self.spawn();
        }
    }

    /// Clears any lines from the field completed by the current Tetromino
    fn clear_lines(&mut self) {
        let mut cleared = vec![];

        for &mino in self.curr.minos().iter() {
            let row = (self.curr.origin().y + mino.y) as usize;

            if self.is_line(row) {
                for col in 0..WIDTH {
                    self.field[row][col] = None;
                }

                cleared.push(row);
            }
        }

        // Drop the rows if any line clears were made
        if cleared.first().is_some() {
            self.drop_rows(*cleared.first().unwrap(), cleared.len());

            // Update the player's status
            self.cleared += cleared.len();

            if self.cleared % 10 == 0 {
                self.level += 1;
            }

            self.score_clear(cleared.len());
        }
    }

    /// Determines if a specific row in the field is a complete line
    fn is_line(&self, row: usize) -> bool {
        for col in 0..WIDTH {
            if self.field[row][col].is_none() {
                return false;
            }
        }

        true
    }

    /// Drops all rows above the given start row by drop
    fn drop_rows(&mut self, start: usize, drop: usize) {

        // We must use rev() because Rust doesn't support backwards iteration
        for row in (0..start).rev() {
            for col in 0..WIDTH {
                if self.field[row][col].is_some() {
                    self.field[row + drop][col] = self.field[row][col];
                    self.field[row][col] = None;
                }
            }
        }
    }

    /// Increases the score based on the number of lines cleared
    fn score_clear(&mut self, lines: usize) {

        // The scoring formula used is identical to the NES version
        self.score += match lines {
            1 =>   40 * (self.level + 1),
            2 =>  100 * (self.level + 1),
            3 =>  300 * (self.level + 1),
            4 => 1200 * (self.level + 1),

            // 4+ lines cleared is impossible in the current system.
            _ => 0,
        }
    }

    /// Moves the current Tetromino to the left
    pub fn left(&mut self) {
        self.move_tetromino(LEFT);
    }

    /// Moves the current Tetromino to the right
    pub fn right(&mut self) {
        self.move_tetromino(RIGHT);
    }

    /// Moves the current Tetromino up
    pub fn up(&mut self) {
        self.move_tetromino(UP);
    }

    /// Moves the current Tetromino down
    pub fn down(&mut self) {
        self.move_tetromino(DOWN);
    }

    /// Continually drops the current Tetromino until it locks
    pub fn drop(&mut self) {
        while self.is_moveable(DOWN) {
            self.do_move(DOWN);
            self.score += HARD_DROP;
        }

        self.clear_lines();
        self.spawn();
    }

    /// Moves the current Tetromino by an (x, y) offset
    fn move_tetromino(&mut self, offset: Point) {
        if self.is_moveable(offset) {
            self.do_move(offset);
        }
    }

    /// Determines if an offset to the current Tetromino is possible or not
    fn is_moveable(&self, offset: Point) -> bool {
        for &mino in self.curr.minos().iter() {
            let pos = self.curr.origin() + mino + offset;

            // Determine if the field's boundaries are respected
            if pos.x < 0 || pos.y < 0 || (pos.x as usize) >= WIDTH || (pos.y as usize) >= HEIGHT {
                return false;
            }

             // Determine if the current Tetromino overlaps only itself if moved
            if self.field[pos.y as usize][pos.x as usize].is_some() && !self.overlaps_active(pos) {
                return false;
            }
        }

        true
    }

    /// Determines if an offset to the current Tetromino would cause it to
    /// only overlap itself or not
    fn overlaps_active(&self, new: Point) -> bool {
        for &mino in self.curr.minos().iter() {
            let pos = self.curr.origin() + mino;

            if new.x == pos.x && new.y == pos.y {
                return true;
            }
        }

        false
    }

    /// Performs the actual movement
    fn do_move(&mut self, offset: Point) {
        let mut minos = self.curr.minos().clone();

        // Moving down or right requires us to handle the blocks in
        // reverse order to prevent blocks from being erased
        match offset {
            RIGHT | DOWN => minos.reverse(),
            _ => { },
        }

        // Perform the move
        for &mino in minos.iter() {
            let org = self.curr.origin() + mino;
            let pos = org + offset;

            self.field[pos.y as usize][pos.x as usize] = self.field[org.y as usize][org.x as usize];
            self.field[org.y as usize][org.x as usize] = None;
        }

        let origin = self.curr.origin() + offset;
        self.curr.set_origin(origin);
    }

    /// Rotates the current Tetromino in a specified direction
    pub fn rotate(&mut self, dir: Direction) {
        match srs::rotate(&self.field, &self.curr, dir) {
            Some((field, rotated)) => {
                self.field = field;
                self.curr = rotated;
            },

            None => { },
        }
    }

    /// Peeks at the next Tetromino
    pub fn peek_next(&self) -> Tetromino {
        Tetromino::new(SPAWN, *self.next.last().unwrap(), Rotation::Spawn)
    }

    /// Spawns the next Tetromino in the sequence
    fn spawn(&mut self) {
        self.curr = Tetromino::new(SPAWN, self.next.pop().unwrap(), Rotation::Spawn);
        self.add_current();

        // All of the pieces have been picked, so reshuffle them
        if self.next.is_empty() {
            self.next = vec![
                TetrominoType::I,
                TetrominoType::J,
                TetrominoType::L,
                TetrominoType::O,
                TetrominoType::S,
                TetrominoType::T,
                TetrominoType::Z,
            ];

            let mut rng = thread_rng();
            rng.shuffle(&mut self.next);
        }
    }

    /// Adds the current Tetromino to the field
    fn add_current(&mut self) {

        // Add the new blocks to the board
        for &mino in self.curr.minos().iter() {
            let pos = self.curr.origin() + mino;

            // The field has been topped out and the game is over
            if self.field[pos.y as usize][pos.x as usize].is_some() {
                self.is_topped_out = true;
                break;
            }

            self.field[pos.y as usize][pos.x as usize] = Some(self.curr.tetromino_type());
        }
    }

    // GETTERS / SETTERS

    pub fn field(&self) -> Field {
        self.field
    }

    pub fn is_topped_out(&self) -> bool {
        self.is_topped_out
    }

    pub fn score(&self) -> usize {
        self.score
    }

    pub fn level(&self) -> usize {
        self.level
    }

    pub fn cleared(&self) -> usize {
        self.cleared
    }
}<|endoftext|>


<commit_before>use std::path::PathBuf;

use json5;

use crate::config::Config;

pub fn build(in_dir: PathBuf) -> Result<(), String> {
    let data_file = in_dir.join("_data/config.json5");
    let data = std::fs::read_to_string(&data_file)
        .map_err(|e| format!("could not read '{}'\n{}", &data_file.to_string_lossy(), e))?;
    let config: Config = json5::from_str(&data)
        .map_err(|e| format!("could not parse '{}':\n{}", &data_file.display(), e))?;

    dbg!("{}", config);

    let content_dir = in_dir.join("content");
    let content_glob = content_dir.to_string_lossy() + "/**/*.md";
    let all_contents: Vec<Result<String, String>> = glob::glob(&content_glob)
        .expect(&format!("bad glob: '{}'", &content_glob))
        .map(|result| {
            result
                .map_err(|e| format!("{}", e))
                .and_then(|file| std::fs::read_to_string(file).map_err(|e| format!("{}", e)))
        })
        .collect();
    println!("{}", all_contents.len());
    Ok(())
}
<commit_msg>Associate path with contents when loading data<commit_after>use std::{collections::HashMap, path::PathBuf};

use json5;

use crate::config::Config;

pub fn build(in_dir: PathBuf) -> Result<(), String> {
    let data_file = in_dir.join("_data/config.json5");
    let data = std::fs::read_to_string(&data_file)
        .map_err(|e| format!("could not read '{}'\n{}", &data_file.to_string_lossy(), e))?;
    let config: Config = json5::from_str(&data)
        .map_err(|e| format!("could not parse '{}':\n{}", &data_file.display(), e))?;

    let content_dir = in_dir.join("content");
    let content_glob = content_dir.to_string_lossy() + "/**/*.md";
    let all_contents: Vec<Result<(PathBuf, String), String>> = glob::glob(&content_glob)
        .expect(&format!("bad glob: '{}'", &content_glob))
        .map(|result| {
            result.map_err(|e| format!("{}", e)).and_then(|file| {
                std::fs::read_to_string(&file)
                    .map(|content| (file, content))
                    .map_err(|e| format!("{}", e))
            })
        })
        .collect();

    dbg!("{}", &all_contents[0]);
    Ok(())
}
<|endoftext|>


<commit_before>//! Error and result types.

use std::fmt;
use std::error::Error;
use std::io::Error as IoError;

use chrono::format::ParseError as ChronoParseError;
use serde_json::{Value, from_str};

use xmlutil::XmlParseError;

/// An error produced when AWS API calls are unsuccessful.
#[derive(Debug, PartialEq)]
pub struct AwsError {
    pub message: String
}

pub fn parse_json_protocol_error(body: &str) -> AwsError {
    match from_str::<Value>(body) {
        Ok(json) => {
            let error_type: &str = match json.find("__type") {
                Some(error_type) => error_type.as_string().unwrap_or("Unknown error"),
                None => "Unknown error",
            };

            let error_message: &str = match json.find("message") {
                Some(error_message) => error_message.as_string().unwrap_or(body),
                None => body,
            };

            AwsError::new(format!("{}: {}", error_type, error_message))
        }
        Err(err) => AwsError::new(
            format!("Failed to parse error as JSON: {}.\nRaw error:\n{}", err, body)
        ),
    }
}

impl AwsError {
    /// Create a new error with the given message.
    pub fn new<S>(message: S) -> AwsError where S: Into<String> {
        AwsError {
            message: message.into(),
        }
    }
}

impl Error for AwsError {
    fn description(&self) -> &str {
        &self.message
    }
}

impl From<ChronoParseError> for AwsError {
    fn from(err: ChronoParseError) -> AwsError {
        AwsError::new(format!("{}", err))
    }
}

impl From<IoError> for AwsError {
    fn from(err: IoError) -> AwsError {
        AwsError::new(format!("{}", err))
    }
}

impl From<XmlParseError> for AwsError {
    fn from(err: XmlParseError) -> AwsError {
        AwsError::new(format!("{:?}", err))
    }
}

impl fmt::Display for AwsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.message)
    }
}

/// The result type produced by AWS API calls.
pub type AwsResult<T> = Result<T, AwsError>;
<commit_msg>no, really.  put error.rs back how it was.<commit_after>//! Error and result types.

use std::fmt;
use std::error::Error;
use std::io::Error as IoError;

use chrono::format::ParseError as ChronoParseError;
use serde_json::{Value, from_str};

use xmlutil::XmlParseError;

/// An error produced when AWS API calls are unsuccessful.
#[derive(Debug, PartialEq)]
pub struct AwsError {
    message: String
}

pub fn parse_json_protocol_error(body: &str) -> AwsError {
    match from_str::<Value>(body) {
        Ok(json) => {
            let error_type: &str = match json.find("__type") {
                Some(error_type) => error_type.as_string().unwrap_or("Unknown error"),
                None => "Unknown error",
            };

            let error_message: &str = match json.find("message") {
                Some(error_message) => error_message.as_string().unwrap_or(body),
                None => body,
            };

            AwsError::new(format!("{}: {}", error_type, error_message))
        }
        Err(err) => AwsError::new(
            format!("Failed to parse error as JSON: {}.\nRaw error:\n{}", err, body)
        ),
    }
}

impl AwsError {
    /// Create a new error with the given message.
    pub fn new<S>(message: S) -> AwsError where S: Into<String> {
        AwsError {
            message: message.into(),
        }
    }
}

impl Error for AwsError {
    fn description(&self) -> &str {
        &self.message
    }
}

impl From<ChronoParseError> for AwsError {
    fn from(err: ChronoParseError) -> AwsError {
        AwsError::new(format!("{}", err))
    }
}

impl From<IoError> for AwsError {
    fn from(err: IoError) -> AwsError {
        AwsError::new(format!("{}", err))
    }
}

impl From<XmlParseError> for AwsError {
    fn from(err: XmlParseError) -> AwsError {
        AwsError::new(format!("{:?}", err))
    }
}

impl fmt::Display for AwsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.message)
    }
}

/// The result type produced by AWS API calls.
pub type AwsResult<T> = Result<T, AwsError>;
<|endoftext|>


<commit_before>//! Error struct and methods

use std::result;
use std::error;
use std::io;
use std::fmt;

use num::traits::FromPrimitive;
use byteorder;

/// A type for results generated by this crate's functions where the `Err` type
/// is hard-wired to `enums::Error`.
///
/// This typedef is generally used to avoid writing out `enums::Error` directly and
/// is otherwise a direct mapping to `std::result::Result`.
pub type Result<T> = result::Result<T, Error>;

/// The various errors this library can produce.
#[derive(Debug)]
pub enum Error {
    /// Input/Output error while communicating with Kafka
    Io(io::Error),
    /// An error as reported by a remote Kafka server
    Kafka(KafkaCode),

    /// Failure to decode a snappy compressed response from Kafka
    InvalidInputSnappy,
    /// Failure to decode a response due to an insufficient number of bytes available
    UnexpectedEOF,
    /// Failure to decode or encode a response or request respectively
    CodecError,
    /// Unable to reach any host
    NoHostReachable,
}

/// Various errors reported by a remote Kafka server.
/// See also [Kafka Errors](https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol#AGuideToTheKafkaProtocol-ErrorCodes)
#[derive(Debug)]
pub enum KafkaCode {
    /// An unexpected server error
    Unknown,
    /// The requested offset is outside the range of offsets
    /// maintained by the server for the given topic/partition
    OffsetOutOfRange,
    /// This indicates that a message contents does not match its CRC
    InvalidMessage,
    /// This request is for a topic or partition that does not exist
    /// on this broker.
    UnknownTopicOrPartition,
    /// The message has a negative size
    InvalidMessageSize,
    /// This error is thrown if we are in the middle of a leadership
    /// election and there is currently no leader for this partition
    /// and hence it is unavailable for writes.
    LeaderNotAvailable,
    /// This error is thrown if the client attempts to send messages
    /// to a replica that is not the leader for some partition. It
    /// indicates that the clients metadata is out of date.
    NotLeaderForPartition,
    /// This error is thrown if the request exceeds the user-specified
    /// time limit in the request.
    RequestTimedOut,
    /// This is not a client facing error and is used mostly by tools
    /// when a broker is not alive.
    BrokerNotAvailable,
    /// If replica is expected on a broker, but is not (this can be
    /// safely ignored).
    ReplicaNotAvailable,
    /// The server has a configurable maximum message size to avoid
    /// unbounded memory allocation. This error is thrown if the
    /// client attempt to produce a message larger than this maximum.
    MessageSizeTooLarge,
    /// Internal error code for broker-to-broker communication.
    StaleControllerEpochCode,
    /// If you specify a string larger than configured maximum for
    /// offset metadata
    OffsetMetadataTooLargeCode,
    /// The broker returns this error code for an offset fetch request
    /// if it is still loading offsets (after a leader change for that
    /// offsets topic partition), or in response to group membership
    /// requests (such as heartbeats) when group metadata is being
    /// loaded by the coordinator.
    OffsetsLoadInProgressCode,
    /// The broker returns this error code for group coordinator
    /// requests, offset commits, and most group management requests
    /// if the offsets topic has not yet been created, or if the group
    /// coordinator is not active.
    ConsumerCoordinatorNotAvailableCode,
    /// The broker returns this error code if it receives an offset
    /// fetch or commit request for a group that it is not a
    /// coordinator for.
    NotCoordinatorForConsumerCode,
    /// For a request which attempts to access an invalid topic
    /// (e.g. one which has an illegal name), or if an attempt is made
    /// to write to an internal topic (such as the consumer offsets
    /// topic).
    InvalidTopicCode,
    /// If a message batch in a produce request exceeds the maximum
    /// configured segment size.
    RecordListTooLargeCode,
    /// Returned from a produce request when the number of in-sync
    /// replicas is lower than the configured minimum and requiredAcks is
    /// -1.
    NotEnoughReplicasCode,
    /// Returned from a produce request when the message was written
    /// to the log, but with fewer in-sync replicas than required.
    NotEnoughReplicasAfterAppendCode,
    /// Returned from a produce request if the requested requiredAcks is
    /// invalid (anything other than -1, 1, or 0).
    InvalidRequiredAcksCode,
    /// Returned from group membership requests (such as heartbeats) when
    /// the generation id provided in the request is not the current
    /// generation.
    IllegalGenerationCode,
    /// Returned in join group when the member provides a protocol type or
    /// set of protocols which is not compatible with the current group.
    InconsistentGroupProtocolCode,
    /// Returned in join group when the groupId is empty or null.
    InvalidGroupIdCode,
    /// Returned from group requests (offset commits/fetches, heartbeats,
    /// etc) when the memberId is not in the current generation.
    UnknownMemberIdCode,
    /// Return in join group when the requested session timeout is outside
    /// of the allowed range on the broker
    InvalidSessionTimeoutCode,
    /// Returned in heartbeat requests when the coordinator has begun
    /// rebalancing the group. This indicates to the client that it
    /// should rejoin the group.
    RebalanceInProgressCode,
    /// This error indicates that an offset commit was rejected because of
    /// oversize metadata.
    InvalidCommitOffsetSizeCode,
    /// Returned by the broker when the client is not authorized to access
    /// the requested topic.
    TopicAuthorizationFailedCode,
    /// Returned by the broker when the client is not authorized to access
    /// a particular groupId.
    GroupAuthorizationFailedCode,
    /// Returned by the broker when the client is not authorized to use an
    /// inter-broker or administrative API.
    ClusterAuthorizationFailedCode,
}

impl FromPrimitive for Error {
    fn from_i16(n: i16) -> Option<Error> {
        match n {
            1 => Some(Error::Kafka(KafkaCode::OffsetOutOfRange)),
            2 => Some(Error::Kafka(KafkaCode::InvalidMessage)),
            3 => Some(Error::Kafka(KafkaCode::UnknownTopicOrPartition)),
            4 => Some(Error::Kafka(KafkaCode::InvalidMessageSize)),
            5 => Some(Error::Kafka(KafkaCode::LeaderNotAvailable)),
            6 => Some(Error::Kafka(KafkaCode::NotLeaderForPartition)),
            7 => Some(Error::Kafka(KafkaCode::RequestTimedOut)),
            8 => Some(Error::Kafka(KafkaCode::BrokerNotAvailable)),
            9 => Some(Error::Kafka(KafkaCode::ReplicaNotAvailable)),
            10 => Some(Error::Kafka(KafkaCode::MessageSizeTooLarge)),
            11 => Some(Error::Kafka(KafkaCode::StaleControllerEpochCode)),
            12 => Some(Error::Kafka(KafkaCode::OffsetMetadataTooLargeCode)),
            14 => Some(Error::Kafka(KafkaCode::OffsetsLoadInProgressCode)),
            15 => Some(Error::Kafka(KafkaCode::ConsumerCoordinatorNotAvailableCode)),
            16 => Some(Error::Kafka(KafkaCode::NotCoordinatorForConsumerCode)),
            17 => Some(Error::Kafka(KafkaCode::InvalidTopicCode)),
            18 => Some(Error::Kafka(KafkaCode::RecordListTooLargeCode)),
            19 => Some(Error::Kafka(KafkaCode::NotEnoughReplicasCode)),
            20 => Some(Error::Kafka(KafkaCode::NotEnoughReplicasAfterAppendCode)),
            21 => Some(Error::Kafka(KafkaCode::InvalidRequiredAcksCode)),
            22 => Some(Error::Kafka(KafkaCode::IllegalGenerationCode)),
            23 => Some(Error::Kafka(KafkaCode::InconsistentGroupProtocolCode)),
            24 => Some(Error::Kafka(KafkaCode::InvalidGroupIdCode)),
            25 => Some(Error::Kafka(KafkaCode::UnknownMemberIdCode)),
            26 => Some(Error::Kafka(KafkaCode::InvalidSessionTimeoutCode)),
            27 => Some(Error::Kafka(KafkaCode::RebalanceInProgressCode)),
            28 => Some(Error::Kafka(KafkaCode::InvalidCommitOffsetSizeCode)),
            29 => Some(Error::Kafka(KafkaCode::TopicAuthorizationFailedCode)),
            30 => Some(Error::Kafka(KafkaCode::GroupAuthorizationFailedCode)),
            31 => Some(Error::Kafka(KafkaCode::ClusterAuthorizationFailedCode)),
            -1 => Some(Error::Kafka(KafkaCode::Unknown)),
            _ => None
        }
    }
    fn from_i64(_: i64) -> Option<Error> {
        Some(Error::Kafka(KafkaCode::Unknown))
    }
    fn from_u64(_: u64) -> Option<Error> {
        Some(Error::Kafka(KafkaCode::Unknown))
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error { Error::Io(err) }
}

impl From<byteorder::Error> for Error {
    fn from(err: byteorder::Error) -> Error {
        match err {
            byteorder::Error::UnexpectedEOF => Error::UnexpectedEOF,
            byteorder::Error::Io(err) => Error::Io(err)
        }
    }
}

impl Clone for Error {
    fn clone(&self) -> Error {
        match *self {
            Error::Io(ref err) => Error::Io(io::Error::new(err.kind(), "Io Error")),
            ref x => x.clone()
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Io(ref err) => error::Error::description(err),
            Error::InvalidInputSnappy => "Snappy decode error. Invalid Input",
            Error::UnexpectedEOF => "Unexpected EOF",
            Error::NoHostReachable => "No Host Reachable",
            Error::CodecError => "Encoding Decoding Error",
            _ => "Kafka Error" // TODO Add others
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Io(ref err) => err.cause(),
            _ => None
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Io(ref err) => err.fmt(f),
            Error::Kafka(ref c) => write!(f, "Kafka Error ({:?})", c),
            Error::InvalidInputSnappy => write!(f, "{}", "Snappy decoding error"),
            Error::UnexpectedEOF => write!(f, "Unexpected EOF"),
            Error::NoHostReachable => write!(f, "No Host Reachable"),
            Error::CodecError => write!(f, "Encoding Decoding Error"),
        }
    }
}
<commit_msg>Add support for "string decoding error"<commit_after>//! Error struct and methods

use std::result;
use std::error;
use std::io;
use std::fmt;

use num::traits::FromPrimitive;
use byteorder;

/// A type for results generated by this crate's functions where the `Err` type
/// is hard-wired to `enums::Error`.
///
/// This typedef is generally used to avoid writing out `enums::Error` directly and
/// is otherwise a direct mapping to `std::result::Result`.
pub type Result<T> = result::Result<T, Error>;

/// The various errors this library can produce.
#[derive(Debug)]
pub enum Error {
    /// Input/Output error while communicating with Kafka
    Io(io::Error),
    /// An error as reported by a remote Kafka server
    Kafka(KafkaCode),

    /// Failure to decode a snappy compressed response from Kafka
    InvalidInputSnappy,
    /// Failure to decode a response due to an insufficient number of bytes available
    UnexpectedEOF,
    /// Failure to decode or encode a response or request respectively
    CodecError,
    /// Failure to decode a string into a valid utf8 byte sequence
    StringDecodeError,
    /// Unable to reach any host
    NoHostReachable,
}

/// Various errors reported by a remote Kafka server.
/// See also [Kafka Errors](https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol#AGuideToTheKafkaProtocol-ErrorCodes)
#[derive(Debug)]
pub enum KafkaCode {
    /// An unexpected server error
    Unknown,
    /// The requested offset is outside the range of offsets
    /// maintained by the server for the given topic/partition
    OffsetOutOfRange,
    /// This indicates that a message contents does not match its CRC
    InvalidMessage,
    /// This request is for a topic or partition that does not exist
    /// on this broker.
    UnknownTopicOrPartition,
    /// The message has a negative size
    InvalidMessageSize,
    /// This error is thrown if we are in the middle of a leadership
    /// election and there is currently no leader for this partition
    /// and hence it is unavailable for writes.
    LeaderNotAvailable,
    /// This error is thrown if the client attempts to send messages
    /// to a replica that is not the leader for some partition. It
    /// indicates that the clients metadata is out of date.
    NotLeaderForPartition,
    /// This error is thrown if the request exceeds the user-specified
    /// time limit in the request.
    RequestTimedOut,
    /// This is not a client facing error and is used mostly by tools
    /// when a broker is not alive.
    BrokerNotAvailable,
    /// If replica is expected on a broker, but is not (this can be
    /// safely ignored).
    ReplicaNotAvailable,
    /// The server has a configurable maximum message size to avoid
    /// unbounded memory allocation. This error is thrown if the
    /// client attempt to produce a message larger than this maximum.
    MessageSizeTooLarge,
    /// Internal error code for broker-to-broker communication.
    StaleControllerEpochCode,
    /// If you specify a string larger than configured maximum for
    /// offset metadata
    OffsetMetadataTooLargeCode,
    /// The broker returns this error code for an offset fetch request
    /// if it is still loading offsets (after a leader change for that
    /// offsets topic partition), or in response to group membership
    /// requests (such as heartbeats) when group metadata is being
    /// loaded by the coordinator.
    OffsetsLoadInProgressCode,
    /// The broker returns this error code for group coordinator
    /// requests, offset commits, and most group management requests
    /// if the offsets topic has not yet been created, or if the group
    /// coordinator is not active.
    ConsumerCoordinatorNotAvailableCode,
    /// The broker returns this error code if it receives an offset
    /// fetch or commit request for a group that it is not a
    /// coordinator for.
    NotCoordinatorForConsumerCode,
    /// For a request which attempts to access an invalid topic
    /// (e.g. one which has an illegal name), or if an attempt is made
    /// to write to an internal topic (such as the consumer offsets
    /// topic).
    InvalidTopicCode,
    /// If a message batch in a produce request exceeds the maximum
    /// configured segment size.
    RecordListTooLargeCode,
    /// Returned from a produce request when the number of in-sync
    /// replicas is lower than the configured minimum and requiredAcks is
    /// -1.
    NotEnoughReplicasCode,
    /// Returned from a produce request when the message was written
    /// to the log, but with fewer in-sync replicas than required.
    NotEnoughReplicasAfterAppendCode,
    /// Returned from a produce request if the requested requiredAcks is
    /// invalid (anything other than -1, 1, or 0).
    InvalidRequiredAcksCode,
    /// Returned from group membership requests (such as heartbeats) when
    /// the generation id provided in the request is not the current
    /// generation.
    IllegalGenerationCode,
    /// Returned in join group when the member provides a protocol type or
    /// set of protocols which is not compatible with the current group.
    InconsistentGroupProtocolCode,
    /// Returned in join group when the groupId is empty or null.
    InvalidGroupIdCode,
    /// Returned from group requests (offset commits/fetches, heartbeats,
    /// etc) when the memberId is not in the current generation.
    UnknownMemberIdCode,
    /// Return in join group when the requested session timeout is outside
    /// of the allowed range on the broker
    InvalidSessionTimeoutCode,
    /// Returned in heartbeat requests when the coordinator has begun
    /// rebalancing the group. This indicates to the client that it
    /// should rejoin the group.
    RebalanceInProgressCode,
    /// This error indicates that an offset commit was rejected because of
    /// oversize metadata.
    InvalidCommitOffsetSizeCode,
    /// Returned by the broker when the client is not authorized to access
    /// the requested topic.
    TopicAuthorizationFailedCode,
    /// Returned by the broker when the client is not authorized to access
    /// a particular groupId.
    GroupAuthorizationFailedCode,
    /// Returned by the broker when the client is not authorized to use an
    /// inter-broker or administrative API.
    ClusterAuthorizationFailedCode,
}

// XXX is it really necessary we do implement `FromPrimitive`?
impl FromPrimitive for Error {
    fn from_i16(n: i16) -> Option<Error> {
        match n {
            1 => Some(Error::Kafka(KafkaCode::OffsetOutOfRange)),
            2 => Some(Error::Kafka(KafkaCode::InvalidMessage)),
            3 => Some(Error::Kafka(KafkaCode::UnknownTopicOrPartition)),
            4 => Some(Error::Kafka(KafkaCode::InvalidMessageSize)),
            5 => Some(Error::Kafka(KafkaCode::LeaderNotAvailable)),
            6 => Some(Error::Kafka(KafkaCode::NotLeaderForPartition)),
            7 => Some(Error::Kafka(KafkaCode::RequestTimedOut)),
            8 => Some(Error::Kafka(KafkaCode::BrokerNotAvailable)),
            9 => Some(Error::Kafka(KafkaCode::ReplicaNotAvailable)),
            10 => Some(Error::Kafka(KafkaCode::MessageSizeTooLarge)),
            11 => Some(Error::Kafka(KafkaCode::StaleControllerEpochCode)),
            12 => Some(Error::Kafka(KafkaCode::OffsetMetadataTooLargeCode)),
            14 => Some(Error::Kafka(KafkaCode::OffsetsLoadInProgressCode)),
            15 => Some(Error::Kafka(KafkaCode::ConsumerCoordinatorNotAvailableCode)),
            16 => Some(Error::Kafka(KafkaCode::NotCoordinatorForConsumerCode)),
            17 => Some(Error::Kafka(KafkaCode::InvalidTopicCode)),
            18 => Some(Error::Kafka(KafkaCode::RecordListTooLargeCode)),
            19 => Some(Error::Kafka(KafkaCode::NotEnoughReplicasCode)),
            20 => Some(Error::Kafka(KafkaCode::NotEnoughReplicasAfterAppendCode)),
            21 => Some(Error::Kafka(KafkaCode::InvalidRequiredAcksCode)),
            22 => Some(Error::Kafka(KafkaCode::IllegalGenerationCode)),
            23 => Some(Error::Kafka(KafkaCode::InconsistentGroupProtocolCode)),
            24 => Some(Error::Kafka(KafkaCode::InvalidGroupIdCode)),
            25 => Some(Error::Kafka(KafkaCode::UnknownMemberIdCode)),
            26 => Some(Error::Kafka(KafkaCode::InvalidSessionTimeoutCode)),
            27 => Some(Error::Kafka(KafkaCode::RebalanceInProgressCode)),
            28 => Some(Error::Kafka(KafkaCode::InvalidCommitOffsetSizeCode)),
            29 => Some(Error::Kafka(KafkaCode::TopicAuthorizationFailedCode)),
            30 => Some(Error::Kafka(KafkaCode::GroupAuthorizationFailedCode)),
            31 => Some(Error::Kafka(KafkaCode::ClusterAuthorizationFailedCode)),
            -1 => Some(Error::Kafka(KafkaCode::Unknown)),
            _ => None
        }
    }
    fn from_i64(_: i64) -> Option<Error> {
        Some(Error::Kafka(KafkaCode::Unknown))
    }
    fn from_u64(_: u64) -> Option<Error> {
        Some(Error::Kafka(KafkaCode::Unknown))
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error { Error::Io(err) }
}

impl From<byteorder::Error> for Error {
    fn from(err: byteorder::Error) -> Error {
        match err {
            byteorder::Error::UnexpectedEOF => Error::UnexpectedEOF,
            byteorder::Error::Io(err) => Error::Io(err)
        }
    }
}

impl Clone for Error {
    fn clone(&self) -> Error {
        match *self {
            Error::Io(ref err) => Error::Io(io::Error::new(err.kind(), "Io Error")),
            ref x => x.clone()
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Io(ref err) => error::Error::description(err),
            Error::Kafka(_) => "Kafka Error",
            Error::InvalidInputSnappy => "Snappy decode error",
            Error::UnexpectedEOF => "Unexpected EOF",
            Error::CodecError => "Encoding/Decoding error",
            Error::StringDecodeError => "String decoding error",
            Error::NoHostReachable => "No host reachable",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Io(ref err) => err.cause(),
            _ => None
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Io(ref err) => err.fmt(f),
            Error::Kafka(ref c) => write!(f, "Kafka Error ({:?})", c),
            Error::InvalidInputSnappy => write!(f, "{}", "Snappy decoding error"),
            Error::UnexpectedEOF => write!(f, "Unexpected EOF"),
            Error::CodecError => write!(f, "Encoding/Decoding Error"),
            // XXX might want to provide some context about parsed string and the error position with in
            Error::StringDecodeError => write!(f, "String decoding error"),
            Error::NoHostReachable => write!(f, "No Host Reachable"),
        }
    }
}
<|endoftext|>


<commit_before>
#[derive(Debug)]
pub enum Error {
    KeyNotFound,
    IndentationError,
    NameError,
}

<commit_msg>impl Error for Error<commit_after>
#[derive(Debug)]
/// nccl Error type.
pub enum Error {
    KeyNotFound,
    IndentationError,
    NameError,
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "An error has ocurred: {:?}", self)
    }
}

#[allow(unreachable_patterns)]
impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::KeyNotFound => "Key not found.",
            Error::IndentationError => "Incorrect indentation: Not 4 spaces.",
            Error::NameError => "Schema not found.",
            _ => "Unknown error."
        }
    }
}

<|endoftext|>


<commit_before>use std::io::Error as IoError;
use hyper::error::Error as HyperError;
use serde_json::error as serde;

pub type Result<T> = ::std::result::Result<T, Error>;

#[derive(Debug)]
pub enum Error
{
    Hyper(HyperError),
    SerdeError(serde::Error),
    Io(IoError),
    ProxerError(u16, String),
    Other(String),
}

impl From<HyperError> for Error
{
    fn from(err: HyperError)
    -> Error
    {
        error!("HyperError with: {:?}", err);
        Error::Hyper(err)
    }
}

impl From<serde::Error> for Error
{
    fn from(err: serde::Error)
    -> Error
    {
        error!("SerdeError with: {:?}", err);
        Error::SerdeError(err)
    }
}

impl From<IoError> for Error
{
    fn from(err: IoError)
    -> Error
    {
        error!("IoError with: {:?}", err);
        Error::Io(err)
    }
}

impl From<(u16, String)> for Error
{
    fn from((code, message): (u16, String))
    -> Error
    {
        error!("ProxerError with: message:{:?}, code:{}({:?})",
            message, code, DecodeErrorCode::decode(code));
        Error::ProxerError(code, message)
    }
}

impl From<String> for Error
{
    fn from(err: String)
    -> Error
    {
        error!("OtherError with {:?}", err);
        Error::Other(err)
    }
}

pub struct DecodeErrorCode;

impl DecodeErrorCode
{
    pub fn decode(p_code: u16)
    -> String
    {
        match p_code
        {
            1000 =>	format!("API-Version existiert nicht."),
            1001 =>	format!("API-Version wurde entfernt."),
            1002 =>	format!("API-Klasse existiert nicht."),
            1003 =>	format!("API-Funktion existiert nicht."),
            1004 =>	format!("Der API-Schlüssel besitzt nicht ausreichend Rechte um diese Aktion durchzuführen."),
            1005 =>	format!("Es wurde ein ungültiges Login-Token verwendet."),
            1006 =>	format!("Die aufgerufene Funktion wurde gesperrt."),

            2000 =>	format!("IP von Firewall geblockt."),
            2001 =>	format!("News: Fehler bei der Abfrage der News."),

            3000 =>	format!("Login: Fehlende Login-Daten."),
            3001 =>	format!("Login: Ungültige Login-Daten."),
            3002 =>	format!("Notifications: User nicht eingeloggt."),
            3003 =>	format!("Userinfo: Userid existiert nicht."),
            3004 =>	format!("Ucp: User nicht eingeloggt."),
            3005 =>	format!("Ucp: Kategorie existiert nicht."),
            3006 =>	format!("Ucp: Ungültige ID."),
            3007 =>	format!("Info: Ungültige ID."),
            3008 =>	format!("Info: setUserInfo: Ungültiger Typ."),
            3009 =>	format!("Info: setUserInfo: User nicht eingeloggt."),
            3010 =>	format!("Info: setUserInfo: Werk bereits in Liste enthalten."),
            3011 =>	format!("Info: setUserInfo: Anzahl zulässiger Favoriten überschritten."),
            3012 =>	format!("Login: Der User ist bereits eingeloggt."),
            3013 =>	format!("Login: Ein anderer User ist bereits eingeloggt."),
            3014 =>	format!("User: Der Zugriff auf die gesuchte Information wurde verweigert (möglicherweise sollte ein User eingeloggt werden)."),
            3015 =>	format!("List: Kategorie existiert nicht."),
            3016 =>	format!("List: Medium existiert nicht."),
            3017 =>	format!("Media: Stil existiert nicht."),
            3018 =>	format!("Media: Eintrag existiert nicht."),
            3019 =>	format!("Manga: Kapitel existiert nicht (nicht hochgeladen)."),
            3020 =>	format!("Anime: Episode existiert nicht (keine Streams)."),
            3021 =>	format!("Anime: Stream existiert nicht."),
            3022 =>	format!("Ucp: Episode existiert nicht."),
            3023 =>	format!("Messages: Der User ist nicht eingeloggt."),
            3024 =>	format!("Messages: Ungültige Konferenz (fehlende Berechtigung oder fehlerhafte Konferenz-ID)."),
            3025 =>	format!("Messages: Ungültige/Fehlende Eingabe bei Meldegrund."),
            3026 =>	format!("Messages: Ungültige/Fehlende Nachricht."),
            3027 =>	format!("Messages: Ungültiger Benutzer."),
            3028 =>	format!("Messages: Die maximale Anzahl an Usern wurde erreicht."),
            3029 =>	format!("Messages: Ungültiges/Fehlendes Thema."),
            3030 =>	format!("Messages: Es muss mindestens ein Benutzer in einer Konferenz hinzugefügt werden."),
            3031 =>	format!("Chat: Ungültiger Raum."),
            3032 =>	format!("Chat: Keine Berechtigungen."),
            3033 =>	format!("Chat: Ungültige Nachricht."),
            3034 =>	format!("Chat: Nicht eingeloggt."),
            3035 =>	format!("List: Ungültige Sprache."),
            3036 =>	format!("List: Ungültiger Typ."),
            3037 =>	format!("List: Ungültige ID."),

            _ => format!("Unknown Code")
        }
    }
}
<commit_msg>remove forgotten p_<commit_after>use std::io::Error as IoError;
use hyper::error::Error as HyperError;
use serde_json::error as serde;

pub type Result<T> = ::std::result::Result<T, Error>;

#[derive(Debug)]
pub enum Error
{
    Hyper(HyperError),
    SerdeError(serde::Error),
    Io(IoError),
    ProxerError(u16, String),
    Other(String),
}

impl From<HyperError> for Error
{
    fn from(err: HyperError)
    -> Error
    {
        error!("HyperError with: {:?}", err);
        Error::Hyper(err)
    }
}

impl From<serde::Error> for Error
{
    fn from(err: serde::Error)
    -> Error
    {
        error!("SerdeError with: {:?}", err);
        Error::SerdeError(err)
    }
}

impl From<IoError> for Error
{
    fn from(err: IoError)
    -> Error
    {
        error!("IoError with: {:?}", err);
        Error::Io(err)
    }
}

impl From<(u16, String)> for Error
{
    fn from((code, message): (u16, String))
    -> Error
    {
        error!("ProxerError with: message:{:?}, code:{}({:?})",
            message, code, DecodeErrorCode::decode(code));
        Error::ProxerError(code, message)
    }
}

impl From<String> for Error
{
    fn from(err: String)
    -> Error
    {
        error!("OtherError with {:?}", err);
        Error::Other(err)
    }
}

pub struct DecodeErrorCode;

impl DecodeErrorCode
{
    pub fn decode(code: u16)
    -> String
    {
        match code
        {
            1000 =>	format!("API-Version existiert nicht."),
            1001 =>	format!("API-Version wurde entfernt."),
            1002 =>	format!("API-Klasse existiert nicht."),
            1003 =>	format!("API-Funktion existiert nicht."),
            1004 =>	format!("Der API-Schlüssel besitzt nicht ausreichend Rechte um diese Aktion durchzuführen."),
            1005 =>	format!("Es wurde ein ungültiges Login-Token verwendet."),
            1006 =>	format!("Die aufgerufene Funktion wurde gesperrt."),

            2000 =>	format!("IP von Firewall geblockt."),
            2001 =>	format!("News: Fehler bei der Abfrage der News."),

            3000 =>	format!("Login: Fehlende Login-Daten."),
            3001 =>	format!("Login: Ungültige Login-Daten."),
            3002 =>	format!("Notifications: User nicht eingeloggt."),
            3003 =>	format!("Userinfo: Userid existiert nicht."),
            3004 =>	format!("Ucp: User nicht eingeloggt."),
            3005 =>	format!("Ucp: Kategorie existiert nicht."),
            3006 =>	format!("Ucp: Ungültige ID."),
            3007 =>	format!("Info: Ungültige ID."),
            3008 =>	format!("Info: setUserInfo: Ungültiger Typ."),
            3009 =>	format!("Info: setUserInfo: User nicht eingeloggt."),
            3010 =>	format!("Info: setUserInfo: Werk bereits in Liste enthalten."),
            3011 =>	format!("Info: setUserInfo: Anzahl zulässiger Favoriten überschritten."),
            3012 =>	format!("Login: Der User ist bereits eingeloggt."),
            3013 =>	format!("Login: Ein anderer User ist bereits eingeloggt."),
            3014 =>	format!("User: Der Zugriff auf die gesuchte Information wurde verweigert (möglicherweise sollte ein User eingeloggt werden)."),
            3015 =>	format!("List: Kategorie existiert nicht."),
            3016 =>	format!("List: Medium existiert nicht."),
            3017 =>	format!("Media: Stil existiert nicht."),
            3018 =>	format!("Media: Eintrag existiert nicht."),
            3019 =>	format!("Manga: Kapitel existiert nicht (nicht hochgeladen)."),
            3020 =>	format!("Anime: Episode existiert nicht (keine Streams)."),
            3021 =>	format!("Anime: Stream existiert nicht."),
            3022 =>	format!("Ucp: Episode existiert nicht."),
            3023 =>	format!("Messages: Der User ist nicht eingeloggt."),
            3024 =>	format!("Messages: Ungültige Konferenz (fehlende Berechtigung oder fehlerhafte Konferenz-ID)."),
            3025 =>	format!("Messages: Ungültige/Fehlende Eingabe bei Meldegrund."),
            3026 =>	format!("Messages: Ungültige/Fehlende Nachricht."),
            3027 =>	format!("Messages: Ungültiger Benutzer."),
            3028 =>	format!("Messages: Die maximale Anzahl an Usern wurde erreicht."),
            3029 =>	format!("Messages: Ungültiges/Fehlendes Thema."),
            3030 =>	format!("Messages: Es muss mindestens ein Benutzer in einer Konferenz hinzugefügt werden."),
            3031 =>	format!("Chat: Ungültiger Raum."),
            3032 =>	format!("Chat: Keine Berechtigungen."),
            3033 =>	format!("Chat: Ungültige Nachricht."),
            3034 =>	format!("Chat: Nicht eingeloggt."),
            3035 =>	format!("List: Ungültige Sprache."),
            3036 =>	format!("List: Ungültiger Typ."),
            3037 =>	format!("List: Ungültige ID."),

            _ => format!("Unknown Code")
        }
    }
}
<|endoftext|>


<commit_before>extern crate num;
extern crate nalgebra as na;

pub const TOTAL_FLUIDS: usize = 8;
pub const NORMAL_DIFFUSION: [f64; TOTAL_FLUIDS] = [0.0001, 4.0, 0.2, 2.0, 0.5, 0.5, 0.5, 0.5];
pub const KILL_FLUID_NORMAL: f64 = 0.05;
pub const KILL_FLUID_DECAY: f64 = 0.01;
pub const KILL_FLUID_UPPER_THRESHOLD: f64 = 0.052;
pub const KILL_FLUID_LOWER_THRESHOLD: f64 = 0.048;
pub const SIGNAL_FLUID_NORMAL: f64 = 0.5;
pub const SIGNAL_FLUID_DECAY: f64 = 0.001;
pub const B_FOOD_RATE: f64 = 0.05;

const TIMESTEP: f64 = 0.2;

#[derive(Default, Debug)]
pub struct Solution {
    pub fluids: [f64; TOTAL_FLUIDS],
    pub coefficients: [f64; TOTAL_FLUIDS],
    pub diffuse: [f64; TOTAL_FLUIDS],
}

impl Solution {
    pub fn new(fluids: [f64; TOTAL_FLUIDS], coefficients: [f64; TOTAL_FLUIDS]) -> Self {
        Solution {
            fluids: fluids,
            coefficients: coefficients,
            diffuse: [0.0; TOTAL_FLUIDS],
        }
    }

    pub fn react_deltas(&self) -> [f64; TOTAL_FLUIDS] {
        let a = self.fluids[1];
        let b = self.fluids[3];
        let kill = self.fluids[2];
        let f = 0.029;
        let k = 0.057;
        [B_FOOD_RATE * b,
         -a * b * b + f * (1.0 - a),
         KILL_FLUID_DECAY * (KILL_FLUID_NORMAL - kill),
         a * b * b - (k + f) * b,
         SIGNAL_FLUID_DECAY * (SIGNAL_FLUID_NORMAL - self.fluids[4]),
         SIGNAL_FLUID_DECAY * (SIGNAL_FLUID_NORMAL - self.fluids[5]),
         SIGNAL_FLUID_DECAY * (SIGNAL_FLUID_NORMAL - self.fluids[6]),
         SIGNAL_FLUID_DECAY * (SIGNAL_FLUID_NORMAL - self.fluids[7])]
    }

    pub fn diffuse_from(&mut self, other: &Solution) {
        for i in 0..TOTAL_FLUIDS {
            self.diffuse[i] += other.fluids[i] * other.coefficients[i] / 6.0;
        }
    }

    pub fn end_cycle(&mut self) {
        let reacts = self.react_deltas();
        for i in 0..TOTAL_FLUIDS {
            self.fluids[i] += TIMESTEP *
                              (reacts[i] + self.diffuse[i] - self.coefficients[i] * self.fluids[i]);
            self.diffuse[i] = 0.0;
        }
    }
}
<commit_msg>raise food diffusion just a little<commit_after>extern crate num;
extern crate nalgebra as na;

pub const TOTAL_FLUIDS: usize = 8;
pub const NORMAL_DIFFUSION: [f64; TOTAL_FLUIDS] = [0.0004, 4.0, 0.2, 2.0, 0.5, 0.5, 0.5, 0.5];
pub const KILL_FLUID_NORMAL: f64 = 0.05;
pub const KILL_FLUID_DECAY: f64 = 0.01;
pub const KILL_FLUID_UPPER_THRESHOLD: f64 = 0.052;
pub const KILL_FLUID_LOWER_THRESHOLD: f64 = 0.048;
pub const SIGNAL_FLUID_NORMAL: f64 = 0.5;
pub const SIGNAL_FLUID_DECAY: f64 = 0.001;
pub const B_FOOD_RATE: f64 = 0.05;

const TIMESTEP: f64 = 0.2;

#[derive(Default, Debug)]
pub struct Solution {
    pub fluids: [f64; TOTAL_FLUIDS],
    pub coefficients: [f64; TOTAL_FLUIDS],
    pub diffuse: [f64; TOTAL_FLUIDS],
}

impl Solution {
    pub fn new(fluids: [f64; TOTAL_FLUIDS], coefficients: [f64; TOTAL_FLUIDS]) -> Self {
        Solution {
            fluids: fluids,
            coefficients: coefficients,
            diffuse: [0.0; TOTAL_FLUIDS],
        }
    }

    pub fn react_deltas(&self) -> [f64; TOTAL_FLUIDS] {
        let a = self.fluids[1];
        let b = self.fluids[3];
        let kill = self.fluids[2];
        let f = 0.029;
        let k = 0.057;
        [B_FOOD_RATE * b,
         -a * b * b + f * (1.0 - a),
         KILL_FLUID_DECAY * (KILL_FLUID_NORMAL - kill),
         a * b * b - (k + f) * b,
         SIGNAL_FLUID_DECAY * (SIGNAL_FLUID_NORMAL - self.fluids[4]),
         SIGNAL_FLUID_DECAY * (SIGNAL_FLUID_NORMAL - self.fluids[5]),
         SIGNAL_FLUID_DECAY * (SIGNAL_FLUID_NORMAL - self.fluids[6]),
         SIGNAL_FLUID_DECAY * (SIGNAL_FLUID_NORMAL - self.fluids[7])]
    }

    pub fn diffuse_from(&mut self, other: &Solution) {
        for i in 0..TOTAL_FLUIDS {
            self.diffuse[i] += other.fluids[i] * other.coefficients[i] / 6.0;
        }
    }

    pub fn end_cycle(&mut self) {
        let reacts = self.react_deltas();
        for i in 0..TOTAL_FLUIDS {
            self.fluids[i] += TIMESTEP *
                              (reacts[i] + self.diffuse[i] - self.coefficients[i] * self.fluids[i]);
            self.diffuse[i] = 0.0;
        }
    }
}
<|endoftext|>