<commit_before>#[macro_use]
extern crate pretty_assertions;

extern crate rexpect;

use rexpect::spawn;

use std::env;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::process::{Command, Stdio};

#[test]
fn fmt_repl() {
    let source = "src/repl.glu";

    let mut before = String::new();
    File::open(source)
        .unwrap()
        .read_to_string(&mut before)
        .unwrap();

    let status = Command::new("../target/debug/gluon")
        .args(&["fmt", source])
        .spawn()
        .expect("Could not find gluon executable")
        .wait()
        .unwrap();
    assert!(status.success());

    let mut after = String::new();
    File::open(source)
        .unwrap()
        .read_to_string(&mut after)
        .unwrap();

    assert_eq!(before, after);
}

#[test]
fn issue_365_run_io_from_command_line() {
    if ::std::env::var("GLUON_PATH").is_err() {
        ::std::env::set_var("GLUON_PATH", "..");
    }

    let path = env::args().next().unwrap();
    let gluon_path = Path::new(&path[..])
        .parent()
        .and_then(|p| p.parent())
        .expect("folder")
        .join("gluon");
    let output = Command::new(&*gluon_path)
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .arg("tests/print.glu")
        .output()
        .unwrap_or_else(|err| panic!("{}\nWhen opening `{}`", err, gluon_path.display()));

    let stderr = String::from_utf8_lossy(&output.stderr);
    if stderr != "" {
        panic!("{}", stderr);
    }
    assert_eq!(String::from_utf8_lossy(&output.stdout), "123\n");
}

static TIMEOUT: u64 = 10_000;
static PROMPT: &str = ">";

#[test]
fn prompt() {
    let mut repl = spawn("../target/debug/gluon -i", Some(TIMEOUT)).unwrap();
    repl.exp_string(PROMPT).unwrap();
}

#[test]
fn exit() {
    let mut repl = spawn("../target/debug/gluon -i", Some(TIMEOUT)).unwrap();
    repl.exp_string(PROMPT).unwrap();

    repl.send_line(":q").unwrap();
    repl.exp_eof().unwrap();
}

#[test]
fn hello_world() {
    let mut repl = spawn("../target/debug/gluon -i", Some(TIMEOUT)).unwrap();
    repl.exp_string(PROMPT).unwrap();

    repl.send_line("let io = import! std.io").unwrap();

    repl.send_line("io.println \"Hello world\"").unwrap();
    repl.exp_string("Hello world").unwrap();
}

#[test]
fn names() {
    let mut repl = spawn("../target/debug/gluon -i", Some(TIMEOUT)).unwrap();
    repl.exp_string(PROMPT).unwrap();

    repl.send_line(":i std.prelude.List").unwrap();
    repl.exp_string("type std.prelude.List a = | Nil | Cons a (std.prelude.List a)").unwrap();
}

#[test]
fn expression_types() {
    let mut repl = spawn("../target/debug/gluon -i", Some(TIMEOUT)).unwrap();
    repl.exp_string(PROMPT).unwrap();

    repl.send_line(":t 5").unwrap();
    repl.exp_string("Int").unwrap();

    repl.send_line(":t 5 + 5").unwrap();
    repl.exp_string("Int -> Int").unwrap();

    repl.send_line(":t \"gluon\"").unwrap();
    repl.exp_string("String").unwrap();
}

#[test]
fn comments() {
    let mut repl = spawn("../target/debug/gluon -i", Some(TIMEOUT)).unwrap();
    repl.exp_string(PROMPT).unwrap();

    repl.send_line("1 + 2 // Calls the + function on 1 and 2").unwrap();
    repl.exp_string("3").unwrap();

    repl.send_line("1 + 2 /* Calls the + function on 1 and 2 */").unwrap();
    repl.exp_string("3").unwrap();
}

#[test]
fn if_expressions() {
    let mut repl = spawn("../target/debug/gluon -i", Some(TIMEOUT)).unwrap();
    repl.exp_string(PROMPT).unwrap();

    repl.send_line("if True then 1 else 0").unwrap();
    repl.exp_string("1").unwrap();

    repl.send_line("if False then 1 else 0").unwrap();
    repl.exp_string("0").unwrap();
}

#[test]
fn records() {
    let mut repl = spawn("../target/debug/gluon -i", Some(TIMEOUT)).unwrap();
    repl.exp_string(PROMPT).unwrap();

    repl.send_line("let record = { pi = 3.14, add1 = (+) 1.0 }").unwrap();

    repl.send_line("record.pi").unwrap();
    repl.exp_string("3.14").unwrap();

    repl.send_line("let record_2 = {x = 1 .. record }").unwrap();

    repl.send_line("record_2.x").unwrap();
    repl.exp_string("1").unwrap();

    repl.send_line("record_2.pi").unwrap();
    repl.exp_string("3.14").unwrap();
}

#[test]
fn arrays() {
    let mut repl = spawn("../target/debug/gluon -i", Some(TIMEOUT)).unwrap();
    repl.exp_string(PROMPT).unwrap();

    repl.send_line("let array = import! std.array").unwrap();
    repl.send_line("array.len [1, 2, 3]").unwrap();
    repl.exp_string("3").unwrap();
}

<commit_msg>Improve test errors output with macro.<commit_after>#[macro_use]
extern crate pretty_assertions;

extern crate rexpect;

use rexpect::spawn;

use std::env;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::process::{Command, Stdio};

#[test]
fn fmt_repl() {
    let source = "src/repl.glu";

    let mut before = String::new();
    File::open(source)
        .unwrap()
        .read_to_string(&mut before)
        .unwrap();

    let status = Command::new("../target/debug/gluon")
        .args(&["fmt", source])
        .spawn()
        .expect("Could not find gluon executable")
        .wait()
        .unwrap();
    assert!(status.success());

    let mut after = String::new();
    File::open(source)
        .unwrap()
        .read_to_string(&mut after)
        .unwrap();

    assert_eq!(before, after);
}

#[test]
fn issue_365_run_io_from_command_line() {
    if ::std::env::var("GLUON_PATH").is_err() {
        ::std::env::set_var("GLUON_PATH", "..");
    }

    let path = env::args().next().unwrap();
    let gluon_path = Path::new(&path[..])
        .parent()
        .and_then(|p| p.parent())
        .expect("folder")
        .join("gluon");
    let output = Command::new(&*gluon_path)
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .arg("tests/print.glu")
        .output()
        .unwrap_or_else(|err| panic!("{}\nWhen opening `{}`", err, gluon_path.display()));

    let stderr = String::from_utf8_lossy(&output.stderr);
    if stderr != "" {
        panic!("{}", stderr);
    }
    assert_eq!(String::from_utf8_lossy(&output.stdout), "123\n");
}

static COMMAND: &str = "../target/debug/gluon -i";
static TIMEOUT: u64 = 10_000;
static PROMPT: &str = "> ";

macro_rules! test {
    ($e: expr) => { $e.unwrap_or_else(|err| panic!("{}", err)) }
}

#[test]
fn prompt() {
    let mut repl = test!(spawn(COMMAND, Some(TIMEOUT)));
    test!(repl.exp_string(PROMPT));
}

#[test]
fn exit() {
    let mut repl = test!(spawn(COMMAND, Some(TIMEOUT)));
    test!(repl.exp_string(PROMPT));

    test!(repl.send_line(":q"));
    test!(repl.exp_eof());
}

#[test]
#[ignore] // TODO fix intermittent test failure
fn hello_world() {
    let mut repl = test!(spawn(COMMAND, Some(TIMEOUT)));
    test!(repl.exp_string(PROMPT));

    test!(repl.send_line("let io = import! std.io"));

    test!(repl.send_line("io.println \"Hello world\""));
    test!(repl.exp_string("Hello world"));
}

#[test]
fn expression_types() {
    let mut repl = test!(spawn(COMMAND, Some(TIMEOUT)));
    test!(repl.exp_string(PROMPT));

    test!(repl.send_line(":t 5"));
    test!(repl.exp_string("Int"));

    test!(repl.send_line(":t 5 + 5"));
    test!(repl.exp_string("Int -> Int"));

    test!(repl.send_line(":t \"gluon\""));
    test!(repl.exp_string("String"));
}

#[test]
fn names() {
    let mut repl = test!(spawn(COMMAND, Some(TIMEOUT)));
    test!(repl.exp_string(PROMPT));

    test!(repl.send_line(":i std.prelude.show"));
    test!(repl.exp_string("std.prelude.show: forall a . [std.prelude.Show a] -> a -> String"));
}

#[test]
fn comments() {
    let mut repl = test!(spawn(COMMAND, Some(TIMEOUT)));
    test!(repl.exp_string(PROMPT));

    test!(repl.send_line("1 + 2 // Calls the + function on 1 and 2"));
    test!(repl.exp_string("3"));

    test!(repl.send_line("1 + 2 /* Calls the + function on 1 and 2 */"));
    test!(repl.exp_string("3"));
}

#[test]
fn if_expressions() {
    let mut repl = test!(spawn(COMMAND, Some(TIMEOUT)));
    test!(repl.exp_string(PROMPT));

    test!(repl.send_line("if True then 1 else 0"));
    test!(repl.exp_string("1"));

    test!(repl.send_line("if False then 1 else 0"));
    test!(repl.exp_string("0"));
}

#[test]
fn records() {
    let mut repl = test!(spawn(COMMAND, Some(TIMEOUT)));
    test!(repl.exp_string(PROMPT));

    test!(repl.send_line("let record = { pi = 3.14, add1 = (+) 1.0 }"));

    test!(repl.send_line("record.pi"));
    test!(repl.exp_string("3.14"));

    test!(repl.send_line("let record_2 = {x = 1 .. record }"));

    test!(repl.send_line("record_2.x"));
    test!(repl.exp_string("1"));

    test!(repl.send_line("record_2.pi"));
    test!(repl.exp_string("3.14"));
}

#[test]
fn arrays() {
    let mut repl = test!(spawn(COMMAND, Some(TIMEOUT)));
    test!(repl.exp_string(PROMPT));

    test!(repl.send_line("let array = import! std.array"));

    test!(repl.send_line("array.len [1, 2, 3]"));
    test!(repl.exp_string("3"));
}

<|endoftext|>


<commit_before>use super::Hash256;
use crate::test_utils::TestRandom;
use bls::PublicKey;
use rand::RngCore;
use serde_derive::Serialize;
use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash};

const STATUS_FLAG_INITIATED_EXIT: u8 = 1;
const STATUS_FLAG_WITHDRAWABLE: u8 = 2;

#[derive(Debug, PartialEq, Clone, Copy, Serialize)]
pub enum StatusFlags {
    InitiatedExit,
    Withdrawable,
}

struct StatusFlagsDecodeError;

impl From<StatusFlagsDecodeError> for DecodeError {
    fn from(_: StatusFlagsDecodeError) -> DecodeError {
        DecodeError::Invalid
    }
}

/// Handles the serialization logic for the `status_flags` field of the `Validator`.
fn status_flag_to_byte(flag: Option<StatusFlags>) -> u8 {
    if let Some(flag) = flag {
        match flag {
            StatusFlags::InitiatedExit => STATUS_FLAG_INITIATED_EXIT,
            StatusFlags::Withdrawable => STATUS_FLAG_WITHDRAWABLE,
        }
    } else {
        0
    }
}

/// Handles the deserialization logic for the `status_flags` field of the `Validator`.
fn status_flag_from_byte(flag: u8) -> Result<Option<StatusFlags>, StatusFlagsDecodeError> {
    match flag {
        0 => Ok(None),
        1 => Ok(Some(StatusFlags::InitiatedExit)),
        2 => Ok(Some(StatusFlags::Withdrawable)),
        _ => Err(StatusFlagsDecodeError),
    }
}

#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct Validator {
    pub pubkey: PublicKey,
    pub withdrawal_credentials: Hash256,
    pub proposer_slots: u64,
    pub activation_slot: u64,
    pub exit_slot: u64,
    pub withdrawal_slot: u64,
    pub penalized_slot: u64,
    pub exit_count: u64,
    pub status_flags: Option<StatusFlags>,
    pub latest_custody_reseed_slot: u64,
    pub penultimate_custody_reseed_slot: u64,
}

impl Validator {
    /// This predicate indicates if the validator represented by this record is considered "active" at `slot`.
    pub fn is_active_at(&self, slot: u64) -> bool {
        self.activation_slot <= slot && slot < self.exit_slot
    }
}

impl Default for Validator {
    /// Yields a "default" `Validator`. Primarily used for testing.
    fn default() -> Self {
        Self {
            pubkey: PublicKey::default(),
            withdrawal_credentials: Hash256::default(),
            proposer_slots: 0,
            activation_slot: std::u64::MAX,
            exit_slot: std::u64::MAX,
            withdrawal_slot: std::u64::MAX,
            penalized_slot: std::u64::MAX,
            exit_count: 0,
            status_flags: None,
            latest_custody_reseed_slot: 0, // NOTE: is `GENESIS_SLOT`
            penultimate_custody_reseed_slot: 0, // NOTE: is `GENESIS_SLOT`
        }
    }
}

impl<T: RngCore> TestRandom<T> for StatusFlags {
    fn random_for_test(rng: &mut T) -> Self {
        let options = vec![StatusFlags::InitiatedExit, StatusFlags::Withdrawable];
        options[(rng.next_u32() as usize) % options.len()].clone()
    }
}

impl Encodable for Validator {
    fn ssz_append(&self, s: &mut SszStream) {
        s.append(&self.pubkey);
        s.append(&self.withdrawal_credentials);
        s.append(&self.proposer_slots);
        s.append(&self.activation_slot);
        s.append(&self.exit_slot);
        s.append(&self.withdrawal_slot);
        s.append(&self.penalized_slot);
        s.append(&self.exit_count);
        s.append(&status_flag_to_byte(self.status_flags));
        s.append(&self.latest_custody_reseed_slot);
        s.append(&self.penultimate_custody_reseed_slot);
    }
}

impl Decodable for Validator {
    fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
        let (pubkey, i) = <_>::ssz_decode(bytes, i)?;
        let (withdrawal_credentials, i) = <_>::ssz_decode(bytes, i)?;
        let (proposer_slots, i) = <_>::ssz_decode(bytes, i)?;
        let (activation_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (exit_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (withdrawal_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (penalized_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (exit_count, i) = <_>::ssz_decode(bytes, i)?;
        let (status_flags_byte, i): (u8, usize) = <_>::ssz_decode(bytes, i)?;
        let (latest_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (penultimate_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?;

        let status_flags = status_flag_from_byte(status_flags_byte)?;

        Ok((
            Self {
                pubkey,
                withdrawal_credentials,
                proposer_slots,
                activation_slot,
                exit_slot,
                withdrawal_slot,
                penalized_slot,
                exit_count,
                status_flags,
                latest_custody_reseed_slot,
                penultimate_custody_reseed_slot,
            },
            i,
        ))
    }
}

impl TreeHash for Validator {
    fn hash_tree_root(&self) -> Vec<u8> {
        let mut result: Vec<u8> = vec![];
        result.append(&mut self.pubkey.hash_tree_root());
        result.append(&mut self.withdrawal_credentials.hash_tree_root());
        result.append(&mut self.proposer_slots.hash_tree_root());
        result.append(&mut self.activation_slot.hash_tree_root());
        result.append(&mut self.exit_slot.hash_tree_root());
        result.append(&mut self.withdrawal_slot.hash_tree_root());
        result.append(&mut self.penalized_slot.hash_tree_root());
        result.append(&mut self.exit_count.hash_tree_root());
        result.append(&mut (status_flag_to_byte(self.status_flags) as u64).hash_tree_root());
        result.append(&mut self.latest_custody_reseed_slot.hash_tree_root());
        result.append(&mut self.penultimate_custody_reseed_slot.hash_tree_root());
        hash(&result)
    }
}

impl<T: RngCore> TestRandom<T> for Validator {
    fn random_for_test(rng: &mut T) -> Self {
        Self {
            pubkey: <_>::random_for_test(rng),
            withdrawal_credentials: <_>::random_for_test(rng),
            proposer_slots: <_>::random_for_test(rng),
            activation_slot: <_>::random_for_test(rng),
            exit_slot: <_>::random_for_test(rng),
            withdrawal_slot: <_>::random_for_test(rng),
            penalized_slot: <_>::random_for_test(rng),
            exit_count: <_>::random_for_test(rng),
            status_flags: Some(<_>::random_for_test(rng)),
            latest_custody_reseed_slot: <_>::random_for_test(rng),
            penultimate_custody_reseed_slot: <_>::random_for_test(rng),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
    use ssz::ssz_encode;

    #[test]
    pub fn test_ssz_round_trip() {
        let mut rng = XorShiftRng::from_seed([42; 16]);
        let original = Validator::random_for_test(&mut rng);

        let bytes = ssz_encode(&original);
        let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap();

        assert_eq!(original, decoded);
    }

    #[test]
    fn test_validator_can_be_active() {
        let mut rng = XorShiftRng::from_seed([42; 16]);
        let mut validator = Validator::random_for_test(&mut rng);

        let activation_slot = u64::random_for_test(&mut rng);
        let exit_slot = activation_slot + 234;

        validator.activation_slot = activation_slot;
        validator.exit_slot = exit_slot;

        for slot in (activation_slot - 100)..(exit_slot + 100) {
            if slot < activation_slot {
                assert!(!validator.is_active_at(slot));
            } else if slot >= exit_slot {
                assert!(!validator.is_active_at(slot));
            } else {
                assert!(validator.is_active_at(slot));
            }
        }
    }

    #[test]
    pub fn test_hash_tree_root() {
        let mut rng = XorShiftRng::from_seed([42; 16]);
        let original = Validator::random_for_test(&mut rng);

        let result = original.hash_tree_root();

        assert_eq!(result.len(), 32);
        // TODO: Add further tests
        // https://github.com/sigp/lighthouse/issues/170
    }
}
<commit_msg>Change bls import method for validator<commit_after>use super::Hash256;
use crate::{test_utils::TestRandom, PublicKey};
use rand::RngCore;
use serde_derive::Serialize;
use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash};

const STATUS_FLAG_INITIATED_EXIT: u8 = 1;
const STATUS_FLAG_WITHDRAWABLE: u8 = 2;

#[derive(Debug, PartialEq, Clone, Copy, Serialize)]
pub enum StatusFlags {
    InitiatedExit,
    Withdrawable,
}

struct StatusFlagsDecodeError;

impl From<StatusFlagsDecodeError> for DecodeError {
    fn from(_: StatusFlagsDecodeError) -> DecodeError {
        DecodeError::Invalid
    }
}

/// Handles the serialization logic for the `status_flags` field of the `Validator`.
fn status_flag_to_byte(flag: Option<StatusFlags>) -> u8 {
    if let Some(flag) = flag {
        match flag {
            StatusFlags::InitiatedExit => STATUS_FLAG_INITIATED_EXIT,
            StatusFlags::Withdrawable => STATUS_FLAG_WITHDRAWABLE,
        }
    } else {
        0
    }
}

/// Handles the deserialization logic for the `status_flags` field of the `Validator`.
fn status_flag_from_byte(flag: u8) -> Result<Option<StatusFlags>, StatusFlagsDecodeError> {
    match flag {
        0 => Ok(None),
        1 => Ok(Some(StatusFlags::InitiatedExit)),
        2 => Ok(Some(StatusFlags::Withdrawable)),
        _ => Err(StatusFlagsDecodeError),
    }
}

#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct Validator {
    pub pubkey: PublicKey,
    pub withdrawal_credentials: Hash256,
    pub proposer_slots: u64,
    pub activation_slot: u64,
    pub exit_slot: u64,
    pub withdrawal_slot: u64,
    pub penalized_slot: u64,
    pub exit_count: u64,
    pub status_flags: Option<StatusFlags>,
    pub latest_custody_reseed_slot: u64,
    pub penultimate_custody_reseed_slot: u64,
}

impl Validator {
    /// This predicate indicates if the validator represented by this record is considered "active" at `slot`.
    pub fn is_active_at(&self, slot: u64) -> bool {
        self.activation_slot <= slot && slot < self.exit_slot
    }
}

impl Default for Validator {
    /// Yields a "default" `Validator`. Primarily used for testing.
    fn default() -> Self {
        Self {
            pubkey: PublicKey::default(),
            withdrawal_credentials: Hash256::default(),
            proposer_slots: 0,
            activation_slot: std::u64::MAX,
            exit_slot: std::u64::MAX,
            withdrawal_slot: std::u64::MAX,
            penalized_slot: std::u64::MAX,
            exit_count: 0,
            status_flags: None,
            latest_custody_reseed_slot: 0, // NOTE: is `GENESIS_SLOT`
            penultimate_custody_reseed_slot: 0, // NOTE: is `GENESIS_SLOT`
        }
    }
}

impl<T: RngCore> TestRandom<T> for StatusFlags {
    fn random_for_test(rng: &mut T) -> Self {
        let options = vec![StatusFlags::InitiatedExit, StatusFlags::Withdrawable];
        options[(rng.next_u32() as usize) % options.len()].clone()
    }
}

impl Encodable for Validator {
    fn ssz_append(&self, s: &mut SszStream) {
        s.append(&self.pubkey);
        s.append(&self.withdrawal_credentials);
        s.append(&self.proposer_slots);
        s.append(&self.activation_slot);
        s.append(&self.exit_slot);
        s.append(&self.withdrawal_slot);
        s.append(&self.penalized_slot);
        s.append(&self.exit_count);
        s.append(&status_flag_to_byte(self.status_flags));
        s.append(&self.latest_custody_reseed_slot);
        s.append(&self.penultimate_custody_reseed_slot);
    }
}

impl Decodable for Validator {
    fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> {
        let (pubkey, i) = <_>::ssz_decode(bytes, i)?;
        let (withdrawal_credentials, i) = <_>::ssz_decode(bytes, i)?;
        let (proposer_slots, i) = <_>::ssz_decode(bytes, i)?;
        let (activation_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (exit_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (withdrawal_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (penalized_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (exit_count, i) = <_>::ssz_decode(bytes, i)?;
        let (status_flags_byte, i): (u8, usize) = <_>::ssz_decode(bytes, i)?;
        let (latest_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?;
        let (penultimate_custody_reseed_slot, i) = <_>::ssz_decode(bytes, i)?;

        let status_flags = status_flag_from_byte(status_flags_byte)?;

        Ok((
            Self {
                pubkey,
                withdrawal_credentials,
                proposer_slots,
                activation_slot,
                exit_slot,
                withdrawal_slot,
                penalized_slot,
                exit_count,
                status_flags,
                latest_custody_reseed_slot,
                penultimate_custody_reseed_slot,
            },
            i,
        ))
    }
}

impl TreeHash for Validator {
    fn hash_tree_root(&self) -> Vec<u8> {
        let mut result: Vec<u8> = vec![];
        result.append(&mut self.pubkey.hash_tree_root());
        result.append(&mut self.withdrawal_credentials.hash_tree_root());
        result.append(&mut self.proposer_slots.hash_tree_root());
        result.append(&mut self.activation_slot.hash_tree_root());
        result.append(&mut self.exit_slot.hash_tree_root());
        result.append(&mut self.withdrawal_slot.hash_tree_root());
        result.append(&mut self.penalized_slot.hash_tree_root());
        result.append(&mut self.exit_count.hash_tree_root());
        result.append(&mut (status_flag_to_byte(self.status_flags) as u64).hash_tree_root());
        result.append(&mut self.latest_custody_reseed_slot.hash_tree_root());
        result.append(&mut self.penultimate_custody_reseed_slot.hash_tree_root());
        hash(&result)
    }
}

impl<T: RngCore> TestRandom<T> for Validator {
    fn random_for_test(rng: &mut T) -> Self {
        Self {
            pubkey: <_>::random_for_test(rng),
            withdrawal_credentials: <_>::random_for_test(rng),
            proposer_slots: <_>::random_for_test(rng),
            activation_slot: <_>::random_for_test(rng),
            exit_slot: <_>::random_for_test(rng),
            withdrawal_slot: <_>::random_for_test(rng),
            penalized_slot: <_>::random_for_test(rng),
            exit_count: <_>::random_for_test(rng),
            status_flags: Some(<_>::random_for_test(rng)),
            latest_custody_reseed_slot: <_>::random_for_test(rng),
            penultimate_custody_reseed_slot: <_>::random_for_test(rng),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
    use ssz::ssz_encode;

    #[test]
    pub fn test_ssz_round_trip() {
        let mut rng = XorShiftRng::from_seed([42; 16]);
        let original = Validator::random_for_test(&mut rng);

        let bytes = ssz_encode(&original);
        let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap();

        assert_eq!(original, decoded);
    }

    #[test]
    fn test_validator_can_be_active() {
        let mut rng = XorShiftRng::from_seed([42; 16]);
        let mut validator = Validator::random_for_test(&mut rng);

        let activation_slot = u64::random_for_test(&mut rng);
        let exit_slot = activation_slot + 234;

        validator.activation_slot = activation_slot;
        validator.exit_slot = exit_slot;

        for slot in (activation_slot - 100)..(exit_slot + 100) {
            if slot < activation_slot {
                assert!(!validator.is_active_at(slot));
            } else if slot >= exit_slot {
                assert!(!validator.is_active_at(slot));
            } else {
                assert!(validator.is_active_at(slot));
            }
        }
    }

    #[test]
    pub fn test_hash_tree_root() {
        let mut rng = XorShiftRng::from_seed([42; 16]);
        let original = Validator::random_for_test(&mut rng);

        let result = original.hash_tree_root();

        assert_eq!(result.len(), 32);
        // TODO: Add further tests
        // https://github.com/sigp/lighthouse/issues/170
    }
}
<|endoftext|>


<commit_before>pub trait Property<T> {
    fn inherits_automatically() -> bool;
    fn compute(&self, _: &T) -> Self;
}

/// Generates a property definition that simply parses strings to enum variants
/// or to a tuple struct of the given type.
///
/// For example, the SVG spec defines the `stroke-linejoin` property
/// to have possible values `miter | round | bevel | inherit`, with a default
/// of `miter`.  We can define the property like this:
///
/// ```ignore
/// make_property!(
/// StrokeLinejoin,
/// default: Miter,
///
/// "miter" => Miter,
/// "round" => Round,
/// "bevel" => Bevel,
/// );
/// ```
///
/// The macro will generate a `StrokeLinejoin` enum with the provided
/// variants.  It will generate an `impl Default for StrokeLinejoin`
/// with the provided `default:` value.  Finally, it will generate an
/// `impl Parse for StrokeLinejoin`, from `parsers::Parse`, where
/// `type Data = ()` and `type Err = ValueErrorKind`.
#[macro_export]
macro_rules! make_property {
    ($computed_values_type: ty,
     $name: ident,
     default: $default: ident,
     inherits_automatically: $inherits_automatically: expr,
     identifiers:
     $($str_prop: expr => $variant: ident,)+
    ) => {
        #[derive(Debug, Copy, Clone, PartialEq)]
        pub enum $name {
            $($variant),+
        }

        impl_default!($name, $name::$default);
        impl_property!($computed_values_type, $name, $inherits_automatically);

        impl crate::parsers::Parse for $name {
            type Err = crate::error::ValueErrorKind;

            fn parse(parser: &mut ::cssparser::Parser<'_, '_>) -> Result<$name, crate::error::ValueErrorKind> {
                let loc = parser.current_source_location();

                parser
                    .expect_ident()
                    .and_then(|cow| match cow.as_ref() {
                        $($str_prop => Ok($name::$variant),)+

                            _ => Err(
                                loc.new_basic_unexpected_token_error(
                                    ::cssparser::Token::Ident(::cssparser::CowRcStr::from(
                                        cow.as_ref().to_string(),
                                    ))),
                            ),
                    })
                    .map_err(|_| {
                        crate::error::ValueErrorKind::Parse(crate::parsers::ParseError::new(
                            "unexpected value",
                        ))
                    })
            }
        }
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     inherits_automatically: $inherits_automatically: expr,
     newtype_parse: $type: ty,
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));

        impl crate::property_macros::Property<$computed_values_type> for $name {
            fn inherits_automatically() -> bool {
                $inherits_automatically
            }

            fn compute(&self, _v: &$computed_values_type) -> Self {
                self.clone()
            }
        }

        impl crate::parsers::Parse for $name {
            type Err = crate::error::ValueErrorKind;

            fn parse(parser: &mut ::cssparser::Parser<'_, '_>) -> Result<$name, crate::error::ValueErrorKind> {
                Ok($name(<$type as crate::parsers::Parse>::parse(parser)?))
            }
        }
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     newtype_parse: $type: ty,
     property_impl: { $prop: item }
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));

        $prop

        impl crate::parsers::Parse for $name {
            type Err = crate::error::ValueErrorKind;

            fn parse(parser: &mut ::cssparser::Parser<'_, '_>) -> Result<$name, crate::error::ValueErrorKind> {
                Ok($name(<$type as crate::parsers::Parse>::parse(parser)?))
            }
        }
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     newtype: $type: ty,
     property_impl: { $prop: item },
     parse_impl: { $parse: item }
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));

        $prop

        $parse
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     inherits_automatically: $inherits_automatically: expr,
     newtype: $type: ty,
     parse_impl: { $parse: item },
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));
        impl_property!($computed_values_type, $name, $inherits_automatically);

        $parse
    };

    ($computed_values_type: ty,
     $name: ident,
     inherits_automatically: $inherits_automatically: expr,
     fields: {
       $($field_name: ident : $field_type: ty, default: $field_default : expr,)+
     }
     parse_impl: { $parse: item }
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name {
            $(pub $field_name: $field_type),+
        }

        impl_default!($name, $name { $($field_name: $field_default),+ });
        impl_property!($computed_values_type, $name, $inherits_automatically);

        $parse
    };
}

macro_rules! impl_default {
    ($name:ident, $default:expr) => {
        impl Default for $name {
            fn default() -> $name {
                $default
            }
        }
    };
}

macro_rules! impl_property {
    ($computed_values_type:ty, $name:ident, $inherits_automatically:expr) => {
        impl crate::property_macros::Property<$computed_values_type> for $name {
            fn inherits_automatically() -> bool {
                $inherits_automatically
            }

            fn compute(&self, _v: &$computed_values_type) -> Self {
                self.clone()
            }
        }
    };
}

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

    use crate::parsers::Parse;
    use cssparser::RGBA;

    #[test]
    fn check_generated_property() {
        make_property! {
            (),
            Foo,
            default: Def,
            inherits_automatically: true,

            identifiers:
            "def" => Def,
            "bar" => Bar,
            "baz" => Baz,
        }

        assert_eq!(<Foo as Default>::default(), Foo::Def);
        assert_eq!(<Foo as Property<()>>::inherits_automatically(), true);
        assert!(<Foo as Parse>::parse_str("blargh").is_err());
        assert_eq!(<Foo as Parse>::parse_str("bar"), Ok(Foo::Bar));
    }

    #[test]
    fn check_compute() {
        make_property! {
            RGBA,
            AddColor,
            default: RGBA::new(1, 1, 1, 1),
            newtype_parse: RGBA,
            property_impl: {
                impl Property<RGBA> for AddColor {
                    fn inherits_automatically() -> bool {
                        true
                    }

                    fn compute(&self, v: &RGBA) -> Self {
                        AddColor(RGBA::new(
                            self.0.red + v.red,
                            self.0.green + v.green,
                            self.0.blue + v.blue,
                            self.0.alpha + v.alpha
                        ))
                    }
                }
            }
        }

        let color = RGBA::new(1, 1, 1, 1);
        let a = <AddColor as Parse>::parse_str("#02030405").unwrap();
        let b = a.compute(&color);

        assert_eq!(b, AddColor(RGBA::new(3, 4, 5, 6)));
    }
}
<commit_msg>make_property! - use impl_property! where there was a bit of duplicated code<commit_after>pub trait Property<T> {
    fn inherits_automatically() -> bool;
    fn compute(&self, _: &T) -> Self;
}

/// Generates a property definition that simply parses strings to enum variants
/// or to a tuple struct of the given type.
///
/// For example, the SVG spec defines the `stroke-linejoin` property
/// to have possible values `miter | round | bevel | inherit`, with a default
/// of `miter`.  We can define the property like this:
///
/// ```ignore
/// make_property!(
/// StrokeLinejoin,
/// default: Miter,
///
/// "miter" => Miter,
/// "round" => Round,
/// "bevel" => Bevel,
/// );
/// ```
///
/// The macro will generate a `StrokeLinejoin` enum with the provided
/// variants.  It will generate an `impl Default for StrokeLinejoin`
/// with the provided `default:` value.  Finally, it will generate an
/// `impl Parse for StrokeLinejoin`, from `parsers::Parse`, where
/// `type Data = ()` and `type Err = ValueErrorKind`.
#[macro_export]
macro_rules! make_property {
    ($computed_values_type: ty,
     $name: ident,
     default: $default: ident,
     inherits_automatically: $inherits_automatically: expr,
     identifiers:
     $($str_prop: expr => $variant: ident,)+
    ) => {
        #[derive(Debug, Copy, Clone, PartialEq)]
        pub enum $name {
            $($variant),+
        }

        impl_default!($name, $name::$default);
        impl_property!($computed_values_type, $name, $inherits_automatically);

        impl crate::parsers::Parse for $name {
            type Err = crate::error::ValueErrorKind;

            fn parse(parser: &mut ::cssparser::Parser<'_, '_>) -> Result<$name, crate::error::ValueErrorKind> {
                let loc = parser.current_source_location();

                parser
                    .expect_ident()
                    .and_then(|cow| match cow.as_ref() {
                        $($str_prop => Ok($name::$variant),)+

                            _ => Err(
                                loc.new_basic_unexpected_token_error(
                                    ::cssparser::Token::Ident(::cssparser::CowRcStr::from(
                                        cow.as_ref().to_string(),
                                    ))),
                            ),
                    })
                    .map_err(|_| {
                        crate::error::ValueErrorKind::Parse(crate::parsers::ParseError::new(
                            "unexpected value",
                        ))
                    })
            }
        }
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     inherits_automatically: $inherits_automatically: expr,
     newtype_parse: $type: ty,
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));
        impl_property!($computed_values_type, $name, $inherits_automatically);

        impl crate::parsers::Parse for $name {
            type Err = crate::error::ValueErrorKind;

            fn parse(parser: &mut ::cssparser::Parser<'_, '_>) -> Result<$name, crate::error::ValueErrorKind> {
                Ok($name(<$type as crate::parsers::Parse>::parse(parser)?))
            }
        }
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     newtype_parse: $type: ty,
     property_impl: { $prop: item }
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));

        $prop

        impl crate::parsers::Parse for $name {
            type Err = crate::error::ValueErrorKind;

            fn parse(parser: &mut ::cssparser::Parser<'_, '_>) -> Result<$name, crate::error::ValueErrorKind> {
                Ok($name(<$type as crate::parsers::Parse>::parse(parser)?))
            }
        }
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     newtype: $type: ty,
     property_impl: { $prop: item },
     parse_impl: { $parse: item }
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));

        $prop

        $parse
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     inherits_automatically: $inherits_automatically: expr,
     newtype: $type: ty,
     parse_impl: { $parse: item },
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));
        impl_property!($computed_values_type, $name, $inherits_automatically);

        $parse
    };

    ($computed_values_type: ty,
     $name: ident,
     inherits_automatically: $inherits_automatically: expr,
     fields: {
       $($field_name: ident : $field_type: ty, default: $field_default : expr,)+
     }
     parse_impl: { $parse: item }
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name {
            $(pub $field_name: $field_type),+
        }

        impl_default!($name, $name { $($field_name: $field_default),+ });
        impl_property!($computed_values_type, $name, $inherits_automatically);

        $parse
    };
}

macro_rules! impl_default {
    ($name:ident, $default:expr) => {
        impl Default for $name {
            fn default() -> $name {
                $default
            }
        }
    };
}

macro_rules! impl_property {
    ($computed_values_type:ty, $name:ident, $inherits_automatically:expr) => {
        impl crate::property_macros::Property<$computed_values_type> for $name {
            fn inherits_automatically() -> bool {
                $inherits_automatically
            }

            fn compute(&self, _v: &$computed_values_type) -> Self {
                self.clone()
            }
        }
    };
}

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

    use crate::parsers::Parse;
    use cssparser::RGBA;

    #[test]
    fn check_generated_property() {
        make_property! {
            (),
            Foo,
            default: Def,
            inherits_automatically: true,

            identifiers:
            "def" => Def,
            "bar" => Bar,
            "baz" => Baz,
        }

        assert_eq!(<Foo as Default>::default(), Foo::Def);
        assert_eq!(<Foo as Property<()>>::inherits_automatically(), true);
        assert!(<Foo as Parse>::parse_str("blargh").is_err());
        assert_eq!(<Foo as Parse>::parse_str("bar"), Ok(Foo::Bar));
    }

    #[test]
    fn check_compute() {
        make_property! {
            RGBA,
            AddColor,
            default: RGBA::new(1, 1, 1, 1),
            newtype_parse: RGBA,
            property_impl: {
                impl Property<RGBA> for AddColor {
                    fn inherits_automatically() -> bool {
                        true
                    }

                    fn compute(&self, v: &RGBA) -> Self {
                        AddColor(RGBA::new(
                            self.0.red + v.red,
                            self.0.green + v.green,
                            self.0.blue + v.blue,
                            self.0.alpha + v.alpha
                        ))
                    }
                }
            }
        }

        let color = RGBA::new(1, 1, 1, 1);
        let a = <AddColor as Parse>::parse_str("#02030405").unwrap();
        let b = a.compute(&color);

        assert_eq!(b, AddColor(RGBA::new(3, 4, 5, 6)));
    }
}
<|endoftext|>


<commit_before>use std::slice::Iter;
use std::iter::Filter;
use rect::*;

fn make_result<'a>() -> Result<Rect, &'a str> {
    Ok(Rect::new(10, 20, "rec"))
}

fn make_err<'a>() -> Result<Rect, &'a str> {
    Err("oops")
}

pub fn run() {
    println!("********* Option<Rect> examples *********");

    demo_basic_matching();
    demo_as_ref();
    demo_as_mut();
    demo_unwrap();
    demo_logical_or_and_combinators();
    demo_chaining();
    // demo_map();
    // demo_collection_of_options();
}

fn demo_basic_matching() {
    println!(">>> demo_basic_matching");

    // Options are in the prelude, so a is automatically deduced as being of type Result<>.
    let a = make_result();
    assert!(a.is_ok());
    assert!(!a.is_err());

    // Match is the most basic way of getting at a Result.
    // Note the difference compared to ints. We need to use 'ref x' to avoid moving the String
    // out of the option.
    match a {
        Ok(ref x) => println!("MATCH: x (from a) = {}. x is of type &Rect.", x),
        Err(e) => panic!("a should not be Err")
    }

    // If let is equivalent to a match where we do nothing in the Err.
    // Demo 'ref' again. If you remove this ref then this will no longer compile due to
    // an error in the 2nd if let.
    if let Ok(ref x) = a {
        println!("IF LET: x (from a) = {}. x is of type &Rect.", x);
    }

    if let Ok(x) = a {
        println!("IF LET: x (from a) = {}. x is of type Rect.", x);
    }
}

fn demo_as_ref() {
    println!(">>> demo_as_ref");
    let a = make_result();

    // b is of type Result<&Rect, &&str>.
    // Now when we pattern match, we get an &Rect by default.
    let b = a.as_ref();
    if let Ok(x) = b {
        println!("IF LET: x (from b) = {}. x is of type &String.", x);
        // x.w = 20;   // Won't compile, *x is immutable.
    }

    // Trying to use & in this case (compare to ints) won't work because we cannot move the Rect.
    // let b = a.as_ref();
    // if let Ok(&x) = b {
    // }
}

fn demo_as_mut() {
    println!(">>> demo_as_mut");
    let mut a = make_result();

    {
        // Again, but using as_mut();
        // b is of type Result<&mut Rect, &mut &str>.
        // Now when we pattern match, we get an &mut String by default.
        let c = a.as_mut();
        if let Ok(x) = c {
            println!("IF LET: x (from c) = {}. x is of type &mut Rect and can be changed.", x);
            x.w = 22;   // Now this will compile.
        }
    }

    {
        // Similarly to the as_ref() case, we can't use the & in the pattern match because
        // it will try to move the content out into x.
        // let c = a.as_mut();
        // if let Ok(&mut x) = c {
        // }
    }
}

fn demo_unwrap() {
    println!(">>> demo_unwrap");
    let a = make_result();

    let x = a.unwrap();
    println!("x = {}", x);

    // These are all no-ops because is an Ok.
    let a = make_result();
    let x = a.unwrap_or(Rect::new(1,1,"a"));
    assert!(x.title == "rec");

    let a = make_result();
    let x = a.unwrap_or_default();
    assert!(x.title == "rec");

    // In contrast to Option<>, for unwrap_or_else we get passed an argument, the err.
    let a = make_result();
    let x = a.unwrap_or_else(|err| Rect::new(1,1,"a"));
    assert!(x.title == "rec");

    // Let's make an Err.
    let a = make_err();
    let x = a.unwrap_or(Rect::new(1,1,"a"));
    assert!(x.title == "a");

    let a = make_err();
    let x : Rect = a.unwrap_or_default();      // Type needs to implement the Default trait.
    assert!(x.w == 0 && x.h == 0 && x.title == "");

    // With Results, we can unwrap the Err if we want. x is of type &str (same as the Err).
    let a = make_err();
    let x = a.unwrap_err();
    assert!(x == "oops");
}

fn demo_logical_or_and_combinators() {
    // This is quite simple, but there is a wrinkle.
    let a = make_result();
    let b = make_err();

    // For 'or', both Results must be of the same type.
    let c = a.or(b);
    assert_eq!(c.unwrap().title, "rec");

    // Reset.
    // But for 'and', they can be different: you get back the second.
    // Hence we need type annotations here.
    let a = make_result();
    let b = make_err();

    let d = a.and(b);
    assert_eq!(d.unwrap_err(), "oops");

    // With different types.
    let a = Ok(5);
    let b = make_err();
    let d = a.and(b);
    assert_eq!(d.unwrap_err(), "oops");
}

fn demo_chaining() {
    println!(">>> demo_chaining");

    // The closure (or function) passed to and_then() only gets called if 'a' is Ok.
    // Since it isn't, we never call the closure. x is of type Rect in this example.
    // The closure must return a Result<T>.
    let a = make_err();
    let result = a.and_then(|x| Ok(x));
    assert!(result.is_err());

    // This can be chained. None of these closures will get called.
    let a = make_err();
    let result = a.and_then(|x| Ok(x)).and_then(|x| Ok(x)).and_then(|x| Ok(x));
    assert!(result.is_err());

    // This will flow the whole way down. Notice that we have to specify mut x for the closure
    // parameter in order to be able to modify the Rect, x.
    let a = make_result();
    let result = a.and_then(|mut x| { x.w += 50; Ok(x) })
        .and_then(|mut x| { x.w += 50; Ok(x) })
        .and_then(|mut x| { x.w += 50; Ok(x) });
    assert_eq!(result.unwrap().w, 160);

    // We can terminate in the middle, but the chain remains safe from panics.
    // Notice that we can even bugger up the types in the last closure.
    let a = make_result();
    let result = a.and_then(|mut x| { x.w += 50; Ok(x) })
        .and_then(|x| Err("oops"))
        .and_then(|x : i32| Ok(x * x));
    assert!(result.is_err());

    // or_else is the other half of the pair.
    let a = make_result();
    let result : Result<Rect, &str> = a.and_then(|mut x| { x.w += 50; Ok(x) })
        .and_then(|x| Err("oops"))
        .or_else(|x| Ok(Rect::new(9,9,"temp")));
    assert_eq!(result.unwrap().title, "temp");

    // and_then - call the closure if the value is Ok
    // or_else  - call the closure if the value is Err

    let a = make_result();
    let result : Result<Rect, &str> = a.or_else(|x| Ok(Rect::new(9,9,"temp")));
    assert_eq!(result.unwrap().title, "rec");

    // and_then can change the type of the Result.
    let a = make_result();
    let result : Result<i32, &str> = a.and_then(|x| Ok(42));
    assert_eq!(result, Ok(42));
}

// fn demo_map() {
//     println!(">>> demo_map");
//     let a = Some("hello".to_string());

//     // Maps an Option<T> to Option<U> by applying a function to a contained value.
//     // * Consumes the original value (see as_ref for a way around this)
//     // * and always applies a new wrapping.
//     let result = a.map(|x| x);

//     // TODO 'Consumes' is NOT misleading in the case of Strings.
//     // This won't compile because a has been moved.
//     // assert_eq!(a, None);
//     // Here is demonstrated the property of automatically wrapping in a Some.
//     // Note that the closure does not have Some.
//     assert_eq!(result, Some("hello".to_string()));

//     // Like and_then(), map can change the type of the Option.
//     let a = Some("hello".to_string());
//     let result = a.map(|x| x.len());
//     // assert_eq!(a, Some("hello".to_string()));     Again, won't compile because a is moved.
//     assert_eq!(result, Some(5));

//     // You can call map() on a None. Nothing happens, the None is propagated and the closure is not called.
//     // TODO: The documentation is really bad here.
//     let a : Option<String> = None;
//     let result = a.map(|x| x.len());
//     assert_eq!(result, None);

//     // The other two map variants basically allow us to deal with the None by
//     // supplying a default or a closure to compute a value.
//     // n.b. These do not return Options, they return values!
//     let a : Option<String> = None;
//     let result = a.map_or("hello".len(), |x| x.len());
//     assert_eq!(result, 5);

//     // Note that the default-calculating closure for map_or_else() takes no arguments.
//     let a : Option<String> = None;
//     let x = "hello".to_string();
//     let result = a.map_or_else(|| x.len(), |y| y.len());
//     assert_eq!(result, 5);
// }
<commit_msg>Rust: more Result<Rect> examples.<commit_after>use std::slice::Iter;
use std::iter::Filter;
use rect::*;

fn make_result<'a>() -> Result<Rect, &'a str> {
    Ok(Rect::new(10, 20, "rec"))
}

fn make_err<'a>() -> Result<Rect, &'a str> {
    Err("oops")
}

pub fn run() {
    println!("********* Option<Rect> examples *********");

    demo_basic_matching();
    demo_as_ref();
    demo_as_mut();
    demo_unwrap();
    demo_logical_or_and_combinators();
    demo_chaining();
    demo_map();
    demo_chaining2();
    // demo_collection_of_options();
}

fn demo_basic_matching() {
    println!(">>> demo_basic_matching");

    // Options are in the prelude, so a is automatically deduced as being of type Result<>.
    let a = make_result();
    assert!(a.is_ok());
    assert!(!a.is_err());

    // Match is the most basic way of getting at a Result.
    // Note the difference compared to ints. We need to use 'ref x' to avoid moving the String
    // out of the option.
    match a {
        Ok(ref x) => println!("MATCH: x (from a) = {}. x is of type &Rect.", x),
        Err(e) => panic!("a should not be Err")
    }

    // If let is equivalent to a match where we do nothing in the Err.
    // Demo 'ref' again. If you remove this ref then this will no longer compile due to
    // an error in the 2nd if let.
    if let Ok(ref x) = a {
        println!("IF LET: x (from a) = {}. x is of type &Rect.", x);
    }

    if let Ok(x) = a {
        println!("IF LET: x (from a) = {}. x is of type Rect.", x);
    }
}

fn demo_as_ref() {
    println!(">>> demo_as_ref");
    let a = make_result();

    // b is of type Result<&Rect, &&str>.
    // Now when we pattern match, we get an &Rect by default.
    let b = a.as_ref();
    if let Ok(x) = b {
        println!("IF LET: x (from b) = {}. x is of type &String.", x);
        // x.w = 20;   // Won't compile, *x is immutable.
    }

    // Trying to use & in this case (compare to ints) won't work because we cannot move the Rect.
    // let b = a.as_ref();
    // if let Ok(&x) = b {
    // }
}

fn demo_as_mut() {
    println!(">>> demo_as_mut");
    let mut a = make_result();

    {
        // Again, but using as_mut();
        // b is of type Result<&mut Rect, &mut &str>.
        // Now when we pattern match, we get an &mut String by default.
        let c = a.as_mut();
        if let Ok(x) = c {
            println!("IF LET: x (from c) = {}. x is of type &mut Rect and can be changed.", x);
            x.w = 22;   // Now this will compile.
        }
    }

    {
        // Similarly to the as_ref() case, we can't use the & in the pattern match because
        // it will try to move the content out into x.
        // let c = a.as_mut();
        // if let Ok(&mut x) = c {
        // }
    }
}

fn demo_unwrap() {
    println!(">>> demo_unwrap");
    let a = make_result();

    let x = a.unwrap();
    println!("x = {}", x);

    // These are all no-ops because is an Ok.
    let a = make_result();
    let x = a.unwrap_or(Rect::new(1,1,"a"));
    assert!(x.title == "rec");

    let a = make_result();
    let x = a.unwrap_or_default();
    assert!(x.title == "rec");

    // In contrast to Option<>, for unwrap_or_else we get passed an argument, the err.
    let a = make_result();
    let x = a.unwrap_or_else(|err| Rect::new(1,1,"a"));
    assert!(x.title == "rec");

    // Let's make an Err.
    let a = make_err();
    let x = a.unwrap_or(Rect::new(1,1,"a"));
    assert!(x.title == "a");

    let a = make_err();
    let x : Rect = a.unwrap_or_default();      // Type needs to implement the Default trait.
    assert!(x.w == 0 && x.h == 0 && x.title == "");

    // With Results, we can unwrap the Err if we want. x is of type &str (same as the Err).
    let a = make_err();
    let x = a.unwrap_err();
    assert!(x == "oops");
}

fn demo_logical_or_and_combinators() {
    // This is quite simple, but there is a wrinkle.
    let a = make_result();
    let b = make_err();

    // For 'or', both Results must be of the same type.
    let c = a.or(b);
    assert_eq!(c.unwrap().title, "rec");

    // Reset.
    // But for 'and', they can be different: you get back the second.
    // Hence we need type annotations here.
    let a = make_result();
    let b = make_err();

    let d = a.and(b);
    assert_eq!(d.unwrap_err(), "oops");

    // With different types.
    let a = Ok(5);
    let b = make_err();
    let d = a.and(b);
    assert_eq!(d.unwrap_err(), "oops");
}

fn demo_chaining() {
    println!(">>> demo_chaining");

    // The closure (or function) passed to and_then() only gets called if 'a' is Ok.
    // Since it isn't, we never call the closure. x is of type Rect in this example.
    // The closure must return a Result<T>.
    let a = make_err();
    let result = a.and_then(|x| Ok(x));
    assert!(result.is_err());

    // This can be chained. None of these closures will get called.
    let a = make_err();
    let result = a.and_then(|x| Ok(x)).and_then(|x| Ok(x)).and_then(|x| Ok(x));
    assert!(result.is_err());

    // This will flow the whole way down. Notice that we have to specify mut x for the closure
    // parameter in order to be able to modify the Rect, x.
    let a = make_result();
    let result = a.and_then(|mut x| { x.w += 50; Ok(x) })
        .and_then(|mut x| { x.w += 50; Ok(x) })
        .and_then(|mut x| { x.w += 50; Ok(x) });
    assert_eq!(result.unwrap().w, 160);

    // We can terminate in the middle, but the chain remains safe from panics.
    // Notice that we can even bugger up the types in the last closure.
    let a = make_result();
    let result = a.and_then(|mut x| { x.w += 50; Ok(x) })
        .and_then(|x| Err("oops"))
        .and_then(|x : i32| Ok(x * x));
    assert!(result.is_err());

    // or_else is the other half of the pair.
    let a = make_result();
    let result : Result<Rect, &str> = a.and_then(|mut x| { x.w += 50; Ok(x) })
        .and_then(|x| Err("oops"))
        .or_else(|x| Ok(Rect::new(9,9,"temp")));
    assert_eq!(result.unwrap().title, "temp");

    // and_then - call the closure if the value is Ok
    // or_else  - call the closure if the value is Err

    let a = make_result();
    let result : Result<Rect, &str> = a.or_else(|x| Ok(Rect::new(9,9,"temp")));
    assert_eq!(result.unwrap().title, "rec");

    // and_then can change the type of the Result.
    let a = make_result();
    let result : Result<i32, &str> = a.and_then(|x| Ok(42));
    assert_eq!(result, Ok(42));
}

fn demo_map() {
    println!(">>> demo_map");
    let a = make_result();

    // Maps an Result<T, E> to Result<U, E> by applying a function to a contained Ok value.
    // Leaves an Err value untouched.
    // * Consumes the original value (see as_ref for a way around this)
    // * and always applies a new wrapping.
    let result = a.map(|x| x);

    // TODO 'Consumes' is NOT misleading in the case of Strings.
    // This won't compile because a has been moved.
    // assert!(a.is_ok());

    // Here is demonstrated the property of automatically wrapping in an Ok.
    // Note that the closure (in the call to map above) does not have OK.
    assert_eq!(result.unwrap().title, "rec");

    // Like and_then(), map can change the type of the Result.
    let a = make_result();
    let result = a.map(|x| x.title.len());
    assert_eq!(result, Ok(3));

    // You can call map() on an Err. Nothing happens, the Err is propagated and the closure is not called.
    // TODO: The documentation is really bad here.
    let a = make_err();
    let result = a.map(|x| x.title.len());
    assert_eq!(result.unwrap_err(), "oops");

    // map_err allows us to apply a function to an Err value, if any.
    let a = make_err();
    let result = a.map_err(|e| e.to_uppercase());
    assert_eq!(result.unwrap_err(), "OOPS");
}

fn demo_chaining2() {
    // We can use a combination of map() and map_err() to pass results through a chain.

    println!(">>> demo_map");
    let a = make_result();
    let result = a.map(|mut x| { x.w += 50; x })
        .map_err(|e| "never called")
        .and_then(|_| { let y : Result<Rect, &str> = Err("This changes the Ok to an Err"); y })
        .map_err(|e| e.to_uppercase())  // Now this map_err closure does get called.
        .or_else(|e| make_result());    // and this will be called too, so we go back to the original Rect.

    println!("result = {:?}", result);
}

// See http://blog.burntsushi.net/rust-error-handling/#composing-option-and-result for more.
<|endoftext|>


<commit_before>//! K-means Classification
//!
//! Provides implementation of K-Means classification.
//!
//! # Usage
//!
//! ```
//! use rusty_machine::linalg::matrix::Matrix;
//! use rusty_machine::learning::k_means::KMeansClassifier;
//! use rusty_machine::learning::UnSupModel;
//!
//! let train_data = Matrix::new(3, 2, vec![1.0, 2.0, 1.0, 3.0, 1.0, 4.0]);
//! let pred_data = Matrix::new(1,2, vec![0.0, 0.0]);
//!
//! // Create model with k(=2) classes.
//! let mut model = KMeansClassifier::new(2);
//!
//! // Where train_data is a Matrix with features in columns.
//! model.train(train_data);
//!
//! // Where pred_data is a Matrix with features in columns.
//! let a = model.predict(pred_data);
//! ```
//!
//! Additionally you can control the initialization
//! algorithm and max number of iterations.
//!
//! # Initializations
//!
//! Three initialization algorithms are supported.
//!
//! ## Forgy initialization
//!
//! Choose initial centroids randomly from the data.
//!
//! ## Random Partition initialization
//!
//! Randomly assign each data point to one of k clusters.
//! The initial centroids are the mean of the data in their class.
//!
//! ## K-means++ initialization
//!
//! The [k-means++](https://en.wikipedia.org/wiki/K-means%2B%2B) scheme.

use linalg::matrix::Matrix;
use linalg::vector::Vector;
use learning::UnSupModel;
use rand;
use rand::Rng;

/// Initialization Algorithm enum.
pub enum InitAlgorithm {
    Forgy,
    RandomPartition,
    KPlusPlus,
}

/// K-Means Classification model.
///
/// Contains option for centroids.
/// Specifies iterations and number of classes.
pub struct KMeansClassifier {
    pub iters: usize,
    pub k: usize,
    pub centroids: Option<Matrix<f64>>,
    pub init_algorithm: InitAlgorithm,
}

impl UnSupModel<Matrix<f64>, Vector<usize>> for KMeansClassifier {
    /// Predict classes from data.
    ///
    /// Model must be trained.
    fn predict(&self, data: Matrix<f64>) -> Vector<usize> {
        match self.centroids {
            Some(ref _c) => return self.find_closest_centroids(&data),
            None => panic!("Model has not been trained."),
        }

    }

    /// Train the classifier using input data.
    fn train(&mut self, data: Matrix<f64>) {
        self.init_centroids(&data);

        for _i in 0..self.iters {
            let idx = self.find_closest_centroids(&data);
            self.update_centroids(&data, idx);
        }
    }
}

impl KMeansClassifier {
    /// Constructs untrained k-means classifier model.
    ///
    /// Requires number of classes to be specified.
    /// Defaults to 100 iterations.
    ///
    /// # Examples
    ///
    /// ```
    /// use rusty_machine::learning::k_means::KMeansClassifier;
    ///
    /// let model = KMeansClassifier::new(5);
    /// ```
    pub fn new(k: usize) -> KMeansClassifier {
        KMeansClassifier {
            iters: 100,
            k: k,
            centroids: None,
            init_algorithm: InitAlgorithm::Forgy,
        }
    }

    /// Initialize the centroids.
    ///
    /// Used internally within model.
    /// Currently only supports Forgy initialization.
    fn init_centroids(&mut self, data: &Matrix<f64>) {
        match self.init_algorithm {
            InitAlgorithm::Forgy => {
                self.centroids = Some(KMeansClassifier::forgy_init(self.k, data))
            }
            InitAlgorithm::RandomPartition => {
                self.centroids = Some(KMeansClassifier::ran_partition_init(self.k, data))
            }
            _ => self.centroids = Some(KMeansClassifier::forgy_init(self.k, data)),
        }
    }

    /// Find the centroid closest to each data point.
    ///
    /// Used internally within model.
    fn find_closest_centroids(&self, data: &Matrix<f64>) -> Vector<usize> {
        let mut idx = Vector::zeros(data.rows);
        let mut distances = Vector::zeros(data.rows);

        match self.centroids {
            Some(ref c) => {
                for i in 0..data.rows {
                    // This works like repmat pulling out row i repeatedly.
                    let centroid_diff = c - data.select_rows(&vec![i; c.rows]);
                    let dist = &centroid_diff.elemul(&centroid_diff).sum_cols();

                    // Now take argmin and this is the centroid.
                    let (min_idx, min_dist) = dist.argmin();
                    idx.data[i]= min_idx;
                    distances.data[i] = min_dist;

                }
            }
            None => panic!("Centroids not defined."),
        }

        idx
    }

    /// Updated the centroids by computing means of assigned classes.
    ///
    /// Used internally within model.
    fn update_centroids(&mut self, data: &Matrix<f64>, classes: Vector<usize>) {
        let mut new_centroids = Vec::with_capacity(self.k * data.cols);
        for i in 0..self.k {
            let mut vec_i = Vec::new();

            for j in classes.data.iter() {
                if *j == i {
                    vec_i.push(*j);
                }
            }

            let mat_i = data.select_rows(&vec_i);
            new_centroids.extend(mat_i.mean(0).data);
        }

        self.centroids = Some(Matrix::new(self.k, data.cols, new_centroids));
    }

    /// Compute initial centroids using Forgy scheme.
    ///
    /// Selects k random points in data for centroids.
    fn forgy_init(k: usize, data: &Matrix<f64>) -> Matrix<f64> {
        assert!(k <= data.rows);

        let mut random_choices = Vec::with_capacity(k);
        while random_choices.len() < k {
            let r = rand::thread_rng().gen_range(0, data.rows);

            if !random_choices.contains(&r) {
                random_choices.push(r);
            }
        }

        data.select_rows(&random_choices)
    }

    /// Compute initial centroids using random partition.
    ///
    /// Selects centroids by assigning each point randomly to a class
    /// and computing the mean of each class.
    fn ran_partition_init(k: usize, data: &Matrix<f64>) -> Matrix<f64> {
        assert!(k <= data.rows);

        let mut random_assignments = Vec::with_capacity(data.rows);

        // Populate so we have something in each class.
        for i in 0..k {
            random_assignments.push(i);
        }

        for _i in k..data.rows {
            random_assignments.push(rand::thread_rng().gen_range(0, k));
        }

        let mut init_centroids = Vec::with_capacity(k * data.cols);
        for i in 0..k {
            let mut vec_i = Vec::new();

            for j in random_assignments.iter() {
                if *j == i {
                    vec_i.push(*j);
                }
            }

            let mat_i = data.select_rows(&vec_i);
            init_centroids.extend(mat_i.mean(0).data);
        }

        Matrix::new(k, data.cols, init_centroids)
    }
}<commit_msg>Preparing for kmeans++ initialization.<commit_after>//! K-means Classification
//!
//! Provides implementation of K-Means classification.
//!
//! # Usage
//!
//! ```
//! use rusty_machine::linalg::matrix::Matrix;
//! use rusty_machine::learning::k_means::KMeansClassifier;
//! use rusty_machine::learning::UnSupModel;
//!
//! let train_data = Matrix::new(3, 2, vec![1.0, 2.0, 1.0, 3.0, 1.0, 4.0]);
//! let pred_data = Matrix::new(1,2, vec![0.0, 0.0]);
//!
//! // Create model with k(=2) classes.
//! let mut model = KMeansClassifier::new(2);
//!
//! // Where train_data is a Matrix with features in columns.
//! model.train(train_data);
//!
//! // Where pred_data is a Matrix with features in columns.
//! let a = model.predict(pred_data);
//! ```
//!
//! Additionally you can control the initialization
//! algorithm and max number of iterations.
//!
//! # Initializations
//!
//! Three initialization algorithms are supported.
//!
//! ## Forgy initialization
//!
//! Choose initial centroids randomly from the data.
//!
//! ## Random Partition initialization
//!
//! Randomly assign each data point to one of k clusters.
//! The initial centroids are the mean of the data in their class.
//!
//! ## K-means++ initialization
//!
//! The [k-means++](https://en.wikipedia.org/wiki/K-means%2B%2B) scheme.

use linalg::matrix::Matrix;
use linalg::vector::Vector;
use learning::UnSupModel;
use rand;
use rand::Rng;

/// Initialization Algorithm enum.
pub enum InitAlgorithm {
    Forgy,
    RandomPartition,
    KPlusPlus,
}

/// K-Means Classification model.
///
/// Contains option for centroids.
/// Specifies iterations and number of classes.
pub struct KMeansClassifier {
    pub iters: usize,
    pub k: usize,
    pub centroids: Option<Matrix<f64>>,
    pub init_algorithm: InitAlgorithm,
}

impl UnSupModel<Matrix<f64>, Vector<usize>> for KMeansClassifier {
    /// Predict classes from data.
    ///
    /// Model must be trained.
    fn predict(&self, data: Matrix<f64>) -> Vector<usize> {
        match self.centroids {
            Some(ref _c) => return self.find_closest_centroids(&data).0,
            None => panic!("Model has not been trained."),
        }

    }

    /// Train the classifier using input data.
    fn train(&mut self, data: Matrix<f64>) {
        self.init_centroids(&data);

        for _i in 0..self.iters {
            let (idx,_) = self.find_closest_centroids(&data);
            self.update_centroids(&data, idx);
        }
    }
}

impl KMeansClassifier {
    /// Constructs untrained k-means classifier model.
    ///
    /// Requires number of classes to be specified.
    /// Defaults to 100 iterations.
    ///
    /// # Examples
    ///
    /// ```
    /// use rusty_machine::learning::k_means::KMeansClassifier;
    ///
    /// let model = KMeansClassifier::new(5);
    /// ```
    pub fn new(k: usize) -> KMeansClassifier {
        KMeansClassifier {
            iters: 100,
            k: k,
            centroids: None,
            init_algorithm: InitAlgorithm::Forgy,
        }
    }

    /// Initialize the centroids.
    ///
    /// Used internally within model.
    /// Currently only supports Forgy initialization.
    fn init_centroids(&mut self, data: &Matrix<f64>) {
        match self.init_algorithm {
            InitAlgorithm::Forgy => {
                self.centroids = Some(KMeansClassifier::forgy_init(self.k, data))
            }
            InitAlgorithm::RandomPartition => {
                self.centroids = Some(KMeansClassifier::ran_partition_init(self.k, data))
            }
            _ => self.centroids = Some(KMeansClassifier::forgy_init(self.k, data)),
        }
    }

    /// Find the centroid closest to each data point.
    ///
    /// Used internally within model.
    /// Returns the index of the closest centroid and the distance to it.
    fn find_closest_centroids(&self, data: &Matrix<f64>) -> (Vector<usize>, Vector<f64>) {
        let mut idx = Vector::zeros(data.rows);
        let mut distances = Vector::zeros(data.rows);

        match self.centroids {
            Some(ref c) => {
                for i in 0..data.rows {
                    // This works like repmat pulling out row i repeatedly.
                    let centroid_diff = c - data.select_rows(&vec![i; c.rows]);
                    let dist = &centroid_diff.elemul(&centroid_diff).sum_cols();

                    // Now take argmin and this is the centroid.
                    let (min_idx, min_dist) = dist.argmin();
                    idx.data[i]= min_idx;
                    distances.data[i] = min_dist;

                }
            }
            None => panic!("Centroids not defined."),
        }

        (idx, distances)
    }

    /// Updated the centroids by computing means of assigned classes.
    ///
    /// Used internally within model.
    fn update_centroids(&mut self, data: &Matrix<f64>, classes: Vector<usize>) {
        let mut new_centroids = Vec::with_capacity(self.k * data.cols);
        for i in 0..self.k {
            let mut vec_i = Vec::new();

            for j in classes.data.iter() {
                if *j == i {
                    vec_i.push(*j);
                }
            }

            let mat_i = data.select_rows(&vec_i);
            new_centroids.extend(mat_i.mean(0).data);
        }

        self.centroids = Some(Matrix::new(self.k, data.cols, new_centroids));
    }

    /// Compute initial centroids using Forgy scheme.
    ///
    /// Selects k random points in data for centroids.
    fn forgy_init(k: usize, data: &Matrix<f64>) -> Matrix<f64> {
        assert!(k <= data.rows);

        let mut random_choices = Vec::with_capacity(k);
        while random_choices.len() < k {
            let r = rand::thread_rng().gen_range(0, data.rows);

            if !random_choices.contains(&r) {
                random_choices.push(r);
            }
        }

        data.select_rows(&random_choices)
    }

    /// Compute initial centroids using random partition.
    ///
    /// Selects centroids by assigning each point randomly to a class
    /// and computing the mean of each class.
    fn ran_partition_init(k: usize, data: &Matrix<f64>) -> Matrix<f64> {
        assert!(k <= data.rows);

        let mut random_assignments = Vec::with_capacity(data.rows);

        // Populate so we have something in each class.
        for i in 0..k {
            random_assignments.push(i);
        }

        for _i in k..data.rows {
            random_assignments.push(rand::thread_rng().gen_range(0, k));
        }

        let mut init_centroids = Vec::with_capacity(k * data.cols);
        for i in 0..k {
            let mut vec_i = Vec::new();

            for j in random_assignments.iter() {
                if *j == i {
                    vec_i.push(*j);
                }
            }

            let mat_i = data.select_rows(&vec_i);
            init_centroids.extend(mat_i.mean(0).data);
        }

        Matrix::new(k, data.cols, init_centroids)
    }

    fn plusplus_init(k: usize, data: &Matrix<f64>) -> Matrix<f64> {
        assert!(k <= data.rows);

        let mut init_centroids = Vec::with_capacity(k * data.cols);

        let first_cen = rand::thread_rng().gen_range(0, data.rows);
        unimplemented!();

        Matrix::new(k, data.cols, init_centroids)
    }
}<|endoftext|>


<commit_before>#![feature(phase)]

extern crate baps3_protocol;
extern crate libc;
extern crate docopt;
#[phase(plugin)] extern crate docopt_macros;

use baps3_protocol::client::{Client, Request, Response};
use baps3_protocol::proto::{Unpacker, Message};

fn main() {
    let (int_request_tx, int_request_rx) = channel();

    std::thread::Thread::spawn(move || { stdin_loop(int_request_tx)})
                        .detach();

    println!("Disconnected");

    for msg in int_request_rx.iter() {
        match msg {
            Request::Quit => break,
            Request::SendMessage(msg) => match msg.as_str_vec().as_slice() {
                ["!connect", dest] => match Client::new(dest) {
                    Ok(client) => {
                        client_main_loop(
                            client,
                            &int_request_rx,
                        );
                        println!("Disconnected");
                    },
                    Err(e) => println!("{}", e)
                },
                _ => println!("can't do that, disconnected!")
            }
        }
    }

    println!("Quitting");
}

fn stdin_loop(
    request_tx: Sender<Request>
) {
    let mut u = Unpacker::new();

    for line in std::io::stdin().lock().lines() {
        match line {
            Ok(l) => send_message(&request_tx, &mut u, l.as_slice()),
            Err(e) => {
                println!("{}", e);
                return;
            }
        }
    }

    request_tx.send(Request::Quit);
}

fn send_message(
    request_tx: &Sender<Request>,
    unpacker: &mut Unpacker,
    message: &str
) {
    for pline in unpacker.feed(message.as_slice()).iter() {
        if let [ref cmd, args..] = pline.as_slice() {
            request_tx.send(Request::SendMessage(Message::new(cmd, args)));
        }
    }
}

fn client_main_loop(Client {
    request_tx,
    response_rx
}: Client, int_request_rx: &Receiver<Request>) {
    let (int_response_tx, int_response_rx) = channel();

    std::thread::Thread::spawn(move || { response_iter(int_response_rx) })
                        .detach();

    let sel = std::comm::Select::new();

    let mut reqh = sel.handle(int_request_rx);
    unsafe { reqh.add(); }

    let mut resh = sel.handle(&response_rx);
    unsafe { resh.add(); }

    loop {
        let id = sel.wait();
        if id == reqh.id() {
            match reqh.recv_opt() {
                Ok(Request::SendMessage(msg)) =>
                  match msg.as_str_vec().as_slice() {
                    ["!disconnect"] => {
                        request_tx.send(Request::Quit);
                        return;
                    },
                    [word, args..] => {
                        println!("> {} {}", word, args);
                        request_tx.send(Request::SendMessage(Message::new(word, args)));
                    },
                    [] => ()
                },
                Ok(req) => request_tx.send(req),
                Err(_) => return
            }
        } else {
            match resh.recv_opt() {
                Ok(Response::Gone) => {
                    int_response_tx.send(Response::Gone);
                    return;
                },
                Ok(r) => int_response_tx.send(r),
                Err(_) => {
                    int_response_tx.send(Response::Gone);
                    return;
                }
            }
        }
    }
}

fn response_iter(response_rx: Receiver<Response>) {
    for m in response_rx.iter() {
        match m {
            Response::Message(m) => match &*m.as_str_vec() {
                [ "TIME", t ] => if let Some(ti) = t.parse::<i64>() {
                    let d = std::time::Duration::microseconds(ti);
                    println!("T {}:{:02}:{:02}",
                         d.num_hours(),
                         d.num_minutes() % 60,
                         d.num_seconds() % 60);
                },
                [ word, args.. ] => println!("< {} {}", word, args),
                [] => ()
            },
            Response::ClientError(e) => {
                println!("! {}", e);
                return;
            }
            Response::Gone => return
        }
    }
}
<commit_msg>Reform and document commands.<commit_after>#![feature(phase)]

extern crate baps3_protocol;
extern crate libc;
extern crate docopt;
#[phase(plugin)] extern crate docopt_macros;

use baps3_protocol::client::{Client, Request, Response};
use baps3_protocol::proto::{Unpacker, Message};

fn commands() {
    println!("Commands: ");
    println!("  !c HOST:PORT - connect (if not connected)");
    println!("  !d           - disconnect (if connected)");
    println!("  !h           - this help message");
    println!("  !q           - quit");
    println!("");
    println!("Anything not prefixed with ! is sent to the server.");
}

fn main() {
    let (int_request_tx, int_request_rx) = channel();

    std::thread::Thread::spawn(move || { stdin_loop(int_request_tx)})
                        .detach();

    println!("Currently disconnected.");
    println!("Type !h <newline> for command help");

    'l: for msg in int_request_rx.iter() {
        match msg {
            Request::Quit => break,
            Request::SendMessage(msg) => match msg.as_str_vec().as_slice() {
                ["!c", dest] => match Client::new(dest) {
                    Ok(client) => {
                        let quit = client_main_loop(
                            client,
                            &int_request_rx,
                        );
                        println!("Disconnected");

                        if quit { break 'l };
                    },
                    Err(e) => println!("{}", e)
                },
                ["!h"] => commands(),
                ["!q"] => return,
                _ => println!("can't do that, disconnected!")
            }
        }
    }

    println!("Quitting");
}

fn stdin_loop(
    request_tx: Sender<Request>
) {
    let mut u = Unpacker::new();

    for line in std::io::stdin().lock().lines() {
        match line {
            Ok(l) => send_message(&request_tx, &mut u, l.as_slice()),
            Err(e) => {
                println!("{}", e);
                return;
            }
        }
    }

    request_tx.send(Request::Quit);
}

fn send_message(
    request_tx: &Sender<Request>,
    unpacker: &mut Unpacker,
    message: &str
) {
    for pline in unpacker.feed(message.as_slice()).iter() {
        if let [ref cmd, args..] = pline.as_slice() {
            request_tx.send(Request::SendMessage(Message::new(cmd, args)));
        }
    }
}

/// Returns true if the outer main loop must exit.
fn client_main_loop(Client {
    request_tx,
    response_rx
}: Client, int_request_rx: &Receiver<Request>) -> bool {
    let (int_response_tx, int_response_rx) = channel();

    std::thread::Thread::spawn(move || { response_iter(int_response_rx) })
                        .detach();

    let sel = std::comm::Select::new();

    let mut reqh = sel.handle(int_request_rx);
    unsafe { reqh.add(); }

    let mut resh = sel.handle(&response_rx);
    unsafe { resh.add(); }

    loop {
        let id = sel.wait();
        if id == reqh.id() {
            match reqh.recv_opt() {
                Ok(Request::SendMessage(msg)) =>
                  match msg.as_str_vec().as_slice() {
                    ["!d"] => {
                        request_tx.send(Request::Quit);
                        return false;
                    },
                    ["!h"] => commands(),
                    ["!q"] => {
                        request_tx.send(Request::Quit);
                        return true;
                    }
                    [word, args..] => {
                        println!("> {} {}", word, args);
                        request_tx.send(Request::SendMessage(Message::new(word, args)));
                    },
                    [] => ()
                },
                Ok(req) => request_tx.send(req),
                Err(_) => return false
            }
        } else {
            match resh.recv_opt() {
                Ok(Response::Gone) => {
                    int_response_tx.send(Response::Gone);
                    return false;
                },
                Ok(r) => int_response_tx.send(r),
                Err(_) => {
                    int_response_tx.send(Response::Gone);
                    return false;
                }
            }
        }
    }
}

fn response_iter(response_rx: Receiver<Response>) {
    let mut last_time = String::new();

    for m in response_rx.iter() {
        match m {
            Response::Message(m) => match &*m.as_str_vec() {
                [ "TIME", t ] => if let Some(ti) = t.parse::<i64>() {
                    let d = std::time::Duration::microseconds(ti);
                    let s = format!("{}{:02}:{:02}",
                         if 0 < d.num_hours() { format!("{}:", d.num_hours()) }
                         else                 { String::new()                 },
                         d.num_minutes() % 60,
                         d.num_seconds() % 60);
                    if s != last_time {
                        println!("T {}", s);
                        last_time = s;
                    }
                },
                [ word, args.. ] => println!("< {} {}", word, args),
                [] => ()
            },
            Response::ClientError(e) => {
                println!("! {}", e);
                return;
            }
            Response::Gone => return
        }
    }
}
<|endoftext|>