<commit_before>use lexer::*;
use errors::*;
use items::*;

#[derive(Debug, Clone)]
pub struct Parser<I>
	where I: Iterator<Item=char>
{
	tokens: ValidLexer<I>,
	peek  : Result<Token>
}

impl<I> Parser<I>
	where I: Iterator<Item=char>
{
	pub fn from(input: I) -> Parser<I> {
		Parser{
			tokens: ValidLexer::from(input),
			peek  : Err(ParseError::new(Loc::new(0, 0), ErrorKind::EmptyTokenStream))
		}
	}

	fn mk_err(&self, kind: ErrorKind) -> ParseError {
		ParseError::new(self.peek_loc(), kind)
	}

	fn token_err(&self, kind: ErrorKind) -> Result<Token> {
		Err(self.mk_err(kind))
	}

	fn formula_err(&self, kind: ErrorKind) -> Result<Formula> {
		Err(self.mk_err(kind))
	}

	fn peek_loc(&self) -> Loc {
		match self.peek {
			Ok(tok)  => tok.loc(),
			Err(err) => err.loc
		}
	}

	fn consume(&mut self) -> Result<Token> {
		self.peek = self.tokens
			.next()
			.unwrap_or(Ok(Token::new(self.peek_loc(), TokenKind::EndOfFile)));
		self.peek
	}

	fn expect(&mut self, expected: TokenKind) -> Result<Token> {
		use self::TokenKind::EndOfFile;
		use self::ErrorKind::{UnexpectedEndOfFile, UnexpectedToken};
		match self.peek?.kind() {
			k if k == expected => self.consume(),
			EndOfFile          => self.token_err(UnexpectedEndOfFile),
			_                  => self.token_err(UnexpectedToken)
		}
	}

	fn is_at_eof(&self) -> bool {
		match self.peek {
			Ok(peek) => peek.kind() == TokenKind::EndOfFile,
			_        => false
		}
	}

	fn expect_nat(&mut self) -> Result<u64> {
		match self.peek?.kind() {
			TokenKind::Nat(val) => {
				self.consume()?;
				Ok(val)
			},
			_ => Err(self.mk_err(ErrorKind::UnexpectedToken))
		}
	}

	fn parse_header(&mut self) -> Result<Instance> {
		use self::TokenKind::{Ident};
		use self::Ident::*;
		self.expect(Ident(Problem))?;
		match self.peek?.kind() {
			Ident(Cnf)   => self.parse_cnf_header(),
			Ident(Sat)   |
			Ident(Sate)  |
			Ident(Satx)  |
			Ident(Satex) => self.parse_sat_header(),
			_ => Err(self.mk_err(ErrorKind::UnexpectedToken))
		}
	}

	fn parse_cnf_header(&mut self) -> Result<Instance> {
		self.expect(TokenKind::Ident(Ident::Cnf))?;
		let num_vars    = self.expect_nat()?;
		let num_clauses = self.expect_nat()?;
		Ok(Instance::cnf(num_vars, self.parse_clauses(num_clauses)?))
	}

	fn parse_lit(&mut self) -> Result<Lit> {
		match self.peek?.kind() {
			TokenKind::Minus => match self.consume()?.kind() {
				TokenKind::Nat(val) => {
					self.consume()?;
					Ok(Lit::from_i64(-(val as i64)))
				},
				_ => Err(self.mk_err(ErrorKind::ExpectedNat))
			},
			TokenKind::Nat(val) => {
				self.consume()?;
				Ok(Lit::from_i64(val as i64))
			},
			_ => Err(self.mk_err(ErrorKind::ExpectedLit))
		}
	}

	fn parse_clause(&mut self) -> Result<Clause> {
		use self::TokenKind::{Minus, Nat, Zero, EndOfFile};
		use self::ErrorKind::{UnexpectedToken};
		let mut lits = Vec::new();
		loop {
			match self.peek?.kind() {
				Minus | Nat(_)   => lits.push(self.parse_lit()?),
				Zero | EndOfFile => { self.consume()?; return Ok(Clause::from_vec(lits)) },
				_                => return Err(self.mk_err(UnexpectedToken))
			}
		}
	}

	fn parse_clauses(&mut self, num_clauses: u64) -> Result<Vec<Clause>> {
		let mut clauses = Vec::with_capacity(num_clauses as usize);
		while !self.is_at_eof() {
			clauses.push(self.parse_clause()?);
		}
		Ok(clauses)
	}

	fn parse_sat_extensions<'a>(&'a mut self) -> Result<Extensions> {
		use self::TokenKind::{Ident};
		use self::Ident::{Sat, Sate, Satx, Satex};
		use self::ErrorKind::*;
		match self.peek?.kind() {
			Ident(Sat)   => { self.consume()?; Ok(NONE) },
			Ident(Sate)  => { self.consume()?; Ok(EQ) },
			Ident(Satx)  => { self.consume()?; Ok(XOR) },
			Ident(Satex) => { self.consume()?; Ok(EQ | XOR) },
			_ => Err(self.mk_err(InvalidSatExtension))
		}
	}

	fn parse_sat_header(&mut self) -> Result<Instance> {
		let extensions = self.parse_sat_extensions()?;
		let num_vars   = self.expect_nat()?;
		Ok(Instance::sat(num_vars, extensions, self.parse_paren_formula()?))
	}

	fn parse_formula(&mut self) -> Result<Formula> {
		use lexer::TokenKind::*;
		use lexer::Ident::*;
		let tok = self.peek?;
		match tok.kind() {
			Nat(val)   => { self.consume()?; Ok(Formula::lit(Lit::from_i64(val as i64))) },
			Open       => self.parse_paren_formula(),
			Plus       => self.parse_or_formula(),
			Star       => self.parse_and_formula(),
			Minus      => self.parse_neg_formula(),
			Eq         => self.parse_eq_formula(),
			Ident(Xor) => self.parse_xor_formula(),
			_          => self.formula_err(ErrorKind::UnexpectedToken)
		}
	}

	fn parse_formula_list(&mut self) -> Result<Vec<Formula>> {
		let mut formulas = Vec::new();
		while self.peek?.kind() != TokenKind::Close {
			formulas.push(self.parse_formula()?);
		}
		Ok(formulas)
	}

	fn parse_formula_params(&mut self) -> Result<Vec<Formula>> {
		self.expect(TokenKind::Open)?;
		let params = self.parse_formula_list()?;
		self.expect(TokenKind::Close)?;
		Ok(params)
	}

	fn parse_paren_formula(&mut self) -> Result<Formula> {
		self.expect(TokenKind::Open)?;
		let formula = Formula::paren(self.parse_formula()?);
		self.expect(TokenKind::Close)?;
		Ok(formula)
	}

	fn parse_neg_formula(&mut self) -> Result<Formula> {
		self.expect(TokenKind::Minus)?;
		let tok = self.peek?;
		match tok.kind() {
			TokenKind::Open => {
				self.expect(TokenKind::Open)?;
				let formula = Formula::neg(self.parse_formula()?);
				self.expect(TokenKind::Close)?;
				Ok(formula)
			},
			TokenKind::Nat(val) => {
				self.expect(TokenKind::Nat(val))?;
				Ok(Formula::lit(Lit::from_i64( -(val as i64) )))
			},
			_ => self.formula_err(ErrorKind::UnexpectedToken)
		}
	}

	fn parse_or_formula(&mut self) -> Result<Formula> {
		self.expect(TokenKind::Plus)?;
		Ok(Formula::or(self.parse_formula_params()?))
	}

	fn parse_and_formula(&mut self) -> Result<Formula> {
		self.expect(TokenKind::Star)?;
		Ok(Formula::and(self.parse_formula_params()?))
	}

	fn parse_eq_formula(&mut self) -> Result<Formula> {
		self.expect(TokenKind::Eq)?;
		Ok(Formula::eq(self.parse_formula_params()?))
	}

	fn parse_xor_formula(&mut self) -> Result<Formula> {
		self.expect(TokenKind::Ident(Ident::Xor))?;
		Ok(Formula::xor(self.parse_formula_params()?))
	}

	pub fn parse_dimacs(&mut self) -> Result<Instance> {
		self.consume()?;
		let instance = self.parse_header();
		if self.is_at_eof() {
			instance
		}
		else {
			Err(self.mk_err(ErrorKind::NotParsedToEnd))
		}
	}
}

pub fn parse_dimacs(input: &str) -> Result<Instance> {
	Parser::from(input.chars()).parse_dimacs()
}

#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn simple_cnf() {
		let sample = r"
			c Sample DIMACS .cnf file
			c holding some information
			c and trying to be some
			c kind of a test.
			p cnf 42 1337
			1 2 0
			-3 4 0
			5 -6 7 0
			-7 -8 -9 0";
		let parsed = parse_dimacs(sample).expect("valid .cnf");
		let expected = Instance::cnf(42, vec![
			Clause::from_vec(vec![Lit::from_i64( 1), Lit::from_i64( 2)]),
			Clause::from_vec(vec![Lit::from_i64(-3), Lit::from_i64( 4)]),
			Clause::from_vec(vec![Lit::from_i64( 5), Lit::from_i64(-6), Lit::from_i64( 7)]),
			Clause::from_vec(vec![Lit::from_i64(-7), Lit::from_i64(-8), Lit::from_i64(-9)])
		]);
		assert_eq!(parsed, expected);
	}

	#[test]
	fn simple_sat() {
		let sample = r"
			c Sample DIMACS .sat file
			p sat 42
			(*(+(1 3 -4)
			+(4)
			+(2 3)))";
		let parsed = parse_dimacs(sample).expect("valid .sat");
		let expected = Instance::sat(42, NONE,
			Formula::paren(
				Formula::and(vec![
					Formula::or(vec![
						Formula::lit(Lit::from_i64(1)), Formula::lit(Lit::from_i64(3)), Formula::lit(Lit::from_i64(-4))
					]),
					Formula::or(vec![
						Formula::lit(Lit::from_i64(4))
					]),
					Formula::or(vec![
						Formula::lit(Lit::from_i64(2)), Formula::lit(Lit::from_i64(3))
					])
				])
			)
		);
		assert_eq!(parsed, expected);
	}
}
<commit_msg>added another test for .cnf files<commit_after>use lexer::*;
use errors::*;
use items::*;

#[derive(Debug, Clone)]
pub struct Parser<I>
	where I: Iterator<Item=char>
{
	tokens: ValidLexer<I>,
	peek  : Result<Token>
}

impl<I> Parser<I>
	where I: Iterator<Item=char>
{
	pub fn from(input: I) -> Parser<I> {
		Parser{
			tokens: ValidLexer::from(input),
			peek  : Err(ParseError::new(Loc::new(0, 0), ErrorKind::EmptyTokenStream))
		}
	}

	fn mk_err(&self, kind: ErrorKind) -> ParseError {
		ParseError::new(self.peek_loc(), kind)
	}

	fn token_err(&self, kind: ErrorKind) -> Result<Token> {
		Err(self.mk_err(kind))
	}

	fn formula_err(&self, kind: ErrorKind) -> Result<Formula> {
		Err(self.mk_err(kind))
	}

	fn peek_loc(&self) -> Loc {
		match self.peek {
			Ok(tok)  => tok.loc(),
			Err(err) => err.loc
		}
	}

	fn consume(&mut self) -> Result<Token> {
		self.peek = self.tokens
			.next()
			.unwrap_or(Ok(Token::new(self.peek_loc(), TokenKind::EndOfFile)));
		self.peek
	}

	fn expect(&mut self, expected: TokenKind) -> Result<Token> {
		use self::TokenKind::EndOfFile;
		use self::ErrorKind::{UnexpectedEndOfFile, UnexpectedToken};
		match self.peek?.kind() {
			k if k == expected => self.consume(),
			EndOfFile          => self.token_err(UnexpectedEndOfFile),
			_                  => self.token_err(UnexpectedToken)
		}
	}

	fn is_at_eof(&self) -> bool {
		match self.peek {
			Ok(peek) => peek.kind() == TokenKind::EndOfFile,
			_        => false
		}
	}

	fn expect_nat(&mut self) -> Result<u64> {
		match self.peek?.kind() {
			TokenKind::Nat(val) => {
				self.consume()?;
				Ok(val)
			},
			_ => Err(self.mk_err(ErrorKind::UnexpectedToken))
		}
	}

	fn parse_header(&mut self) -> Result<Instance> {
		use self::TokenKind::{Ident};
		use self::Ident::*;
		self.expect(Ident(Problem))?;
		match self.peek?.kind() {
			Ident(Cnf)   => self.parse_cnf_header(),
			Ident(Sat)   |
			Ident(Sate)  |
			Ident(Satx)  |
			Ident(Satex) => self.parse_sat_header(),
			_ => Err(self.mk_err(ErrorKind::UnexpectedToken))
		}
	}

	fn parse_cnf_header(&mut self) -> Result<Instance> {
		self.expect(TokenKind::Ident(Ident::Cnf))?;
		let num_vars    = self.expect_nat()?;
		let num_clauses = self.expect_nat()?;
		Ok(Instance::cnf(num_vars, self.parse_clauses(num_clauses)?))
	}

	fn parse_lit(&mut self) -> Result<Lit> {
		match self.peek?.kind() {
			TokenKind::Minus => match self.consume()?.kind() {
				TokenKind::Nat(val) => {
					self.consume()?;
					Ok(Lit::from_i64(-(val as i64)))
				},
				_ => Err(self.mk_err(ErrorKind::ExpectedNat))
			},
			TokenKind::Nat(val) => {
				self.consume()?;
				Ok(Lit::from_i64(val as i64))
			},
			_ => Err(self.mk_err(ErrorKind::ExpectedLit))
		}
	}

	fn parse_clause(&mut self) -> Result<Clause> {
		use self::TokenKind::{Minus, Nat, Zero, EndOfFile};
		use self::ErrorKind::{UnexpectedToken};
		let mut lits = Vec::new();
		loop {
			match self.peek?.kind() {
				Minus | Nat(_)   => lits.push(self.parse_lit()?),
				Zero | EndOfFile => { self.consume()?; return Ok(Clause::from_vec(lits)) },
				_                => return Err(self.mk_err(UnexpectedToken))
			}
		}
	}

	fn parse_clauses(&mut self, num_clauses: u64) -> Result<Vec<Clause>> {
		let mut clauses = Vec::with_capacity(num_clauses as usize);
		while !self.is_at_eof() {
			clauses.push(self.parse_clause()?);
		}
		Ok(clauses)
	}

	fn parse_sat_extensions<'a>(&'a mut self) -> Result<Extensions> {
		use self::TokenKind::{Ident};
		use self::Ident::{Sat, Sate, Satx, Satex};
		use self::ErrorKind::*;
		match self.peek?.kind() {
			Ident(Sat)   => { self.consume()?; Ok(NONE) },
			Ident(Sate)  => { self.consume()?; Ok(EQ) },
			Ident(Satx)  => { self.consume()?; Ok(XOR) },
			Ident(Satex) => { self.consume()?; Ok(EQ | XOR) },
			_ => Err(self.mk_err(InvalidSatExtension))
		}
	}

	fn parse_sat_header(&mut self) -> Result<Instance> {
		let extensions = self.parse_sat_extensions()?;
		let num_vars   = self.expect_nat()?;
		Ok(Instance::sat(num_vars, extensions, self.parse_paren_formula()?))
	}

	fn parse_formula(&mut self) -> Result<Formula> {
		use lexer::TokenKind::*;
		use lexer::Ident::*;
		let tok = self.peek?;
		match tok.kind() {
			Nat(val)   => { self.consume()?; Ok(Formula::lit(Lit::from_i64(val as i64))) },
			Open       => self.parse_paren_formula(),
			Plus       => self.parse_or_formula(),
			Star       => self.parse_and_formula(),
			Minus      => self.parse_neg_formula(),
			Eq         => self.parse_eq_formula(),
			Ident(Xor) => self.parse_xor_formula(),
			_          => self.formula_err(ErrorKind::UnexpectedToken)
		}
	}

	fn parse_formula_list(&mut self) -> Result<Vec<Formula>> {
		let mut formulas = Vec::new();
		while self.peek?.kind() != TokenKind::Close {
			formulas.push(self.parse_formula()?);
		}
		Ok(formulas)
	}

	fn parse_formula_params(&mut self) -> Result<Vec<Formula>> {
		self.expect(TokenKind::Open)?;
		let params = self.parse_formula_list()?;
		self.expect(TokenKind::Close)?;
		Ok(params)
	}

	fn parse_paren_formula(&mut self) -> Result<Formula> {
		self.expect(TokenKind::Open)?;
		let formula = Formula::paren(self.parse_formula()?);
		self.expect(TokenKind::Close)?;
		Ok(formula)
	}

	fn parse_neg_formula(&mut self) -> Result<Formula> {
		self.expect(TokenKind::Minus)?;
		let tok = self.peek?;
		match tok.kind() {
			TokenKind::Open => {
				self.expect(TokenKind::Open)?;
				let formula = Formula::neg(self.parse_formula()?);
				self.expect(TokenKind::Close)?;
				Ok(formula)
			},
			TokenKind::Nat(val) => {
				self.expect(TokenKind::Nat(val))?;
				Ok(Formula::lit(Lit::from_i64( -(val as i64) )))
			},
			_ => self.formula_err(ErrorKind::UnexpectedToken)
		}
	}

	fn parse_or_formula(&mut self) -> Result<Formula> {
		self.expect(TokenKind::Plus)?;
		Ok(Formula::or(self.parse_formula_params()?))
	}

	fn parse_and_formula(&mut self) -> Result<Formula> {
		self.expect(TokenKind::Star)?;
		Ok(Formula::and(self.parse_formula_params()?))
	}

	fn parse_eq_formula(&mut self) -> Result<Formula> {
		self.expect(TokenKind::Eq)?;
		Ok(Formula::eq(self.parse_formula_params()?))
	}

	fn parse_xor_formula(&mut self) -> Result<Formula> {
		self.expect(TokenKind::Ident(Ident::Xor))?;
		Ok(Formula::xor(self.parse_formula_params()?))
	}

	pub fn parse_dimacs(&mut self) -> Result<Instance> {
		self.consume()?;
		let instance = self.parse_header();
		if self.is_at_eof() {
			instance
		}
		else {
			Err(self.mk_err(ErrorKind::NotParsedToEnd))
		}
	}
}

pub fn parse_dimacs(input: &str) -> Result<Instance> {
	Parser::from(input.chars()).parse_dimacs()
}

#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn simple_cnf_1() {
		let sample = r"
			c Sample DIMACS .cnf file
			c holding some information
			c and trying to be some
			c kind of a test.
			p cnf 42 1337
			1 2 0
			-3 4 0
			5 -6 7 0
			-7 -8 -9 0";
		let parsed = parse_dimacs(sample).expect("valid .cnf");
		let expected = Instance::cnf(42, vec![
			Clause::from_vec(vec![Lit::from_i64( 1), Lit::from_i64( 2)]),
			Clause::from_vec(vec![Lit::from_i64(-3), Lit::from_i64( 4)]),
			Clause::from_vec(vec![Lit::from_i64( 5), Lit::from_i64(-6), Lit::from_i64( 7)]),
			Clause::from_vec(vec![Lit::from_i64(-7), Lit::from_i64(-8), Lit::from_i64(-9)])
		]);
		assert_eq!(parsed, expected);
	}

	#[test]
	fn simple_cnf_2() {
		let sample = r"
			c Example CNF format file
			c
			p cnf 4 3
			1 3 -4 0
			4 0 2
			-3";
		let parsed = parse_dimacs(sample).expect("valid .cnf");
		let expected = Instance::cnf(4, vec![
			Clause::from_vec(vec![Lit::from_i64( 1), Lit::from_i64( 3), Lit::from_i64(-4)]),
			Clause::from_vec(vec![Lit::from_i64( 4)]),
			Clause::from_vec(vec![Lit::from_i64( 2), Lit::from_i64(-3)])
		]);
		assert_eq!(parsed, expected);
	}

	#[test]
	fn simple_sat() {
		let sample = r"
			c Sample DIMACS .sat file
			p sat 42
			(*(+(1 3 -4)
			+(4)
			+(2 3)))";
		let parsed = parse_dimacs(sample).expect("valid .sat");
		let expected = Instance::sat(42, NONE,
			Formula::paren(
				Formula::and(vec![
					Formula::or(vec![
						Formula::lit(Lit::from_i64(1)), Formula::lit(Lit::from_i64(3)), Formula::lit(Lit::from_i64(-4))
					]),
					Formula::or(vec![
						Formula::lit(Lit::from_i64(4))
					]),
					Formula::or(vec![
						Formula::lit(Lit::from_i64(2)), Formula::lit(Lit::from_i64(3))
					])
				])
			)
		);
		assert_eq!(parsed, expected);
	}
}
<|endoftext|>


<commit_before>use std::str;

#[derive(Debug)]
pub enum Message {
    Settings(SettingsValue),
    SetupMap{name: &'static str, value: Vec<u64>},
    PickStartingRegion{time_left: u64, ids: Vec<u64>},
    UpdateMap,
    GoPlaceArmies,
    GoAttackTranser,
}

#[derive(Debug)]
pub enum SettingsValue {
    Timebank(u64),
    TimePerMove(u64),
    MaxRounds(u64),
    YourBot(String),
    OpponentBot(String),
    StartingRegions(Vec<u64>),
    StartingPickAmount(u64),
    StartingArmies(u64)
}

pub fn parse(line: &'static str) -> Result<Message, String> {
    let mut words = line.split(' ');
    match words.next().unwrap() {
        "settings" => parse_settings(words),
        "update_map" => {
            Ok(Message::UpdateMap)
        }
        _ => Err("unknown command".to_owned())
    }
}

fn parse_settings(mut parts: str::Split<char>) -> Result<Message, String> {
    let command = try!(parts.next().ok_or("no command given to setting"));

    match command {
        "timebank" => Ok(Message::Settings(SettingsValue::Timebank(try!(parts_to_u64(parts, command.to_owned()))))),
        "time_per_move" => Ok(Message::Settings(SettingsValue::TimePerMove(try!(parts_to_u64(parts, command.to_owned()))))),
        "max_rounds" => Ok(Message::Settings(SettingsValue::MaxRounds(try!(parts_to_u64(parts, command.to_owned()))))),
        "starting_pick_amount" => Ok(Message::Settings(SettingsValue::StartingPickAmount(try!(parts_to_u64(parts, command.to_owned()))))),
        "starting_armies" => Ok(Message::Settings(SettingsValue::StartingArmies(try!(parts_to_u64(parts, command.to_owned()))))),
        "starting_regions" => {
            let mut peeker = parts.peekable();
            try!(peeker.peek().ok_or("got starting_regions setting with no value"));
            let mut value = Vec::new();
            for word in peeker {
                value.push(try!(u64::from_str_radix(word, 10).map_err(|e| e.to_string())));
            }
            Ok(Message::Settings(SettingsValue::StartingRegions(value)))
        }
        "your_bot" => {
            let raw_value = try!(parts.next().ok_or("got your_bot setting with no value"));
            Ok(Message::Settings(SettingsValue::YourBot(raw_value.to_owned())))
        }
        "opponent_bot" => {
            let raw_value = try!(parts.next().ok_or("got opponent_bot setting with no value"));
            Ok(Message::Settings(SettingsValue::OpponentBot(raw_value.to_owned())))
        }
        _ => Err(format!("unknown setting {}", command).to_owned())
    }
}

fn parts_to_u64(mut parts: str::Split<char>, command: String) -> Result<u64, String> {
    let raw_value = try!(parts.next().ok_or(format!("got {} setting with no value", command)));
    Ok(try!(u64::from_str_radix(raw_value, 10).map_err(|e| e.to_string())))
}

#[test]
fn blank() {
    match parse("") {
        Ok(_) => panic!("should return error because it is malformed"),
        Err(e) => assert_eq!(e, "unknown command".to_owned())
    }
}

#[test]
fn setting_blank() {
    match parse("settings") {
        Ok(_) => panic!("should return error because it is malformed"),
        Err(e) => assert_eq!(e, "no command given to setting".to_owned())
    }
}

#[test]
fn setting_timebank_blank() {
    match parse("settings timebank") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got timebank setting with no value".to_owned())
    }
}

#[test]
fn setting_timebank_bad_value() {
    match parse("settings timebank five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_timebank_proper() {
    match parse("settings timebank 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::Timebank(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a timebank")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_time_per_move_blank() {
    match parse("settings time_per_move") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got time_per_move setting with no value".to_owned())
    }
}

#[test]
fn setting_time_per_move_bad_value() {
    match parse("settings time_per_move five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_time_per_move_proper() {
    match parse("settings time_per_move 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::TimePerMove(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a time_per_move")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_max_rounds_blank() {
    match parse("settings max_rounds") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got max_rounds setting with no value".to_owned())
    }
}

#[test]
fn setting_max_rounds_bad_value() {
    match parse("settings max_rounds five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_max_rounds_proper() {
    match parse("settings max_rounds 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::MaxRounds(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a max_rounds")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_starting_pick_amount_blank() {
    match parse("settings starting_pick_amount") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got starting_pick_amount setting with no value".to_owned())
    }
}

#[test]
fn setting_starting_pick_amount_bad_value() {
    match parse("settings starting_pick_amount five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_starting_pick_amount_proper() {
    match parse("settings starting_pick_amount 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::StartingPickAmount(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a starting_pick_amount")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_starting_armies_blank() {
    match parse("settings starting_armies") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got starting_armies setting with no value".to_owned())
    }
}

#[test]
fn setting_starting_armies_bad_value() {
    match parse("settings starting_armies five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_starting_armies_proper() {
    match parse("settings starting_armies 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::StartingArmies(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a starting_armies")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_starting_regions_blank() {
    match parse("settings starting_regions") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got starting_regions setting with no value".to_owned())
    }
}

#[test]
fn setting_starting_regions_bad_value() {
    match parse("settings starting_regions five 50") {
        Ok(a) => panic!("got a setting back when expecting an error, {:?}", a),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_starting_regions_proper() {
    match parse("settings starting_regions 4 7 11").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::StartingRegions(value) => assert_eq!(value, vec![4, 7, 11]),
            _ => panic!("got a setting that wasn't a starting_regions")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_your_bot_blank() {
    match parse("settings your_bot") {
        Ok(a) => panic!("got a setting back when expecting an error, {:?}", a),
        Err(e) => assert_eq!(e, "got your_bot setting with no value".to_owned())
    }
}

#[test]
fn setting_your_bot_proper() {
    match parse("settings your_bot fig").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::YourBot(value) => assert_eq!(value, "fig".to_owned()),
            _ => panic!("got a setting that wasn't a your_bot")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_opponent_bot_blank() {
    match parse("settings opponent_bot") {
        Ok(a) => panic!("got a setting back when expecting an error, {:?}", a),
        Err(e) => assert_eq!(e, "got opponent_bot setting with no value".to_owned())
    }
}

#[test]
fn setting_opponent_bot_proper() {
    match parse("settings opponent_bot fig").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::OpponentBot(value) => assert_eq!(value, "fig".to_owned()),
            _ => panic!("got a setting that wasn't a opponent_bot")
        },
        _ => panic!("didn't get a settings object")
    }
}
<commit_msg>readable lines<commit_after>use std::str;

#[derive(Debug)]
pub enum Message {
    Settings(SettingsValue),
    SetupMap{name: &'static str, value: Vec<u64>},
    PickStartingRegion{time_left: u64, ids: Vec<u64>},
    UpdateMap,
    GoPlaceArmies,
    GoAttackTranser,
}

#[derive(Debug)]
pub enum SettingsValue {
    Timebank(u64),
    TimePerMove(u64),
    MaxRounds(u64),
    YourBot(String),
    OpponentBot(String),
    StartingRegions(Vec<u64>),
    StartingPickAmount(u64),
    StartingArmies(u64)
}

pub fn parse(line: &'static str) -> Result<Message, String> {
    let mut words = line.split(' ');
    match words.next().unwrap() {
        "settings" => parse_settings(words),
        "update_map" => {
            Ok(Message::UpdateMap)
        }
        _ => Err("unknown command".to_owned())
    }
}

fn parse_settings(mut parts: str::Split<char>) -> Result<Message, String> {
    let command = try!(parts.next().ok_or("no command given to setting"));

    match command {
        "timebank" => {
            let value = try!(parts_to_u64(parts, command.to_owned()));
            Ok(Message::Settings(SettingsValue::Timebank(value)))
        }
        "time_per_move" => {
            let value = try!(parts_to_u64(parts, command.to_owned()));
            Ok(Message::Settings(SettingsValue::TimePerMove(value)))
        }
        "max_rounds" => {
            let value = try!(parts_to_u64(parts, command.to_owned()));
            Ok(Message::Settings(SettingsValue::MaxRounds(value)))
        }
        "starting_pick_amount" => {
            let value = try!(parts_to_u64(parts, command.to_owned()));
            Ok(Message::Settings(SettingsValue::StartingPickAmount(value)))
        }
        "starting_armies" => {
            let value = try!(parts_to_u64(parts, command.to_owned()));
            Ok(Message::Settings(SettingsValue::StartingArmies(value)))
        }
        "starting_regions" => {
            let mut peeker = parts.peekable();
            try!(peeker.peek().ok_or("got starting_regions setting with no value"));
            let mut value = Vec::new();
            for word in peeker {
                value.push(try!(u64::from_str_radix(word, 10).map_err(|e| e.to_string())));
            }
            Ok(Message::Settings(SettingsValue::StartingRegions(value)))
        }
        "your_bot" => {
            let raw_value = try!(parts.next().ok_or("got your_bot setting with no value"));
            Ok(Message::Settings(SettingsValue::YourBot(raw_value.to_owned())))
        }
        "opponent_bot" => {
            let raw_value = try!(parts.next().ok_or("got opponent_bot setting with no value"));
            Ok(Message::Settings(SettingsValue::OpponentBot(raw_value.to_owned())))
        }
        _ => Err(format!("unknown setting {}", command).to_owned())
    }
}

fn parts_to_u64(mut parts: str::Split<char>, command: String) -> Result<u64, String> {
    let raw_value = try!(parts.next().ok_or(format!("got {} setting with no value", command)));
    Ok(try!(u64::from_str_radix(raw_value, 10).map_err(|e| e.to_string())))
}

#[test]
fn blank() {
    match parse("") {
        Ok(_) => panic!("should return error because it is malformed"),
        Err(e) => assert_eq!(e, "unknown command".to_owned())
    }
}

#[test]
fn setting_blank() {
    match parse("settings") {
        Ok(_) => panic!("should return error because it is malformed"),
        Err(e) => assert_eq!(e, "no command given to setting".to_owned())
    }
}

#[test]
fn setting_timebank_blank() {
    match parse("settings timebank") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got timebank setting with no value".to_owned())
    }
}

#[test]
fn setting_timebank_bad_value() {
    match parse("settings timebank five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_timebank_proper() {
    match parse("settings timebank 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::Timebank(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a timebank")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_time_per_move_blank() {
    match parse("settings time_per_move") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got time_per_move setting with no value".to_owned())
    }
}

#[test]
fn setting_time_per_move_bad_value() {
    match parse("settings time_per_move five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_time_per_move_proper() {
    match parse("settings time_per_move 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::TimePerMove(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a time_per_move")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_max_rounds_blank() {
    match parse("settings max_rounds") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got max_rounds setting with no value".to_owned())
    }
}

#[test]
fn setting_max_rounds_bad_value() {
    match parse("settings max_rounds five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_max_rounds_proper() {
    match parse("settings max_rounds 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::MaxRounds(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a max_rounds")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_starting_pick_amount_blank() {
    match parse("settings starting_pick_amount") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got starting_pick_amount setting with no value".to_owned())
    }
}

#[test]
fn setting_starting_pick_amount_bad_value() {
    match parse("settings starting_pick_amount five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_starting_pick_amount_proper() {
    match parse("settings starting_pick_amount 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::StartingPickAmount(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a starting_pick_amount")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_starting_armies_blank() {
    match parse("settings starting_armies") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got starting_armies setting with no value".to_owned())
    }
}

#[test]
fn setting_starting_armies_bad_value() {
    match parse("settings starting_armies five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_starting_armies_proper() {
    match parse("settings starting_armies 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::StartingArmies(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a starting_armies")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_starting_regions_blank() {
    match parse("settings starting_regions") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got starting_regions setting with no value".to_owned())
    }
}

#[test]
fn setting_starting_regions_bad_value() {
    match parse("settings starting_regions five 50") {
        Ok(a) => panic!("got a setting back when expecting an error, {:?}", a),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_starting_regions_proper() {
    match parse("settings starting_regions 4 7 11").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::StartingRegions(value) => assert_eq!(value, vec![4, 7, 11]),
            _ => panic!("got a setting that wasn't a starting_regions")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_your_bot_blank() {
    match parse("settings your_bot") {
        Ok(a) => panic!("got a setting back when expecting an error, {:?}", a),
        Err(e) => assert_eq!(e, "got your_bot setting with no value".to_owned())
    }
}

#[test]
fn setting_your_bot_proper() {
    match parse("settings your_bot fig").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::YourBot(value) => assert_eq!(value, "fig".to_owned()),
            _ => panic!("got a setting that wasn't a your_bot")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_opponent_bot_blank() {
    match parse("settings opponent_bot") {
        Ok(a) => panic!("got a setting back when expecting an error, {:?}", a),
        Err(e) => assert_eq!(e, "got opponent_bot setting with no value".to_owned())
    }
}

#[test]
fn setting_opponent_bot_proper() {
    match parse("settings opponent_bot fig").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::OpponentBot(value) => assert_eq!(value, "fig".to_owned()),
            _ => panic!("got a setting that wasn't a opponent_bot")
        },
        _ => panic!("didn't get a settings object")
    }
}
<|endoftext|>


<commit_before>use std;
use std::io::prelude::*;
use std::io::{Error, ErrorKind};
use std::net::TcpStream;

/// Reads bytes from the TCP stream until CRLF is found or the given limit is
/// reached.
pub fn read_line(stream: &TcpStream, limit: usize) -> std::io::Result<String> {
  let mut result = String::with_capacity(limit);

  for byte in stream.take(limit as u64).bytes() {
    let raw = try!(byte);
    let chr = raw as char;

    result.push(chr);

    if chr == '\n' {
      break;
    }
  }

  if result.ends_with("\r\n") {
    // .truncate() panics if the new length is not at char boundary. Since we
    // know the string ends with \r\n, that won't happen.
    let new_len = result.len() - 2;
    result.truncate(new_len);

    // Return the result.
    Ok(result)
  } else if result.len() == limit {
    // The line did not fit in the given limit.
    Err(Error::new(ErrorKind::Other, "Line too long."))
  } else {
    // The line ending was invalid or missing.
    Err(Error::new(ErrorKind::Other, "The line must end in CRLF sequence."))
  }
}

/// Reads the given number of bytes from the stream into a Vec<u8> or returns
/// an error if the exact number of bytes cannot be read.
pub fn read_bytes(stream: &TcpStream, amount: usize) -> std::io::Result<Vec<u8>> {
  let mut buf: Vec<u8> = vec![];
  let read = try!(stream.take(amount as u64).read_to_end(&mut buf));

  if read < amount {
    Err(Error::new(ErrorKind::Other, "Unexpected end of stream."))
  } else {
    Ok(buf)
  }
}

/// Reads four bytes from the stream and combines them together to form a 32 bit
/// unsigned integer.
pub fn read_u32(stream: &TcpStream) -> std::io::Result<u32> {
  let mut buf = try!(read_bytes(stream, 4));

  Ok(buf.iter().enumerate().fold(0, |result, (i, value)| {
    result | ((*value as u32) << (3 - i) * 8)
  }))
}

#[cfg(test)]
mod tests {
  use super::*;
  use test::create_tcp_stream;

  #[test]
  fn test_readline_basic() {
    let stream = create_tcp_stream("foo\r\n".as_bytes());
    assert_eq!(read_line(&stream, 255).unwrap(), "foo");
  }

  #[test]
  fn test_readline_empty_line() {
    let stream = create_tcp_stream("\r\n".as_bytes());
    assert_eq!(read_line(&stream, 255).unwrap(), "");
  }

  #[test]
  fn test_readline_multiple_lines_reads_only_first() {
    let stream = create_tcp_stream("bar\r\nfoo\r\n".as_bytes());
    assert_eq!(read_line(&stream, 255).unwrap(), "bar");
    assert_eq!(read_line(&stream, 255).unwrap(), "foo");
  }

  #[test]
  fn test_readline_line_too_long() {
    let stream = create_tcp_stream("barbazfoo\r\n".as_bytes());
    assert!(read_line(&stream, 5).is_err());
  }

  #[test]
  fn test_readline_line_at_limit() {
    let stream = create_tcp_stream("123\r\n".as_bytes());
    assert_eq!(read_line(&stream, 5).unwrap(), "123");
  }

  #[test]
  fn test_readline_no_cr_before_lf() {
    let stream = create_tcp_stream("barbazfoo\n".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_no_crlf() {
    let stream = create_tcp_stream("barbazfoo".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_no_lf() {
    let stream = create_tcp_stream("barb\razfoo".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_empty() {
    let stream = create_tcp_stream("".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_only_lf() {
    let stream = create_tcp_stream("\n".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_read_u32_basic() {
    let stream = create_tcp_stream(&[0x29, 0xb7, 0xf4, 0xaa, 0xff, 0xff]);
    assert_eq!(read_u32(&stream).unwrap(), 0x29b7f4aa);
  }

  #[test]
  fn test_read_u32_basic_zeroes() {
    let stream = create_tcp_stream(&[0x29, 0x00, 0xf4, 0xaa, 0xff, 0xff]);
    assert_eq!(read_u32(&stream).unwrap(), 0x2900f4aa);
  }

  #[test]
  fn test_read_u32_too_little_data() {
    let stream = create_tcp_stream(&[0x29, 0xb7, 0xf4]);
    assert!(read_u32(&stream).is_err());
  }

  #[test]
  fn test_read_bytes_basic() {
    let stream = create_tcp_stream(&[1,2,3,4,5,6]);
    assert_eq!(read_bytes(&stream, 2).unwrap(), [1,2]);
    assert_eq!(read_bytes(&stream, 3).unwrap(), [3,4,5]);
    assert_eq!(read_bytes(&stream, 1).unwrap(), [6]);
  }

  #[test]
  fn test_read_bytes_too_little_data() {
    let stream = create_tcp_stream(&[1,2,3,4,5,6]);
    assert!(read_bytes(&stream, 10).is_err())
  }
}
<commit_msg>Remove unneeded mutable variable from read_u32().<commit_after>use std;
use std::io::prelude::*;
use std::io::{Error, ErrorKind};
use std::net::TcpStream;

/// Reads bytes from the TCP stream until CRLF is found or the given limit is
/// reached.
pub fn read_line(stream: &TcpStream, limit: usize) -> std::io::Result<String> {
  let mut result = String::with_capacity(limit);

  for byte in stream.take(limit as u64).bytes() {
    let raw = try!(byte);
    let chr = raw as char;

    result.push(chr);

    if chr == '\n' {
      break;
    }
  }

  if result.ends_with("\r\n") {
    // .truncate() panics if the new length is not at char boundary. Since we
    // know the string ends with \r\n, that won't happen.
    let new_len = result.len() - 2;
    result.truncate(new_len);

    // Return the result.
    Ok(result)
  } else if result.len() == limit {
    // The line did not fit in the given limit.
    Err(Error::new(ErrorKind::Other, "Line too long."))
  } else {
    // The line ending was invalid or missing.
    Err(Error::new(ErrorKind::Other, "The line must end in CRLF sequence."))
  }
}

/// Reads the given number of bytes from the stream into a Vec<u8> or returns
/// an error if the exact number of bytes cannot be read.
pub fn read_bytes(stream: &TcpStream, amount: usize) -> std::io::Result<Vec<u8>> {
  let mut buf: Vec<u8> = vec![];
  let read = try!(stream.take(amount as u64).read_to_end(&mut buf));

  if read < amount {
    Err(Error::new(ErrorKind::Other, "Unexpected end of stream."))
  } else {
    Ok(buf)
  }
}

/// Reads four bytes from the stream and combines them together to form a 32 bit
/// unsigned integer.
pub fn read_u32(stream: &TcpStream) -> std::io::Result<u32> {
  let buf = try!(read_bytes(stream, 4));

  Ok(buf.iter().enumerate().fold(0, |result, (i, value)| {
    result | ((*value as u32) << (3 - i) * 8)
  }))
}

#[cfg(test)]
mod tests {
  use super::*;
  use test::create_tcp_stream;

  #[test]
  fn test_readline_basic() {
    let stream = create_tcp_stream("foo\r\n".as_bytes());
    assert_eq!(read_line(&stream, 255).unwrap(), "foo");
  }

  #[test]
  fn test_readline_empty_line() {
    let stream = create_tcp_stream("\r\n".as_bytes());
    assert_eq!(read_line(&stream, 255).unwrap(), "");
  }

  #[test]
  fn test_readline_multiple_lines_reads_only_first() {
    let stream = create_tcp_stream("bar\r\nfoo\r\n".as_bytes());
    assert_eq!(read_line(&stream, 255).unwrap(), "bar");
    assert_eq!(read_line(&stream, 255).unwrap(), "foo");
  }

  #[test]
  fn test_readline_line_too_long() {
    let stream = create_tcp_stream("barbazfoo\r\n".as_bytes());
    assert!(read_line(&stream, 5).is_err());
  }

  #[test]
  fn test_readline_line_at_limit() {
    let stream = create_tcp_stream("123\r\n".as_bytes());
    assert_eq!(read_line(&stream, 5).unwrap(), "123");
  }

  #[test]
  fn test_readline_no_cr_before_lf() {
    let stream = create_tcp_stream("barbazfoo\n".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_no_crlf() {
    let stream = create_tcp_stream("barbazfoo".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_no_lf() {
    let stream = create_tcp_stream("barb\razfoo".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_empty() {
    let stream = create_tcp_stream("".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_only_lf() {
    let stream = create_tcp_stream("\n".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_read_u32_basic() {
    let stream = create_tcp_stream(&[0x29, 0xb7, 0xf4, 0xaa, 0xff, 0xff]);
    assert_eq!(read_u32(&stream).unwrap(), 0x29b7f4aa);
  }

  #[test]
  fn test_read_u32_basic_zeroes() {
    let stream = create_tcp_stream(&[0x29, 0x00, 0xf4, 0xaa, 0xff, 0xff]);
    assert_eq!(read_u32(&stream).unwrap(), 0x2900f4aa);
  }

  #[test]
  fn test_read_u32_too_little_data() {
    let stream = create_tcp_stream(&[0x29, 0xb7, 0xf4]);
    assert!(read_u32(&stream).is_err());
  }

  #[test]
  fn test_read_bytes_basic() {
    let stream = create_tcp_stream(&[1,2,3,4,5,6]);
    assert_eq!(read_bytes(&stream, 2).unwrap(), [1,2]);
    assert_eq!(read_bytes(&stream, 3).unwrap(), [3,4,5]);
    assert_eq!(read_bytes(&stream, 1).unwrap(), [6]);
  }

  #[test]
  fn test_read_bytes_too_little_data() {
    let stream = create_tcp_stream(&[1,2,3,4,5,6]);
    assert!(read_bytes(&stream, 10).is_err())
  }
}
<|endoftext|>


<commit_before>use cgmath;
use gfx;
use gfx::traits::{Device, Factory as Factory_, FactoryExt};
#[cfg(feature = "opengl")]
use gfx_device_gl as back;
#[cfg(feature = "opengl")]
use gfx_window_glutin;
#[cfg(feature = "opengl")]
use glutin;

pub use self::back::Factory as BackendFactory;
pub use self::back::Resources as BackendResources;
use camera::Camera;
use factory::{Factory, Texture};
use scene::{Color, Material};
use {Scene};

pub type ColorFormat = gfx::format::Srgba8;
pub type DepthFormat = gfx::format::DepthStencil;

gfx_vertex_struct!(Vertex {
    pos: [f32; 4] = "a_Position",
    uv: [f32; 2] = "a_TexCoord",
    normal: [gfx::format::I8Norm; 4] = "a_Normal",
});

gfx_pipeline!(pipe {
    vbuf: gfx::VertexBuffer<Vertex> = (),
    mx_vp: gfx::Global<[[f32; 4]; 4]> = "u_ViewProj",
    mx_world: gfx::Global<[[f32; 4]; 4]> = "u_World",
    color: gfx::Global<[f32; 4]> = "u_Color",
    tex_map: gfx::TextureSampler<[f32; 4]> = "t_Map",
    out_color: gfx::BlendTarget<ColorFormat> =
        ("Target0", gfx::state::MASK_ALL, gfx::preset::blend::REPLACE),
});

const LINE_VS: &'static [u8] = b"
    #version 150 core
    in vec4 a_Position;
    uniform mat4 u_ViewProj;
    uniform mat4 u_World;
    void main() {
        gl_Position = u_ViewProj * u_World * a_Position;
    }
";
const LINE_FS: &'static [u8] = b"
    #version 150 core
    uniform vec4 u_Color;
    void main() {
        gl_FragColor = u_Color;
    }
";

const MESH_VS: &'static [u8] = b"
    #version 150 core
    in vec4 a_Position;
    in vec2 a_TexCoord;
    out vec2 v_TexCoord;
    uniform mat4 u_ViewProj;
    uniform mat4 u_World;
    void main() {
        gl_Position = u_ViewProj * u_World * a_Position;
    }
";
const MESH_FS: &'static [u8] = b"
    #version 150 core
    in vec2 v_TexCoord; //TODO
    uniform vec4 u_Color;
    void main() {
        gl_FragColor = u_Color;
    }
";

const SPRITE_VS: &'static [u8] = b"
    #version 150 core
    in vec4 a_Position;
    in vec2 a_TexCoord;
    out vec2 v_TexCoord;
    uniform mat4 u_ViewProj;
    uniform mat4 u_World;
    void main() {
        v_TexCoord = a_TexCoord;
        gl_Position = u_ViewProj * u_World * a_Position;
    }
";
const SPRITE_FS: &'static [u8] = b"
    #version 150 core
    in vec2 v_TexCoord;
    uniform sampler2D t_Map;
    void main() {
        gl_FragColor = texture(t_Map, v_TexCoord);
    }
";

fn color_to_f32(c: Color) -> [f32; 4] {
    [((c>>16)&0xFF) as f32 / 255.0,
     ((c>>8) &0xFF) as f32 / 255.0,
     (c&0xFF) as f32 / 255.0,
     1.0]
}

//TODO: private fields?
#[derive(Clone)]
pub struct GpuData {
    pub slice: gfx::Slice<back::Resources>,
    pub vertices: gfx::handle::Buffer<back::Resources, Vertex>,
}

pub struct Renderer {
    device: back::Device,
    encoder: gfx::Encoder<back::Resources, back::CommandBuffer>,
    out_color: gfx::handle::RenderTargetView<back::Resources, ColorFormat>,
    out_depth: gfx::handle::DepthStencilView<back::Resources, DepthFormat>,
    pso_line_basic: gfx::PipelineState<back::Resources, pipe::Meta>,
    pso_mesh_basic: gfx::PipelineState<back::Resources, pipe::Meta>,
    pso_sprite: gfx::PipelineState<back::Resources, pipe::Meta>,
    map_default: Texture,
    size: (u32, u32),
    #[cfg(feature = "opengl")]
    window: glutin::Window,
}

impl Renderer {
    #[cfg(feature = "opengl")]
    pub fn new(builder: glutin::WindowBuilder, event_loop: &glutin::EventsLoop)
               -> (Self, Factory) {
        let (window, device, mut gl_factory, color, depth) =
            gfx_window_glutin::init(builder, event_loop);
        let prog_line = gl_factory.link_program(LINE_VS, LINE_FS).unwrap();
        let prog_mesh = gl_factory.link_program(MESH_VS, MESH_FS).unwrap();
        let prog_sprite = gl_factory.link_program(SPRITE_VS, SPRITE_FS).unwrap();
        let rast_fill = gfx::state::Rasterizer::new_fill().with_cull_back();
        let (_, srv_white) = gl_factory.create_texture_immutable::<gfx::format::Rgba8>(
            gfx::texture::Kind::D2(1, 1, gfx::texture::AaMode::Single), &[&[[0xFF; 4]]]
            ).unwrap();
        let sampler = gl_factory.create_sampler_linear();
        let renderer = Renderer {
            device: device,
            encoder: gl_factory.create_command_buffer().into(),
            out_color: color,
            out_depth: depth,
            pso_line_basic: gl_factory.create_pipeline_from_program(&prog_line,
                gfx::Primitive::LineStrip, rast_fill, pipe::new()
                ).unwrap(),
            pso_mesh_basic: gl_factory.create_pipeline_from_program(&prog_mesh,
                gfx::Primitive::TriangleList, rast_fill, pipe::new()
                ).unwrap(),
            pso_sprite: gl_factory.create_pipeline_from_program(&prog_sprite,
                gfx::Primitive::TriangleStrip, rast_fill, pipe::Init {
                    out_color: ("Target0", gfx::state::MASK_ALL, gfx::preset::blend::ALPHA),
                    .. pipe::new()
                }).unwrap(),
            map_default: Texture::new(srv_white, sampler),
            size: window.get_inner_size_pixels().unwrap(),
            window: window,
        };
        let factory = Factory::new(gl_factory);
        (renderer, factory)
    }

    pub fn resize(&mut self) {
        self.size = self.window.get_inner_size_pixels().unwrap();
        gfx_window_glutin::update_views(&self.window, &mut self.out_color, &mut self.out_depth);
    }

    pub fn get_aspect(&self) -> f32 {
        self.size.0 as f32 / self.size.1 as f32
    }

    pub fn map_to_ndc(&self, x: i32, y: i32) -> (f32, f32) {
        (2.0 * x as f32 / self.size.0 as f32 - 1.0,
         1.0 - 2.0 * y as f32 / self.size.1 as f32)
    }

    pub fn render<C: Camera>(&mut self, scene: &Scene, cam: &C) {
        self.device.cleanup();
        self.encoder.clear(&self.out_color, [0.0, 0.0, 0.0, 1.0]);
        self.encoder.clear_depth(&self.out_depth, 1.0);

        let mx_vp = cam.to_view_proj();
        for visual in &scene.visuals {
            let (pso, color, map) = match visual.material {
                Material::LineBasic { color } => (&self.pso_line_basic, color, None),
                Material::MeshBasic { color } => (&self.pso_mesh_basic, color, None),
                Material::Sprite { ref map } => (&self.pso_sprite, !0, Some(map)),
            };
            let mx_world = cgmath::Matrix4::from(scene.nodes[&visual.node].world);
            let data = pipe::Data {
                vbuf: visual.gpu_data.vertices.clone(),
                mx_vp: mx_vp.into(),
                mx_world: mx_world.into(),
                color: color_to_f32(color),
                tex_map: map.unwrap_or(&self.map_default).to_param(),
                out_color: self.out_color.clone(),
            };
            self.encoder.draw(&visual.gpu_data.slice, pso, &data);
        }

        self.encoder.flush(&mut self.device);
        self.window.swap_buffers().unwrap();
    }
}
<commit_msg>Depth test enabled<commit_after>use cgmath;
use gfx;
use gfx::traits::{Device, Factory as Factory_, FactoryExt};
#[cfg(feature = "opengl")]
use gfx_device_gl as back;
#[cfg(feature = "opengl")]
use gfx_window_glutin;
#[cfg(feature = "opengl")]
use glutin;

pub use self::back::Factory as BackendFactory;
pub use self::back::Resources as BackendResources;
use camera::Camera;
use factory::{Factory, Texture};
use scene::{Color, Material};
use {Scene};

pub type ColorFormat = gfx::format::Srgba8;
pub type DepthFormat = gfx::format::DepthStencil;

gfx_vertex_struct!(Vertex {
    pos: [f32; 4] = "a_Position",
    uv: [f32; 2] = "a_TexCoord",
    normal: [gfx::format::I8Norm; 4] = "a_Normal",
});

gfx_pipeline!(pipe {
    vbuf: gfx::VertexBuffer<Vertex> = (),
    mx_vp: gfx::Global<[[f32; 4]; 4]> = "u_ViewProj",
    mx_world: gfx::Global<[[f32; 4]; 4]> = "u_World",
    color: gfx::Global<[f32; 4]> = "u_Color",
    tex_map: gfx::TextureSampler<[f32; 4]> = "t_Map",
    out_color: gfx::BlendTarget<ColorFormat> =
        ("Target0", gfx::state::MASK_ALL, gfx::preset::blend::REPLACE),
    out_depth: gfx::DepthTarget<DepthFormat> =
        gfx::preset::depth::LESS_EQUAL_WRITE,
});

const LINE_VS: &'static [u8] = b"
    #version 150 core
    in vec4 a_Position;
    uniform mat4 u_ViewProj;
    uniform mat4 u_World;
    void main() {
        gl_Position = u_ViewProj * u_World * a_Position;
    }
";
const LINE_FS: &'static [u8] = b"
    #version 150 core
    uniform vec4 u_Color;
    void main() {
        gl_FragColor = u_Color;
    }
";

const MESH_VS: &'static [u8] = b"
    #version 150 core
    in vec4 a_Position;
    in vec2 a_TexCoord;
    out vec2 v_TexCoord;
    uniform mat4 u_ViewProj;
    uniform mat4 u_World;
    void main() {
        gl_Position = u_ViewProj * u_World * a_Position;
    }
";
const MESH_FS: &'static [u8] = b"
    #version 150 core
    in vec2 v_TexCoord; //TODO
    uniform vec4 u_Color;
    void main() {
        gl_FragColor = u_Color;
    }
";

const SPRITE_VS: &'static [u8] = b"
    #version 150 core
    in vec4 a_Position;
    in vec2 a_TexCoord;
    out vec2 v_TexCoord;
    uniform mat4 u_ViewProj;
    uniform mat4 u_World;
    void main() {
        v_TexCoord = a_TexCoord;
        gl_Position = u_ViewProj * u_World * a_Position;
    }
";
const SPRITE_FS: &'static [u8] = b"
    #version 150 core
    in vec2 v_TexCoord;
    uniform sampler2D t_Map;
    void main() {
        gl_FragColor = texture(t_Map, v_TexCoord);
    }
";

fn color_to_f32(c: Color) -> [f32; 4] {
    [((c>>16)&0xFF) as f32 / 255.0,
     ((c>>8) &0xFF) as f32 / 255.0,
     (c&0xFF) as f32 / 255.0,
     1.0]
}

//TODO: private fields?
#[derive(Clone)]
pub struct GpuData {
    pub slice: gfx::Slice<back::Resources>,
    pub vertices: gfx::handle::Buffer<back::Resources, Vertex>,
}

pub struct Renderer {
    device: back::Device,
    encoder: gfx::Encoder<back::Resources, back::CommandBuffer>,
    out_color: gfx::handle::RenderTargetView<back::Resources, ColorFormat>,
    out_depth: gfx::handle::DepthStencilView<back::Resources, DepthFormat>,
    pso_line_basic: gfx::PipelineState<back::Resources, pipe::Meta>,
    pso_mesh_basic: gfx::PipelineState<back::Resources, pipe::Meta>,
    pso_sprite: gfx::PipelineState<back::Resources, pipe::Meta>,
    map_default: Texture,
    size: (u32, u32),
    #[cfg(feature = "opengl")]
    window: glutin::Window,
}

impl Renderer {
    #[cfg(feature = "opengl")]
    pub fn new(builder: glutin::WindowBuilder, event_loop: &glutin::EventsLoop)
               -> (Self, Factory) {
        let (window, device, mut gl_factory, color, depth) =
            gfx_window_glutin::init(builder, event_loop);
        let prog_line = gl_factory.link_program(LINE_VS, LINE_FS).unwrap();
        let prog_mesh = gl_factory.link_program(MESH_VS, MESH_FS).unwrap();
        let prog_sprite = gl_factory.link_program(SPRITE_VS, SPRITE_FS).unwrap();
        let rast_fill = gfx::state::Rasterizer::new_fill().with_cull_back();
        let (_, srv_white) = gl_factory.create_texture_immutable::<gfx::format::Rgba8>(
            gfx::texture::Kind::D2(1, 1, gfx::texture::AaMode::Single), &[&[[0xFF; 4]]]
            ).unwrap();
        let sampler = gl_factory.create_sampler_linear();
        let renderer = Renderer {
            device: device,
            encoder: gl_factory.create_command_buffer().into(),
            out_color: color,
            out_depth: depth,
            pso_line_basic: gl_factory.create_pipeline_from_program(&prog_line,
                gfx::Primitive::LineStrip, rast_fill, pipe::new()
                ).unwrap(),
            pso_mesh_basic: gl_factory.create_pipeline_from_program(&prog_mesh,
                gfx::Primitive::TriangleList, rast_fill, pipe::new()
                ).unwrap(),
            pso_sprite: gl_factory.create_pipeline_from_program(&prog_sprite,
                gfx::Primitive::TriangleStrip, rast_fill, pipe::Init {
                    out_color: ("Target0", gfx::state::MASK_ALL, gfx::preset::blend::ALPHA),
                    .. pipe::new()
                }).unwrap(),
            map_default: Texture::new(srv_white, sampler),
            size: window.get_inner_size_pixels().unwrap(),
            window: window,
        };
        let factory = Factory::new(gl_factory);
        (renderer, factory)
    }

    pub fn resize(&mut self) {
        self.size = self.window.get_inner_size_pixels().unwrap();
        gfx_window_glutin::update_views(&self.window, &mut self.out_color, &mut self.out_depth);
    }

    pub fn get_aspect(&self) -> f32 {
        self.size.0 as f32 / self.size.1 as f32
    }

    pub fn map_to_ndc(&self, x: i32, y: i32) -> (f32, f32) {
        (2.0 * x as f32 / self.size.0 as f32 - 1.0,
         1.0 - 2.0 * y as f32 / self.size.1 as f32)
    }

    pub fn render<C: Camera>(&mut self, scene: &Scene, cam: &C) {
        self.device.cleanup();
        self.encoder.clear(&self.out_color, [0.0, 0.0, 0.0, 1.0]);
        self.encoder.clear_depth(&self.out_depth, 1.0);

        let mx_vp = cam.to_view_proj();
        for visual in &scene.visuals {
            let (pso, color, map) = match visual.material {
                Material::LineBasic { color } => (&self.pso_line_basic, color, None),
                Material::MeshBasic { color } => (&self.pso_mesh_basic, color, None),
                Material::Sprite { ref map } => (&self.pso_sprite, !0, Some(map)),
            };
            let mx_world = cgmath::Matrix4::from(scene.nodes[&visual.node].world);
            let data = pipe::Data {
                vbuf: visual.gpu_data.vertices.clone(),
                mx_vp: mx_vp.into(),
                mx_world: mx_world.into(),
                color: color_to_f32(color),
                tex_map: map.unwrap_or(&self.map_default).to_param(),
                out_color: self.out_color.clone(),
                out_depth: self.out_depth.clone(),
            };
            self.encoder.draw(&visual.gpu_data.slice, pso, &data);
        }

        self.encoder.flush(&mut self.device);
        self.window.swap_buffers().unwrap();
    }
}
<|endoftext|>


<commit_before>//! Basic rendering engine

use gl;
use hgl;
use png;
use glfw;
use std::rc::Rc;
use std::mem::size_of;
use std::cell::RefCell;
use gl::types::{GLfloat, GLint, GLuint};
use collections::{HashMap, DList, Deque};
use hgl::{Vao, Vbo, Program, Shader, Ebo};

pub struct Engine {
    // FIXME: implement remove or modify_iter on DList, rather than having an
    // unbounded list of Option.
    sprites: DList<Option<Rc<RefCell<Sprite>>>>,
    textures: HashMap<&'static str, Rc<Tex>>,
    /// Width of the render surface (used to normalize sprite coordinates)
    width: GLint,
    /// Height of the render surface (used to normalize sprite coordinates)
    height: GLint,
    vao: hgl::Vao,
    vbo: hgl::Vbo,
    ebo: hgl::Ebo,
}

impl Engine {
    pub fn new(width: GLint, height: GLint) -> Engine {
        gl::load_with(glfw::get_proc_address);
        gl::Viewport(0, 0, width, height);
        gl::ClearColor(1.0, 1.0, 1.0, 0.0);
        gl::Enable(gl::BLEND);
        gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);

        let vao = Vao::new();
        vao.bind();

        let program = Program::link(&[Shader::from_file("vertex.glsl", hgl::program::VertexShader).unwrap().unwrap(),
                                     Shader::from_file("fragment.glsl", hgl::program::FragmentShader).unwrap().unwrap()
                                    ]).unwrap();
        program.bind_frag(0, "out_color");
        program.bind();

        gl::Uniform1i(program.uniform("sprite"), 0);
        gl::Uniform2f(program.uniform("windowsize"), width as GLfloat, height as GLfloat);

        let vbo = Vbo::new();
        vbo.bind();
        let ebo = Ebo::new();
        ebo.bind();

        vao.enable_int_attrib(&program, "position", gl::INT, 2, 4*size_of::<GLint>() as i32, 0);
        vao.enable_int_attrib(&program, "texcoord", gl::INT, 2, 4*size_of::<GLint>() as i32, 2 * size_of::<i32>());

        Engine {
            sprites: DList::new(),
            textures: HashMap::new(),
            width: width,
            height: height,
            vao: vao,
            vbo: vbo,
            ebo: ebo,
        }
    }

    pub fn load_texture(&mut self, name: &'static str, path: &str) -> Rc<Tex> {
        let tex = Rc::new(Tex::from_png(path));
        self.textures.insert(name, tex.clone());
        tex
    }

    pub fn add_sprite(&mut self, s: Sprite) -> Rc<RefCell<Sprite>> {
        let s = Rc::new(RefCell::new(s));
        self.sprites.push_back(Some(s.clone()));
        s
    }

    pub fn remove_sprite(&mut self, spr: Rc<RefCell<Sprite>>) {
        let mut rem = false;
        for sprite in self.sprites.mut_iter() {
            match sprite {
                &Some(ref s) => {
                    if (s.borrow() as *RefCell<Sprite> as int) == (spr.borrow() as *RefCell<Sprite> as int) {
                        rem = true;
                    }
                },
                &None => ()
            }
            if rem {
                sprite.take_unwrap();
                break;
            }
        }
    }

    pub fn render(&self) {
        // this is really awful, and probably going to be slow?
        // one way to do it differently is to take an Iterator<Rc<Sprite>>,
        // and create the vbo/ebo from that. Then, the Game would chain
        // together its list of sprites (by enemy type, projectile type, etc),
        // to avoid swapping out different textures all the time.
        //
        // probably not going to matter though.
        let mut data = ~[];
        let mut indices = ~[];
        let mut base = 0 as GLuint;

        gl::Clear(gl::COLOR_BUFFER_BIT);

        for sprite in self.sprites.iter().filter_map(|x| x.as_ref()) {
            let &Sprite { x, y, height, width, .. } = (*sprite).borrow().borrow().get();

            // points of the rectangle that makes up this sprite, ccw
            let sdata: &[GLint] = &[
                 x, y, 0, 0,
                 x + width, y, 1, 0,
                 x + width, y + height, 1, 1,
                 x, y + height, 0, 1
            ];
            data.extend(&mut sdata.iter().map(|&x| x));

            let new_indices = &[base, base+1, base+2,
                                base+2, base+3, base];
            indices.extend(&mut new_indices.iter().map(|&x| x));
            base += 4;
        }

        self.vbo.load_data(data, hgl::buffer::DynamicDraw);
        self.ebo.load_data(indices, hgl::buffer::DynamicDraw);

        let mut first = true;
        for (idx, sprite) in self.sprites.iter().filter_map(|x| x.as_ref()).enumerate() {
            let sprite = (*sprite).borrow().borrow();
            let tex = sprite.get().texture.borrow();

            tex.texture.activate(0);

            // eugh
            let start = if first { first = false; 0 } else { (idx * 6) };

            debug!("Drawing {} indices starting at {}", 6, start);
            self.vao.draw_elements(hgl::Triangles, start as GLint * 4, 6);
        }
    }
}

/// A sprite; a textured rectangle. The origin (x, y) is the bottom left. The
/// top right is (x + width, y + height).
pub struct Sprite {
    x: GLint,
    y: GLint,
    height: GLint,
    width: GLint,
    texture: Rc<Tex>
}

impl Sprite {
    pub fn new(x: GLint, y: GLint, height: GLint, width: GLint, texture: Rc<Tex>) -> Sprite {
        Sprite { x: x, y: y, height: height, width: width, texture: texture }
    }
}

pub struct Tex {
    texture: hgl::Texture
}

impl Tex {
    pub fn from_png(p: &str) -> Tex {
        let path = Path::new(p);
        let img = match png::load_png(&path) {
            Ok(i) => i,
            Err(s) => fail!("Could not load png: {}", s)
        };

        let fmt = match img.color_type {
            png::RGBA8 => { info!("loaded rgba8 png file {}", p); hgl::texture::pixel::RGBA },
            t => fail!("unsupported color type {:?} in png", t),
        };

        let ii = hgl::texture::ImageInfo::new()
            .pixel_format(fmt).pixel_type(hgl::texture::pixel::UNSIGNED_BYTE)
            .width(img.width as GLint).height(img.height as GLint);

        let gltex = hgl::Texture::new(hgl::texture::Texture2D, ii, img.pixels.as_slice().as_ptr());
        gltex.gen_mipmaps();
        gltex.filter(hgl::texture::NearestMipmapNearest);
        gltex.wrap(hgl::texture::ClampToBorder);

        Tex { texture: gltex }
    }
}
<commit_msg>Right-way-around textures!<commit_after>//! Basic rendering engine

use gl;
use hgl;
use png;
use glfw;
use std::rc::Rc;
use std::mem::size_of;
use std::cell::RefCell;
use gl::types::{GLfloat, GLint, GLuint};
use collections::{HashMap, DList, Deque};
use hgl::{Vao, Vbo, Program, Shader, Ebo};

pub struct Engine {
    // FIXME: implement remove or modify_iter on DList, rather than having an
    // unbounded list of Option.
    sprites: DList<Option<Rc<RefCell<Sprite>>>>,
    textures: HashMap<&'static str, Rc<Tex>>,
    /// Width of the render surface (used to normalize sprite coordinates)
    width: GLint,
    /// Height of the render surface (used to normalize sprite coordinates)
    height: GLint,
    vao: hgl::Vao,
    vbo: hgl::Vbo,
    ebo: hgl::Ebo,
}

impl Engine {
    pub fn new(width: GLint, height: GLint) -> Engine {
        gl::load_with(glfw::get_proc_address);
        gl::Viewport(0, 0, width, height);
        gl::ClearColor(1.0, 1.0, 1.0, 0.0);
        gl::Enable(gl::BLEND);
        gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);

        let vao = Vao::new();
        vao.bind();

        let program = Program::link(&[Shader::from_file("vertex.glsl", hgl::program::VertexShader).unwrap().unwrap(),
                                     Shader::from_file("fragment.glsl", hgl::program::FragmentShader).unwrap().unwrap()
                                    ]).unwrap();
        program.bind_frag(0, "out_color");
        program.bind();

        gl::Uniform1i(program.uniform("sprite"), 0);
        gl::Uniform2f(program.uniform("windowsize"), width as GLfloat, height as GLfloat);

        let vbo = Vbo::new();
        vbo.bind();
        let ebo = Ebo::new();
        ebo.bind();

        vao.enable_int_attrib(&program, "position", gl::INT, 2, 4*size_of::<GLint>() as i32, 0);
        vao.enable_int_attrib(&program, "texcoord", gl::INT, 2, 4*size_of::<GLint>() as i32, 2 * size_of::<i32>());

        Engine {
            sprites: DList::new(),
            textures: HashMap::new(),
            width: width,
            height: height,
            vao: vao,
            vbo: vbo,
            ebo: ebo,
        }
    }

    pub fn load_texture(&mut self, name: &'static str, path: &str) -> Rc<Tex> {
        let tex = Rc::new(Tex::from_png(path));
        self.textures.insert(name, tex.clone());
        tex
    }

    pub fn add_sprite(&mut self, s: Sprite) -> Rc<RefCell<Sprite>> {
        let s = Rc::new(RefCell::new(s));
        self.sprites.push_back(Some(s.clone()));
        s
    }

    pub fn remove_sprite(&mut self, spr: Rc<RefCell<Sprite>>) {
        let mut rem = false;
        for sprite in self.sprites.mut_iter() {
            match sprite {
                &Some(ref s) => {
                    if (s.borrow() as *RefCell<Sprite> as int) == (spr.borrow() as *RefCell<Sprite> as int) {
                        rem = true;
                    }
                },
                &None => ()
            }
            if rem {
                sprite.take_unwrap();
                break;
            }
        }
    }

    pub fn render(&self) {
        // this is really awful, and probably going to be slow?
        // one way to do it differently is to take an Iterator<Rc<Sprite>>,
        // and create the vbo/ebo from that. Then, the Game would chain
        // together its list of sprites (by enemy type, projectile type, etc),
        // to avoid swapping out different textures all the time.
        //
        // probably not going to matter though.
        let mut data = ~[];
        let mut indices = ~[];
        let mut base = 0 as GLuint;

        gl::Clear(gl::COLOR_BUFFER_BIT);

        for sprite in self.sprites.iter().filter_map(|x| x.as_ref()) {
            let &Sprite { x, y, height, width, .. } = (*sprite).borrow().borrow().get();

            // points of the rectangle that makes up this sprite, ccw
            let sdata: &[GLint] = &[
                 x, y, 0, 1,
                 x + width, y, 1, 1,
                 x + width, y + height, 1, 0,
                 x, y + height, 0, 0
            ];
            data.extend(&mut sdata.iter().map(|&x| x));

            let new_indices = &[base, base+1, base+2,
                                base+2, base+3, base];
            indices.extend(&mut new_indices.iter().map(|&x| x));
            base += 4;
        }

        self.vbo.load_data(data, hgl::buffer::DynamicDraw);
        self.ebo.load_data(indices, hgl::buffer::DynamicDraw);

        let mut first = true;
        for (idx, sprite) in self.sprites.iter().filter_map(|x| x.as_ref()).enumerate() {
            let sprite = (*sprite).borrow().borrow();
            let tex = sprite.get().texture.borrow();

            tex.texture.activate(0);

            // eugh
            let start = if first { first = false; 0 } else { (idx * 6) };

            debug!("Drawing {} indices starting at {}", 6, start);
            self.vao.draw_elements(hgl::Triangles, start as GLint * 4, 6);
        }
    }
}

/// A sprite; a textured rectangle. The origin (x, y) is the bottom left. The
/// top right is (x + width, y + height).
pub struct Sprite {
    x: GLint,
    y: GLint,
    height: GLint,
    width: GLint,
    texture: Rc<Tex>
}

impl Sprite {
    pub fn new(x: GLint, y: GLint, height: GLint, width: GLint, texture: Rc<Tex>) -> Sprite {
        Sprite { x: x, y: y, height: height, width: width, texture: texture }
    }
}

pub struct Tex {
    texture: hgl::Texture
}

impl Tex {
    pub fn from_png(p: &str) -> Tex {
        let path = Path::new(p);
        let img = match png::load_png(&path) {
            Ok(i) => i,
            Err(s) => fail!("Could not load png: {}", s)
        };

        let fmt = match img.color_type {
            png::RGBA8 => { info!("loaded rgba8 png file {}", p); hgl::texture::pixel::RGBA },
            t => fail!("unsupported color type {:?} in png", t),
        };

        let ii = hgl::texture::ImageInfo::new()
            .pixel_format(fmt).pixel_type(hgl::texture::pixel::UNSIGNED_BYTE)
            .width(img.width as GLint).height(img.height as GLint);

        let gltex = hgl::Texture::new(hgl::texture::Texture2D, ii, img.pixels.as_slice().as_ptr());
        gltex.gen_mipmaps();
        gltex.filter(hgl::texture::NearestMipmapNearest);
        gltex.wrap(hgl::texture::Repeat);

        Tex { texture: gltex }
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2021 Google LLC All Rights Reserved.
 *
 * 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.
 */

use std::fs::File;
use std::sync::Arc;

use clap::App;
use slog::{info, o, Logger};
use tokio::signal;
use tokio::sync::watch;

use crate::config::Config;
use crate::extensions::{default_registry, FilterFactory, FilterRegistry};
use crate::proxy::{logger, Builder};

const VERSION: &str = env!("CARGO_PKG_VERSION");

#[cfg(debug_assertions)]
fn version() -> String {
    format!("{}+debug", VERSION)
}

#[cfg(not(debug_assertions))]
fn version() -> String {
    VERSION.into()
}

/// Wraps an error message returned by [`run`].
#[derive(Debug)]
pub struct Error(String);

/// Start and run a proxy. Any passed in [`FilterFactory`] are included
/// alongside the default filter factories..
pub async fn run(filter_factories: Vec<Box<dyn FilterFactory>>) -> Result<(), Error> {
    let version = version();
    let base_logger = logger();
    let log = base_logger.new(o!("source" => "run"));

    let matches = App::new("Quilkin Proxy")
        .version(version.as_str())
        .about("Quilkin is a non-transparent UDP proxy specifically designed for use with large scale multiplayer dedicated game servers")
        .arg(clap::Arg::with_name("filename")
            .short("f")
            .long("filename")
            .value_name("FILE")
            .help("The yaml configuration file")
            .required(true)
            .takes_value(true))
        .get_matches();

    let filename = matches
        .value_of("filename")
        .ok_or_else(|| Error("missing argument `filename`".into()))?;
    info!(log, "Starting Quilkin"; "version" => version);

    let config = Arc::new(
        Config::from_reader(File::open(filename).map_err(|err| Error(format!("{}", err)))?)
            .map_err(|err| Error(format!("{}", err)))?,
    );
    let server = Builder::from(config)
        .with_log(base_logger)
        .with_filter_registry(create_filter_registry(&log, filter_factories))
        .validate()
        .map_err(|err| Error(format!("{:?}", err)))?
        .build();

    let (shutdown_tx, shutdown_rx) = watch::channel::<()>(());
    tokio::spawn(async move {
        // Don't unwrap in order to ensure that we execute
        // any subsequent shutdown tasks.
        signal::ctrl_c().await.ok();
        shutdown_tx.send(()).ok();
    });

    match server.run(shutdown_rx).await {
        Ok(()) => {
            info!(log, "Shutting down");
            Ok(())
        }
        Err(err) => {
            info!(log, "Shutting down with error"; "error" => %err);
            Err(Error(format!("{:?}", err)))
        }
    }
}

fn create_filter_registry(
    log: &Logger,
    additional_filter_factories: Vec<Box<dyn FilterFactory>>,
) -> FilterRegistry {
    let mut registry = default_registry(log);
    registry.insert_all(additional_filter_factories);
    registry
}
<commit_msg>Use clap macro for crate name and description (#257)<commit_after>/*
 * Copyright 2021 Google LLC All Rights Reserved.
 *
 * 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.
 */

use std::fs::File;
use std::sync::Arc;

use clap::App;
use slog::{info, o, Logger};
use tokio::signal;
use tokio::sync::watch;

use crate::config::Config;
use crate::extensions::{default_registry, FilterFactory, FilterRegistry};
use crate::proxy::{logger, Builder};

const VERSION: &str = env!("CARGO_PKG_VERSION");

#[cfg(debug_assertions)]
fn version() -> String {
    format!("{}+debug", VERSION)
}

#[cfg(not(debug_assertions))]
fn version() -> String {
    VERSION.into()
}

/// Wraps an error message returned by [`run`].
#[derive(Debug)]
pub struct Error(String);

/// Start and run a proxy. Any passed in [`FilterFactory`] are included
/// alongside the default filter factories..
pub async fn run(filter_factories: Vec<Box<dyn FilterFactory>>) -> Result<(), Error> {
    let version = version();
    let base_logger = logger();
    let log = base_logger.new(o!("source" => "run"));

    let matches = App::new(clap::crate_name!())
        .version(version.as_str())
        .about(clap::crate_description!())
        .arg(
            clap::Arg::with_name("filename")
                .short("f")
                .long("filename")
                .value_name("FILE")
                .help("The yaml configuration file")
                .required(true)
                .takes_value(true),
        )
        .get_matches();

    let filename = matches
        .value_of("filename")
        .ok_or_else(|| Error("missing argument `filename`".into()))?;
    info!(log, "Starting Quilkin"; "version" => version);

    let config = Arc::new(
        Config::from_reader(File::open(filename).map_err(|err| Error(format!("{}", err)))?)
            .map_err(|err| Error(format!("{}", err)))?,
    );
    let server = Builder::from(config)
        .with_log(base_logger)
        .with_filter_registry(create_filter_registry(&log, filter_factories))
        .validate()
        .map_err(|err| Error(format!("{:?}", err)))?
        .build();

    let (shutdown_tx, shutdown_rx) = watch::channel::<()>(());
    tokio::spawn(async move {
        // Don't unwrap in order to ensure that we execute
        // any subsequent shutdown tasks.
        signal::ctrl_c().await.ok();
        shutdown_tx.send(()).ok();
    });

    match server.run(shutdown_rx).await {
        Ok(()) => {
            info!(log, "Shutting down");
            Ok(())
        }
        Err(err) => {
            info!(log, "Shutting down with error"; "error" => %err);
            Err(Error(format!("{:?}", err)))
        }
    }
}

fn create_filter_registry(
    log: &Logger,
    additional_filter_factories: Vec<Box<dyn FilterFactory>>,
) -> FilterRegistry {
    let mut registry = default_registry(log);
    registry.insert_all(additional_filter_factories);
    registry
}
<|endoftext|>


<commit_before>//! Running of programs.

use parser::Instruction;
use std;

///
/// A runner of programs.
///
#[derive(Default)]
pub struct Runner {
    /// Program to be run.
    prog: Vec<Instruction>,
    /// A potentially infinite number of data cells.
    data: Vec<u8>,
    /// Instruction pointer (index).
    ip: usize,
    /// Data-cell pointer (index).
    dp: usize,
}

impl Runner {
    ///
    /// Constructs a new runner.
    ///
    pub fn new() -> Self {
        Runner::default()
    }

    ///
    /// Runs the given program.
    ///
    /// # Arguments
    ///
    /// * `prog` - Program to be run.
    /// * `input` - Input for the program.
    /// * `output` - Output for the program.
    ///
    pub fn run(&mut self,
               prog: Vec<Instruction>,
               mut input: &mut std::io::Read,
               mut output: &mut std::io::Write)
               -> Result<(), String> {
        self.prog = prog;
        self.data.clear();
        self.ip = 0;
        self.dp = 0;

        while !self.is_prog_end() {
            try!(self.run_instruction(&mut input, &mut output));
        }

        Ok(())
    }

    fn is_prog_end(&self) -> bool {
        self.ip >= self.prog.len()
    }

    fn run_instruction(&mut self,
                       mut input: &mut std::io::Read,
                       mut output: &mut std::io::Write)
                       -> Result<(), String> {
        match self.prog[self.ip] {
            Instruction::Next => {
                self.dp += 1;
            }
            Instruction::Prev => {
                try!(self.ensure_can_decrement_dp_by_one());
                self.dp -= 1;
            }
            Instruction::Inc => {
                let value = self.load_value();
                self.store_value(value.wrapping_add(1));
            }
            Instruction::Dec => {
                let value = self.load_value();
                self.store_value(value.wrapping_sub(1));
            }
            Instruction::Read => {
                let value = try!(self.read_value(&mut input));
                self.store_value(value);
            }
            Instruction::Write => {
                let value = self.load_value();
                try!(self.write_value(&mut output, value));
            }
            Instruction::LoopStart(loop_end_ip) => {
                if self.is_current_cell_zero() {
                    self.ip = loop_end_ip;
                }
            }
            Instruction::LoopEnd(loop_start_ip) => {
                if !self.is_current_cell_zero() {
                    self.ip = loop_start_ip;
                }
            }
        }
        self.ip += 1;
        Ok(())
    }

    fn load_value(&mut self) -> u8 {
        self.ensure_current_cell_is_accessible();
        self.data[self.dp]
    }

    fn store_value(&mut self, value: u8) {
        self.ensure_current_cell_is_accessible();
        self.data[self.dp] = value;
    }

    fn read_value(&self, mut input: &mut std::io::Read) -> Result<u8, String> {
        let mut buf = [0u8];
        match input.read_exact(&mut buf) {
            Err(err) => match err.kind() {
                // There are multiple ways of dealing with EOF. We have chosen
                // to return 0.
                std::io::ErrorKind::UnexpectedEof => Ok(0),
                _ => Err(format!("reading of a value failed (reason: {:?})", err.kind())),
            },
            Ok(_) => Ok(buf[0]),
        }
    }

    fn write_value(&self, mut output: &mut std::io::Write, value: u8) -> Result<(), String> {
        match output.write(&[value]) {
            Err(err) => Err(
                format!("writing of a value failed (reason: {})", err)
            ),
            Ok(_) => Ok(()),
        }
    }

    fn is_current_cell_zero(&mut self) -> bool {
        self.load_value() == 0
    }

    fn ensure_current_cell_is_accessible(&mut self) {
        let cell_count = self.data.len();
        if self.dp >= cell_count {
            for _ in 0..(self.dp - cell_count + 1) {
                self.data.push(0);
            }
        }
    }

    fn ensure_can_decrement_dp_by_one(&self) -> Result<(), String> {
        match self.dp {
            0 => Err("cannot decrement the data pointer because it is 0".to_string()),
            _ => Ok(()),
        }
    }
}

///
/// Runs the given program.
///
/// # Arguments
///
/// * `prog` - Program to be run.
/// * `input` - Input for the program.
/// * `output` - Output for the program.
///
pub fn run(prog: Vec<Instruction>,
           mut input: &mut std::io::Read,
           mut output: &mut std::io::Write)
           -> Result<(), String> {
    let mut runner = Runner::new();
    runner.run(prog, &mut input, &mut output)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std;
    use parser::Instruction;

    fn run_and_get_output(prog: Vec<Instruction>,
                          input: &[u8])
                          -> Result<Vec<u8>, String> {
        let mut output = Vec::new();
        match {
            let mut input_reader = std::io::BufReader::new(input.as_ref());
            let mut output_writer = std::io::BufWriter::new(&mut output);
            run(prog, &mut input_reader, &mut output_writer)
        } {
            Ok(_) => Ok(output),
            Err(err) => Err(err),
        }
    }

    fn assert_run_writes_correct_output(prog: Vec<Instruction>,
                                        input: &[u8],
                                        expected_output: &[u8]) {
        let output = run_and_get_output(prog, input).unwrap();
        assert_eq!(output, expected_output);
    }

    fn assert_run_returns_error(prog: Vec<Instruction>,
                         input: &[u8]) {
        let result = run_and_get_output(prog, input);
        assert!(result.is_err());
    }

    #[test]
    fn test_run_does_nothing_when_program_has_no_instructions() {
        assert_run_writes_correct_output(
            vec![],
            &[],
            &[]
        );
    }

    #[test]
    fn test_run_can_read_from_input_and_write_to_output() {
        assert_run_writes_correct_output(
            vec![
                Instruction::Read,
                Instruction::Write
            ],
            &[1],
            &[1]
        );
    }

    #[test]
    fn test_read_stores_zero_when_at_end_of_file() {
        assert_run_writes_correct_output(
            vec![
                Instruction::Read,
                Instruction::Write
            ],
            &[],
            &[0]
        );
    }

    #[test]
    fn test_run_can_increment_cell() {
        assert_run_writes_correct_output(
            vec![
                Instruction::Read,
                Instruction::Inc,
                Instruction::Write,
            ],
            &[1],
            &[2]
        );
    }

    #[test]
    fn test_run_can_decrement_cell() {
        assert_run_writes_correct_output(
            vec![
                Instruction::Read,
                Instruction::Dec,
                Instruction::Write,
            ],
            &[1],
            &[0]
        );
    }

    #[test]
    fn test_run_can_move_between_cells() {
        assert_run_writes_correct_output(
            vec![
                Instruction::Read,
                Instruction::Next,
                Instruction::Read,
                Instruction::Next,
                Instruction::Prev,
                Instruction::Write,
                Instruction::Prev,
                Instruction::Write,
            ],
            &[1, 2],
            &[2, 1]
        );
    }

    #[test]
    fn test_run_can_loop() {
        assert_run_writes_correct_output(
            vec![
                Instruction::Read,
                Instruction::LoopStart(6),
                Instruction::Dec,
                Instruction::Next,
                Instruction::Inc,
                Instruction::Prev,
                Instruction::LoopEnd(1),
                Instruction::Write,
                Instruction::Next,
                Instruction::Write
            ],
            &[2],
            &[0, 2]
        );
    }

    #[test]
    fn test_copies_input_to_output() {
        assert_run_writes_correct_output(
            vec![
                // Read the whole input:
                Instruction::Next,
                Instruction::Inc,
                Instruction::LoopStart(5),
                Instruction::Next,
                Instruction::Read,
                Instruction::LoopEnd(2),
                // Rewind to the beginning:
                Instruction::Prev,
                Instruction::LoopStart(9),
                Instruction::Prev,
                Instruction::LoopEnd(7),
                // Write the whole input:
                Instruction::Next,
                Instruction::Next,
                Instruction::LoopStart(15),
                Instruction::Write,
                Instruction::Next,
                Instruction::LoopEnd(12)
            ],
            &[4, 3, 1, 5],
            &[4, 3, 1, 5]
        );
    }

    #[test]
    fn test_returns_error_when_underflowing_data_pointer() {
        assert_run_returns_error(vec![Instruction::Prev], &[]);
    }
}
<commit_msg>Ensure that the data pointer can be incremented before incrementing it.<commit_after>//! Running of programs.

use parser::Instruction;
use std;

///
/// A runner of programs.
///
#[derive(Default)]
pub struct Runner {
    /// Program to be run.
    prog: Vec<Instruction>,
    /// A potentially infinite number of data cells.
    data: Vec<u8>,
    /// Instruction pointer (index).
    ip: usize,
    /// Data-cell pointer (index).
    dp: usize,
}

impl Runner {
    ///
    /// Constructs a new runner.
    ///
    pub fn new() -> Self {
        Runner::default()
    }

    ///
    /// Runs the given program.
    ///
    /// # Arguments
    ///
    /// * `prog` - Program to be run.
    /// * `input` - Input for the program.
    /// * `output` - Output for the program.
    ///
    pub fn run(&mut self,
               prog: Vec<Instruction>,
               mut input: &mut std::io::Read,
               mut output: &mut std::io::Write)
               -> Result<(), String> {
        self.prog = prog;
        self.data.clear();
        self.ip = 0;
        self.dp = 0;

        while !self.is_prog_end() {
            try!(self.run_instruction(&mut input, &mut output));
        }

        Ok(())
    }

    fn is_prog_end(&self) -> bool {
        self.ip >= self.prog.len()
    }

    fn run_instruction(&mut self,
                       mut input: &mut std::io::Read,
                       mut output: &mut std::io::Write)
                       -> Result<(), String> {
        match self.prog[self.ip] {
            Instruction::Next => {
                try!(self.ensure_can_increment_dp_by_one());
                self.dp += 1;
            }
            Instruction::Prev => {
                try!(self.ensure_can_decrement_dp_by_one());
                self.dp -= 1;
            }
            Instruction::Inc => {
                let value = self.load_value();
                self.store_value(value.wrapping_add(1));
            }
            Instruction::Dec => {
                let value = self.load_value();
                self.store_value(value.wrapping_sub(1));
            }
            Instruction::Read => {
                let value = try!(self.read_value(&mut input));
                self.store_value(value);
            }
            Instruction::Write => {
                let value = self.load_value();
                try!(self.write_value(&mut output, value));
            }
            Instruction::LoopStart(loop_end_ip) => {
                if self.is_current_cell_zero() {
                    self.ip = loop_end_ip;
                }
            }
            Instruction::LoopEnd(loop_start_ip) => {
                if !self.is_current_cell_zero() {
                    self.ip = loop_start_ip;
                }
            }
        }
        self.ip += 1;
        Ok(())
    }

    fn load_value(&mut self) -> u8 {
        self.ensure_current_cell_is_accessible();
        self.data[self.dp]
    }

    fn store_value(&mut self, value: u8) {
        self.ensure_current_cell_is_accessible();
        self.data[self.dp] = value;
    }

    fn read_value(&self, mut input: &mut std::io::Read) -> Result<u8, String> {
        let mut buf = [0u8];
        match input.read_exact(&mut buf) {
            Err(err) => match err.kind() {
                // There are multiple ways of dealing with EOF. We have chosen
                // to return 0.
                std::io::ErrorKind::UnexpectedEof => Ok(0),
                _ => Err(format!("reading of a value failed (reason: {:?})", err.kind())),
            },
            Ok(_) => Ok(buf[0]),
        }
    }

    fn write_value(&self, mut output: &mut std::io::Write, value: u8) -> Result<(), String> {
        match output.write(&[value]) {
            Err(err) => Err(
                format!("writing of a value failed (reason: {})", err)
            ),
            Ok(_) => Ok(()),
        }
    }

    fn is_current_cell_zero(&mut self) -> bool {
        self.load_value() == 0
    }

    fn ensure_current_cell_is_accessible(&mut self) {
        let cell_count = self.data.len();
        if self.dp >= cell_count {
            for _ in 0..(self.dp - cell_count + 1) {
                self.data.push(0);
            }
        }
    }

    fn ensure_can_increment_dp_by_one(&self) -> Result<(), String> {
        match self.dp {
            std::usize::MAX => Err(
                "cannot increment the data pointer because it is MAX".to_string()
            ),
            _ => Ok(()),
        }
    }

    fn ensure_can_decrement_dp_by_one(&self) -> Result<(), String> {
        match self.dp {
            0 => Err("cannot decrement the data pointer because it is 0".to_string()),
            _ => Ok(()),
        }
    }
}

///
/// Runs the given program.
///
/// # Arguments
///
/// * `prog` - Program to be run.
/// * `input` - Input for the program.
/// * `output` - Output for the program.
///
pub fn run(prog: Vec<Instruction>,
           mut input: &mut std::io::Read,
           mut output: &mut std::io::Write)
           -> Result<(), String> {
    let mut runner = Runner::new();
    runner.run(prog, &mut input, &mut output)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std;
    use parser::Instruction;

    fn run_and_get_output(prog: Vec<Instruction>,
                          input: &[u8])
                          -> Result<Vec<u8>, String> {
        let mut output = Vec::new();
        match {
            let mut input_reader = std::io::BufReader::new(input.as_ref());
            let mut output_writer = std::io::BufWriter::new(&mut output);
            run(prog, &mut input_reader, &mut output_writer)
        } {
            Ok(_) => Ok(output),
            Err(err) => Err(err),
        }
    }

    fn assert_run_writes_correct_output(prog: Vec<Instruction>,
                                        input: &[u8],
                                        expected_output: &[u8]) {
        let output = run_and_get_output(prog, input).unwrap();
        assert_eq!(output, expected_output);
    }

    fn assert_run_returns_error(prog: Vec<Instruction>,
                         input: &[u8]) {
        let result = run_and_get_output(prog, input);
        assert!(result.is_err());
    }

    #[test]
    fn test_run_does_nothing_when_program_has_no_instructions() {
        assert_run_writes_correct_output(
            vec![],
            &[],
            &[]
        );
    }

    #[test]
    fn test_run_can_read_from_input_and_write_to_output() {
        assert_run_writes_correct_output(
            vec![
                Instruction::Read,
                Instruction::Write
            ],
            &[1],
            &[1]
        );
    }

    #[test]
    fn test_read_stores_zero_when_at_end_of_file() {
        assert_run_writes_correct_output(
            vec![
                Instruction::Read,
                Instruction::Write
            ],
            &[],
            &[0]
        );
    }

    #[test]
    fn test_run_can_increment_cell() {
        assert_run_writes_correct_output(
            vec![
                Instruction::Read,
                Instruction::Inc,
                Instruction::Write,
            ],
            &[1],
            &[2]
        );
    }

    #[test]
    fn test_run_can_decrement_cell() {
        assert_run_writes_correct_output(
            vec![
                Instruction::Read,
                Instruction::Dec,
                Instruction::Write,
            ],
            &[1],
            &[0]
        );
    }

    #[test]
    fn test_run_can_move_between_cells() {
        assert_run_writes_correct_output(
            vec![
                Instruction::Read,
                Instruction::Next,
                Instruction::Read,
                Instruction::Next,
                Instruction::Prev,
                Instruction::Write,
                Instruction::Prev,
                Instruction::Write,
            ],
            &[1, 2],
            &[2, 1]
        );
    }

    #[test]
    fn test_run_can_loop() {
        assert_run_writes_correct_output(
            vec![
                Instruction::Read,
                Instruction::LoopStart(6),
                Instruction::Dec,
                Instruction::Next,
                Instruction::Inc,
                Instruction::Prev,
                Instruction::LoopEnd(1),
                Instruction::Write,
                Instruction::Next,
                Instruction::Write
            ],
            &[2],
            &[0, 2]
        );
    }

    #[test]
    fn test_copies_input_to_output() {
        assert_run_writes_correct_output(
            vec![
                // Read the whole input:
                Instruction::Next,
                Instruction::Inc,
                Instruction::LoopStart(5),
                Instruction::Next,
                Instruction::Read,
                Instruction::LoopEnd(2),
                // Rewind to the beginning:
                Instruction::Prev,
                Instruction::LoopStart(9),
                Instruction::Prev,
                Instruction::LoopEnd(7),
                // Write the whole input:
                Instruction::Next,
                Instruction::Next,
                Instruction::LoopStart(15),
                Instruction::Write,
                Instruction::Next,
                Instruction::LoopEnd(12)
            ],
            &[4, 3, 1, 5],
            &[4, 3, 1, 5]
        );
    }

    #[test]
    fn test_returns_error_when_underflowing_data_pointer() {
        assert_run_returns_error(vec![Instruction::Prev], &[]);
    }
}
<|endoftext|>