<commit_before>// Copyright (C) 2016, 2017, 2018 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Crowbook is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

//!  Note: this documentation is relative to `crowbook` *as a library*.
//!  For documentation regarding the *program* `crowbook`, see
//!  [the Github page](https://github.com/lise-henry/crowbook).
//!
//! # Usage
//!
//! Just like any other library, just add a dependency on `crowbook`
//! in your `Cargo.toml` file. You will probably want to deactivate
//! default features that are mostly useful for the binary:
//!
//! ```ignore
//! crowbook = {version = "0.11", default-features = false}
//! ```
//!
//! # Book
//!
//! The central structure of Crowbook is `Book`, which coordinates everything.
//!
//! Its roles are:
//!
//! * read a book configuration file and setting the book options accordingly;
//! * read the chapters (written in Markdown) listed in this
//!   configuration file and pass them to to `Parser`, get back an AST and store it in memory
//! * call the various renderers according to the book's parameters
//! and generate the appopriate files.
//!
//! ## Example
//!
//! ```ignore
//! use crowbook::Book;
//! // Reads configuration file "foo.book" and render all formats according to this
//! // configuration file
//! Book::new()
//!      .load_file("foo.book").unwrap()
//!      .render_all().unwrap();
//! ```
//!
//! This is basically the code for the `crowbook` binary (though it contains a
//! bit more error handling, checking parameters from command line and so on).
//! This is, however, not very interesting for a library usage.
//!
//! The `Book` structure, however, exposes its `chapter` fields, which contains
//! a vector with an element by chapter. With it, you can access the Markdown
//! for all chapters represented as an Abstact Syntax Tree (i.e., a vector of `Token`s).
//! It is thus possible to create a new renderer (or manipulate this AST in other ways).
//!
//! # Parser
//!
//! It is also possible to directly use `Parser` to transform some markdown string or file
//! to this AST:
//!
//! ```
//! use crowbook::{Parser,Token};
//! let mut parser = Parser::new();
//! let result = parser.parse("Some *valid* Markdown").unwrap();
//! assert_eq!(format!("{:?}", result),
//! r#"[Paragraph([Str("Some "), Emphasis([Str("valid")]), Str(" Markdown")])]"#);
//! ```
//!
//! Of course, you probably want to do something else with this AST than display it.
//! Let's assume you want to count the number of links in a document.
//!
//! ```
//! use crowbook::{Parser,Token};
//! fn count(ast: &[Token]) -> u32 {
//!    let mut n = 0;
//!    for token in ast {
//!        match *token {
//!            // It's a link, increase counter
//!            Token::Link(_,_,_) => n += 1,
//!            // It's not a link, let's count the number of links
//!            // inside of the inner element (if there is one)
//!           _ => {
//!                if let Some(sub_ast) = token.inner() {
//!                    n += count(sub_ast);
//!                }
//!            }
//!       }
//!    }
//!    n
//! }
//!
//! let md = "# Here's a [link](http://foo.bar) #\n And *another [one](http://foo.bar)* !";
//!
//! let mut parser = Parser::new();
//! let ast = parser.parse(md).unwrap();
//! assert_eq!(count(&ast), 2);
//! ```

extern crate pulldown_cmark as cmark;
extern crate mustache;
extern crate yaml_rust;
extern crate mime_guess;
extern crate walkdir;
extern crate rustc_serialize;
extern crate rayon;
extern crate crowbook_text_processing;
extern crate crowbook_intl_runtime;
extern crate numerals;
extern crate epub_builder;
extern crate uuid;
extern crate hyphenation;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;

#[cfg(feature = "indicatif")]
extern crate indicatif;
#[cfg(feature = "binary")]
extern crate console;
#[cfg(feature = "binary")]
extern crate textwrap;
#[cfg(feature = "proofread")]
extern crate hyper;
#[cfg(feature = "proofread")]
extern crate url;
#[cfg(feature = "proofread")]
extern crate caribon;
#[cfg(feature = "proofread")]
extern crate serde;
#[cfg(feature = "proofread")]
extern crate serde_json;
#[cfg(feature = "proofread")]
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "syntect")]
extern crate syntect;
#[cfg(feature = "nightly")]
extern crate punkt;

pub use parser::Parser;
pub use book::Book;
pub use bookoption::BookOption;
pub use bookoptions::BookOptions;
pub use error::{Result, Error, Source};
pub use token::Token;
pub use token::Data;
pub use number::Number;
pub use resource_handler::ResourceHandler;
pub use renderer::Renderer;
pub use book_renderer::BookRenderer;
pub use chapter::Chapter;
pub use stats::Stats;

#[macro_use]
#[doc(hidden)]
mod localize_macros;
#[macro_use]
mod html;
mod html_dir;
mod error;
mod book;
mod epub;
mod latex;
mod odt;
mod parser;
mod token;
mod cleaner;
mod chapter;
mod number;
mod resource_handler;
mod bookoptions;
mod lang;
mod renderer;
mod book_renderer;
mod html_single;
mod html_if;
mod syntax;
mod stats;

#[cfg(feature = "binary")]
mod style;
#[cfg(not(feature = "binary"))]
mod style_stubs;
#[cfg(not(feature = "binary"))]
use style_stubs as style;

#[cfg(feature = "indicatif")]
mod book_bars;
#[cfg(not(feature = "indicatif"))]
mod book_bars_stubs;
#[cfg(not(feature = "indicatif"))]
use book_bars_stubs as book_bars;

mod zipper;
mod templates;
mod bookoption;
mod misc;
mod text_view;

#[cfg(feature = "proofread")]
mod grammar_check;
#[cfg(feature = "proofread")]
mod grammalecte;
#[cfg(feature = "proofread")]
mod repetition_check;

#[cfg(test)]
mod tests;
<commit_msg>Bump crowbook version in lib.rs<commit_after>// Copyright (C) 2016, 2017, 2018 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Crowbook is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

//!  Note: this documentation is relative to `crowbook` *as a library*.
//!  For documentation regarding the *program* `crowbook`, see
//!  [the Github page](https://github.com/lise-henry/crowbook).
//!
//! # Usage
//!
//! Just like any other library, just add a dependency on `crowbook`
//! in your `Cargo.toml` file. You will probably want to deactivate
//! default features that are mostly useful for the binary:
//!
//! ```ignore
//! crowbook = {version = "0.14.1", default-features = false}
//! ```
//!
//! # Book
//!
//! The central structure of Crowbook is `Book`, which coordinates everything.
//!
//! Its roles are:
//!
//! * read a book configuration file and setting the book options accordingly;
//! * read the chapters (written in Markdown) listed in this
//!   configuration file and pass them to to `Parser`, get back an AST and store it in memory
//! * call the various renderers according to the book's parameters
//! and generate the appopriate files.
//!
//! ## Example
//!
//! ```ignore
//! use crowbook::Book;
//! // Reads configuration file "foo.book" and render all formats according to this
//! // configuration file
//! Book::new()
//!      .load_file("foo.book").unwrap()
//!      .render_all().unwrap();
//! ```
//!
//! This is basically the code for the `crowbook` binary (though it contains a
//! bit more error handling, checking parameters from command line and so on).
//! This is, however, not very interesting for a library usage.
//!
//! The `Book` structure, however, exposes its `chapter` fields, which contains
//! a vector with an element by chapter. With it, you can access the Markdown
//! for all chapters represented as an Abstact Syntax Tree (i.e., a vector of `Token`s).
//! It is thus possible to create a new renderer (or manipulate this AST in other ways).
//!
//! # Parser
//!
//! It is also possible to directly use `Parser` to transform some markdown string or file
//! to this AST:
//!
//! ```
//! use crowbook::{Parser,Token};
//! let mut parser = Parser::new();
//! let result = parser.parse("Some *valid* Markdown").unwrap();
//! assert_eq!(format!("{:?}", result),
//! r#"[Paragraph([Str("Some "), Emphasis([Str("valid")]), Str(" Markdown")])]"#);
//! ```
//!
//! Of course, you probably want to do something else with this AST than display it.
//! Let's assume you want to count the number of links in a document.
//!
//! ```
//! use crowbook::{Parser,Token};
//! fn count(ast: &[Token]) -> u32 {
//!    let mut n = 0;
//!    for token in ast {
//!        match *token {
//!            // It's a link, increase counter
//!            Token::Link(_,_,_) => n += 1,
//!            // It's not a link, let's count the number of links
//!            // inside of the inner element (if there is one)
//!           _ => {
//!                if let Some(sub_ast) = token.inner() {
//!                    n += count(sub_ast);
//!                }
//!            }
//!       }
//!    }
//!    n
//! }
//!
//! let md = "# Here's a [link](http://foo.bar) #\n And *another [one](http://foo.bar)* !";
//!
//! let mut parser = Parser::new();
//! let ast = parser.parse(md).unwrap();
//! assert_eq!(count(&ast), 2);
//! ```

extern crate pulldown_cmark as cmark;
extern crate mustache;
extern crate yaml_rust;
extern crate mime_guess;
extern crate walkdir;
extern crate rustc_serialize;
extern crate rayon;
extern crate crowbook_text_processing;
extern crate crowbook_intl_runtime;
extern crate numerals;
extern crate epub_builder;
extern crate uuid;
extern crate hyphenation;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;

#[cfg(feature = "indicatif")]
extern crate indicatif;
#[cfg(feature = "binary")]
extern crate console;
#[cfg(feature = "binary")]
extern crate textwrap;
#[cfg(feature = "proofread")]
extern crate hyper;
#[cfg(feature = "proofread")]
extern crate url;
#[cfg(feature = "proofread")]
extern crate caribon;
#[cfg(feature = "proofread")]
extern crate serde;
#[cfg(feature = "proofread")]
extern crate serde_json;
#[cfg(feature = "proofread")]
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "syntect")]
extern crate syntect;
#[cfg(feature = "nightly")]
extern crate punkt;

pub use parser::Parser;
pub use book::Book;
pub use bookoption::BookOption;
pub use bookoptions::BookOptions;
pub use error::{Result, Error, Source};
pub use token::Token;
pub use token::Data;
pub use number::Number;
pub use resource_handler::ResourceHandler;
pub use renderer::Renderer;
pub use book_renderer::BookRenderer;
pub use chapter::Chapter;
pub use stats::Stats;

#[macro_use]
#[doc(hidden)]
mod localize_macros;
#[macro_use]
mod html;
mod html_dir;
mod error;
mod book;
mod epub;
mod latex;
mod odt;
mod parser;
mod token;
mod cleaner;
mod chapter;
mod number;
mod resource_handler;
mod bookoptions;
mod lang;
mod renderer;
mod book_renderer;
mod html_single;
mod html_if;
mod syntax;
mod stats;

#[cfg(feature = "binary")]
mod style;
#[cfg(not(feature = "binary"))]
mod style_stubs;
#[cfg(not(feature = "binary"))]
use style_stubs as style;

#[cfg(feature = "indicatif")]
mod book_bars;
#[cfg(not(feature = "indicatif"))]
mod book_bars_stubs;
#[cfg(not(feature = "indicatif"))]
use book_bars_stubs as book_bars;

mod zipper;
mod templates;
mod bookoption;
mod misc;
mod text_view;

#[cfg(feature = "proofread")]
mod grammar_check;
#[cfg(feature = "proofread")]
mod grammalecte;
#[cfg(feature = "proofread")]
mod repetition_check;

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>use cpu::{Cpu};
use std::old_io::{File, BytesReader, Seek};
use std::old_io;

pub fn load_bin(file: &mut File, cpu: &mut Cpu) -> () {
	let mut i: usize = 0;
	for byte in file.bytes() {
		let byte = match byte {
			Ok(number) => number as i8,
			Err(e) => panic!("{}", e.desc),
		};
		cpu.memory.write_byte(i, byte);
		i += 1;
	}
}

pub fn load_c16(file: &mut File, cpu: &mut Cpu) -> () {
	let magic_number = match file.read_be_u32() {
		Ok(number) => number,
		Err(e) => panic!("{}", e.desc)
	};
	if magic_number == 0x43483135 {
		file.read_u8();
		file.read_u8();

		let rom_size: u32 = match file.read_be_u32() {
			Ok(rom) => rom,
			Err(e) => panic!("{}", e.desc)
		};
		cpu.pc = match file.read_be_i16() {
			Ok(ip) => ip,
			Err(e) => panic!("{}", e.desc)
		};
		let checksum: u32 = match file.read_be_u32(){
			Ok(sum) => sum,
			Err(e) => panic!("{}", e.desc)
		};

		check_rom_size(file, rom_size);
		crc32_checksum(file, checksum);

		match file.seek(0x10, old_io::SeekSet){
			Ok(ok) => ok,
			Err(e) => panic!("{}", e.desc)
		};
		load_bin(file, cpu);
	} else {
		match file.seek(0, old_io::SeekSet){
			Ok(ok) => ok,
			Err(e) => panic!("{}", e.desc)
		};
		load_bin(file, cpu);
	}
}

fn check_rom_size(file: &mut File, rom_size: u32) -> () {
	let file_size = match file.stat() {
		Ok(file_stat) => file_stat.size - 0x10,
		Err(e) => panic!("{}", e.desc)
	};
	if rom_size as u64 != file_size {
		panic!("The ROM size is not what the header says");
	}
}

fn crc32_checksum(file: &mut File, checksum: u32) -> () {
	()
}

<commit_msg>Added checking the checksum of a .c16 file<commit_after>use cpu::{Cpu};
use std::old_io::{File, BytesReader, Seek};
use std::old_io;

pub fn load_bin(file: &mut File, cpu: &mut Cpu) -> () {
	let mut i: usize = 0;
	for byte in file.bytes() {
		let byte = match byte {
			Ok(number) => number as i8,
			Err(e) => panic!("{}", e.desc),
		};
		cpu.memory.write_byte(i, byte);
		i += 1;
	}
}

pub fn load_c16(file: &mut File, cpu: &mut Cpu) -> () {
	let magic_number = match file.read_be_u32() {
		Ok(number) => number,
		Err(e) => panic!("{}", e.desc)
	};
	if magic_number == 0x43483135 {
		file.read_u8();
		file.read_u8();

		let rom_size: u32 = match file.read_be_u32() {
			Ok(rom) => rom,
			Err(e) => panic!("{}", e.desc)
		};
		cpu.pc = match file.read_be_i16() {
			Ok(ip) => ip,
			Err(e) => panic!("{}", e.desc)
		};
		let checksum: u32 = match file.read_be_u32(){
			Ok(sum) => sum,
			Err(e) => panic!("{}", e.desc)
		};

		check_rom_size(file, rom_size);
		crc32_checksum(file, checksum);

		match file.seek(0x10, old_io::SeekSet){
			Ok(ok) => ok,
			Err(e) => panic!("{}", e.desc)
		};
		load_bin(file, cpu);
	} else {
		match file.seek(0, old_io::SeekSet){
			Ok(ok) => ok,
			Err(e) => panic!("{}", e.desc)
		};
		load_bin(file, cpu);
	}
}

fn check_rom_size(file: &mut File, rom_size: u32) -> () {
	let file_size = match file.stat() {
		Ok(file_stat) => file_stat.size - 0x10,
		Err(e) => panic!("{}", e.desc)
	};
	if rom_size as u64 != file_size {
		panic!("The ROM size is not what the header says");
	}
}

fn crc32_checksum(file: &mut File, checksum: u32) -> () {
	let table: [u32; 256] =[ //Precalculated table
	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
	0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
	0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
	0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
	0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
	0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
	0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
	0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
	0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
	0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
	0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
	0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
	0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
	0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
	0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
	0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
	0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
	0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
	0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
	0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
	0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
	0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
	0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
	0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
	0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
	0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
	0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
	0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
	0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
	0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
	0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
	0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
	0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
	0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
	0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d];

	let mut crc: u32 = 0xFFFFFFFF;

	match file.seek(0x10, old_io::SeekSet){
		Ok(ok) => ok,
		Err(e) => panic!("{}", e.desc)
	};

	for byte in file.bytes() {
		let byte = match byte {
			Ok(number) => number as u8,
			Err(e) => panic!("{}", e.desc),
		};
		crc = (crc >> 8) ^ table[(crc as u8 ^ byte) as usize];
	}

	crc = !crc;

	if checksum != crc {
		panic!("The ROM is corrupted.");
	};

	()
}<|endoftext|>


<commit_before>//! TIS-100 emulator implementations.

use core::Port::*;
use io::IoBus;
use node::{Node, BasicExecutionNode};
use save::Save;

/// Implements the *Simple Sandbox* puzzle from the game.
///
/// Example:
///
/// ```
/// use tis_100::save::parse_save;
/// use tis_100::machine::Sandbox;
///
/// // This program reads the value from the console and simply passes it to the console output.
/// let src = "@1\nMOV UP DOWN\n@5\nMOV UP DOWN\n@9\nMOV UP RIGHT\n@10\nMOV LEFT DOWN\n";
///
/// let save = parse_save(src).unwrap();
/// let mut sandbox = Sandbox::with_save(&save);
///
/// sandbox.write_console(42);
///
/// for _ in 0..5 {
///     sandbox.step();
/// }
///
/// assert_eq!(sandbox.read_console(), Some(42));
/// ```
#[derive(Debug)]
pub struct Sandbox {
    nodes: Vec<BasicExecutionNode>,
    bus: IoBus,
}

impl Sandbox {
    /// Construct a new `Sandbox` with programs from the `Save`.
    pub fn with_save(save: &Save) -> Sandbox {
        let mut sandbox = Sandbox {
            nodes: Vec::new(),
            bus: IoBus::new(),
        };
        sandbox.setup(save);
        sandbox
    }

    /// Setup the connections between nodes. Each node is fully connected to its neighbors.
    fn setup(&mut self, save: &Save) {
        for node_num in 0..12 {
            match save.get(&node_num) {
                Some(prog) => self.nodes.push(BasicExecutionNode::with_program(prog.clone())),
                None => self.nodes.push(BasicExecutionNode::new()),
            };
        }

        self.bus.connect_full(0, 1, RIGHT)
            .connect_full(1, 2, RIGHT)
            .connect_full(2, 3, RIGHT)
            .connect_full(4, 5, RIGHT)
            .connect_full(5, 6, RIGHT)
            .connect_full(6, 7, RIGHT)
            .connect_full(8, 9, RIGHT)
            .connect_full(9, 10, RIGHT)
            .connect_full(10, 11, RIGHT);

        self.bus.connect_full(0, 4, DOWN)
            .connect_full(1, 5, DOWN)
            .connect_full(2, 6, DOWN)
            .connect_full(3, 7, DOWN)
            .connect_full(4, 8, DOWN)
            .connect_full(5, 9, DOWN)
            .connect_full(6, 10, DOWN)
            .connect_full(7, 11, DOWN)
            // The console output is connected to node 10.
            .connect_half(10, 12, DOWN)
            // The console input is connected to node 1.
            .connect_half(12, 1, DOWN);
    }

    /// Step each node through one instruction.
    pub fn step(&mut self) {
        // Step each node
        for (id, node) in self.nodes.iter_mut().enumerate() {
            let mut view = self.bus.view(id);
            node.step(&mut view);
        }

        // Synchronize writes and reads on each node
        for (id, node) in self.nodes.iter_mut().enumerate() {
            let mut view = self.bus.view(id);
            node.sync(&mut view);
        }

        // Commit writes so they are available on the next cycle.
        self.bus.commit();
    }

    /// Send a value from the console to node 1.
    pub fn write_console(&mut self, value: isize) {
        self.bus.view(12).write(DOWN, value);
    }

    /// Read a value from node 10 and send it to the console.
    pub fn read_console(&mut self) -> Option<isize> {
        self.bus.view(12).read(UP)
    }
}
<commit_msg>Refactor Sandbox to use a lower-level Tis100 CPU.<commit_after>//! TIS-100 emulator implementations.

use std::collections::VecMap;
use core::Port::*;
use io::IoBus;
use node::{Node, BasicExecutionNode};
use save::Save;

/// Implements the *Simple Sandbox* puzzle from the game.
///
/// Example:
///
/// ```
/// use tis_100::save::parse_save;
/// use tis_100::machine::Sandbox;
///
/// // This program reads the value from the console and simply passes it to the console output.
/// let src = "@1\nMOV UP DOWN\n@5\nMOV UP DOWN\n@9\nMOV UP RIGHT\n@10\nMOV LEFT DOWN\n";
///
/// let save = parse_save(src).unwrap();
/// let mut sandbox = Sandbox::with_save(&save);
///
/// sandbox.write_console(42);
///
/// for _ in 0..5 {
///     sandbox.step();
/// }
///
/// assert_eq!(sandbox.read_console(), Some(42));
/// ```
#[derive(Debug)]
pub struct Sandbox {
    cpu: Tis100,
    write_node: usize,
    read_node: usize,
}

impl Sandbox {
    /// Construct a new `Sandbox` with programs from the `Save`.
    pub fn with_save(save: &Save) -> Sandbox {
        let mut sandbox = Sandbox {
            cpu: Tis100::new(),
            // write_node and read_node will both be set during setup.
            write_node: 0,
            read_node: 0,
        };
        sandbox.setup(save);
        sandbox
    }

    /// Setup the connections between nodes. Each node is fully connected to its neighbors.
    fn setup(&mut self, save: &Save) {
        for node_num in 0..12 {
            match save.get(&node_num) {
                Some(prog) => self.cpu.add_node(node_num, Box::new(BasicExecutionNode::with_program(prog.clone()))),
                None => self.cpu.add_node(node_num, Box::new(BasicExecutionNode::new())),
            };
        }

        self.write_node = self.cpu.add_input(1);
        self.read_node = self.cpu.add_output(10);
    }

    /// Step each node through one instruction.
    pub fn step(&mut self) {
        self.cpu.step();
    }

    /// Send a value from the console to node 1.
    pub fn write_console(&mut self, value: isize) {
        self.cpu.write(self.write_node, value);
    }

    /// Read a value from node 10 and send it to the console.
    pub fn read_console(&mut self) -> Option<isize> {
        self.cpu.read(self.read_node)
    }
}

/// An empty TIS-100 CPU.
#[derive(Debug)]
pub struct Tis100 {
    nodes: VecMap<Box<Node>>,
    // Used to track available node IDs for input and output ports
    next_node: usize,
    bus: IoBus,
}

impl Tis100 {
    /// Construct a new, empty `Tis100`.
    pub fn new() -> Tis100 {
        let mut tis100 = Tis100 {
            nodes: VecMap::new(),
            next_node: 12,
            bus: IoBus::new(),
        };
        tis100.setup();
        tis100
    }

    /// Setup the IO connections between nodes.
    fn setup(&mut self) {
        self.bus.connect_full(0, 1, RIGHT)
            .connect_full(1, 2, RIGHT)
            .connect_full(2, 3, RIGHT)
            .connect_full(4, 5, RIGHT)
            .connect_full(5, 6, RIGHT)
            .connect_full(6, 7, RIGHT)
            .connect_full(8, 9, RIGHT)
            .connect_full(9, 10, RIGHT)
            .connect_full(10, 11, RIGHT);

        self.bus.connect_full(0, 4, DOWN)
            .connect_full(1, 5, DOWN)
            .connect_full(2, 6, DOWN)
            .connect_full(3, 7, DOWN)
            .connect_full(4, 8, DOWN)
            .connect_full(5, 9, DOWN)
            .connect_full(6, 10, DOWN)
            .connect_full(7, 11, DOWN);
    }

    /// Add a new node with the given ID to the system.
    pub fn add_node(&mut self, index: usize, node: Box<Node>) {
        self.nodes.insert(index, node);
    }

    /// Create a new input port to a CPU node. Returns a node ID that can be used to write values on
    /// the port. Only nodes 0 through 3 can receive inputs. All inputs are connected `UP` from the
    /// receiving nodes.
    pub fn add_input(&mut self, node: usize) -> usize {
        assert!(node < 4);
        let input_node = self.next_node;
        self.next_node += 1;
        self.bus.connect_half(input_node, node, DOWN);
        input_node
    }

    /// Create a new output port from a CPU node. Returns a node ID that can be used to read values
    /// from the port. Only nodes 8 through 11 can send outputs. All outputs are connected `DOWN`
    /// from the sending nodes.
    pub fn add_output(&mut self, node: usize) -> usize {
        assert!(node > 7 && node < 12);
        let output_node = self.next_node;
        self.next_node += 1;
        self.bus.connect_half(node, output_node, DOWN);
        output_node
    }

    /// Read a value from an output node.
    pub fn read(&mut self, node: usize) -> Option<isize> {
        self.bus.view(node).read(UP)
    }

    /// Send a value to an input node.
    pub fn write(&mut self, node: usize, value: isize) {
        self.bus.view(node).write(DOWN, value);
    }

    /// Execute one instruction cycle on all nodes in the system.
    pub fn step(&mut self) {
        // Step each node
        for (id, node) in self.nodes.iter_mut() {
            let mut view = self.bus.view(id);
            node.step(&mut view);
        }

        // Synchronize writes and reads on each node
        for (id, node) in self.nodes.iter_mut() {
            let mut view = self.bus.view(id);
            node.sync(&mut view);
        }

        // Commit writes so they are available on the next cycle.
        self.bus.commit();
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::string::ToString;
use std::default::Default;
use rustc_serialize::json::{Json,ToJson};

use parse::*;
use parse::Presence::*;
use record;
use record::{Record, PartialRecord};

#[derive(Clone, PartialEq, Debug)]
pub enum MailboxRole {
    Inbox,
    Archive,
    Drafts,
    Outbox,
    Sent,
    Trash,
    Spam,
    Templates,
    Custom(String),
}

impl ToString for MailboxRole {
    fn to_string(&self) -> String {
        match *self {
            MailboxRole::Inbox         => "inbox".to_string(),
            MailboxRole::Archive       => "archive".to_string(),
            MailboxRole::Drafts        => "drafts".to_string(),
            MailboxRole::Outbox        => "outbox".to_string(),
            MailboxRole::Sent          => "sent".to_string(),
            MailboxRole::Trash         => "trash".to_string(),
            MailboxRole::Spam          => "spam".to_string(),
            MailboxRole::Templates     => "templates".to_string(),
            MailboxRole::Custom(ref r) => {
                let mut s = "x-".to_string();
                s.push_str(r.as_ref());
                s
            },
        }
    }
}

impl ToJson for MailboxRole {
    fn to_json(&self) -> Json {
        Json::String(self.to_string())
    }
}

impl FromJson for MailboxRole {
    fn from_json(json: &Json) -> Result<MailboxRole,ParseError> {
        match *json {
            Json::String(ref v) => match v.as_ref() {
                "inbox"     => Ok(MailboxRole::Inbox),
                "archive"   => Ok(MailboxRole::Archive),
                "drafts"    => Ok(MailboxRole::Drafts),
                "outbox"    => Ok(MailboxRole::Outbox),
                "sent"      => Ok(MailboxRole::Sent),
                "trash"     => Ok(MailboxRole::Trash),
                "spam"      => Ok(MailboxRole::Spam),
                "templates" => Ok(MailboxRole::Templates),
                r => {
                    let bits: Vec<&str> = r.splitn(2, '-').collect();
                    match bits.len() {
                        2 => Ok(MailboxRole::Custom(bits[1].to_string())),
                        _ => Err(ParseError::InvalidStructure("MailboxRole".to_string())),
                    }
                }
            },
            _ => Err(ParseError::InvalidJsonType("MailboxRole".to_string())),
        }
    }
}

make_record_type!(Mailbox, PartialMailbox, "Mailbox",
    name:                 String              => "name",
    parent_id:            Option<String>      => "parentId",
    role:                 Option<MailboxRole> => "role",
    precedence:           i32                 => "precedence",
    must_be_only_mailbox: bool                => "mustBeOnlyMailbox",
    may_read_items:       bool                => "mayReadItems",
    may_add_items:        bool                => "mayAddItems",
    may_remove_items:     bool                => "mayRemoveItems",
    may_create_child:     bool                => "mayCreateChild",
    may_rename:           bool                => "mayRename",
    may_delete:           bool                => "mayDelete",
    total_messages:       usize               => "totalMessages",
    unread_messages:      usize               => "unreadMessages",
    total_threads:        usize               => "totalThreads",
    unread_threads:       usize               => "unreadThreads"
);
<commit_msg>Rename Mailbox precedence to sortOrder (latest spec)<commit_after>use std::collections::BTreeMap;
use std::string::ToString;
use std::default::Default;
use rustc_serialize::json::{Json,ToJson};

use parse::*;
use parse::Presence::*;
use record;
use record::{Record, PartialRecord};

#[derive(Clone, PartialEq, Debug)]
pub enum MailboxRole {
    Inbox,
    Archive,
    Drafts,
    Outbox,
    Sent,
    Trash,
    Spam,
    Templates,
    Custom(String),
}

impl ToString for MailboxRole {
    fn to_string(&self) -> String {
        match *self {
            MailboxRole::Inbox         => "inbox".to_string(),
            MailboxRole::Archive       => "archive".to_string(),
            MailboxRole::Drafts        => "drafts".to_string(),
            MailboxRole::Outbox        => "outbox".to_string(),
            MailboxRole::Sent          => "sent".to_string(),
            MailboxRole::Trash         => "trash".to_string(),
            MailboxRole::Spam          => "spam".to_string(),
            MailboxRole::Templates     => "templates".to_string(),
            MailboxRole::Custom(ref r) => {
                let mut s = "x-".to_string();
                s.push_str(r.as_ref());
                s
            },
        }
    }
}

impl ToJson for MailboxRole {
    fn to_json(&self) -> Json {
        Json::String(self.to_string())
    }
}

impl FromJson for MailboxRole {
    fn from_json(json: &Json) -> Result<MailboxRole,ParseError> {
        match *json {
            Json::String(ref v) => match v.as_ref() {
                "inbox"     => Ok(MailboxRole::Inbox),
                "archive"   => Ok(MailboxRole::Archive),
                "drafts"    => Ok(MailboxRole::Drafts),
                "outbox"    => Ok(MailboxRole::Outbox),
                "sent"      => Ok(MailboxRole::Sent),
                "trash"     => Ok(MailboxRole::Trash),
                "spam"      => Ok(MailboxRole::Spam),
                "templates" => Ok(MailboxRole::Templates),
                r => {
                    let bits: Vec<&str> = r.splitn(2, '-').collect();
                    match bits.len() {
                        2 => Ok(MailboxRole::Custom(bits[1].to_string())),
                        _ => Err(ParseError::InvalidStructure("MailboxRole".to_string())),
                    }
                }
            },
            _ => Err(ParseError::InvalidJsonType("MailboxRole".to_string())),
        }
    }
}

make_record_type!(Mailbox, PartialMailbox, "Mailbox",
    name:                 String              => "name",
    parent_id:            Option<String>      => "parentId",
    role:                 Option<MailboxRole> => "role",
    sort_order:           i32                 => "sortOrder",
    must_be_only_mailbox: bool                => "mustBeOnlyMailbox",
    may_read_items:       bool                => "mayReadItems",
    may_add_items:        bool                => "mayAddItems",
    may_remove_items:     bool                => "mayRemoveItems",
    may_create_child:     bool                => "mayCreateChild",
    may_rename:           bool                => "mayRename",
    may_delete:           bool                => "mayDelete",
    total_messages:       usize               => "totalMessages",
    unread_messages:      usize               => "unreadMessages",
    total_threads:        usize               => "totalThreads",
    unread_threads:       usize               => "unreadThreads"
);
<|endoftext|>


<commit_before>/// Given a list of entries `items` and the query string, filter out the
/// matched entries using fuzzy search algorithm.

use std::sync::{Arc, RwLock};

use std::sync::mpsc::Sender;
use std::collections::HashMap;
use event::Event;
use item::{Item, MatchedItem, MatchedRange};
use util::eventbox::EventBox;
use score;
use orderedvec::OrderedVec;

pub struct Matcher {
    tx_output: Sender<MatchedItem>,   // channel to send output to
    eb_req: Arc<EventBox<Event>>,       // event box that recieve requests
    eb_notify: Arc<EventBox<Event>>,    // event box that send out notification
    items: Arc<RwLock<Vec<Item>>>,
    item_pos: usize,
    num_matched: u64,
    query: String,
    cache: HashMap<String, MatcherCache>,
}

impl Matcher {
    pub fn new(items: Arc<RwLock<Vec<Item>>>, tx_output: Sender<MatchedItem>,
               eb_req: Arc<EventBox<Event>>, eb_notify: Arc<EventBox<Event>>) -> Self {
        Matcher {
            tx_output: tx_output,
            eb_req: eb_req,
            eb_notify: eb_notify,
            items: items,
            item_pos: 0,
            num_matched: 0,
            query: String::new(),
            cache: HashMap::new(),
        }
    }

    pub fn process(&mut self) {
        let ref mut cache = self.cache.get_mut(&self.query).unwrap().matched_items;

        loop {
            let items = self.items.read().unwrap();
            if let Some(item) = items.get(self.item_pos) {
                if let Some(matched) = match_item(self.item_pos, &item.text, &self.query) {
                    self.num_matched += 1;
                    cache.push(matched.clone());
                    let _ = self.tx_output.send(matched);
                }
            } else {
                (*self.eb_notify).set(Event::EvMatcherEnd, Box::new(true));
                break;
            }

            self.item_pos += 1;
            (*self.eb_notify).set(Event::EvMatcherUpdateProcess, Box::new((self.num_matched, items.len() as u64, self.item_pos as u64)));

            // check if the current process need to be stopped
            if !self.eb_req.is_empty() {
                break;
            }
        }
    }

    pub fn output_from_cache(&mut self) {
        if !self.cache.contains_key(&self.query) {
            return;
        }

        let ref mut matched_items = self.cache.get_mut(&self.query).unwrap().matched_items;
        let total = matched_items.len();
        loop {
            if let Some(ref matched) = matched_items.get(self.item_pos) {
                self.num_matched += 1;
                let _ = self.tx_output.send((*matched).clone());

                self.item_pos += 1;
                (*self.eb_notify).set(Event::EvMatcherUpdateProcess, Box::new((self.num_matched, total as u64, self.item_pos as u64)));
            } else {
                break;
            }

            if !self.eb_req.is_empty() {
                break;
            }
        }
    }

    fn reset_query(&mut self, query: &str) {
        self.query.clear();
        self.query.push_str(query);
        self.num_matched = 0;
        self.item_pos = 0;
        self.cache.entry(query.to_string()).or_insert_with(|| MatcherCache::new());
    }

    pub fn run(&mut self) {
        loop {
            for (e, val) in (*self.eb_req).wait() {
                match e {
                    Event::EvMatcherNewItem => {}
                    Event::EvMatcherResetQuery => {
                        self.reset_query(&val.downcast::<String>().unwrap());
                        (*self.eb_notify).set(Event::EvMatcherStart, Box::new(true));
                    }
                    _ => {}
                }
            }

            self.output_from_cache();
            self.process();
        }
    }
}

fn match_item(index: usize, item: &str, query: &str) -> Option<MatchedItem> {
    let matched_result = score::fuzzy_match(item, query);
    if matched_result == None {
        return None;
    }

    let (score, matched_range) = matched_result.unwrap();

    let mut item = MatchedItem::new(index);
    item.set_matched_range(MatchedRange::Chars(matched_range));
    item.set_score(score);
    Some(item)
}

struct MatcherCache {
    matched_items: OrderedVec<MatchedItem>,
}

impl MatcherCache {
    pub fn new() -> Self {
        MatcherCache {
            matched_items: OrderedVec::new(),
        }
    }

    pub fn push(&mut self, matched_item: MatchedItem) {
        self.matched_items.push(matched_item);
    }
}

<commit_msg>[matcher] add cache<commit_after>/// Given a list of entries `items` and the query string, filter out the
/// matched entries using fuzzy search algorithm.

use std::sync::{Arc, RwLock};

use std::sync::mpsc::Sender;
use std::collections::HashMap;
use event::Event;
use item::{Item, MatchedItem, MatchedRange};
use util::eventbox::EventBox;
use score;
use orderedvec::OrderedVec;

pub struct Matcher {
    tx_output: Sender<MatchedItem>,   // channel to send output to
    eb_req: Arc<EventBox<Event>>,       // event box that recieve requests
    eb_notify: Arc<EventBox<Event>>,    // event box that send out notification
    items: Arc<RwLock<Vec<Item>>>,
    item_pos: usize,
    num_matched: u64,
    query: String,
    cache: HashMap<String, MatcherCache>,
}

impl Matcher {
    pub fn new(items: Arc<RwLock<Vec<Item>>>, tx_output: Sender<MatchedItem>,
               eb_req: Arc<EventBox<Event>>, eb_notify: Arc<EventBox<Event>>) -> Self {
        let mut cache = HashMap::new();
        cache.entry("".to_string()).or_insert(MatcherCache::new());
        Matcher {
            tx_output: tx_output,
            eb_req: eb_req,
            eb_notify: eb_notify,
            items: items,
            item_pos: 0,
            num_matched: 0,
            query: String::new(),
            cache: cache,
        }
    }

    pub fn process(&mut self) {
        let ref mut cache = self.cache.get_mut(&self.query).unwrap();

        self.item_pos = cache.item_pos;

        loop {
            let items = self.items.read().unwrap();
            if let Some(item) = items.get(self.item_pos) {
                if let Some(matched) = match_item(self.item_pos, &item.text, &self.query) {
                    self.num_matched += 1;
                    cache.matched_items.push(matched.clone());
                    let _ = self.tx_output.send(matched);
                }
            } else {
                (*self.eb_notify).set(Event::EvMatcherEnd, Box::new(true));
                break;
            }

            self.item_pos += 1;
            cache.item_pos = self.item_pos;
            (*self.eb_notify).set(Event::EvMatcherUpdateProcess, Box::new((self.num_matched, items.len() as u64, self.item_pos as u64)));

            // check if the current process need to be stopped
            if !self.eb_req.is_empty() {
                break;
            }
        }
    }

    pub fn output_from_cache(&mut self) {
        if !self.cache.contains_key(&self.query) {
            return;
        }

        let ref mut matched_items = self.cache.get_mut(&self.query).unwrap().matched_items;
        let total = matched_items.len();
        loop {
            if let Some(ref matched) = matched_items.get(self.item_pos) {
                self.num_matched += 1;
                let _ = self.tx_output.send((*matched).clone());

                self.item_pos += 1;
                (*self.eb_notify).set(Event::EvMatcherUpdateProcess, Box::new((self.num_matched, total as u64, self.item_pos as u64)));
            } else {
                break;
            }

            if !self.eb_req.is_empty() {
                break;
            }
        }
    }

    fn reset_query(&mut self, query: &str) {
        self.query.clear();
        self.query.push_str(query);
        self.num_matched = 0;
        self.item_pos = 0;
        self.cache.entry(query.to_string()).or_insert(MatcherCache::new());
    }

    pub fn run(&mut self) {
        loop {
            for (e, val) in (*self.eb_req).wait() {
                match e {
                    Event::EvMatcherNewItem => {}
                    Event::EvMatcherResetQuery => {
                        self.reset_query(&val.downcast::<String>().unwrap());
                        (*self.eb_notify).set(Event::EvMatcherStart, Box::new(true));
                    }
                    _ => {}
                }
            }

            self.output_from_cache();
            self.process();
        }
    }
}

fn match_item(index: usize, item: &str, query: &str) -> Option<MatchedItem> {
    let matched_result = score::fuzzy_match(item, query);
    if matched_result == None {
        return None;
    }

    let (score, matched_range) = matched_result.unwrap();

    let mut item = MatchedItem::new(index);
    item.set_matched_range(MatchedRange::Chars(matched_range));
    item.set_score(score);
    Some(item)
}

struct MatcherCache {
    matched_items: OrderedVec<MatchedItem>,
    pub item_pos: usize,
}

impl MatcherCache {
    pub fn new() -> Self {
        MatcherCache {
            item_pos: 0,
            matched_items: OrderedVec::new(),
        }
    }

    pub fn push(&mut self, matched_item: MatchedItem) {
        self.matched_items.push(matched_item);
    }
}
<|endoftext|>


<commit_before>//! MiniDfs Cluster module
//!
//! MiniDFS launches a embedded HDFS cluster, which is a full feature of a real HDFS cluster
//! It is usually for testing.
//!
//! ## Example
//!
//! ```ignore
//!  let mut conf = MiniDfsConf::new();
//!  let dfs = MiniDFS::start(&mut conf).unwrap();
//!  let port = dfs.namenode_port();
//!  ...
//!  dfs.stop()
//! ```

use libc::{c_char, c_int};
use std::ffi;
use std::mem;
use std::str;

use native::*;

pub struct MiniDFS
{
  cluster: *const NativeMiniDfsCluster
}

impl MiniDFS
{
  pub fn start(conf: &MiniDfsConf) -> Option<MiniDFS>
  {
    match unsafe { nmdCreate(conf) } {
      val if !val.is_null() => Some(MiniDFS { cluster: val }),
      _ => None
    }
  }

  pub fn stop(&self)
  {
    unsafe {
      nmdShutdown(self.cluster);
      nmdFree(self.cluster);
    }
  }

  pub fn wait_for_clusterup(&self) -> bool
  {
    if unsafe { nmdWaitClusterUp(self.cluster) } == 0 { true } else { false }
  }

  pub fn namenode_port(&self) -> Option<i32>
  {
    match unsafe { nmdGetNameNodePort(self.cluster) as i32 } {
      val if val > 0 => Some(val),
      _ => None
    }
  }

  pub fn namenode_http_addr(&self) -> Option<(&str, i32)>
  {
    let mut hostname: *mut c_char = unsafe {mem::zeroed()};
    let mut port: c_int = 0;

    match unsafe {
      nmdGetNameNodeHttpAddress(self.cluster, &mut port, &mut hostname)
    } {
      0 => {
        let slice = unsafe { ffi::CStr::from_ptr(hostname) }.to_bytes();
        let str = str::from_utf8(slice).unwrap();

        Some((str, port as i32))
      },
      _ => None
    }
  }

  pub fn set_hdfs_builder(&self, builder: *mut hdfsBuilder) -> bool
  {
    if unsafe { nmdConfigureHdfsBuilder(self.cluster, builder) } == 0
    { true } else { false }
  }
}<commit_msg>Changed the comments in minidfs.<commit_after>//! MiniDfs Cluster
//!
//! MiniDFS provides a embedded HDFS cluster. It is usually for testing.
//!
//! ## Example
//!
//! ```ignore
//!  let mut conf = MiniDfsConf::new();
//!  let dfs = MiniDFS::start(&mut conf).unwrap();
//!  let port = dfs.namenode_port();
//!  ...
//!  dfs.stop()
//! ```

use libc::{c_char, c_int};
use std::ffi;
use std::mem;
use std::str;

use native::*;

pub struct MiniDFS
{
  cluster: *const NativeMiniDfsCluster
}

impl MiniDFS
{
  pub fn start(conf: &MiniDfsConf) -> Option<MiniDFS>
  {
    match unsafe { nmdCreate(conf) } {
      val if !val.is_null() => Some(MiniDFS { cluster: val }),
      _ => None
    }
  }

  pub fn stop(&self)
  {
    unsafe {
      nmdShutdown(self.cluster);
      nmdFree(self.cluster);
    }
  }

  pub fn wait_for_clusterup(&self) -> bool
  {
    if unsafe { nmdWaitClusterUp(self.cluster) } == 0 { true } else { false }
  }

  pub fn namenode_port(&self) -> Option<i32>
  {
    match unsafe { nmdGetNameNodePort(self.cluster) as i32 } {
      val if val > 0 => Some(val),
      _ => None
    }
  }

  pub fn namenode_http_addr(&self) -> Option<(&str, i32)>
  {
    let mut hostname: *mut c_char = unsafe {mem::zeroed()};
    let mut port: c_int = 0;

    match unsafe {
      nmdGetNameNodeHttpAddress(self.cluster, &mut port, &mut hostname)
    } {
      0 => {
        let slice = unsafe { ffi::CStr::from_ptr(hostname) }.to_bytes();
        let str = str::from_utf8(slice).unwrap();

        Some((str, port as i32))
      },
      _ => None
    }
  }

  pub fn set_hdfs_builder(&self, builder: *mut hdfsBuilder) -> bool
  {
    if unsafe { nmdConfigureHdfsBuilder(self.cluster, builder) } == 0
    { true } else { false }
  }
}<|endoftext|>


<commit_before>use std::{iter, thread};
use std::sync::atomic::{AtomicIsize, AtomicUsize, AtomicBool, Ordering};
use comm;
use config;
use player::Player;
use field::{Pos, Field};
use trajectories_pruning::TrajectoriesPruning;
use common;

const MINIMAX_STR: &'static str = "minimax";

fn alpha_beta(field: &mut Field, depth: u32, last_pos: Pos, player: Player, trajectories_pruning: &TrajectoriesPruning, mut alpha: i32, beta: i32, empty_board: &mut Vec<u32>) -> i32 {
  let enemy = player.next();
  if common::is_last_move_stupid(field, last_pos, enemy) {
    return i32::max_value();
  }
  if depth == 0 {
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  if moves.is_empty() {
    return field.score(player);
  }
  for pos in moves {
    field.put_point(pos, player);
    if common::is_penult_move_stuped(field) {
      field.undo();
      return i32::max_value();
    }
    let next_trajectories_pruning = TrajectoriesPruning::from_last(field, enemy, depth - 1, empty_board, trajectories_pruning, pos);
    let mut cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -alpha - 1, -alpha, empty_board);
    if cur_estimation > alpha && cur_estimation < beta {
      cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, empty_board);
    }
    field.undo();
    if cur_estimation > alpha {
      alpha = cur_estimation;
      if alpha >= beta {
        break;
      }
    }
  }
  alpha
}

fn alpha_beta_parallel(field: &mut Field, player: Player, depth: u32, alpha: i32, beta: i32, trajectories_pruning: &TrajectoriesPruning, empty_board: &mut Vec<u32>, best_move: &mut Option<Pos>, should_stop: &AtomicBool) -> i32 {
  info!(target: MINIMAX_STR, "Starting parellel alpha beta with depth {}, player {} and beta {}.", depth, player, beta);
  if depth == 0 || should_stop.load(Ordering::Relaxed) {
    *best_move = None;
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  debug!(target: MINIMAX_STR, "Moves in consideration: {:?}.", moves.iter().map(|&pos| (field.to_x(pos), field.to_y(pos))).collect::<Vec<(u32, u32)>>());
  if moves.is_empty() || should_stop.load(Ordering::Relaxed) {
    *best_move = None;
    return field.score(player);
  }
  let (producer, consumer) = comm::spmc::unbounded::new();
  for pos in moves {
    producer.send(pos).ok();
  }
  let threads_count = config::threads_count();
  let atomic_alpha = AtomicIsize::new(alpha as isize);
  let atomic_best_move = AtomicUsize::new(0);
  let mut guards = Vec::with_capacity(threads_count);
    for _ in 0 .. threads_count {
    guards.push(thread::scoped(|| {
      let local_consumer = consumer.clone();
      let mut local_field = field.clone();
      let mut local_empty_board = iter::repeat(0u32).take(field.length()).collect();
      let enemy = player.next();
      while let Some(pos) = local_consumer.recv_async().ok() {
        if should_stop.load(Ordering::Relaxed) {
          debug!(target: MINIMAX_STR, "Time-out!");
          break;
        }
        local_field.put_point(pos, player);
        let next_trajectories_pruning = TrajectoriesPruning::from_last(&mut local_field, enemy, depth - 1, &mut local_empty_board, &trajectories_pruning, pos);
        if should_stop.load(Ordering::Relaxed) {
          debug!(target: MINIMAX_STR, "Time-out!");
          break;
        }
        let cur_alpha = atomic_alpha.load(Ordering::SeqCst) as i32;
        if cur_alpha >= beta {
          break;
        }
        let mut cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -cur_alpha - 1, -cur_alpha, &mut local_empty_board);
        if cur_estimation > cur_alpha {
          if !should_stop.load(Ordering::Relaxed) {
            cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, &mut local_empty_board);
          } else {
            debug!(target: MINIMAX_STR, "Time-out! Next estimation ma be approximated.");
          }
        }
        local_field.undo();
        loop {
          let last_pos = atomic_best_move.load(Ordering::SeqCst);
          let last_alpha = atomic_alpha.load(Ordering::SeqCst);
          if cur_estimation > last_alpha as i32 {
            if atomic_alpha.compare_and_swap(last_alpha, cur_estimation as isize, Ordering::SeqCst) == last_alpha && atomic_best_move.compare_and_swap(last_pos, pos, Ordering::SeqCst) == last_pos {
              debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
              break;
            }
          } else {
            debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation > cur_alpha { if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" } } else { "Upper bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
            break;
          }
        }
      }
    }));
  }
  drop(guards);
  let result = atomic_best_move.load(Ordering::SeqCst);
  if result != 0 {
    info!(target: MINIMAX_STR, "Best move is ({}, {}).", field.to_x(result), field.to_y(result));
    *best_move = Some(result);
  } else {
    info!(target: MINIMAX_STR, "Best move is not found.");
    *best_move = None;
  }
  let cur_alpha = atomic_alpha.load(Ordering::SeqCst);
  info!(target: MINIMAX_STR, "Estimation is {}.", cur_alpha);
  cur_alpha as i32
}

pub fn minimax(field: &mut Field, player: Player, depth: u32) -> Option<Pos> {
  info!(target: MINIMAX_STR, "Starting minimax with depth {} and player {}.", depth, player);
  if depth == 0 {
    return None;
  }
  let should_stop = AtomicBool::new(false);
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board);
  let mut best_move = None;
  info!(target: MINIMAX_STR, "Calculating of our estimation. Player is {}", player);
  let estimation = alpha_beta_parallel(field, player, depth, i32::min_value() + 1, i32::max_value(), &trajectories_pruning, &mut empty_board, &mut best_move, &should_stop);
  let enemy = player.next();
  let mut enemy_best_move = best_move;
  let enemy_trajectories_pruning = TrajectoriesPruning::dec_exists(&field, enemy, depth - 1, &mut empty_board, &trajectories_pruning);
  info!(target: MINIMAX_STR, "Calculating of enemy estimation with upper bound {}. Player is {}", -estimation + 1, enemy);
  if -alpha_beta_parallel(field, enemy, depth - 1, -estimation, -estimation + 1, &enemy_trajectories_pruning, &mut empty_board, &mut enemy_best_move, &should_stop) < estimation {
    info!(target: MINIMAX_STR,  "Estimation is greater than enemy estimation. So the best move is {:?}, estimation is {}.", best_move.map(|pos| (field.to_x(pos), field.to_y(pos))), estimation);
    best_move
  } else {
    info!(target: MINIMAX_STR,  "Estimation is less than or equal enemy estimation. So all moves have the same estimation {}.", estimation);
    None
  }
}

pub fn minimax_with_time(field: &mut Field, player: Player, time: u32) -> Option<Pos> {
  let should_stop = AtomicBool::new(false);
  let guard = thread::scoped(|| {
    thread::sleep_ms(time);
    should_stop.store(true, Ordering::Relaxed);
  });
  let enemy = player.next();
  let mut depth = 1;
  let mut best_move = None;
  let mut cur_best_move = None;
  let mut enemy_best_move = None;
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let mut trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board);
  while !should_stop.load(Ordering::Relaxed) {
    let estimation = alpha_beta_parallel(field, player, depth, i32::min_value() + 1, i32::max_value(), &trajectories_pruning, &mut empty_board, &mut cur_best_move, &should_stop);
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    let enemy_trajectories_pruning = TrajectoriesPruning::dec_exists(&field, enemy, depth - 1, &mut empty_board, &trajectories_pruning);
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    best_move = if -alpha_beta_parallel(field, enemy, depth - 1, -estimation, -estimation + 1, &enemy_trajectories_pruning, &mut empty_board, &mut enemy_best_move, &should_stop) < estimation {
      cur_best_move
    } else {
      None
    };
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    depth += 1;
    trajectories_pruning = TrajectoriesPruning::inc_exists(field, player, depth, &mut empty_board, &trajectories_pruning);
  }
  drop(guard);
  best_move
}
<commit_msg>Small improvement.<commit_after>use std::{iter, thread};
use std::sync::atomic::{AtomicIsize, AtomicUsize, AtomicBool, Ordering};
use comm;
use config;
use player::Player;
use field::{Pos, Field};
use trajectories_pruning::TrajectoriesPruning;
use common;

const MINIMAX_STR: &'static str = "minimax";

fn alpha_beta(field: &mut Field, depth: u32, last_pos: Pos, player: Player, trajectories_pruning: &TrajectoriesPruning, mut alpha: i32, beta: i32, empty_board: &mut Vec<u32>) -> i32 {
  let enemy = player.next();
  if common::is_last_move_stupid(field, last_pos, enemy) {
    return i32::max_value();
  }
  if depth == 0 {
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  if moves.is_empty() {
    return field.score(player);
  }
  for pos in moves {
    field.put_point(pos, player);
    if common::is_penult_move_stuped(field) {
      field.undo();
      return i32::max_value();
    }
    let next_trajectories_pruning = TrajectoriesPruning::from_last(field, enemy, depth - 1, empty_board, trajectories_pruning, pos);
    let mut cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -alpha - 1, -alpha, empty_board);
    if cur_estimation > alpha && cur_estimation < beta {
      cur_estimation = -alpha_beta(field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, empty_board);
    }
    field.undo();
    if cur_estimation > alpha {
      alpha = cur_estimation;
      if alpha >= beta {
        break;
      }
    }
  }
  alpha
}

fn alpha_beta_parallel(field: &mut Field, player: Player, depth: u32, alpha: i32, beta: i32, trajectories_pruning: &TrajectoriesPruning, empty_board: &mut Vec<u32>, best_move: &mut Option<Pos>, should_stop: &AtomicBool) -> i32 {
  info!(target: MINIMAX_STR, "Starting parellel alpha beta with depth {}, player {} and beta {}.", depth, player, beta);
  if depth == 0 || should_stop.load(Ordering::Relaxed) {
    *best_move = None;
    return field.score(player);
  }
  let moves = trajectories_pruning.calculate_moves(empty_board);
  debug!(target: MINIMAX_STR, "Moves in consideration: {:?}.", moves.iter().map(|&pos| (field.to_x(pos), field.to_y(pos))).collect::<Vec<(u32, u32)>>());
  if moves.is_empty() || should_stop.load(Ordering::Relaxed) {
    *best_move = None;
    return field.score(player);
  }
  let (producer, consumer) = comm::spmc::unbounded::new();
  for pos in moves {
    producer.send(pos).ok();
  }
  let threads_count = config::threads_count();
  let atomic_alpha = AtomicIsize::new(alpha as isize);
  let atomic_best_move = AtomicUsize::new(0);
  let mut guards = Vec::with_capacity(threads_count);
    for _ in 0 .. threads_count {
    guards.push(thread::scoped(|| {
      let local_consumer = consumer.clone();
      let mut local_field = field.clone();
      let mut local_empty_board = iter::repeat(0u32).take(field.length()).collect();
      let enemy = player.next();
      while let Some(pos) = local_consumer.recv_async().ok() {
        if should_stop.load(Ordering::Relaxed) {
          debug!(target: MINIMAX_STR, "Time-out!");
          break;
        }
        local_field.put_point(pos, player);
        let next_trajectories_pruning = TrajectoriesPruning::from_last(&mut local_field, enemy, depth - 1, &mut local_empty_board, &trajectories_pruning, pos);
        if should_stop.load(Ordering::Relaxed) {
          debug!(target: MINIMAX_STR, "Time-out!");
          break;
        }
        let cur_alpha = atomic_alpha.load(Ordering::SeqCst) as i32;
        if cur_alpha >= beta {
          break;
        }
        let mut cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -cur_alpha - 1, -cur_alpha, &mut local_empty_board);
        if cur_estimation > cur_alpha {
          if !should_stop.load(Ordering::Relaxed) {
            cur_estimation = -alpha_beta(&mut local_field, depth - 1, pos, enemy, &next_trajectories_pruning, -beta, -cur_estimation, &mut local_empty_board);
          } else {
            debug!(target: MINIMAX_STR, "Time-out! Next estimation ma be approximated.");
          }
        }
        local_field.undo();
        loop {
          let last_pos = atomic_best_move.load(Ordering::SeqCst);
          let last_alpha = atomic_alpha.load(Ordering::SeqCst);
          if cur_estimation > last_alpha as i32 {
            if atomic_alpha.compare_and_swap(last_alpha, cur_estimation as isize, Ordering::SeqCst) == last_alpha && atomic_best_move.compare_and_swap(last_pos, pos, Ordering::SeqCst) == last_pos {
              debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
              break;
            }
          } else {
            debug!(target: MINIMAX_STR, "{} for move ({}, {}) is {}.", if cur_estimation > cur_alpha { if cur_estimation < beta { "Estimation" } else { "Lower bound of estimation" } } else { "Upper bound of estimation" }, field.to_x(pos), field.to_y(pos), cur_estimation);
            break;
          }
        }
      }
    }));
  }
  drop(guards);
  let result = atomic_best_move.load(Ordering::SeqCst);
  if result != 0 {
    info!(target: MINIMAX_STR, "Best move is ({}, {}).", field.to_x(result), field.to_y(result));
    *best_move = Some(result);
  } else {
    info!(target: MINIMAX_STR, "Best move is not found.");
    *best_move = None;
  }
  let cur_alpha = atomic_alpha.load(Ordering::SeqCst);
  info!(target: MINIMAX_STR, "Estimation is {}.", cur_alpha);
  cur_alpha as i32
}

pub fn minimax(field: &mut Field, player: Player, depth: u32) -> Option<Pos> {
  info!(target: MINIMAX_STR, "Starting minimax with depth {} and player {}.", depth, player);
  if depth == 0 {
    return None;
  }
  let should_stop = AtomicBool::new(false);
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board);
  let mut best_move = None;
  info!(target: MINIMAX_STR, "Calculating of our estimation. Player is {}", player);
  let estimation = alpha_beta_parallel(field, player, depth, i32::min_value() + 1, i32::max_value(), &trajectories_pruning, &mut empty_board, &mut best_move, &should_stop);
  let enemy = player.next();
  let mut enemy_best_move = best_move;
  let enemy_trajectories_pruning = TrajectoriesPruning::dec_exists(&field, enemy, depth - 1, &mut empty_board, &trajectories_pruning);
  info!(target: MINIMAX_STR, "Calculating of enemy estimation with upper bound {}. Player is {}", -estimation + 1, enemy);
  if -alpha_beta_parallel(field, enemy, depth - 1, -estimation, -estimation + 1, &enemy_trajectories_pruning, &mut empty_board, &mut enemy_best_move, &should_stop) < estimation {
    info!(target: MINIMAX_STR,  "Estimation is greater than enemy estimation. So the best move is {:?}, estimation is {}.", best_move.map(|pos| (field.to_x(pos), field.to_y(pos))), estimation);
    best_move
  } else {
    info!(target: MINIMAX_STR,  "Estimation is less than or equal enemy estimation. So all moves have the same estimation {}.", estimation);
    None
  }
}

pub fn minimax_with_time(field: &mut Field, player: Player, time: u32) -> Option<Pos> {
  let should_stop = AtomicBool::new(false);
  let guard = thread::scoped(|| {
    thread::sleep_ms(time);
    should_stop.store(true, Ordering::Relaxed);
  });
  let enemy = player.next();
  let mut depth = 1;
  let mut best_move = None;
  let mut cur_best_move = None;
  let mut enemy_best_move = None;
  let mut empty_board = iter::repeat(0u32).take(field.length()).collect();
  let mut trajectories_pruning = TrajectoriesPruning::new(field, player, depth, &mut empty_board);
  while !should_stop.load(Ordering::Relaxed) {
    let estimation = alpha_beta_parallel(field, player, depth, i32::min_value() + 1, i32::max_value(), &trajectories_pruning, &mut empty_board, &mut cur_best_move, &should_stop);
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    let enemy_trajectories_pruning = TrajectoriesPruning::dec_exists(&field, enemy, depth - 1, &mut empty_board, &trajectories_pruning);
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    best_move = if -alpha_beta_parallel(field, enemy, depth - 1, -estimation, -estimation + 1, &enemy_trajectories_pruning, &mut empty_board, &mut enemy_best_move, &should_stop) < estimation || should_stop.load(Ordering::Relaxed) {
      cur_best_move
    } else {
      None
    };
    if should_stop.load(Ordering::Relaxed) {
      break;
    }
    depth += 1;
    trajectories_pruning = TrajectoriesPruning::inc_exists(field, player, depth, &mut empty_board, &trajectories_pruning);
  }
  drop(guard);
  best_move
}
<|endoftext|>


<commit_before>// Copyright 2017 CoreOS, Inc.
//
// 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.

//! network deals abstracts away the manipulation of network device and
//! interface unit files. All that is left is to write the resulting string to
//! the necessary unit.

use std::net::IpAddr;
use pnet::util::MacAddr;
use std::string::String;
use std::string::ToString;

use ipnetwork::IpNetwork;
use errors::*;

pub const BONDING_MODE_BALANCE_RR: u32 = 0;
pub const BONDING_MODE_ACTIVE_BACKUP: u32 = 1;
pub const BONDING_MODE_BALANCE_XOR: u32 = 2;
pub const BONDING_MODE_BROADCAST: u32 = 3;
pub const BONDING_MODE_LACP: u32 = 4;
pub const BONDING_MODE_BALANCE_TLB: u32 = 5;
pub const BONDING_MODE_BALANCE_ALB: u32 = 6;

const BONDING_MODES: [(u32,&str); 7] = [
    (BONDING_MODE_BALANCE_RR,"balance-rr"),
    (BONDING_MODE_ACTIVE_BACKUP,"active-backup"),
    (BONDING_MODE_BALANCE_XOR,"balance-xor"),
    (BONDING_MODE_BROADCAST,"broadcast"),
    (BONDING_MODE_LACP,"802.3ad"),
    (BONDING_MODE_BALANCE_TLB,"balance-tlb"),
    (BONDING_MODE_BALANCE_ALB,"balance-alb"),
];

pub fn bonding_mode_to_string(mode: &u32) -> Result<String> {
    for &(m,s) in &BONDING_MODES {
        if m == *mode {
            return Ok(s.to_owned())
        }
    }
    Err(format!("no such bonding mode: {}", mode).into())
}

#[derive(Clone, Copy, Debug)]
pub struct NetworkRoute {
    pub destination: IpNetwork,
    pub gateway: IpAddr,
}

/// for naming purposes an interface needs either a name or an address.
/// it can have both. but it can't have neither.
/// there isn't really a way to express this in the type system
/// so we just panic! if it's not what we expected.
/// I guess that there aren't really type systems with inclusive disjunction
/// so it's not really that big of a deal.
#[derive(Clone, Debug)]
pub struct Interface {
    pub name: Option<String>,
    pub mac_address: Option<MacAddr>,
    pub priority: Option<u32>,
    pub nameservers: Vec<IpAddr>,
    pub ip_addresses: Vec<IpNetwork>,
    pub routes: Vec<NetworkRoute>,
    pub bond: Option<String>,
}

#[derive(Clone, Debug)]
pub struct Section {
    pub name: String,
    pub attributes: Vec<(String, String)>,
}

#[derive(Clone, Debug)]
pub struct Device {
    pub name: String,
    pub kind: String,
    pub mac_address: MacAddr,
    pub priority: Option<u32>,
    pub sections: Vec

}

impl Interface {
    pub fn unit_name(&self) -> String {
        format!("{:02}-{}.network",
                self.priority.unwrap_or(10),
                self.name.clone().unwrap_or_else(
                    // needs to be a lambda or we panic immediately
                    // yay, manual thunking!
                    ||self.mac_address.unwrap_or_else(
                        ||panic!("interface needs either name or mac address (or both)")
                    ).to_string()
                ))
    }
    pub fn config(&self) -> String {
        let mut config = String::new();

        // [Match] section
        config.push_str("[Match]\n");
        self.name.clone().map(|name| config.push_str(&format!("Name={}\n", name)));
        self.mac_address.map(|mac| config.push_str(&format!("MACAddress={}\n", mac)));

        // [Network] section
        config.push_str("\n[Network]\n");
        for ns in &self.nameservers {
            config.push_str(&format!("DNS={}\n", ns))
        }
        self.bond.clone().map(|bond| config.push_str(&format!("Bond={}\n", bond)));

        // [Address] sections
        for addr in &self.ip_addresses {
            config.push_str(&format!("\n[Address]\nAddress={}\n", addr));
        }

        // [Route] sections
        for route in &self.routes {
            config.push_str(&format!("\n[Route]\nDestination={}\nGateway={}\n", route.destination, route.gateway));
        }

        config
    }
}

impl Device {
    pub fn unit_name(&self) -> String {
        format!("{:02}-{}.netdev", self.priority.unwrap_or(10), self.name)
    }
    pub fn config(&self) -> String {
        let mut config = String::new();

        // [NetDev] section
        config.push_str("[NetDev]\n");
        config.push_str(&format!("Name={}\n", self.name));
        config.push_str(&format!("Kind={}\n", self.kind));
        config.push_str(&format!("MACAddress={}\n", self.mac_address));

        // custom sections
        for section in &self.sections {
            config.push_str(&format!("\n[{}]\n", section.name));
            for attr in &section.attributes {
                config.push_str(&format!("{}={}\n", attr.0, attr.1));
            }
        }

        config
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::net::{Ipv4Addr, Ipv6Addr};
    use ipnetwork::{Ipv4Network,Ipv6Network};

    #[test]
    fn mac_addr_display() {
        let m = MacAddr(0xf4,0x00,0x34,0x09,0x73,0xee);
        assert_eq!(m.to_string(), "f4:00:34:09:73:ee");
    }

    #[test]
    fn interface_unit_name() {
        let is = vec![
            (Interface {
                name: Some(String::from("lo")),
                mac_address: Some(MacAddr(0,0,0,0,0,0)),
                priority: Some(20),
                nameservers: vec![],
                ip_addresses: vec![],
                routes: vec![],
                bond: None,
            }, "20-lo.network"),
            (Interface {
                name: Some(String::from("lo")),
                mac_address: Some(MacAddr(0,0,0,0,0,0)),
                priority: None,
                nameservers: vec![],
                ip_addresses: vec![],
                routes: vec![],
                bond: None,
            }, "10-lo.network"),
            (Interface {
                name: None,
                mac_address: Some(MacAddr(0,0,0,0,0,0)),
                priority: Some(20),
                nameservers: vec![],
                ip_addresses: vec![],
                routes: vec![],
                bond: None,
            }, "20-00:00:00:00:00:00.network"),
            (Interface {
                name: Some(String::from("lo")),
                mac_address: None,
                priority: Some(20),
                nameservers: vec![],
                ip_addresses: vec![],
                routes: vec![],
                bond: None,
            }, "20-lo.network"),
        ];

        for (i, s) in is {
            assert_eq!(i.unit_name(), s);
        }
    }

    #[test]
    #[should_panic]
    fn interface_unit_name_no_name_no_mac() {
        let i = Interface {
            name: None,
            mac_address: None,
            priority: Some(20),
            nameservers: vec![],
            ip_addresses: vec![],
            routes: vec![],
            bond: None,
        };
        let _name = i.unit_name();
    }

    #[test]
    fn device_unit_name() {
        let ds = vec![
            (Device {
                name: String::from("vlan0"),
                kind: String::from("vlan"),
                mac_address: MacAddr(0,0,0,0,0,0),
                priority: Some(20),
                sections: vec![],
            }, "20-vlan0.netdev"),
            (Device {
                name: String::from("vlan0"),
                kind: String::from("vlan"),
                mac_address: MacAddr(0,0,0,0,0,0),
                priority: None,
                sections: vec![],
            }, "10-vlan0.netdev"),
        ];

        for (d, s) in ds {
            assert_eq!(d.unit_name(), s);
        }
    }

    #[test]
    fn interface_config() {
        let is = vec![
            (Interface {
                name: Some(String::from("lo")),
                mac_address: Some(MacAddr(0,0,0,0,0,0)),
                priority: Some(20),
                nameservers: vec![
                    IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
                    IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
                ],
                ip_addresses: vec![
                    IpNetwork::V4(Ipv4Network::new(
                            Ipv4Addr::new(127, 0, 0, 1),
                            8
                        ).unwrap()),
                    IpNetwork::V6(Ipv6Network::new(
                            Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1),
                            128
                        ).unwrap()),
                ],
                routes: vec![
                    NetworkRoute {
                        destination: IpNetwork::V4(Ipv4Network::new(
                                Ipv4Addr::new(127, 0, 0, 1),
                                8
                            ).unwrap()),
                        gateway: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
                    }
                ],
                bond: Some(String::from("james")),
            }, "[Match]
Name=lo
MACAddress=00:00:00:00:00:00

[Network]
DNS=127.0.0.1
DNS=::1
Bond=james

[Address]
Address=127.0.0.1/8

[Address]
Address=::1/128

[Route]
Destination=127.0.0.1/8
Gateway=127.0.0.1
"),
            // this isn't really a valid interface object, but it's testing
            // the minimum possible configuration for all peices at the same
            // time, so I'll allow it. (sdemos)
            (Interface {
                name: None,
                mac_address: None,
                priority: None,
                nameservers: vec![],
                ip_addresses: vec![],
                routes: vec![],
                bond: None,
            }, "[Match]

[Network]
")
        ];

        for (i, s) in is {
            assert_eq!(i.config(), s);
        }
    }

    #[test]
    fn device_config() {
        let ds = vec![
            (Device {
                name: String::from("vlan0"),
                kind: String::from("vlan"),
                mac_address: MacAddr(0,0,0,0,0,0),
                priority: Some(20),
                sections: vec![
                    Section {
                        name: String::from("Test"),
                        attributes: vec![
                            (String::from("foo"), String::from("bar")),
                            (String::from("oingo"), String::from("boingo")),
                        ]
                    },
                    Section {
                        name: String::from("Empty"),
                        attributes: vec![],
                    }
                ],
            }, "[NetDev]
Name=vlan0
Kind=vlan
MACAddress=00:00:00:00:00:00

[Test]
foo=bar
oingo=boingo

[Empty]
"),
            (Device {
                name: String::from("vlan0"),
                kind: String::from("vlan"),
                mac_address: MacAddr(0,0,0,0,0,0),
                priority: Some(20),
                sections: vec![],
            }, "[NetDev]
Name=vlan0
Kind=vlan
MACAddress=00:00:00:00:00:00
")
        ];

        for (d, s) in ds {
            assert_eq!(d.config(), s);
        }
    }
}
<commit_msg>network: add Eq and PartialEq derives to structs<commit_after>// Copyright 2017 CoreOS, Inc.
//
// 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.

//! network deals abstracts away the manipulation of network device and
//! interface unit files. All that is left is to write the resulting string to
//! the necessary unit.

use std::net::IpAddr;
use pnet::util::MacAddr;
use std::string::String;
use std::string::ToString;

use ipnetwork::IpNetwork;
use errors::*;

pub const BONDING_MODE_BALANCE_RR: u32 = 0;
pub const BONDING_MODE_ACTIVE_BACKUP: u32 = 1;
pub const BONDING_MODE_BALANCE_XOR: u32 = 2;
pub const BONDING_MODE_BROADCAST: u32 = 3;
pub const BONDING_MODE_LACP: u32 = 4;
pub const BONDING_MODE_BALANCE_TLB: u32 = 5;
pub const BONDING_MODE_BALANCE_ALB: u32 = 6;

const BONDING_MODES: [(u32,&str); 7] = [
    (BONDING_MODE_BALANCE_RR,"balance-rr"),
    (BONDING_MODE_ACTIVE_BACKUP,"active-backup"),
    (BONDING_MODE_BALANCE_XOR,"balance-xor"),
    (BONDING_MODE_BROADCAST,"broadcast"),
    (BONDING_MODE_LACP,"802.3ad"),
    (BONDING_MODE_BALANCE_TLB,"balance-tlb"),
    (BONDING_MODE_BALANCE_ALB,"balance-alb"),
];

pub fn bonding_mode_to_string(mode: &u32) -> Result<String> {
    for &(m,s) in &BONDING_MODES {
        if m == *mode {
            return Ok(s.to_owned())
        }
    }
    Err(format!("no such bonding mode: {}", mode).into())
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NetworkRoute {
    pub destination: IpNetwork,
    pub gateway: IpAddr,
}

/// for naming purposes an interface needs either a name or an address.
/// it can have both. but it can't have neither.
/// there isn't really a way to express this in the type system
/// so we just panic! if it's not what we expected.
/// I guess that there aren't really type systems with inclusive disjunction
/// so it's not really that big of a deal.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Interface {
    pub name: Option<String>,
    pub mac_address: Option<MacAddr>,
    pub priority: Option<u32>,
    pub nameservers: Vec<IpAddr>,
    pub ip_addresses: Vec<IpNetwork>,
    pub routes: Vec<NetworkRoute>,
    pub bond: Option<String>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Section {
    pub name: String,
    pub attributes: Vec<(String, String)>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Device {
    pub name: String,
    pub kind: String,
    pub mac_address: MacAddr,
    pub priority: Option<u32>,
    pub sections: Vec

}

impl Interface {
    pub fn unit_name(&self) -> String {
        format!("{:02}-{}.network",
                self.priority.unwrap_or(10),
                self.name.clone().unwrap_or_else(
                    // needs to be a lambda or we panic immediately
                    // yay, manual thunking!
                    ||self.mac_address.unwrap_or_else(
                        ||panic!("interface needs either name or mac address (or both)")
                    ).to_string()
                ))
    }
    pub fn config(&self) -> String {
        let mut config = String::new();

        // [Match] section
        config.push_str("[Match]\n");
        self.name.clone().map(|name| config.push_str(&format!("Name={}\n", name)));
        self.mac_address.map(|mac| config.push_str(&format!("MACAddress={}\n", mac)));

        // [Network] section
        config.push_str("\n[Network]\n");
        for ns in &self.nameservers {
            config.push_str(&format!("DNS={}\n", ns))
        }
        self.bond.clone().map(|bond| config.push_str(&format!("Bond={}\n", bond)));

        // [Address] sections
        for addr in &self.ip_addresses {
            config.push_str(&format!("\n[Address]\nAddress={}\n", addr));
        }

        // [Route] sections
        for route in &self.routes {
            config.push_str(&format!("\n[Route]\nDestination={}\nGateway={}\n", route.destination, route.gateway));
        }

        config
    }
}

impl Device {
    pub fn unit_name(&self) -> String {
        format!("{:02}-{}.netdev", self.priority.unwrap_or(10), self.name)
    }
    pub fn config(&self) -> String {
        let mut config = String::new();

        // [NetDev] section
        config.push_str("[NetDev]\n");
        config.push_str(&format!("Name={}\n", self.name));
        config.push_str(&format!("Kind={}\n", self.kind));
        config.push_str(&format!("MACAddress={}\n", self.mac_address));

        // custom sections
        for section in &self.sections {
            config.push_str(&format!("\n[{}]\n", section.name));
            for attr in &section.attributes {
                config.push_str(&format!("{}={}\n", attr.0, attr.1));
            }
        }

        config
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::net::{Ipv4Addr, Ipv6Addr};
    use ipnetwork::{Ipv4Network,Ipv6Network};

    #[test]
    fn mac_addr_display() {
        let m = MacAddr(0xf4,0x00,0x34,0x09,0x73,0xee);
        assert_eq!(m.to_string(), "f4:00:34:09:73:ee");
    }

    #[test]
    fn interface_unit_name() {
        let is = vec![
            (Interface {
                name: Some(String::from("lo")),
                mac_address: Some(MacAddr(0,0,0,0,0,0)),
                priority: Some(20),
                nameservers: vec![],
                ip_addresses: vec![],
                routes: vec![],
                bond: None,
            }, "20-lo.network"),
            (Interface {
                name: Some(String::from("lo")),
                mac_address: Some(MacAddr(0,0,0,0,0,0)),
                priority: None,
                nameservers: vec![],
                ip_addresses: vec![],
                routes: vec![],
                bond: None,
            }, "10-lo.network"),
            (Interface {
                name: None,
                mac_address: Some(MacAddr(0,0,0,0,0,0)),
                priority: Some(20),
                nameservers: vec![],
                ip_addresses: vec![],
                routes: vec![],
                bond: None,
            }, "20-00:00:00:00:00:00.network"),
            (Interface {
                name: Some(String::from("lo")),
                mac_address: None,
                priority: Some(20),
                nameservers: vec![],
                ip_addresses: vec![],
                routes: vec![],
                bond: None,
            }, "20-lo.network"),
        ];

        for (i, s) in is {
            assert_eq!(i.unit_name(), s);
        }
    }

    #[test]
    #[should_panic]
    fn interface_unit_name_no_name_no_mac() {
        let i = Interface {
            name: None,
            mac_address: None,
            priority: Some(20),
            nameservers: vec![],
            ip_addresses: vec![],
            routes: vec![],
            bond: None,
        };
        let _name = i.unit_name();
    }

    #[test]
    fn device_unit_name() {
        let ds = vec![
            (Device {
                name: String::from("vlan0"),
                kind: String::from("vlan"),
                mac_address: MacAddr(0,0,0,0,0,0),
                priority: Some(20),
                sections: vec![],
            }, "20-vlan0.netdev"),
            (Device {
                name: String::from("vlan0"),
                kind: String::from("vlan"),
                mac_address: MacAddr(0,0,0,0,0,0),
                priority: None,
                sections: vec![],
            }, "10-vlan0.netdev"),
        ];

        for (d, s) in ds {
            assert_eq!(d.unit_name(), s);
        }
    }

    #[test]
    fn interface_config() {
        let is = vec![
            (Interface {
                name: Some(String::from("lo")),
                mac_address: Some(MacAddr(0,0,0,0,0,0)),
                priority: Some(20),
                nameservers: vec![
                    IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
                    IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
                ],
                ip_addresses: vec![
                    IpNetwork::V4(Ipv4Network::new(
                            Ipv4Addr::new(127, 0, 0, 1),
                            8
                        ).unwrap()),
                    IpNetwork::V6(Ipv6Network::new(
                            Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1),
                            128
                        ).unwrap()),
                ],
                routes: vec![
                    NetworkRoute {
                        destination: IpNetwork::V4(Ipv4Network::new(
                                Ipv4Addr::new(127, 0, 0, 1),
                                8
                            ).unwrap()),
                        gateway: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
                    }
                ],
                bond: Some(String::from("james")),
            }, "[Match]
Name=lo
MACAddress=00:00:00:00:00:00

[Network]
DNS=127.0.0.1
DNS=::1
Bond=james

[Address]
Address=127.0.0.1/8

[Address]
Address=::1/128

[Route]
Destination=127.0.0.1/8
Gateway=127.0.0.1
"),
            // this isn't really a valid interface object, but it's testing
            // the minimum possible configuration for all peices at the same
            // time, so I'll allow it. (sdemos)
            (Interface {
                name: None,
                mac_address: None,
                priority: None,
                nameservers: vec![],
                ip_addresses: vec![],
                routes: vec![],
                bond: None,
            }, "[Match]

[Network]
")
        ];

        for (i, s) in is {
            assert_eq!(i.config(), s);
        }
    }

    #[test]
    fn device_config() {
        let ds = vec![
            (Device {
                name: String::from("vlan0"),
                kind: String::from("vlan"),
                mac_address: MacAddr(0,0,0,0,0,0),
                priority: Some(20),
                sections: vec![
                    Section {
                        name: String::from("Test"),
                        attributes: vec![
                            (String::from("foo"), String::from("bar")),
                            (String::from("oingo"), String::from("boingo")),
                        ]
                    },
                    Section {
                        name: String::from("Empty"),
                        attributes: vec![],
                    }
                ],
            }, "[NetDev]
Name=vlan0
Kind=vlan
MACAddress=00:00:00:00:00:00

[Test]
foo=bar
oingo=boingo

[Empty]
"),
            (Device {
                name: String::from("vlan0"),
                kind: String::from("vlan"),
                mac_address: MacAddr(0,0,0,0,0,0),
                priority: Some(20),
                sections: vec![],
            }, "[NetDev]
Name=vlan0
Kind=vlan
MACAddress=00:00:00:00:00:00
")
        ];

        for (d, s) in ds {
            assert_eq!(d.config(), s);
        }
    }
}
<|endoftext|>