<commit_before>use message::Message;
use MiliSec;
use state::State;

const FIRST_OPENS_DEFAULT: bool = false;
const LAST_CLOSES_DEFAULT: bool = false;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Conditions {
    pub timeout: MiliSec,
    pub renew_timeout: Option<MiliSec>,
    pub first_opens: Option<bool>,
    pub last_closes: Option<bool>,
    pub max_size: Option<usize>,
    pub patterns: Vec<String>,
}

impl Conditions {
    fn new(timeout: MiliSec) -> Conditions {
        Conditions {
            timeout: timeout,
            renew_timeout: None,
            first_opens: None,
            last_closes: None,
            max_size: None,
            patterns: Vec::new(),
        }
    }

    pub fn is_opening(&self, message: &Message) -> bool {
        if self.first_opens.unwrap_or(FIRST_OPENS_DEFAULT) {
            message.ids().any(|x| x == self.patterns.first().unwrap())
        } else {
            true
        }
    }

    pub fn is_closing(&self, state: &State) -> bool {
        trace!("Conditions: shoud we close this context?");
        state.is_open() && self.is_closing_condition_met(state)
    }

    fn is_closing_condition_met(&self, state: &State) -> bool {
        self.is_max_size_reached(state) ||
        self.is_closing_message(state) ||
        self.is_any_timer_expired(state)
    }

    fn is_max_size_reached(&self, state: &State) -> bool {
        self.max_size.map_or(false, |max_size| state.messages().len() >= max_size)
    }

    fn is_closing_message(&self, state: &State) -> bool {
        if self.last_closes.unwrap_or(LAST_CLOSES_DEFAULT) {
            let last_message = state.messages().last().unwrap();
            last_message.ids().any(|x| x == self.patterns.last().unwrap())
        } else {
            false
        }
    }

    fn is_any_timer_expired(&self, state: &State) -> bool {
        self.is_timeout_expired(state) || self.is_renew_timeout_expired(state)
    }

    fn is_timeout_expired(&self, state: &State) -> bool {
        state.elapsed_time() >= self.timeout
    }

    fn is_renew_timeout_expired(&self, state: &State) -> bool {
        self.renew_timeout.map_or(false,
                                  |renew_timeout| {
                                      state.elapsed_time_since_last_message() >= renew_timeout
                                  })
    }
}

pub struct ConditionsBuilder {
    conditions: Conditions,
}

impl ConditionsBuilder {
    pub fn new(timeout: MiliSec) -> ConditionsBuilder {
        ConditionsBuilder { conditions: Conditions::new(timeout) }
    }

    pub fn renew_timeout(&mut self, timeout: MiliSec) -> &mut ConditionsBuilder {
        self.conditions.renew_timeout = Some(timeout);
        self
    }

    pub fn first_opens(&mut self, first_opens: bool) -> &mut ConditionsBuilder {
        self.conditions.first_opens = Some(first_opens);
        self
    }

    pub fn last_closes(&mut self, last_closes: bool) -> &mut ConditionsBuilder {
        self.conditions.last_closes = Some(last_closes);
        self
    }
    pub fn max_size(&mut self, max_size: usize) -> &mut ConditionsBuilder {
        self.conditions.max_size = Some(max_size);
        self
    }

    pub fn patterns(&mut self, patterns: Vec<String>) -> &mut ConditionsBuilder {
        self.conditions.patterns = patterns;
        self
    }

    pub fn build(&mut self) -> Conditions {
        self.conditions.clone()
    }
}

#[cfg(test)]
mod test {
    use serde_json::from_str;
    use super::Conditions;
    use std::rc::Rc;

    use message::MessageBuilder;
    use state::State;
    use super::ConditionsBuilder;
    use context::BaseContextBuilder;
    use uuid::Uuid;

    #[test]
    fn test_given_condition_when_an_opening_message_is_received_then_the_state_becomes_opened() {
        let timeout = 100;
        let msg_id1 = "11eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let msg_id2 = "21eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let patterns = vec![
            msg_id1.clone(),
        ];
        let condition = ConditionsBuilder::new(timeout)
                            .patterns(patterns)
                            .first_opens(true)
                            .build();
        let msg_which_should_not_be_ignored = MessageBuilder::new(&msg_id1, "message").build();
        let msg_which_should_be_ignored = MessageBuilder::new(&msg_id2, "message").build();
        assert_false!(condition.is_opening(&msg_which_should_be_ignored));
        assert_true!(condition.is_opening(&msg_which_should_not_be_ignored));
    }

    #[test]
    fn test_given_condition_when_a_closing_message_is_received_then_the_state_becomes_closed() {
        let timeout = 100;
        let msg_id1 = "11eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let msg_id2 = "21eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let patterns = vec![
            msg_id1.clone(),
            msg_id2.clone(),
        ];
        let mut state = State::new();
        let conditions = ConditionsBuilder::new(timeout)
                            .patterns(patterns)
                            .last_closes(true)
                            .build();
        let context = BaseContextBuilder::new(Uuid::new_v4(), conditions).build();
        let msg_opening = Rc::new(MessageBuilder::new(&msg_id1, "message").build());
        let msg_closing = Rc::new(MessageBuilder::new(&msg_id2, "message").build());
        assert_false!(state.is_open());
        state.on_message(msg_opening, &context);
        assert_true!(state.is_open());
        state.on_message(msg_closing, &context);
        assert_false!(state.is_open());
    }

    #[test]
    fn test_given_conditions_in_json_when_we_have_only_the_required_ones_then_we_get_the_expected_result
        () {
        let json = r#"
        {
            "timeout": 100
        }
        "#;

        let conditions = from_str(json);
        println!("{:?}", &conditions);
        let conditions: Conditions = conditions.ok().expect("Failed to deserialize a Conditions \
                                                             struct with only a timeout field");
        assert_eq!(conditions.timeout, 100);
    }

    #[test]
    fn test_given_conditions_in_json_when_we_have_all_fields_then_we_get_the_expected_result() {
        let json = r#"
        {
            "timeout": 100,
            "renew_timeout": 50,
            "first_opens": true,
            "last_closes": false,
            "max_size": 42,
            "patterns": [
                "1f78c9f1-cd33-4f83-bbcd-9d59f73094d5",
                "2f78c9f1-cd33-4f83-bbcd-9d59f73094d5",
                "PATTERN_NAME"
            ]
        }
        "#;

        let expected_patterns = vec![
                "1f78c9f1-cd33-4f83-bbcd-9d59f73094d5".to_string(),
                "2f78c9f1-cd33-4f83-bbcd-9d59f73094d5".to_string(),
                "PATTERN_NAME".to_string(),
        ];
        let conditions = from_str(json);
        println!("{:?}", &conditions);
        let conditions: Conditions = conditions.ok()
                                               .expect("Failed to deserialize a Conditions struct");
        assert_eq!(conditions.timeout, 100);
        assert_eq!(conditions.renew_timeout, Some(50));
        assert_eq!(conditions.first_opens, Some(true));
        assert_eq!(conditions.last_closes, Some(false));
        assert_eq!(conditions.max_size, Some(42));
        assert_eq!(conditions.patterns, expected_patterns);
    }

    #[test]
    fn test_given_condition_when_there_are_no_patterns_then_any_message_can_open_the_context() {
        let timeout = 100;
        let msg_id = "11eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let condition = ConditionsBuilder::new(timeout).build();
        let msg = MessageBuilder::new(&msg_id, "message").build();
        assert_true!(condition.is_opening(&msg));
    }

    #[test]
    fn test_given_condition_when_first_opens_is_set_then_the_right_message_can_open_the_context
                                                                                () {
        let timeout = 100;
        let patterns = vec![
                "p1".to_string(),
                "p2".to_string(),
                "p3".to_string(),
        ];
        let uuid = "e4f3f8b2-3135-4916-a5ea-621a754dab0d".to_string();
        let msg_id = "p1".to_string();
        let condition = ConditionsBuilder::new(timeout)
                            .patterns(patterns)
                            .first_opens(true)
                            .build();
        let msg = MessageBuilder::new(&uuid, "message").name(Some(&msg_id)).build();
        assert_true!(condition.is_opening(&msg));
    }

    #[test]
    fn test_given_conditions_when_last_closes_is_set_and_the_message_has_a_name_then_we_check_that_name
        () {
        let timeout = 100;
        let patterns = vec!["p1".to_string(), "p2".to_string()];
        let p1_uuid = "e4f3f8b2-3135-4916-a5ea-621a754dab0d".to_string();
        let p2_uuid = "f4f3f8b2-3135-4916-a5ea-621a754dab0d".to_string();
        let p1 = "p1".to_string();
        let p2 = "p2".to_string();
        let mut state = State::new();
        let conditions = ConditionsBuilder::new(timeout)
                            .patterns(patterns)
                            .first_opens(true)
                            .last_closes(true)
                            .build();
        let p1_msg = MessageBuilder::new(&p1_uuid, "message").name(Some(&p1)).build();
        let p2_msg = MessageBuilder::new(&p2_uuid, "message").name(Some(&p2)).build();
        let context = BaseContextBuilder::new(Uuid::new_v4(), conditions).build();
        assert_false!(state.is_open());
        state.on_message(Rc::new(p1_msg), &context);
        state.on_message(Rc::new(p2_msg), &context);
        assert_false!(state.is_open());
    }
}

mod deser {
    use MiliSec;
    use super::Conditions;
    use serde::de::{Deserialize, Deserializer, Error, MapVisitor, Visitor};

    impl Deserialize for Conditions {
        fn deserialize<D>(deserializer: &mut D) -> Result<Conditions, D::Error>
            where D: Deserializer
        {
            deserializer.visit_struct("Conditions", &[], ConditionsVisitor)
        }
    }

    enum Field {
        Timeout,
        RenewTimeout,
        FirstOpens,
        LastCloses,
        MaxSize,
        Patterns,
    }

    impl Deserialize for Field {
        fn deserialize<D>(deserializer: &mut D) -> Result<Field, D::Error>
            where D: Deserializer
        {
            struct FieldVisitor;

            impl Visitor for FieldVisitor {
                type Value = Field;

                fn visit_str<E>(&mut self, value: &str) -> Result<Field, E>
                    where E: Error
                {
                    match value {
                        "timeout" => Ok(Field::Timeout),
                        "renew_timeout" => Ok(Field::RenewTimeout),
                        "first_opens" => Ok(Field::FirstOpens),
                        "last_closes" => Ok(Field::LastCloses),
                        "max_size" => Ok(Field::MaxSize),
                        "patterns" => Ok(Field::Patterns),
                        name @ _ => Err(Error::syntax(&format!("Unexpected field: {}", name))),
                    }
                }
            }

            deserializer.visit(FieldVisitor)
        }
    }

    struct ConditionsVisitor;

    impl Visitor for ConditionsVisitor {
        type Value = Conditions;

        fn visit_map<V>(&mut self, mut visitor: V) -> Result<Conditions, V::Error>
            where V: MapVisitor
        {
            let mut timeout: Option<MiliSec> = None;
            let mut renew_timeout = None;
            let mut first_opens = None;
            let mut last_closes = None;
            let mut max_size = None;
            let mut patterns = None;

            loop {
                match try!(visitor.visit_key()) {
                    Some(Field::Timeout) => {
                        timeout = Some(try!(visitor.visit_value()));
                    }
                    Some(Field::RenewTimeout) => {
                        renew_timeout = Some(try!(visitor.visit_value()));
                    }
                    Some(Field::FirstOpens) => {
                        first_opens = Some(try!(visitor.visit_value()));
                    }
                    Some(Field::LastCloses) => {
                        last_closes = Some(try!(visitor.visit_value()));
                    }
                    Some(Field::MaxSize) => {
                        max_size = Some(try!(visitor.visit_value()));
                    }
                    Some(Field::Patterns) => {
                        patterns = Some(try!(visitor.visit_value()));
                    }
                    None => {
                        break;
                    }
                }
            }

            let timeout: MiliSec = match timeout {
                Some(timeout) => timeout,
                None => return visitor.missing_field("timeout"),
            };

            try!(visitor.end());

            Ok(Conditions {
                timeout: timeout,
                renew_timeout: renew_timeout,
                first_opens: first_opens,
                last_closes: last_closes,
                max_size: max_size,
                patterns: patterns.unwrap_or(Vec::new()),
            })
        }
    }
}
<commit_msg>Conditions: simplify loop in visit_map()<commit_after>use message::Message;
use MiliSec;
use state::State;

const FIRST_OPENS_DEFAULT: bool = false;
const LAST_CLOSES_DEFAULT: bool = false;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Conditions {
    pub timeout: MiliSec,
    pub renew_timeout: Option<MiliSec>,
    pub first_opens: Option<bool>,
    pub last_closes: Option<bool>,
    pub max_size: Option<usize>,
    pub patterns: Vec<String>,
}

impl Conditions {
    fn new(timeout: MiliSec) -> Conditions {
        Conditions {
            timeout: timeout,
            renew_timeout: None,
            first_opens: None,
            last_closes: None,
            max_size: None,
            patterns: Vec::new(),
        }
    }

    pub fn is_opening(&self, message: &Message) -> bool {
        if self.first_opens.unwrap_or(FIRST_OPENS_DEFAULT) {
            message.ids().any(|x| x == self.patterns.first().unwrap())
        } else {
            true
        }
    }

    pub fn is_closing(&self, state: &State) -> bool {
        trace!("Conditions: shoud we close this context?");
        state.is_open() && self.is_closing_condition_met(state)
    }

    fn is_closing_condition_met(&self, state: &State) -> bool {
        self.is_max_size_reached(state) ||
        self.is_closing_message(state) ||
        self.is_any_timer_expired(state)
    }

    fn is_max_size_reached(&self, state: &State) -> bool {
        self.max_size.map_or(false, |max_size| state.messages().len() >= max_size)
    }

    fn is_closing_message(&self, state: &State) -> bool {
        if self.last_closes.unwrap_or(LAST_CLOSES_DEFAULT) {
            let last_message = state.messages().last().unwrap();
            last_message.ids().any(|x| x == self.patterns.last().unwrap())
        } else {
            false
        }
    }

    fn is_any_timer_expired(&self, state: &State) -> bool {
        self.is_timeout_expired(state) || self.is_renew_timeout_expired(state)
    }

    fn is_timeout_expired(&self, state: &State) -> bool {
        state.elapsed_time() >= self.timeout
    }

    fn is_renew_timeout_expired(&self, state: &State) -> bool {
        self.renew_timeout.map_or(false,
                                  |renew_timeout| {
                                      state.elapsed_time_since_last_message() >= renew_timeout
                                  })
    }
}

pub struct ConditionsBuilder {
    conditions: Conditions,
}

impl ConditionsBuilder {
    pub fn new(timeout: MiliSec) -> ConditionsBuilder {
        ConditionsBuilder { conditions: Conditions::new(timeout) }
    }

    pub fn renew_timeout(&mut self, timeout: MiliSec) -> &mut ConditionsBuilder {
        self.conditions.renew_timeout = Some(timeout);
        self
    }

    pub fn first_opens(&mut self, first_opens: bool) -> &mut ConditionsBuilder {
        self.conditions.first_opens = Some(first_opens);
        self
    }

    pub fn last_closes(&mut self, last_closes: bool) -> &mut ConditionsBuilder {
        self.conditions.last_closes = Some(last_closes);
        self
    }
    pub fn max_size(&mut self, max_size: usize) -> &mut ConditionsBuilder {
        self.conditions.max_size = Some(max_size);
        self
    }

    pub fn patterns(&mut self, patterns: Vec<String>) -> &mut ConditionsBuilder {
        self.conditions.patterns = patterns;
        self
    }

    pub fn build(&mut self) -> Conditions {
        self.conditions.clone()
    }
}

#[cfg(test)]
mod test {
    use serde_json::from_str;
    use super::Conditions;
    use std::rc::Rc;

    use message::MessageBuilder;
    use state::State;
    use super::ConditionsBuilder;
    use context::BaseContextBuilder;
    use uuid::Uuid;

    #[test]
    fn test_given_condition_when_an_opening_message_is_received_then_the_state_becomes_opened() {
        let timeout = 100;
        let msg_id1 = "11eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let msg_id2 = "21eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let patterns = vec![
            msg_id1.clone(),
        ];
        let condition = ConditionsBuilder::new(timeout)
                            .patterns(patterns)
                            .first_opens(true)
                            .build();
        let msg_which_should_not_be_ignored = MessageBuilder::new(&msg_id1, "message").build();
        let msg_which_should_be_ignored = MessageBuilder::new(&msg_id2, "message").build();
        assert_false!(condition.is_opening(&msg_which_should_be_ignored));
        assert_true!(condition.is_opening(&msg_which_should_not_be_ignored));
    }

    #[test]
    fn test_given_condition_when_a_closing_message_is_received_then_the_state_becomes_closed() {
        let timeout = 100;
        let msg_id1 = "11eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let msg_id2 = "21eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let patterns = vec![
            msg_id1.clone(),
            msg_id2.clone(),
        ];
        let mut state = State::new();
        let conditions = ConditionsBuilder::new(timeout)
                            .patterns(patterns)
                            .last_closes(true)
                            .build();
        let context = BaseContextBuilder::new(Uuid::new_v4(), conditions).build();
        let msg_opening = Rc::new(MessageBuilder::new(&msg_id1, "message").build());
        let msg_closing = Rc::new(MessageBuilder::new(&msg_id2, "message").build());
        assert_false!(state.is_open());
        state.on_message(msg_opening, &context);
        assert_true!(state.is_open());
        state.on_message(msg_closing, &context);
        assert_false!(state.is_open());
    }

    #[test]
    fn test_given_conditions_in_json_when_we_have_only_the_required_ones_then_we_get_the_expected_result
        () {
        let json = r#"
        {
            "timeout": 100
        }
        "#;

        let conditions = from_str(json);
        println!("{:?}", &conditions);
        let conditions: Conditions = conditions.ok().expect("Failed to deserialize a Conditions \
                                                             struct with only a timeout field");
        assert_eq!(conditions.timeout, 100);
    }

    #[test]
    fn test_given_conditions_in_json_when_we_have_all_fields_then_we_get_the_expected_result() {
        let json = r#"
        {
            "timeout": 100,
            "renew_timeout": 50,
            "first_opens": true,
            "last_closes": false,
            "max_size": 42,
            "patterns": [
                "1f78c9f1-cd33-4f83-bbcd-9d59f73094d5",
                "2f78c9f1-cd33-4f83-bbcd-9d59f73094d5",
                "PATTERN_NAME"
            ]
        }
        "#;

        let expected_patterns = vec![
                "1f78c9f1-cd33-4f83-bbcd-9d59f73094d5".to_string(),
                "2f78c9f1-cd33-4f83-bbcd-9d59f73094d5".to_string(),
                "PATTERN_NAME".to_string(),
        ];
        let conditions = from_str(json);
        println!("{:?}", &conditions);
        let conditions: Conditions = conditions.ok()
                                               .expect("Failed to deserialize a Conditions struct");
        assert_eq!(conditions.timeout, 100);
        assert_eq!(conditions.renew_timeout, Some(50));
        assert_eq!(conditions.first_opens, Some(true));
        assert_eq!(conditions.last_closes, Some(false));
        assert_eq!(conditions.max_size, Some(42));
        assert_eq!(conditions.patterns, expected_patterns);
    }

    #[test]
    fn test_given_condition_when_there_are_no_patterns_then_any_message_can_open_the_context() {
        let timeout = 100;
        let msg_id = "11eaf6f8-0640-460f-aee2-a72d2f2ab258".to_string();
        let condition = ConditionsBuilder::new(timeout).build();
        let msg = MessageBuilder::new(&msg_id, "message").build();
        assert_true!(condition.is_opening(&msg));
    }

    #[test]
    fn test_given_condition_when_first_opens_is_set_then_the_right_message_can_open_the_context
                                                                                () {
        let timeout = 100;
        let patterns = vec![
                "p1".to_string(),
                "p2".to_string(),
                "p3".to_string(),
        ];
        let uuid = "e4f3f8b2-3135-4916-a5ea-621a754dab0d".to_string();
        let msg_id = "p1".to_string();
        let condition = ConditionsBuilder::new(timeout)
                            .patterns(patterns)
                            .first_opens(true)
                            .build();
        let msg = MessageBuilder::new(&uuid, "message").name(Some(&msg_id)).build();
        assert_true!(condition.is_opening(&msg));
    }

    #[test]
    fn test_given_conditions_when_last_closes_is_set_and_the_message_has_a_name_then_we_check_that_name
        () {
        let timeout = 100;
        let patterns = vec!["p1".to_string(), "p2".to_string()];
        let p1_uuid = "e4f3f8b2-3135-4916-a5ea-621a754dab0d".to_string();
        let p2_uuid = "f4f3f8b2-3135-4916-a5ea-621a754dab0d".to_string();
        let p1 = "p1".to_string();
        let p2 = "p2".to_string();
        let mut state = State::new();
        let conditions = ConditionsBuilder::new(timeout)
                            .patterns(patterns)
                            .first_opens(true)
                            .last_closes(true)
                            .build();
        let p1_msg = MessageBuilder::new(&p1_uuid, "message").name(Some(&p1)).build();
        let p2_msg = MessageBuilder::new(&p2_uuid, "message").name(Some(&p2)).build();
        let context = BaseContextBuilder::new(Uuid::new_v4(), conditions).build();
        assert_false!(state.is_open());
        state.on_message(Rc::new(p1_msg), &context);
        state.on_message(Rc::new(p2_msg), &context);
        assert_false!(state.is_open());
    }
}

mod deser {
    use MiliSec;
    use super::Conditions;
    use serde::de::{Deserialize, Deserializer, Error, MapVisitor, Visitor};

    impl Deserialize for Conditions {
        fn deserialize<D>(deserializer: &mut D) -> Result<Conditions, D::Error>
            where D: Deserializer
        {
            deserializer.visit_struct("Conditions", &[], ConditionsVisitor)
        }
    }

    enum Field {
        Timeout,
        RenewTimeout,
        FirstOpens,
        LastCloses,
        MaxSize,
        Patterns,
    }

    impl Deserialize for Field {
        fn deserialize<D>(deserializer: &mut D) -> Result<Field, D::Error>
            where D: Deserializer
        {
            struct FieldVisitor;

            impl Visitor for FieldVisitor {
                type Value = Field;

                fn visit_str<E>(&mut self, value: &str) -> Result<Field, E>
                    where E: Error
                {
                    match value {
                        "timeout" => Ok(Field::Timeout),
                        "renew_timeout" => Ok(Field::RenewTimeout),
                        "first_opens" => Ok(Field::FirstOpens),
                        "last_closes" => Ok(Field::LastCloses),
                        "max_size" => Ok(Field::MaxSize),
                        "patterns" => Ok(Field::Patterns),
                        name @ _ => Err(Error::syntax(&format!("Unexpected field: {}", name))),
                    }
                }
            }

            deserializer.visit(FieldVisitor)
        }
    }

    struct ConditionsVisitor;

    impl Visitor for ConditionsVisitor {
        type Value = Conditions;

        fn visit_map<V>(&mut self, mut visitor: V) -> Result<Conditions, V::Error>
            where V: MapVisitor
        {
            let mut timeout: Option<MiliSec> = None;
            let mut renew_timeout = None;
            let mut first_opens = None;
            let mut last_closes = None;
            let mut max_size = None;
            let mut patterns = None;

            while let Some(field) = try!(visitor.visit_key()) {
                match field {
                    Field::Timeout => timeout = Some(try!(visitor.visit_value())),
                    Field::RenewTimeout => renew_timeout = Some(try!(visitor.visit_value())),
                    Field::FirstOpens => first_opens = Some(try!(visitor.visit_value())),
                    Field::LastCloses => last_closes = Some(try!(visitor.visit_value())),
                    Field::MaxSize => max_size = Some(try!(visitor.visit_value())),
                    Field::Patterns => patterns = Some(try!(visitor.visit_value())),
                }
            }

            let timeout: MiliSec = match timeout {
                Some(timeout) => timeout,
                None => return visitor.missing_field("timeout"),
            };

            try!(visitor.end());

            Ok(Conditions {
                timeout: timeout,
                renew_timeout: renew_timeout,
                first_opens: first_opens,
                last_closes: last_closes,
                max_size: max_size,
                patterns: patterns.unwrap_or(Vec::new()),
            })
        }
    }
}
<|endoftext|>


<commit_before>#![feature(try_trait)]

extern crate pgn_reader;
extern crate memmap;
extern crate madvise;
extern crate btoi;
extern crate rand;
extern crate reqwest;

use std::env;
use std::mem;
use std::str;
use std::cmp::min;
use std::fs::File;
use std::option::NoneError;
use std::io::Read;

use memmap::Mmap;
use madvise::{AccessPattern, AdviseMemory};
use pgn_reader::{Visitor, Skip, Reader, San};
use btoi::ParseIntegerError;
use rand::{random, Closed01};

const BATCH_SIZE: usize = 50;

const MAX_PLIES: usize = 50;

#[derive(Debug)]
enum TimeControl {
    UltraBullet,
    Bullet,
    Blitz,
    Rapid,
    Classical,
    Correspondence,
}

#[derive(Debug)]
struct TimeControlError;

impl From<NoneError> for TimeControlError {
    fn from(_: NoneError) -> TimeControlError {
        TimeControlError { }
    }
}

impl From<ParseIntegerError> for TimeControlError {
    fn from(_: ParseIntegerError) -> TimeControlError {
        TimeControlError { }
    }
}

impl TimeControl {
    fn from_seconds_and_increment(seconds: u64, increment: u64) -> TimeControl {
        let total = seconds + 40 * increment;

        if total < 30 {
            TimeControl::UltraBullet
        } else if total < 180 {
            TimeControl::Bullet
        } else if total < 480 {
            TimeControl::Blitz
        } else if total < 1500 {
            TimeControl::Rapid
        } else if total < 21_600 {
            TimeControl::Classical
        } else {
            TimeControl::Correspondence
        }
    }

    fn from_bytes(bytes: &[u8]) -> Result<TimeControl, TimeControlError> {
        if bytes == b"-" {
            return Ok(TimeControl::Correspondence);
        }

        let mut parts = bytes.splitn(2, |ch| *ch == b'+');
        let seconds = btoi::btou(parts.next()?)?;
        let increment = btoi::btou(parts.next()?)?;
        Ok(TimeControl::from_seconds_and_increment(seconds, increment))
    }
}

struct Indexer<'pgn> {
    filename: String,
    date: &'pgn str,

    white_elo: i16,
    black_elo: i16,
    time_control: TimeControl,
    skip: bool,

    current_game: Vec<u8>,
    plies: usize,
    standard: bool,

    batch: Vec<u8>,
    batch_size: usize,
}

impl<'pgn> Indexer<'pgn> {
    fn new(filename: &str) -> Indexer {
        Indexer {
            filename: filename.into(),
            date: "0000-00-00",

            white_elo: 0,
            black_elo: 0,
            time_control: TimeControl::Correspondence,
            skip: true,

            current_game: Vec::new(),
            plies: 0,
            standard: true,

            batch: Vec::new(),
            batch_size: 0,
        }
    }

    fn send(&mut self) {
        if self.batch_size > 0 {
            self.batch_size = 0;

            let mut res = reqwest::Client::new()
                .put("http://localhost:9000/import/lichess")
                .body(mem::replace(&mut self.batch, Vec::new()))
                .send().expect("send batch");

            let mut answer = String::new();
            res.read_to_string(&mut answer).expect("decode response");
            println!("{} d: {} t: {}", self.filename, self.date, answer);
            assert!(res.status().is_success());
        }
    }
}

impl<'pgn> Visitor<'pgn> for Indexer<'pgn> {
    type Result = ();

    fn begin_game(&mut self) {
        self.current_game.clear();
        self.plies = 0;
    }

    fn begin_headers(&mut self) {
        self.white_elo = 0;
        self.black_elo = 0;
        self.time_control = TimeControl::Correspondence;
        self.standard = true;
    }

    fn header(&mut self, key: &'pgn [u8], value: &'pgn [u8]) {
        if key == b"WhiteElo" {
            self.white_elo = if value == b"?" { 0 } else { btoi::btoi(value).expect("WhiteElo") };
        } else if key == b"BlackElo" {
            self.black_elo = if value == b"?" { 0 } else { btoi::btoi(value).expect("BlackElo") };
        } else if key == b"TimeControl" {
            self.time_control = TimeControl::from_bytes(value).expect("TimeControl");
        } else if key == b"Variant" {
            self.standard = value == b"Standard";
            if self.standard {
                return; // we add this unconditionally later
            }
        } else if key == b"Date" || key == b"UTCDate" {
            self.date = str::from_utf8(value).expect("date is valid utf-8");
        }

        let (key, value) = if key == b"Site" {
            (&b"LichessID"[..], value.rsplitn(2, |ch| *ch == b'/').next().expect("Site"))
        } else {
            (key, value)
        };

        self.current_game.push(b'[');
        self.current_game.extend(key);
        self.current_game.extend(b" \"");
        self.current_game.extend(value);
        self.current_game.extend(b"\"]\n");
    }

    fn end_headers(&mut self) -> Skip {
        let rating = (self.white_elo + self.black_elo) / 2;

        let probability = if self.standard {
            self.current_game.extend(b"[Variant \"Standard\"]\n");

            match self.time_control {
                TimeControl::Correspondence | TimeControl::Classical => 1.0,
                TimeControl::Rapid if rating >= 2000 => 1.0,
                TimeControl::Rapid if rating >= 1800 => 2.0 / 5.0,
                TimeControl::Rapid => 1.0 / 8.0,
                TimeControl::Blitz if rating >= 2000 => 1.0,
                TimeControl::Blitz if rating >= 1800 => 1.0 / 4.0,
                TimeControl::Blitz => 1.0 / 15.0,
                TimeControl::Bullet if rating >= 2300 => 1.0,
                TimeControl::Bullet if rating >= 2200 => 4.0 / 5.0,
                TimeControl::Bullet if rating >= 2000 => 1.0 / 4.0,
                TimeControl::Bullet if rating >= 1800 => 1.0 / 7.0,
                _ => 1.0 / 20.0,
            }
        } else {
            if rating >= 1600 { 1.0 } else { 0.5 } // variant games
        };

        self.current_game.push(b'\n');

        let Closed01(rnd) = random::<Closed01<f64>>();
        let accept = min(self.white_elo, self.black_elo) >= 1500 && probability >= rnd;

        self.skip = !accept;
        Skip(self.skip)
    }

    fn san(&mut self, san: San) {
        if self.plies < MAX_PLIES {
            if self.plies > 0 {
                self.current_game.push(b' ');
            }

            self.current_game.extend(san.to_string().as_bytes());
            self.plies += 1;
        }
    }

    fn begin_variation(&mut self) -> Skip {
        Skip(true) // stay in the mainline
    }

    fn end_game(&mut self, _game: &'pgn [u8]) {
        if !self.skip && self.plies > 8 {
            if self.batch_size >= BATCH_SIZE {
                self.send();
            }

            if self.batch_size > 0 {
                self.batch.extend(b"\n\n\n");
            }

            self.batch.extend(&self.current_game);
            self.batch_size += 1;
        }
    }
}

fn main() {
    for arg in env::args().skip(1) {
        eprintln!("% indexing {} ...", arg);
        let file = File::open(&arg).expect("fopen");
        let pgn = unsafe { Mmap::map(&file).expect("mmap") };
        pgn.advise_memory_access(AccessPattern::Sequential).expect("madvise");

        let mut indexer = Indexer::new(&arg);
        Reader::new(&mut indexer, &pgn[..]).read_all();
        indexer.send(); // send last
    }
}
<commit_msg>micro optimize date printing<commit_after>#![feature(try_trait)]

extern crate pgn_reader;
extern crate memmap;
extern crate madvise;
extern crate btoi;
extern crate rand;
extern crate reqwest;

use std::env;
use std::mem;
use std::str;
use std::cmp::min;
use std::fs::File;
use std::option::NoneError;
use std::io::Read;

use memmap::Mmap;
use madvise::{AccessPattern, AdviseMemory};
use pgn_reader::{Visitor, Skip, Reader, San};
use btoi::ParseIntegerError;
use rand::{random, Closed01};

const BATCH_SIZE: usize = 50;

const MAX_PLIES: usize = 50;

#[derive(Debug)]
enum TimeControl {
    UltraBullet,
    Bullet,
    Blitz,
    Rapid,
    Classical,
    Correspondence,
}

#[derive(Debug)]
struct TimeControlError;

impl From<NoneError> for TimeControlError {
    fn from(_: NoneError) -> TimeControlError {
        TimeControlError { }
    }
}

impl From<ParseIntegerError> for TimeControlError {
    fn from(_: ParseIntegerError) -> TimeControlError {
        TimeControlError { }
    }
}

impl TimeControl {
    fn from_seconds_and_increment(seconds: u64, increment: u64) -> TimeControl {
        let total = seconds + 40 * increment;

        if total < 30 {
            TimeControl::UltraBullet
        } else if total < 180 {
            TimeControl::Bullet
        } else if total < 480 {
            TimeControl::Blitz
        } else if total < 1500 {
            TimeControl::Rapid
        } else if total < 21_600 {
            TimeControl::Classical
        } else {
            TimeControl::Correspondence
        }
    }

    fn from_bytes(bytes: &[u8]) -> Result<TimeControl, TimeControlError> {
        if bytes == b"-" {
            return Ok(TimeControl::Correspondence);
        }

        let mut parts = bytes.splitn(2, |ch| *ch == b'+');
        let seconds = btoi::btou(parts.next()?)?;
        let increment = btoi::btou(parts.next()?)?;
        Ok(TimeControl::from_seconds_and_increment(seconds, increment))
    }
}

struct Indexer<'pgn> {
    filename: String,
    date: &'pgn [u8],

    white_elo: i16,
    black_elo: i16,
    time_control: TimeControl,
    skip: bool,

    current_game: Vec<u8>,
    plies: usize,
    standard: bool,

    batch: Vec<u8>,
    batch_size: usize,
}

impl<'pgn> Indexer<'pgn> {
    fn new(filename: &str) -> Indexer {
        Indexer {
            filename: filename.into(),
            date: b"0000.00.00",

            white_elo: 0,
            black_elo: 0,
            time_control: TimeControl::Correspondence,
            skip: true,

            current_game: Vec::new(),
            plies: 0,
            standard: true,

            batch: Vec::new(),
            batch_size: 0,
        }
    }

    fn send(&mut self) {
        if self.batch_size > 0 {
            self.batch_size = 0;

            let mut res = reqwest::Client::new()
                .put("http://localhost:9000/import/lichess")
                .body(mem::replace(&mut self.batch, Vec::new()))
                .send().expect("send batch");

            let mut answer = String::new();
            res.read_to_string(&mut answer).expect("decode response");
            println!("{} d: {} t: {}", self.filename, str::from_utf8(self.date).expect("date is valid utf8"), answer);
            assert!(res.status().is_success());
        }
    }
}

impl<'pgn> Visitor<'pgn> for Indexer<'pgn> {
    type Result = ();

    fn begin_game(&mut self) {
        self.current_game.clear();
        self.plies = 0;
    }

    fn begin_headers(&mut self) {
        self.white_elo = 0;
        self.black_elo = 0;
        self.time_control = TimeControl::Correspondence;
        self.standard = true;
    }

    fn header(&mut self, key: &'pgn [u8], value: &'pgn [u8]) {
        if key == b"WhiteElo" {
            self.white_elo = if value == b"?" { 0 } else { btoi::btoi(value).expect("WhiteElo") };
        } else if key == b"BlackElo" {
            self.black_elo = if value == b"?" { 0 } else { btoi::btoi(value).expect("BlackElo") };
        } else if key == b"TimeControl" {
            self.time_control = TimeControl::from_bytes(value).expect("TimeControl");
        } else if key == b"Variant" {
            self.standard = value == b"Standard";
            if self.standard {
                return; // we add this unconditionally later
            }
        } else if key == b"Date" || key == b"UTCDate" {
            self.date = value;
        }

        let (key, value) = if key == b"Site" {
            (&b"LichessID"[..], value.rsplitn(2, |ch| *ch == b'/').next().expect("Site"))
        } else {
            (key, value)
        };

        self.current_game.push(b'[');
        self.current_game.extend(key);
        self.current_game.extend(b" \"");
        self.current_game.extend(value);
        self.current_game.extend(b"\"]\n");
    }

    fn end_headers(&mut self) -> Skip {
        let rating = (self.white_elo + self.black_elo) / 2;

        let probability = if self.standard {
            self.current_game.extend(b"[Variant \"Standard\"]\n");

            match self.time_control {
                TimeControl::Correspondence | TimeControl::Classical => 1.0,
                TimeControl::Rapid if rating >= 2000 => 1.0,
                TimeControl::Rapid if rating >= 1800 => 2.0 / 5.0,
                TimeControl::Rapid => 1.0 / 8.0,
                TimeControl::Blitz if rating >= 2000 => 1.0,
                TimeControl::Blitz if rating >= 1800 => 1.0 / 4.0,
                TimeControl::Blitz => 1.0 / 15.0,
                TimeControl::Bullet if rating >= 2300 => 1.0,
                TimeControl::Bullet if rating >= 2200 => 4.0 / 5.0,
                TimeControl::Bullet if rating >= 2000 => 1.0 / 4.0,
                TimeControl::Bullet if rating >= 1800 => 1.0 / 7.0,
                _ => 1.0 / 20.0,
            }
        } else {
            if rating >= 1600 { 1.0 } else { 0.5 } // variant games
        };

        self.current_game.push(b'\n');

        let Closed01(rnd) = random::<Closed01<f64>>();
        let accept = min(self.white_elo, self.black_elo) >= 1500 && probability >= rnd;

        self.skip = !accept;
        Skip(self.skip)
    }

    fn san(&mut self, san: San) {
        if self.plies < MAX_PLIES {
            if self.plies > 0 {
                self.current_game.push(b' ');
            }

            self.current_game.extend(san.to_string().as_bytes());
            self.plies += 1;
        }
    }

    fn begin_variation(&mut self) -> Skip {
        Skip(true) // stay in the mainline
    }

    fn end_game(&mut self, _game: &'pgn [u8]) {
        if !self.skip && self.plies > 8 {
            if self.batch_size >= BATCH_SIZE {
                self.send();
            }

            if self.batch_size > 0 {
                self.batch.extend(b"\n\n\n");
            }

            self.batch.extend(&self.current_game);
            self.batch_size += 1;
        }
    }
}

fn main() {
    for arg in env::args().skip(1) {
        eprintln!("% indexing {} ...", arg);
        let file = File::open(&arg).expect("fopen");
        let pgn = unsafe { Mmap::map(&file).expect("mmap") };
        pgn.advise_memory_access(AccessPattern::Sequential).expect("madvise");

        let mut indexer = Indexer::new(&arg);
        Reader::new(&mut indexer, &pgn[..]).read_all();
        indexer.send(); // send last
    }
}
<|endoftext|>


<commit_before>use core::CPU;
mod core;

fn setup_blank_cpu() -> CPU {
    CPU::new(Vec::from_elem(0x1, 0u8))
}

#[test]
fn check_address_converter() {
    let get_addr = CPU::to_addr;
    assert_eq!(get_addr(0x1, 0x2, 0x3), 0x123);
    assert_eq!(get_addr(0xF, 0xF, 0xF), 0xFFF);
    assert_eq!(get_addr(0x0, 0x0 ,0x0), 0x000);
}

#[test]
fn check_value_converter() {
    let get_val = CPU::to_val;
    assert_eq!(get_val( 0xF, 0xF), 0xFF);
    assert_eq!(get_val( 0x0, 0x0), 0x0);
    assert_eq!(get_val( 0x9, 0x5), 0x95);
}

#[test]
fn check_hex_to_digits_converter() {
    let get_vec = CPU::u16_to_hex_vec;
    assert_eq!(get_vec(0xFFFF), (0xF, 0xF, 0xF, 0xF));
    assert_eq!(get_vec(0x0000), (0x0, 0x0, 0x0, 0x0));
    assert_eq!(get_vec(0x0456), (0x0, 0x4, 0x5, 0x6));
}

/*#[test]
fn check_add_regs() {
    let mut cpu = setup_blank_cpu();
    let mut cpu2 = setup_blank_cpu();
    cpu.interpret(0x6205); /* Load 0x5 into reg 2 */
    cpu.interpret(0x6510); /* Load 0x10 into reg 5 */
    cpu.interpret(0x8254); /* Add reg 5 to reg 2 and store in reg 2 */
    cpu2.interpret(0x6205);
    assert_eq!(cpu.get_reg(0xF), 0x0);
    assert_eq!(cpu.get_reg(2), 0x15);
}*/

#[test]
fn check_add_regs_overlow_value() {
    let mut cpu = setup_blank_cpu();
    cpu.interpret(0x61FF); /* Load FF into reg 1 */
    cpu.interpret(0x62FF); /* Load FF into reg 2 */
    cpu.interpret(0x8124); /* Add reg2 to reg1 and store in reg 1 */

    assert_eq!(cpu.get_reg(0xF),  0x1); /* Check flag set */
    assert_eq!(cpu.get_reg(1), 0xFE);
}

<commit_msg>Added tests for subtraction and adding to index register<commit_after>use core::CPU;
mod core;

fn setup_blank_cpu() -> CPU {
    CPU::new(Vec::from_elem(0x1, 0u8))
}

#[test]
fn check_address_converter() {
    let get_addr = CPU::to_addr;
    assert_eq!(get_addr(0x1, 0x2, 0x3), 0x123);
    assert_eq!(get_addr(0xF, 0xF, 0xF), 0xFFF);
    assert_eq!(get_addr(0x0, 0x0 ,0x0), 0x000);
}

#[test]
fn check_value_converter() {
    let get_val = CPU::to_val;
    assert_eq!(get_val( 0xF, 0xF), 0xFF);
    assert_eq!(get_val( 0x0, 0x0), 0x0);
    assert_eq!(get_val( 0x9, 0x5), 0x95);
}

#[test]
fn check_hex_to_digits_converter() {
    let get_vec = CPU::u16_to_hex_vec;
    assert_eq!(get_vec(0xFFFF), (0xF, 0xF, 0xF, 0xF));
    assert_eq!(get_vec(0x0000), (0x0, 0x0, 0x0, 0x0));
    assert_eq!(get_vec(0x0456), (0x0, 0x4, 0x5, 0x6));
}

#[test]
fn check_add_regs() {
    let mut cpu = setup_blank_cpu();
    cpu.interpret(0x6205); /* Load 0x5 into reg 2 */
    cpu.interpret(0x6510); /* Load 0x10 into reg 5 */
    cpu.interpret(0x8254); /* Add reg 5 to reg 2 and store in reg 2 */

    assert_eq!(cpu.get_reg(0xF), 0x0);
    assert_eq!(cpu.get_reg(2), 0x15);
}

#[test]
fn check_add_regs_overlow_value() {
    let mut cpu = setup_blank_cpu();
    cpu.interpret(0x61FF); /* Load FF into reg 1 */
    cpu.interpret(0x62FF); /* Load FF into reg 2 */
    cpu.interpret(0x8124); /* Add reg2 to reg1 and store in reg 1 */

    assert_eq!(cpu.get_reg(0xF),  0x1); /* Check flag set */
    assert_eq!(cpu.get_reg(1), 0xFE);
}

/*** Test opcode 8XY5 ***/

/* Check a subtraction which results
 * in a value greater than zero */
#[test]
fn check_sub_regs() {
    let mut cpu = setup_blank_cpu();
    cpu.interpret(0x64F0); /* Load F0 into reg 4 */
    cpu.interpret(0x6301); /* load 1 into reg 3 */
    cpu.interpret(0x8435); /* sub reg 3 from reg 4 and store in reg 4 */

    assert_eq!(cpu.get_reg(0xF), 0x1); /* check no overflow */
    assert_eq!(cpu.get_reg(4), 0xF0 - 0x1);

}

/* Check a subtraction which results
 * in a values less than zero */
#[test]
fn check_sub_regs_overflow() {
    let mut cpu = setup_blank_cpu();
    cpu.interpret(0x6712); /* Load 0x12 into reg 7 */
    cpu.interpret(0x6920); /* Load 020 into reg 9 */
    cpu.interpret(0x8795); /* sub r9 from r7 store result in r7 */

    assert_eq!(cpu.get_reg(0xF), 0x0); /* check overflow */
    assert_eq!(cpu.get_reg(0x7), (0x12 - 0x20) as u8);

}

/*** Test opcode 8XY7, subbing registers ***
 *** the other way round than from 8XY5  ***/

/* Check a subtraction which results
 * in a value greater than zero */
#[test]
fn check_sub_inverted_regs() {
    let mut cpu = setup_blank_cpu();
    cpu.interpret(0x6A23);
    cpu.interpret(0x6B30);
    cpu.interpret(0x8AB7);

    assert_eq!(cpu.get_reg(0xF), 0x1);
    assert_eq!(cpu.get_reg(0xA), 0x30 - 0x23);
}

/* Check a subtraction which results
 * in a value less than zero */
#[test]
fn check_sub_inverted_regs_overflow() {
    let mut cpu = setup_blank_cpu();
    cpu.interpret(0x6CD3);
    cpu.interpret(0x6E30);
    cpu.interpret(0x8CE7);

    assert_eq!(cpu.get_reg(0xF), 0x0);
    assert_eq!(cpu.get_reg(0xC), (0x30 - 0xD3) as u8);
}

#[test]
fn check_index_add_reg() {
    let mut cpu = setup_blank_cpu();
    cpu.interpret(0xA111); //set I to 0x111
    cpu.interpret(0x6120); // set reg 1 to 20
    cpu.interpret(0xF11E); // Add reg 1 to I

    assert_eq!(cpu.get_reg(0xF), 0x0);
    assert_eq!(cpu.get_index_reg(), 0x20 + 0x111);
}

#[test]
fn check_index_add_reg_overflow() {
    let mut cpu = setup_blank_cpu();
    cpu.interpret(0xAFFF); //set I to 0xFFF
    cpu.interpret(0x6C56); // set reg C to 0x56
    cpu.interpret(0xFC1E); // Add reg C to I

    assert_eq!(cpu.get_reg(0xF), 0x1);
    assert_eq!(cpu.get_index_reg(), (0xFFF + 0x056) % 0x1000);
}

<|endoftext|>


<commit_before>use std::error::FromError;
use std::io::{BufferedStream, IoError};
use std::io::net::tcp::TcpStream;

#[derive(Show)]
pub enum MemcacheError {
    InternalIoError(IoError),
    ServerError
}

impl FromError<IoError> for MemcacheError {
    fn from_error(err: IoError) -> MemcacheError {
        MemcacheError::InternalIoError(err)
    }
}

pub type MemcacheResult<T> = Result<T, MemcacheError>;

pub struct Connection {
    pub host: String,
    pub port: u16,
    stream: BufferedStream<TcpStream>,
}

impl Connection {
    pub fn flush(&mut self) -> MemcacheResult<()> {
        try!{ self.stream.write_str("flush_all\r\n") };
        try!{ self.stream.flush() };
        let result = try!{ self.stream.read_line() };
        if result.as_slice() != "OK\r\n" {
            return Err(MemcacheError::ServerError);
        }
        return Ok(());
    }

    pub fn delete(&mut self, key: &str) -> MemcacheResult<bool> {
        try!{ self.stream.write_str(format!("delete {}\r\n", key).as_slice()) };
        try!{ self.stream.flush() };
        let result = try! { self.stream.read_line() };
        if result.as_slice() == "DELETED\r\n" {
            return Ok(true);
        } else if result.as_slice() == "NOT_FOUND\r\n" {
            return Ok(false);
        } else {
            return Err(MemcacheError::ServerError);
        }
    }

    pub fn get(&mut self, key: &str) -> MemcacheResult<Option<(Vec<u8>, u16)>> {
        try!{ self.stream.write_str(format!("get {}\r\n", key).as_slice()) };
        try!{ self.stream.flush() };
        let result = try!{ self.stream.read_line() };
        if result.as_slice() == "END\r\n" {
            return Ok(None);
        }
        let header: Vec<&str> = result.split(' ').collect();
        if header.len() != 4 || header[0] != "VALUE" || header[1] != key {
            return Err(MemcacheError::ServerError);
        }
        let flags: u16 = match header[2].trim().parse() {
            Some(flags) => { flags }
            None => { return Err(MemcacheError::ServerError); }
        };
        let length: usize = match header[3].trim().parse() {
            Some(length) => { length }
            None => { return Err(MemcacheError::ServerError); }
        };
        let value = try!{ self.stream.read_exact(length) };
        return Ok(Some((value, flags)));
    }

    pub fn set(&mut self, key: &str, value: &[u8], exptime: isize, flags: u16) -> MemcacheResult<bool> {
        try!{ self.stream.write_str(format!("set {} {} {} {}\r\n", key, flags, exptime, value.len()).as_slice()) };
        try!{ self.stream.write(value) };
        try!{ self.stream.write_str("\r\n") };
        try!{ self.stream.flush() };
        let result = try!{ self.stream.read_line() };
        if result.as_slice() == "STORED\r\n" {
            return Ok(true);
        } else if result.as_slice() == "NOT_STORED\r\n" {
            return Ok(false);
        }
        return Err(MemcacheError::ServerError);
    }

    pub fn incr(&mut self, key: &str, value: u64) -> MemcacheResult<Option<(isize)>> {
        try!{ self.stream.write_str(format!("incr {} {:b}\r\n", key, value).as_slice()) };
        try!{ self.stream.flush() };
        let result = try!{ self.stream.read_line() };
        if result.as_slice() == "NOT_FOUND\r\n" {
            return Ok(None);
        }
        let x: &[_] = &['\r', '\n'];
        // let trimed_result = result.trim_right_matches(x);
        let value: isize = match result.trim_right_matches(x).parse() {
            Some(value) => { value }
            None => { return Err(MemcacheError::ServerError) }
        };
        return Ok(Some(value));
    }

    pub fn decr(&mut self, key: &str, value: u64) -> MemcacheResult<Option<(isize)>> {
        try!{ self.stream.write_str(format!("decr {} {:b}\r\n", key, value).as_slice()) };
        try!{ self.stream.flush() };
        let result = try!{ self.stream.read_line() };
        if result.as_slice() == "NOT_FOUND\r\n" {
            return Ok(None);
        }
        let x: &[_] = &['\r', '\n'];
        // let trimed_result = result.trim_right_matches(x);
        let value: isize = match result.trim_right_matches(x).parse() {
            Some(value) => { value }
            None => { return Err(MemcacheError::ServerError) }
        };
        return Ok(Some(value));
    }

    pub fn connect(host: &str, port: u16) -> MemcacheResult<Connection> {
        let stream = try!{ TcpStream::connect((host, port)) };
        return Ok(Connection{
            host: host.to_string(),
            port: port,
            stream: BufferedStream::new(stream)
        });
    }
}

#[test]
fn test_connect() {
    assert!{ Connection::connect("localhost", 2333).is_ok() };
}

#[test]
fn test_flush() {
    let mut conn = Connection::connect("localhost", 2333).ok().unwrap();
    assert!{ conn.flush().is_ok() };
}

#[test]
fn test_set() {
    let mut conn = Connection::connect("localhost", 2333).ok().unwrap();
    assert!{ conn.flush().is_ok() };
    assert!{ conn.set("foo", b"bar", 10, 0).ok().unwrap() == true };
}

// #[test]
// fn test_get() {
//     let mut conn = Connection::connect("localhost", 2333).ok().unwrap();
//     assert!{ conn.flush().is_ok() };
//     assert!{ conn.get("foo").ok().unwrap() == None };
//
//     assert!{ conn.set("foo", b"bar", 0, 10).ok().unwrap() == true };
//     let result = conn.get("foo").ok().unwrap();
//     assert!{ result.unwrap().0 == b"bar" };
//     assert!{ result.unwrap().1 == 10 };
// }

#[test]
fn test_delete() {
    let mut conn = Connection::connect("localhost", 2333).ok().unwrap();
    assert!{ conn.flush().is_ok() };
    assert!{ conn.delete("foo").ok().unwrap() == false };
}

#[test]
fn test_incr() {
    let mut conn = Connection::connect("localhost", 2333).ok().unwrap();
    assert!{ conn.flush().is_ok() };
    let mut result = conn.incr("lie", 42);
    assert!{ result.ok().unwrap() == None };

    assert!{ conn.flush().is_ok() };
    conn.set("truth", b"42", 0, 0).ok().unwrap();
    result = conn.incr("truth", 1);
    assert!{ result.ok().unwrap().unwrap() == 43 };
}

#[test]
fn test_decr() {
    let mut conn = Connection::connect("localhost", 2333).ok().unwrap();
    assert!{ conn.flush().is_ok() };
    let mut result = conn.decr("lie", 42);
    assert!{ result.ok().unwrap() == None };

    assert!{ conn.flush().is_ok() };
    conn.set("truth", b"42", 0, 0).ok().unwrap();
    result = conn.decr("truth", 1);
    assert!{ result.ok().unwrap().unwrap() == 41 };
}
<commit_msg>fix test case for nightly compiler<commit_after>use std::error::FromError;
use std::io::{BufferedStream, IoError};
use std::io::net::tcp::TcpStream;

#[derive(Show)]
pub enum MemcacheError {
    InternalIoError(IoError),
    ServerError
}

impl FromError<IoError> for MemcacheError {
    fn from_error(err: IoError) -> MemcacheError {
        MemcacheError::InternalIoError(err)
    }
}

pub type MemcacheResult<T> = Result<T, MemcacheError>;

pub struct Connection {
    pub host: String,
    pub port: u16,
    stream: BufferedStream<TcpStream>,
}

impl Connection {
    pub fn flush(&mut self) -> MemcacheResult<()> {
        try!{ self.stream.write_str("flush_all\r\n") };
        try!{ self.stream.flush() };
        let result = try!{ self.stream.read_line() };
        if result.as_slice() != "OK\r\n" {
            return Err(MemcacheError::ServerError);
        }
        return Ok(());
    }

    pub fn delete(&mut self, key: &str) -> MemcacheResult<bool> {
        try!{ self.stream.write_str(format!("delete {}\r\n", key).as_slice()) };
        try!{ self.stream.flush() };
        let result = try! { self.stream.read_line() };
        if result.as_slice() == "DELETED\r\n" {
            return Ok(true);
        } else if result.as_slice() == "NOT_FOUND\r\n" {
            return Ok(false);
        } else {
            return Err(MemcacheError::ServerError);
        }
    }

    pub fn get(&mut self, key: &str) -> MemcacheResult<Option<(Vec<u8>, u16)>> {
        try!{ self.stream.write_str(format!("get {}\r\n", key).as_slice()) };
        try!{ self.stream.flush() };
        let result = try!{ self.stream.read_line() };
        if result.as_slice() == "END\r\n" {
            return Ok(None);
        }
        let header: Vec<&str> = result.split(' ').collect();
        if header.len() != 4 || header[0] != "VALUE" || header[1] != key {
            return Err(MemcacheError::ServerError);
        }
        let flags: u16 = match header[2].trim().parse() {
            Some(flags) => { flags }
            None => { return Err(MemcacheError::ServerError); }
        };
        let length: usize = match header[3].trim().parse() {
            Some(length) => { length }
            None => { return Err(MemcacheError::ServerError); }
        };
        let value = try!{ self.stream.read_exact(length) };
        return Ok(Some((value, flags)));
    }

    pub fn set(&mut self, key: &str, value: &[u8], exptime: isize, flags: u16) -> MemcacheResult<bool> {
        try!{ self.stream.write_str(format!("set {} {} {} {}\r\n", key, flags, exptime, value.len()).as_slice()) };
        try!{ self.stream.write(value) };
        try!{ self.stream.write_str("\r\n") };
        try!{ self.stream.flush() };
        let result = try!{ self.stream.read_line() };
        if result.as_slice() == "STORED\r\n" {
            return Ok(true);
        } else if result.as_slice() == "NOT_STORED\r\n" {
            return Ok(false);
        }
        return Err(MemcacheError::ServerError);
    }

    pub fn incr(&mut self, key: &str, value: u64) -> MemcacheResult<Option<(isize)>> {
        try!{ self.stream.write_str(format!("incr {} {:b}\r\n", key, value).as_slice()) };
        try!{ self.stream.flush() };
        let result = try!{ self.stream.read_line() };
        if result.as_slice() == "NOT_FOUND\r\n" {
            return Ok(None);
        }
        let x: &[_] = &['\r', '\n'];
        // let trimed_result = result.trim_right_matches(x);
        let value: isize = match result.trim_right_matches(x).parse() {
            Some(value) => { value }
            None => { return Err(MemcacheError::ServerError) }
        };
        return Ok(Some(value));
    }

    pub fn decr(&mut self, key: &str, value: u64) -> MemcacheResult<Option<(isize)>> {
        try!{ self.stream.write_str(format!("decr {} {:b}\r\n", key, value).as_slice()) };
        try!{ self.stream.flush() };
        let result = try!{ self.stream.read_line() };
        if result.as_slice() == "NOT_FOUND\r\n" {
            return Ok(None);
        }
        let x: &[_] = &['\r', '\n'];
        // let trimed_result = result.trim_right_matches(x);
        let value: isize = match result.trim_right_matches(x).parse() {
            Some(value) => { value }
            None => { return Err(MemcacheError::ServerError) }
        };
        return Ok(Some(value));
    }

    pub fn connect(host: &str, port: u16) -> MemcacheResult<Connection> {
        let stream = try!{ TcpStream::connect((host, port)) };
        return Ok(Connection{
            host: host.to_string(),
            port: port,
            stream: BufferedStream::new(stream)
        });
    }
}

#[test]
fn test_connect() {
    assert!{ Connection::connect("localhost", 2333).is_ok() };
}

#[test]
fn test_flush() {
    let mut conn = Connection::connect("localhost", 2333).ok().unwrap();
    assert!{ conn.flush().is_ok() };
}

#[test]
fn test_set() {
    let mut conn = Connection::connect("localhost", 2333).ok().unwrap();
    assert!{ conn.flush().is_ok() };
    assert!{ conn.set("foo", b"bar", 10, 0).ok().unwrap() == true };
}

#[test]
fn test_get() {
    let mut conn = Connection::connect("localhost", 2333).ok().unwrap();
    assert!{ conn.flush().is_ok() };
    assert!{ conn.get("foo").ok().unwrap() == None };

    assert!{ conn.set("foo", b"bar", 0, 10).ok().unwrap() == true };
    let result = conn.get("foo");
    let result_tuple = result.ok().unwrap().unwrap();
    assert!{ result_tuple.0 == b"bar" };
    assert!{ result_tuple.1 == 10 };
}

#[test]
fn test_delete() {
    let mut conn = Connection::connect("localhost", 2333).ok().unwrap();
    assert!{ conn.flush().is_ok() };
    assert!{ conn.delete("foo").ok().unwrap() == false };
}

#[test]
fn test_incr() {
    let mut conn = Connection::connect("localhost", 2333).ok().unwrap();
    assert!{ conn.flush().is_ok() };
    let mut result = conn.incr("lie", 42);
    assert!{ result.ok().unwrap() == None };

    assert!{ conn.flush().is_ok() };
    conn.set("truth", b"42", 0, 0).ok().unwrap();
    result = conn.incr("truth", 1);
    assert!{ result.ok().unwrap().unwrap() == 43 };
}

#[test]
fn test_decr() {
    let mut conn = Connection::connect("localhost", 2333).ok().unwrap();
    assert!{ conn.flush().is_ok() };
    let mut result = conn.decr("lie", 42);
    assert!{ result.ok().unwrap() == None };

    assert!{ conn.flush().is_ok() };
    conn.set("truth", b"42", 0, 0).ok().unwrap();
    result = conn.decr("truth", 1);
    assert!{ result.ok().unwrap().unwrap() == 41 };
}
<|endoftext|>


<commit_before>use std::collections::RingBuf;
use std::task::TaskBuilder;
use std::io::{TcpStream, IoResult, LineBufferedWriter, BufferedReader};
use std::default::Default;

use core_plugins::{
    MessageResponder,
    CtcpVersionResponder,
};

use event::IrcEvent;
use message::IrcMessage;
use watchers::{
    Bundler,
    BundlerManager,
    RegisterError,
    RegisterEventWatcher,
    JoinBundlerTrigger,
    JoinResult,
    JoinEventWatcher,
    WhoBundlerTrigger,
    WhoResult,
    WhoEventWatcher,
    EventWatcher,
    BundlerTrigger,
};

pub struct IrcConnection {
    command_queue: SyncSender<IrcConnectionCommand>,
    has_registered: bool
}

struct IrcConnectionInternalState {
    // The output stream towards the user
    event_queue_tx: SyncSender<IrcEvent>,

    // Automatic responders e.g. the PING and CTCP handlers
    responders: RingBuf<Box<MessageResponder+Send>>,

    // manages active bundlers and emits events when they finish
    bundler_man: BundlerManager,

    // Current nickname held by the client
    current_nick: Option<String>
}

impl IrcConnectionInternalState {
    pub fn new(event_queue_tx: SyncSender<IrcEvent>) -> IrcConnectionInternalState {
        IrcConnectionInternalState {
            event_queue_tx: event_queue_tx,
            responders: Default::default(),
            bundler_man: BundlerManager::new(),
            current_nick: Default::default(),
        }
    }

    fn dispatch(&mut self, message: IrcMessage, raw_sender: &SyncSender<String>) {
        if message.command() == "PING" {
            raw_sender.send(format!("PONG :{}\n", message.get_args()[0]));
        }

        if message.command() == "001" {
            self.current_nick = Some(message.get_args()[0].to_string());
        }

        if message.command() == "NICK" {
            self.current_nick = match (message.source_nick(), self.current_nick.take()) {
                (Some(source_nick), Some(current_nick)) => {
                    if source_nick == current_nick.as_slice() {
                        Some(message.get_args()[0].to_string())
                    } else {
                        Some(current_nick)
                    }
                },
                (_, any) => any
            };
        }

        // XXX //
        let outgoing_events = self.bundler_man.on_message(&message);

        for responder in self.responders.iter_mut() {
            for message in responder.on_message(&message).into_iter() {
                raw_sender.send(message.to_irc());
            }
        }

        for event in outgoing_events.into_iter() {
            self.event_queue_tx.send(event);
        }
    }

    // Do we really need Send here?
    fn add_watcher(&mut self, watcher: Box<EventWatcher+Send>) {
        self.bundler_man.add_watcher(watcher);
    }

    fn add_bundler(&mut self, bundler: Box<Bundler+Send>) {
        self.bundler_man.add_bundler(bundler);
    }
}

pub enum IrcConnectionCommand {
    RawWrite(String),
    AddWatcher(Box<EventWatcher+Send>),
    AddBundler(Box<Bundler+Send>),
}

impl IrcConnection {
    pub fn new(host: &str, port: u16) -> IoResult<(IrcConnection, Receiver<IrcEvent>)> {
        let stream = match TcpStream::connect(host, port) {
            Ok(stream) => stream,
            Err(err) => return Err(err)
        };

        let (command_queue_tx, command_queue_rx) = sync_channel::<IrcConnectionCommand>(10);
        let (event_queue_tx, event_queue_rx) = sync_channel(1024);

        let reader = BufferedReader::new(stream.clone());

        let tmp_stream = stream.clone();
        let (raw_writer_tx, raw_writer_rx) = sync_channel::<String>(0);
        let (raw_reader_tx, raw_reader_rx) = sync_channel::<String>(0);

        TaskBuilder::new().named("core-writer").spawn(proc() {
            let mut writer = LineBufferedWriter::new(tmp_stream);
            for message in raw_writer_rx.iter() {
                let mut message = message.clone();
                message.push_str("\n");
                assert!(writer.write_str(message.as_slice()).is_ok());
            }
        });

        TaskBuilder::new().named("core-reader").spawn(proc() {
            let mut reader = reader;
            loop {
                let string = String::from_str(match reader.read_line() {
                        Ok(string) => string,
                        Err(err) => panic!("{}", err)
                    }.as_slice().trim_right());
                raw_reader_tx.send(string);
            }
        });

        TaskBuilder::new().named("core-dispatch").spawn(proc() {
            let mut state = IrcConnectionInternalState::new(event_queue_tx);

            state.bundler_man.add_bundler_trigger(box JoinBundlerTrigger::new());
            state.bundler_man.add_bundler_trigger(box WhoBundlerTrigger::new());
            state.responders.push(box CtcpVersionResponder::new());

            loop {
                select! {
                    command = command_queue_rx.recv() => {
                        match command {
                            RawWrite(value) => raw_writer_tx.send(value),
                            AddWatcher(value) => state.add_watcher(value),
                            AddBundler(value) => state.add_bundler(value),
                        }
                    },
                    string = raw_reader_rx.recv() => {
                        state.dispatch(match IrcMessage::from_str(string.as_slice()) {
                            Ok(message) => message,
                            Err(err) => {
                                println!("Invalid IRC message: {} for {}", err, string);
                                continue;
                            }
                        }, &raw_writer_tx);
                    }
                }

            }
        });

        let conn = IrcConnection {
            command_queue: command_queue_tx,
            has_registered: false
        };
        Ok((conn, event_queue_rx))
    }

    pub fn register(&mut self, nick: &str) -> Result<(), RegisterError> {
        let mut reg_watcher = RegisterEventWatcher::new();
        let result_rx = reg_watcher.get_monitor();
        let watcher: Box<EventWatcher+Send> = box reg_watcher;
        self.command_queue.send(AddWatcher(watcher));
        self.write_str(format!("NICK {}", nick).as_slice());
        if !self.has_registered {
            self.write_str("USER rustbot 8 *: Rust Bot");
        }
        result_rx.recv()
    }

    pub fn join(&mut self, channel: &str) -> JoinResult {
        let mut join_watcher = JoinEventWatcher::new(channel);
        let result_rx = join_watcher.get_monitor();
        let watcher: Box<EventWatcher+Send> = box join_watcher;
        self.command_queue.send(AddWatcher(watcher));
        self.command_queue.send(RawWrite(format!("JOIN {}", channel.as_slice())));
        result_rx.recv()
    }

    pub fn who(&mut self, target: &str) -> WhoResult {
        let mut who_watcher = WhoEventWatcher::new(target);
        let result_rx = who_watcher.get_monitor();
        let watcher: Box<EventWatcher+Send> = box who_watcher;
        self.command_queue.send(AddWatcher(watcher));
        self.command_queue.send(RawWrite(format!("WHO {}", target)));
        result_rx.recv()
    }

    pub fn write_str(&mut self, content: &str) {
        self.command_queue.send(RawWrite(String::from_str(content)))
    }

    pub fn get_command_queue(&mut self) -> SyncSender<IrcConnectionCommand> {
        self.command_queue.clone()
    }
}
<commit_msg>Maybe handle UTF-8 failures better?<commit_after>use std::collections::RingBuf;
use std::task::TaskBuilder;
use std::io::{TcpStream, IoResult, LineBufferedWriter, BufferedReader};
use std::default::Default;

use core_plugins::{
    MessageResponder,
    CtcpVersionResponder,
};

use event::IrcEvent;
use message::IrcMessage;
use watchers::{
    Bundler,
    BundlerManager,
    RegisterError,
    RegisterEventWatcher,
    JoinBundlerTrigger,
    JoinResult,
    JoinEventWatcher,
    WhoBundlerTrigger,
    WhoResult,
    WhoEventWatcher,
    EventWatcher,
    BundlerTrigger,
};

pub struct IrcConnection {
    command_queue: SyncSender<IrcConnectionCommand>,
    has_registered: bool
}

struct IrcConnectionInternalState {
    // The output stream towards the user
    event_queue_tx: SyncSender<IrcEvent>,

    // Automatic responders e.g. the PING and CTCP handlers
    responders: RingBuf<Box<MessageResponder+Send>>,

    // manages active bundlers and emits events when they finish
    bundler_man: BundlerManager,

    // Current nickname held by the client
    current_nick: Option<String>
}

impl IrcConnectionInternalState {
    pub fn new(event_queue_tx: SyncSender<IrcEvent>) -> IrcConnectionInternalState {
        IrcConnectionInternalState {
            event_queue_tx: event_queue_tx,
            responders: Default::default(),
            bundler_man: BundlerManager::new(),
            current_nick: Default::default(),
        }
    }

    fn dispatch(&mut self, message: IrcMessage, raw_sender: &SyncSender<String>) {
        if message.command() == "PING" {
            raw_sender.send(format!("PONG :{}\n", message.get_args()[0]));
        }

        if message.command() == "001" {
            self.current_nick = Some(message.get_args()[0].to_string());
        }

        if message.command() == "NICK" {
            self.current_nick = match (message.source_nick(), self.current_nick.take()) {
                (Some(source_nick), Some(current_nick)) => {
                    if source_nick == current_nick.as_slice() {
                        Some(message.get_args()[0].to_string())
                    } else {
                        Some(current_nick)
                    }
                },
                (_, any) => any
            };
        }

        // XXX //
        let outgoing_events = self.bundler_man.on_message(&message);

        for responder in self.responders.iter_mut() {
            for message in responder.on_message(&message).into_iter() {
                raw_sender.send(message.to_irc());
            }
        }

        for event in outgoing_events.into_iter() {
            self.event_queue_tx.send(event);
        }
    }

    // Do we really need Send here?
    fn add_watcher(&mut self, watcher: Box<EventWatcher+Send>) {
        self.bundler_man.add_watcher(watcher);
    }

    fn add_bundler(&mut self, bundler: Box<Bundler+Send>) {
        self.bundler_man.add_bundler(bundler);
    }
}

pub enum IrcConnectionCommand {
    RawWrite(String),
    AddWatcher(Box<EventWatcher+Send>),
    AddBundler(Box<Bundler+Send>),
}

impl IrcConnection {
    pub fn new(host: &str, port: u16) -> IoResult<(IrcConnection, Receiver<IrcEvent>)> {
        let stream = match TcpStream::connect(host, port) {
            Ok(stream) => stream,
            Err(err) => return Err(err)
        };

        let (command_queue_tx, command_queue_rx) = sync_channel::<IrcConnectionCommand>(10);
        let (event_queue_tx, event_queue_rx) = sync_channel(1024);

        let reader = BufferedReader::new(stream.clone());

        let tmp_stream = stream.clone();
        let (raw_writer_tx, raw_writer_rx) = sync_channel::<String>(0);
        let (raw_reader_tx, raw_reader_rx) = sync_channel::<String>(0);

        TaskBuilder::new().named("core-writer").spawn(proc() {
            let mut writer = LineBufferedWriter::new(tmp_stream);
            for message in raw_writer_rx.iter() {
                let mut message = message.clone();
                message.push_str("\n");
                assert!(writer.write_str(message.as_slice()).is_ok());
            }
        });

        TaskBuilder::new().named("core-reader").spawn(proc() {
            let mut reader = reader;
            loop {
                let line_bin = match reader.read_until('\n' as u8) {
                    Ok(line_bin) => line_bin,
                    Err(err) => panic!("I/O Error: {}", err)
                };
                let string = String::from_utf8_lossy(line_bin.as_slice());
                let string = string.as_slice().trim_right().to_string();
                raw_reader_tx.send(string);
            }
        });

        TaskBuilder::new().named("core-dispatch").spawn(proc() {
            let mut state = IrcConnectionInternalState::new(event_queue_tx);

            state.bundler_man.add_bundler_trigger(box JoinBundlerTrigger::new());
            state.bundler_man.add_bundler_trigger(box WhoBundlerTrigger::new());
            state.responders.push(box CtcpVersionResponder::new());

            loop {
                select! {
                    command = command_queue_rx.recv() => {
                        match command {
                            RawWrite(value) => raw_writer_tx.send(value),
                            AddWatcher(value) => state.add_watcher(value),
                            AddBundler(value) => state.add_bundler(value),
                        }
                    },
                    string = raw_reader_rx.recv() => {
                        state.dispatch(match IrcMessage::from_str(string.as_slice()) {
                            Ok(message) => message,
                            Err(err) => {
                                println!("Invalid IRC message: {} for {}", err, string);
                                continue;
                            }
                        }, &raw_writer_tx);
                    }
                }

            }
        });

        let conn = IrcConnection {
            command_queue: command_queue_tx,
            has_registered: false
        };
        Ok((conn, event_queue_rx))
    }

    pub fn register(&mut self, nick: &str) -> Result<(), RegisterError> {
        let mut reg_watcher = RegisterEventWatcher::new();
        let result_rx = reg_watcher.get_monitor();
        let watcher: Box<EventWatcher+Send> = box reg_watcher;
        self.command_queue.send(AddWatcher(watcher));
        self.write_str(format!("NICK {}", nick).as_slice());
        if !self.has_registered {
            self.write_str("USER rustirc 0 *: http://github.com/infinityb/rust-irc");
        }
        result_rx.recv()
    }

    pub fn join(&mut self, channel: &str) -> JoinResult {
        let mut join_watcher = JoinEventWatcher::new(channel);
        let result_rx = join_watcher.get_monitor();
        let watcher: Box<EventWatcher+Send> = box join_watcher;
        self.command_queue.send(AddWatcher(watcher));
        self.command_queue.send(RawWrite(format!("JOIN {}", channel.as_slice())));
        result_rx.recv()
    }

    pub fn who(&mut self, target: &str) -> WhoResult {
        let mut who_watcher = WhoEventWatcher::new(target);
        let result_rx = who_watcher.get_monitor();
        let watcher: Box<EventWatcher+Send> = box who_watcher;
        self.command_queue.send(AddWatcher(watcher));
        self.command_queue.send(RawWrite(format!("WHO {}", target)));
        result_rx.recv()
    }

    pub fn write_str(&mut self, content: &str) {
        self.command_queue.send(RawWrite(String::from_str(content)))
    }

    pub fn get_command_queue(&mut self) -> SyncSender<IrcConnectionCommand> {
        self.command_queue.clone()
    }
}
<|endoftext|>


<commit_before>extern crate dbus_bytestream;
use self::dbus_bytestream::connection::Connection;

use super::error::DBusError;
use super::message::DBusMessage;
use super::value::{DBusBasicValue, DBusValue};

pub struct DBusConnection {
    conn: Connection,
}

pub enum DBusRequestNameFlags {
    AllowReplacement = 0x1,
    ReplaceExisting  = 0x2,
    DoNotQueue       = 0x4,
}

pub enum DBusRequestNameReply {
    PrimaryOwner,
    InQueue,
    Exists,
    AlreadyOwner,
}

pub enum DBusReleaseNameReply {
    Released,
    NonExistent,
    NotOwner,
}

pub struct DBusMessages<'a> {
    conn: &'a Connection,
}

impl DBusConnection {
    pub fn session_new() -> Result<DBusConnection, DBusError> {
        Ok(DBusConnection {
            conn: try!(Connection::connect_session()),
        })
    }

    pub fn system_new() -> Result<DBusConnection, DBusError> {
        Ok(DBusConnection {
            conn: try!(Connection::connect_system()),
        })
    }

    pub fn request_name(&self, name: &str, flags: DBusRequestNameFlags) -> Result<DBusRequestNameReply, DBusError> {
        let msg = DBusMessage::new_method_call(
                "org.freedesktop.DBus",
                "/org/freedesktop/DBus",
                "org.freedesktop.DBus",
                "RequestName")
            .add_argument(&name)
            .add_argument(&(flags as u32));
        if let Some(mut results) = try!(self.conn.call_sync(msg.extract())) {
            if let Some(DBusValue::BasicValue(DBusBasicValue::Uint32(r))) = results.pop() {
                match r {
                    1 => Ok(DBusRequestNameReply::PrimaryOwner),
                    2 => Ok(DBusRequestNameReply::InQueue),
                    3 => Ok(DBusRequestNameReply::Exists),
                    4 => Ok(DBusRequestNameReply::AlreadyOwner),
                    _ => Err(DBusError::InvalidReply(format!("RequestName: invalid response {}", r))),
                }
            } else {
                return Err(DBusError::InvalidReply("RequestName: invalid response".to_owned()));
            }
        } else {
            return Err(DBusError::InvalidReply("RequestName: no response".to_owned()));
        }
    }

    pub fn release_name(&self, name: &str) -> Result<DBusReleaseNameReply, DBusError> {
        let msg = DBusMessage::new_method_call(
                "org.freedesktop.DBus",
                "/org/freedesktop/DBus",
                "org.freedesktop.DBus",
                "ReleaseName")
            .add_argument(&name);
        if let Some(mut results) = try!(self.conn.call_sync(msg.extract())) {
            if let Some(DBusValue::BasicValue(DBusBasicValue::Uint32(r))) = results.pop() {
                match r {
                    1 => Ok(DBusReleaseNameReply::Released),
                    2 => Ok(DBusReleaseNameReply::NonExistent),
                    3 => Ok(DBusReleaseNameReply::NotOwner),
                    _ => Err(DBusError::InvalidReply(format!("ReleaseName: invalid response {}", r))),
                }
            } else {
                return Err(DBusError::InvalidReply("ReleaseName: invalid response".to_owned()));
            }
        } else {
            return Err(DBusError::InvalidReply("ReleaseName: no response".to_owned()));
        }
    }

    pub fn add_match(&self, match_rule: &str) -> Result<(), DBusError> {
        let msg = DBusMessage::new_method_call(
                "org.freedesktop.DBus",
                "/org/freedesktop/DBus",
                "org.freedesktop.DBus",
                "AddMatch")
            .add_argument(&match_rule);
        try!(self.conn.call_sync(msg.extract()));
        Ok(())
    }

    pub fn iter(&self) -> DBusMessages {
        DBusMessages {
            conn: &self.conn,
        }
    }
}

impl<'a> Iterator for DBusMessages<'a> {
    type Item = DBusMessage;

    fn next(&mut self) -> Option<Self::Item> {
        let res = self.conn.read_msg();
        match res {
            Ok(message) => {
                let dbus_message = DBusMessage::new(message);
                if dbus_message.should_handle() {
                    Some(dbus_message)
                } else {
                    None
                }
            },
            Err(_)      => None,
        }
    }
}
<commit_msg>connection: add method to send a message<commit_after>extern crate dbus_bytestream;
use self::dbus_bytestream::connection::Connection;

use super::error::DBusError;
use super::message::DBusMessage;
use super::value::{DBusBasicValue, DBusValue};

pub struct DBusConnection {
    conn: Connection,
}

pub enum DBusRequestNameFlags {
    AllowReplacement = 0x1,
    ReplaceExisting  = 0x2,
    DoNotQueue       = 0x4,
}

pub enum DBusRequestNameReply {
    PrimaryOwner,
    InQueue,
    Exists,
    AlreadyOwner,
}

pub enum DBusReleaseNameReply {
    Released,
    NonExistent,
    NotOwner,
}

pub struct DBusMessages<'a> {
    conn: &'a Connection,
}

impl DBusConnection {
    pub fn session_new() -> Result<DBusConnection, DBusError> {
        Ok(DBusConnection {
            conn: try!(Connection::connect_session()),
        })
    }

    pub fn system_new() -> Result<DBusConnection, DBusError> {
        Ok(DBusConnection {
            conn: try!(Connection::connect_system()),
        })
    }

    pub fn request_name(&self, name: &str, flags: DBusRequestNameFlags) -> Result<DBusRequestNameReply, DBusError> {
        let msg = DBusMessage::new_method_call(
                "org.freedesktop.DBus",
                "/org/freedesktop/DBus",
                "org.freedesktop.DBus",
                "RequestName")
            .add_argument(&name)
            .add_argument(&(flags as u32));
        if let Some(mut results) = try!(self.conn.call_sync(msg.extract())) {
            if let Some(DBusValue::BasicValue(DBusBasicValue::Uint32(r))) = results.pop() {
                match r {
                    1 => Ok(DBusRequestNameReply::PrimaryOwner),
                    2 => Ok(DBusRequestNameReply::InQueue),
                    3 => Ok(DBusRequestNameReply::Exists),
                    4 => Ok(DBusRequestNameReply::AlreadyOwner),
                    _ => Err(DBusError::InvalidReply(format!("RequestName: invalid response {}", r))),
                }
            } else {
                return Err(DBusError::InvalidReply("RequestName: invalid response".to_owned()));
            }
        } else {
            return Err(DBusError::InvalidReply("RequestName: no response".to_owned()));
        }
    }

    pub fn release_name(&self, name: &str) -> Result<DBusReleaseNameReply, DBusError> {
        let msg = DBusMessage::new_method_call(
                "org.freedesktop.DBus",
                "/org/freedesktop/DBus",
                "org.freedesktop.DBus",
                "ReleaseName")
            .add_argument(&name);
        if let Some(mut results) = try!(self.conn.call_sync(msg.extract())) {
            if let Some(DBusValue::BasicValue(DBusBasicValue::Uint32(r))) = results.pop() {
                match r {
                    1 => Ok(DBusReleaseNameReply::Released),
                    2 => Ok(DBusReleaseNameReply::NonExistent),
                    3 => Ok(DBusReleaseNameReply::NotOwner),
                    _ => Err(DBusError::InvalidReply(format!("ReleaseName: invalid response {}", r))),
                }
            } else {
                return Err(DBusError::InvalidReply("ReleaseName: invalid response".to_owned()));
            }
        } else {
            return Err(DBusError::InvalidReply("ReleaseName: no response".to_owned()));
        }
    }

    pub fn add_match(&self, match_rule: &str) -> Result<(), DBusError> {
        let msg = DBusMessage::new_method_call(
                "org.freedesktop.DBus",
                "/org/freedesktop/DBus",
                "org.freedesktop.DBus",
                "AddMatch")
            .add_argument(&match_rule);
        try!(self.conn.call_sync(msg.extract()));
        Ok(())
    }

    pub fn send(&self, msg: DBusMessage) -> Result<u32, DBusError> {
        Ok(try!(self.conn.send(msg.extract())))
    }

    pub fn iter(&self) -> DBusMessages {
        DBusMessages {
            conn: &self.conn,
        }
    }
}

impl<'a> Iterator for DBusMessages<'a> {
    type Item = DBusMessage;

    fn next(&mut self) -> Option<Self::Item> {
        let res = self.conn.read_msg();
        match res {
            Ok(message) => {
                let dbus_message = DBusMessage::new(message);
                if dbus_message.should_handle() {
                    Some(dbus_message)
                } else {
                    None
                }
            },
            Err(_)      => None,
        }
    }
}
<|endoftext|>