<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;

            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()),
            })
        }
    }
}
<commit_msg>Conditions: change the type of the first_opens field<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: 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: FIRST_OPENS_DEFAULT,
            last_closes: None,
            max_size: None,
            patterns: Vec::new(),
        }
    }

    pub fn is_opening(&self, message: &Message) -> bool {
        if self.first_opens {
            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 = 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, 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, FIRST_OPENS_DEFAULT};
    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 = FIRST_OPENS_DEFAULT;
            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 = 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>#![allow(dead_code)]

use std::path::Path;
use std::fs::File;
use std::io::Read;
use std::fmt::{Debug, Formatter, Result as FmtResult};

use rand::{thread_rng, Rng};
use sdl2::Sdl;
use sdl2::rect::Rect;
use ep::FromPrimitive;

use screen::Screen;
use instruction::{Opcodes, Instruction};

const FONT_SET: [u8; 80] = [
    0xf0, 0x90, 0x90, 0x90, 0xf0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xf0, 0x10, 0xf0, 0x80, 0xf0, // 2
    0xf0, 0x10, 0xf0, 0x10, 0xf0, // 3
    0x90, 0x90, 0xf0, 0x10, 0x10, // 4
    0xf0, 0x80, 0xf0, 0x10, 0xf0, // 5
    0xf0, 0x80, 0xf0, 0x90, 0xf0, // 6
    0xf0, 0x10, 0x20, 0x40, 0x40, // 7
    0xf0, 0x90, 0xf0, 0x90, 0xf0, // 8
    0xf0, 0x90, 0xf0, 0x10, 0xf0, // 9
    0xf0, 0x90, 0xf0, 0x90, 0x90, // A
    0xe0, 0x90, 0xe0, 0x90, 0xe0, // B
    0xf0, 0x80, 0x80, 0x80, 0x80, // C
    0xe0, 0x90, 0x90, 0x90, 0xe0, // D
    0xf0, 0x80, 0xf0, 0x80, 0xf0, // E
    0xf0, 0x80, 0xf0, 0x80, 0x80, // F
];

pub struct Chip8<'a> {
    regs:   [u8; 16],
    i:      u16,
    dt:     u8,
    st:     u8,
    pc:     usize,

    inst:   Instruction,
    jmp:    bool,

    key:    u8,
    mem:    [u8; 4096],
    stack:  Vec<usize>,
    screen: Screen<'a>,
}

impl<'a> Debug for Chip8<'a> {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        self.regs[..].fmt(f)
    }
}

impl<'a> Chip8<'a> {
    pub fn new(sdl: &Sdl) -> Chip8<'a> {
        Chip8 {
            regs:   [0; 16],
            i:      0,
            dt:     0,
            st:     0,
            pc:     0x200,
            inst:   Instruction::new(),
            jmp:    false,
            key:    0,
            mem:    [0; 4096],
            stack:  vec![],
            screen: Screen::new(sdl),
        }
    }

    pub fn load_rom(&mut self, path: &str) {
        let mut rom: Vec<u8> = Vec::new();
        let mut file = File::open(Path::new(path)).unwrap();
        file.read_to_end(&mut rom).unwrap();
        for i in 0usize..rom.len() {
            self.mem[0x200 + i] = rom[i];
        }
        // TODO: move this
        for i in 0usize..FONT_SET.len() {
            self.mem[0x00 + i] = FONT_SET[i];
        }
    }

    pub fn run(&mut self) {
        let raw_data = (self.mem[self.pc], self.mem[self.pc +1]);
        self.inst.decode(raw_data);

        self.jmp = false;

        println!("{:#x}: {:#x}", self.pc, self.inst.opcode);

        match Opcodes::from_u16(self.inst.opcode).unwrap() {
            Opcodes::CLS    => self.cls(),
            Opcodes::RET    => self.ret(),
            Opcodes::JMP    => self.jmp(),
            Opcodes::JMP_VA => self.jmp_va(),
            Opcodes::CALL   => self.call(),
            Opcodes::SE_VB  => self.se_vb(),
            Opcodes::SE_VV  => self.se_vv(),
            Opcodes::SNE_VB => self.sne_vb(),
            Opcodes::SNE_VV => self.sne_vv(),
            Opcodes::OR     => self.or(),
            Opcodes::ADD_VB => self.add_vb(),
            Opcodes::ADD_VV => self.add_vv(),
            Opcodes::ADD_IV => self.add_iv(),
            Opcodes::SUB    => self.sub(),
            Opcodes::XOR    => self.xor(),
            Opcodes::AND    => self.and(),
            Opcodes::LD_VB  => self.ld_vb(),
            Opcodes::LD_BV  => self.ld_bv(),
            Opcodes::LD_VV  => self.ld_vv(),
            Opcodes::LD_VI  => self.ld_vi(),
            Opcodes::LD_VK  => self.ld_vk(),
            Opcodes::LD_IV  => self.ld_iv(),
            Opcodes::LD_FV  => self.ld_fv(),
            Opcodes::LD_IA  => self.ld_ia(),
            Opcodes::LD_VDT => self.ld_vdt(),
            Opcodes::LD_DTV => self.ld_dtv(),
            Opcodes::LD_STV => self.ld_stv(),
            Opcodes::SKP    => self.skp(),
            Opcodes::SKNP   => self.sknp(),
            Opcodes::RND    => self.rnd(),
            Opcodes::SHR    => self.shr(),
            Opcodes::DRW    => self.drw(),
            _               => panic!("Unrecognized opcode: {:?}", self.inst.opcode),
        }

        println!("i: {}", self.i);

        if !self.jmp { self.inc_pc(); }

        if self.dt > 0 { self.dt -= 1; }
        if self.st > 0 { self.st -= 1; }

        // TODO: refactor this to self.screen.present();
        self.screen.renderer.present();
    }

    fn set_pc(&mut self, addr: u16) {
        self.pc = addr as usize;
    }

    fn inc_pc(&mut self) {
        self.pc += 2;
    }

    fn cls(&mut self) {
        self.screen.clear();
    }

    fn ret(&mut self) {
        let addr = self.stack.pop().unwrap();
        self.set_pc(addr as u16);
    }

    fn jmp(&mut self) {
        let addr = self.inst.nnn;
        self.set_pc(addr);
        self.jmp = true;
    }

    fn jmp_va(&mut self) {
        let offset = self.regs[0] as u16;
        let addr = self.inst.nnn + offset;
        self.set_pc(addr);
        self.jmp = true;
    }

    fn call(&mut self) {
        let old_addr = self.pc;
        let cur_addr = self.inst.nnn;
        self.stack.push(old_addr);
        self.set_pc(cur_addr);
        self.jmp = true;
    }

    fn se_vb(&mut self) {
        let idx = self.inst.x;
        if self.regs[idx] == self.inst.kk {
            self.inc_pc();
        }
    }

    fn se_vv(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        if self.regs[idx_x] == self.regs[idx_y] {
            self.inc_pc();
        }
    }

    fn sne_vb(&mut self) {
        let idx = self.inst.x;
        if self.regs[idx] != self.inst.kk {
            self.inc_pc();
        }
    }

    fn sne_vv(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        if self.regs[idx_x] != self.regs[idx_y] {
            self.inc_pc();
        }
    }

    fn or(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] |= self.regs[idx_y]
    }

    fn and(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] &= self.regs[idx_y];
    }

    fn add_vb(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] += self.regs[idx_y];
    }

    fn add_vv(&mut self) {
        let idx_x = self.inst.x;
        let x = self.regs[idx_x] as u16;
        let y = self.regs[self.inst.y] as u16;

        let res = x + y;
        if res > 0xFF {
            self.regs[15] = 1;
        } else {
            self.regs[15] = 0;
        }

        self.regs[idx_x] = res as u8;
    }

    fn add_iv(&mut self) {
        let idx = self.inst.x;
        self.i += self.regs[idx] as u16;
    }

    fn sub(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        let x = self.regs[idx_x];
        let y = self.regs[idx_y];

        self.regs[15] = if x > y { 1 } else { 0 };
        self.regs[idx_x] -= y;
    }

    fn xor(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] ^= self.regs[idx_y];
    }

    fn shr(&mut self) {
        let idx_x = self.inst.x;
        let vx    = self.regs[idx_x];
        if vx & 0x01 == 0 {
            self.regs[15] = 1;
        }
        self.regs[idx_x] = vx >> 1;
    }

    fn ld_vv(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] = self.regs[idx_y];
    }

    fn ld_vb(&mut self) {
        let idx = self.inst.x;
        self.regs[idx] = self.inst.kk;
    }

    fn ld_bv(&mut self) {
        let idx = self.inst.x;
        let mut vx  = self.regs[idx];
        let mut hundred = 100;

        for i in 0usize..3 {
            let bcd = vx / hundred;
            self.mem[self.i as usize + i] = bcd;
            vx -= bcd * hundred;
            hundred /= 10;
        }
    }

    fn ld_fv(&mut self) {
        let x = self.regs[self.inst.x];
        self.i = (x * 5) as u16;
    }

    fn ld_vi(&mut self) {
        let x = self.inst.x as usize;

        println!("ld_vi x: {}", x);

        for i in 0usize..x {
            self.regs[i] = self.mem[self.i as usize + i];
        }

        // TODO: is this really important?
        // self.i += x as u16 + 1;
    }

    fn ld_iv(&mut self) {
        let x = self.inst.x as usize;

        for i in 0usize..x {
            self.mem[self.i as usize + i] = self.regs[i];
        }

        // TODO: is this really important?
        // self.i += x as u16 + 1;
    }

    fn ld_ia(&mut self) {
        self.i = self.inst.nnn;
    }

    fn ld_vk(&mut self) {
        let mut key_pressed = false;

        if self.key != 0 {
            self.regs[self.inst.x] = self.key;
            key_pressed = true;
        }

        if !key_pressed {
            // TODO: create pc_dec
            self.pc -= 2;
        }
    }

    fn ld_vdt(&mut self) {
        let idx = self.inst.x;
        self.regs[idx] = self.dt;
    }

    fn ld_dtv(&mut self) {
        let idx: usize = self.inst.x;
        self.dt = self.regs[idx];
    }

    fn ld_stv(&mut self) {
        let idx = self.inst.x;
        self.st = self.regs[idx];
    }

    fn rnd(&mut self) {
        let idx = self.inst.x;
        let byte = self.inst.kk;
        let n: u8 = thread_rng().gen_range(0,255);
        self.regs[idx] = n & byte;
    }

    fn drw(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        let (x, y) = (self.regs[idx_x] as usize, self.regs[idx_y] as usize);
        let n = self.inst.n as usize;

        let start = self.i;

        self.regs[15] = 0;

        for i in 0..n {
            let px = self.mem[(start + i as u16) as usize];
            for j in 0..8 {
                if px & (0x80 >> j) != 0 {
                    if self.screen.buffer[(x+j + (y+i) * 64) as usize] == 1 {
                        self.regs[15] = 1;
                    }
                    self.screen.buffer[(x+j + (y+i) * 64) as usize] ^= 1;
                }
            }
        }

        for i in 0usize..320 {
            for j in 0usize..640 {
                if self.screen.buffer[(j/10)+(i/10)*64] == 1 {
                    self.screen.renderer.fill_rect(
                        Rect::new(j as i32, i as i32, 1, 1)
                        );
                }
            }
        }
    }

    fn skp(&mut self) {
        let x = self.regs[self.inst.x];
        if x == self.key {
            self.inc_pc();
        }
    }

    fn sknp(&mut self) {
        let x = self.regs[self.inst.x];
        if x != self.key {
            self.inc_pc();
        }
    }
}
<commit_msg>fixed ADD VB instruction due to arithmetic overflow<commit_after>#![allow(dead_code)]

use std::path::Path;
use std::fs::File;
use std::io::Read;
use std::fmt::{Debug, Formatter, Result as FmtResult};

use rand::{thread_rng, Rng};
use sdl2::Sdl;
use sdl2::rect::Rect;
use ep::FromPrimitive;

use screen::Screen;
use instruction::{Opcodes, Instruction};

const FONT_SET: [u8; 80] = [
    0xf0, 0x90, 0x90, 0x90, 0xf0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xf0, 0x10, 0xf0, 0x80, 0xf0, // 2
    0xf0, 0x10, 0xf0, 0x10, 0xf0, // 3
    0x90, 0x90, 0xf0, 0x10, 0x10, // 4
    0xf0, 0x80, 0xf0, 0x10, 0xf0, // 5
    0xf0, 0x80, 0xf0, 0x90, 0xf0, // 6
    0xf0, 0x10, 0x20, 0x40, 0x40, // 7
    0xf0, 0x90, 0xf0, 0x90, 0xf0, // 8
    0xf0, 0x90, 0xf0, 0x10, 0xf0, // 9
    0xf0, 0x90, 0xf0, 0x90, 0x90, // A
    0xe0, 0x90, 0xe0, 0x90, 0xe0, // B
    0xf0, 0x80, 0x80, 0x80, 0x80, // C
    0xe0, 0x90, 0x90, 0x90, 0xe0, // D
    0xf0, 0x80, 0xf0, 0x80, 0xf0, // E
    0xf0, 0x80, 0xf0, 0x80, 0x80, // F
];

pub struct Chip8<'a> {
    regs:   [u8; 16],
    i:      u16,
    dt:     u8,
    st:     u8,
    pc:     usize,

    inst:   Instruction,
    jmp:    bool,

    key:    u8,
    mem:    [u8; 4096],
    stack:  Vec<usize>,
    screen: Screen<'a>,
}

impl<'a> Debug for Chip8<'a> {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        self.regs[..].fmt(f)
    }
}

impl<'a> Chip8<'a> {
    pub fn new(sdl: &Sdl) -> Chip8<'a> {
        Chip8 {
            regs:   [0; 16],
            i:      0,
            dt:     0,
            st:     0,
            pc:     0x200,
            inst:   Instruction::new(),
            jmp:    false,
            key:    0,
            mem:    [0; 4096],
            stack:  vec![],
            screen: Screen::new(sdl),
        }
    }

    pub fn load_rom(&mut self, path: &str) {
        let mut rom: Vec<u8> = Vec::new();
        let mut file = File::open(Path::new(path)).unwrap();
        file.read_to_end(&mut rom).unwrap();
        for i in 0usize..rom.len() {
            self.mem[0x200 + i] = rom[i];
        }
        // TODO: move this
        for i in 0usize..FONT_SET.len() {
            self.mem[0x00 + i] = FONT_SET[i];
        }
    }

    pub fn run(&mut self) {
        let raw_data = (self.mem[self.pc], self.mem[self.pc +1]);
        self.inst.decode(raw_data);

        self.jmp = false;

        println!("{:#x}: {:#x}", self.pc, self.inst.opcode);

        match Opcodes::from_u16(self.inst.opcode).unwrap() {
            Opcodes::CLS    => self.cls(),
            Opcodes::RET    => self.ret(),
            Opcodes::JMP    => self.jmp(),
            Opcodes::JMP_VA => self.jmp_va(),
            Opcodes::CALL   => self.call(),
            Opcodes::SE_VB  => self.se_vb(),
            Opcodes::SE_VV  => self.se_vv(),
            Opcodes::SNE_VB => self.sne_vb(),
            Opcodes::SNE_VV => self.sne_vv(),
            Opcodes::OR     => self.or(),
            Opcodes::ADD_VB => self.add_vb(),
            Opcodes::ADD_VV => self.add_vv(),
            Opcodes::ADD_IV => self.add_iv(),
            Opcodes::SUB    => self.sub(),
            Opcodes::XOR    => self.xor(),
            Opcodes::AND    => self.and(),
            Opcodes::LD_VB  => self.ld_vb(),
            Opcodes::LD_BV  => self.ld_bv(),
            Opcodes::LD_VV  => self.ld_vv(),
            Opcodes::LD_VI  => self.ld_vi(),
            Opcodes::LD_VK  => self.ld_vk(),
            Opcodes::LD_IV  => self.ld_iv(),
            Opcodes::LD_FV  => self.ld_fv(),
            Opcodes::LD_IA  => self.ld_ia(),
            Opcodes::LD_VDT => self.ld_vdt(),
            Opcodes::LD_DTV => self.ld_dtv(),
            Opcodes::LD_STV => self.ld_stv(),
            Opcodes::SKP    => self.skp(),
            Opcodes::SKNP   => self.sknp(),
            Opcodes::RND    => self.rnd(),
            Opcodes::SHR    => self.shr(),
            Opcodes::DRW    => self.drw(),
            _               => panic!("Unrecognized opcode: {:?}", self.inst.opcode),
        }

        println!("i: {}", self.i);

        if !self.jmp { self.inc_pc(); }

        if self.dt > 0 { self.dt -= 1; }
        if self.st > 0 { self.st -= 1; }

        // TODO: refactor this to self.screen.present();
        self.screen.renderer.present();
    }

    fn set_pc(&mut self, addr: u16) {
        self.pc = addr as usize;
    }

    fn inc_pc(&mut self) {
        self.pc += 2;
    }

    fn cls(&mut self) {
        self.screen.clear();
    }

    fn ret(&mut self) {
        let addr = self.stack.pop().unwrap();
        self.set_pc(addr as u16);
    }

    fn jmp(&mut self) {
        let addr = self.inst.nnn;
        self.set_pc(addr);
        self.jmp = true;
    }

    fn jmp_va(&mut self) {
        let offset = self.regs[0] as u16;
        let addr = self.inst.nnn + offset;
        self.set_pc(addr);
        self.jmp = true;
    }

    fn call(&mut self) {
        let old_addr = self.pc;
        let cur_addr = self.inst.nnn;
        self.stack.push(old_addr);
        self.set_pc(cur_addr);
        self.jmp = true;
    }

    fn se_vb(&mut self) {
        let idx = self.inst.x;
        if self.regs[idx] == self.inst.kk {
            self.inc_pc();
        }
    }

    fn se_vv(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        if self.regs[idx_x] == self.regs[idx_y] {
            self.inc_pc();
        }
    }

    fn sne_vb(&mut self) {
        let idx = self.inst.x;
        if self.regs[idx] != self.inst.kk {
            self.inc_pc();
        }
    }

    fn sne_vv(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        if self.regs[idx_x] != self.regs[idx_y] {
            self.inc_pc();
        }
    }

    fn or(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] |= self.regs[idx_y]
    }

    fn and(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] &= self.regs[idx_y];
    }

    fn add_vb(&mut self) {
        let idx_x = self.inst.x;

        // TODO: I guess this shouldn't be this way
        let res = self.regs[idx_x]  as u16 + self.inst.kk as u16;

        if res > 0xFF {
            self.regs[idx_x] = (res & 0xFF) as u8;
        } else {
            self.regs[idx_x] += self.inst.kk;
        }
    }

    fn add_vv(&mut self) {
        let idx_x = self.inst.x;
        let x = self.regs[idx_x] as u16;
        let y = self.regs[self.inst.y] as u16;

        let res = x + y;
        if res > 0xFF {
            self.regs[15] = 1;
        } else {
            self.regs[15] = 0;
        }

        self.regs[idx_x] = res as u8;
    }

    fn add_iv(&mut self) {
        let idx = self.inst.x;
        self.i += self.regs[idx] as u16;
    }

    fn sub(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        let x = self.regs[idx_x];
        let y = self.regs[idx_y];

        self.regs[15] = if x > y { 1 } else { 0 };
        self.regs[idx_x] -= y;
    }

    fn xor(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] ^= self.regs[idx_y];
    }

    fn shr(&mut self) {
        let idx_x = self.inst.x;
        let vx    = self.regs[idx_x];
        if vx & 0x01 == 0 {
            self.regs[15] = 1;
        }
        self.regs[idx_x] = vx >> 1;
    }

    fn ld_vv(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        self.regs[idx_x] = self.regs[idx_y];
    }

    fn ld_vb(&mut self) {
        let idx = self.inst.x;
        self.regs[idx] = self.inst.kk;
    }

    fn ld_bv(&mut self) {
        let idx = self.inst.x;
        let mut vx  = self.regs[idx];
        let mut hundred = 100;

        for i in 0usize..3 {
            let bcd = vx / hundred;
            self.mem[self.i as usize + i] = bcd;
            vx -= bcd * hundred;
            hundred /= 10;
        }
    }

    fn ld_fv(&mut self) {
        let x = self.regs[self.inst.x];
        self.i = (x * 5) as u16;
    }

    fn ld_vi(&mut self) {
        let x = self.inst.x as usize;

        println!("ld_vi x: {}", x);

        for i in 0usize..x {
            self.regs[i] = self.mem[self.i as usize + i];
        }

        // TODO: is this really important?
        // self.i += x as u16 + 1;
    }

    fn ld_iv(&mut self) {
        let x = self.inst.x as usize;

        for i in 0usize..x {
            self.mem[self.i as usize + i] = self.regs[i];
        }

        // TODO: is this really important?
        // self.i += x as u16 + 1;
    }

    fn ld_ia(&mut self) {
        self.i = self.inst.nnn;
    }

    fn ld_vk(&mut self) {
        let mut key_pressed = false;

        if self.key != 0 {
            self.regs[self.inst.x] = self.key;
            key_pressed = true;
        }

        if !key_pressed {
            // TODO: create pc_dec
            self.pc -= 2;
        }
    }

    fn ld_vdt(&mut self) {
        let idx = self.inst.x;
        self.regs[idx] = self.dt;
    }

    fn ld_dtv(&mut self) {
        let idx: usize = self.inst.x;
        self.dt = self.regs[idx];
    }

    fn ld_stv(&mut self) {
        let idx = self.inst.x;
        self.st = self.regs[idx];
    }

    fn rnd(&mut self) {
        let idx = self.inst.x;
        let byte = self.inst.kk;
        let n: u8 = thread_rng().gen_range(0,255);
        self.regs[idx] = n & byte;
    }

    fn drw(&mut self) {
        let idx_x = self.inst.x;
        let idx_y = self.inst.y;
        let (x, y) = (self.regs[idx_x] as usize, self.regs[idx_y] as usize);
        let n = self.inst.n as usize;

        let start = self.i;

        self.regs[15] = 0;

        for i in 0..n {
            let px = self.mem[(start + i as u16) as usize];
            for j in 0..8 {
                if px & (0x80 >> j) != 0 {
                    if self.screen.buffer[(x+j + (y+i) * 64) as usize] == 1 {
                        self.regs[15] = 1;
                    }
                    self.screen.buffer[(x+j + (y+i) * 64) as usize] ^= 1;
                }
            }
        }

        for i in 0usize..320 {
            for j in 0usize..640 {
                if self.screen.buffer[(j/10)+(i/10)*64] == 1 {
                    self.screen.renderer.fill_rect(
                        Rect::new(j as i32, i as i32, 1, 1)
                        );
                }
            }
        }
    }

    fn skp(&mut self) {
        let x = self.regs[self.inst.x];
        if x == self.key {
            self.inc_pc();
        }
    }

    fn sknp(&mut self) {
        let x = self.regs[self.inst.x];
        if x != self.key {
            self.inc_pc();
        }
    }
}
<|endoftext|>


<commit_before>//! Endpoint for searching and discovery functionality

use diesel_full_text_search::*;

use crate::controllers::helpers::Paginate;
use crate::controllers::prelude::*;
use crate::models::{Crate, CrateBadge, CrateVersions, OwnerKind, Version};
use crate::schema::*;
use crate::views::EncodableCrate;

use crate::models::krate::{canon_crate_name, ALL_COLUMNS};

/// Handles the `GET /crates` route.
/// Returns a list of crates. Called in a variety of scenarios in the
/// front end, including:
/// - Alphabetical listing of crates
/// - List of crates under a specific owner
/// - Listing a user's followed crates
///
/// Notes:
/// The different use cases this function covers is handled through passing
/// in parameters in the GET request.
///
/// We would like to stop adding functionality in here. It was built like
/// this to keep the number of database queries low, though given Rust's
/// low performance overhead, this is a soft goal to have, and can afford
/// more database transactions if it aids understandability.
///
/// All of the edge cases for this function are not currently covered
/// in testing, and if they fail, it is difficult to determine what
/// caused the break. In the future, we should look at splitting this
/// function out to cover the different use cases, and create unit tests
/// for them.
pub fn search(req: &mut dyn Request) -> CargoResult<Response> {
    use diesel::sql_types::Bool;

    let conn = req.db_conn()?;
    let (offset, limit) = req.pagination(10, 100)?;
    let params = req.query();
    let sort = params
        .get("sort")
        .map(|s| &**s)
        .unwrap_or("recent-downloads");

    let mut query = crates::table
        .left_join(recent_crate_downloads::table)
        .select((
            ALL_COLUMNS,
            false.into_sql::<Bool>(),
            recent_crate_downloads::downloads.nullable(),
        ))
        .into_boxed();

    if let Some(q_string) = params.get("q") {
        if !q_string.is_empty() {
            let sort = params.get("sort").map(|s| &**s).unwrap_or("relevance");
            let q = plainto_tsquery(q_string);
            query = query.filter(
                q.matches(crates::textsearchable_index_col)
                    .or(Crate::with_name(q_string)),
            );

            query = query.select((
                ALL_COLUMNS,
                Crate::with_name(q_string),
                recent_crate_downloads::downloads.nullable(),
            ));
            query = query.order(Crate::with_name(q_string).desc());

            if sort == "relevance" {
                let rank = ts_rank_cd(crates::textsearchable_index_col, q);
                query = query.then_order_by(rank.desc())
            }
        }
    }

    if let Some(cat) = params.get("category") {
        query = query.filter(
            crates::id.eq_any(
                crates_categories::table
                    .select(crates_categories::crate_id)
                    .inner_join(categories::table)
                    .filter(
                        categories::slug
                            .eq(cat)
                            .or(categories::slug.like(format!("{}::%", cat))),
                    ),
            ),
        );
    }

    if let Some(kw) = params.get("keyword") {
        query = query.filter(
            crates::id.eq_any(
                crates_keywords::table
                    .select(crates_keywords::crate_id)
                    .inner_join(keywords::table)
                    .filter(crate::lower(keywords::keyword).eq(crate::lower(kw))),
            ),
        );
    } else if let Some(letter) = params.get("letter") {
        let pattern = format!(
            "{}%",
            letter
                .chars()
                .next()
                .unwrap()
                .to_lowercase()
                .collect::<String>()
        );
        query = query.filter(canon_crate_name(crates::name).like(pattern));
    } else if let Some(user_id) = params.get("user_id").and_then(|s| s.parse::<i32>().ok()) {
        query = query.filter(
            crates::id.eq_any(
                crate_owners::table
                    .select(crate_owners::crate_id)
                    .filter(crate_owners::owner_id.eq(user_id))
                    .filter(crate_owners::deleted.eq(false))
                    .filter(crate_owners::owner_kind.eq(OwnerKind::User as i32)),
            ),
        );
    } else if let Some(team_id) = params.get("team_id").and_then(|s| s.parse::<i32>().ok()) {
        query = query.filter(
            crates::id.eq_any(
                crate_owners::table
                    .select(crate_owners::crate_id)
                    .filter(crate_owners::owner_id.eq(team_id))
                    .filter(crate_owners::deleted.eq(false))
                    .filter(crate_owners::owner_kind.eq(OwnerKind::Team as i32)),
            ),
        );
    } else if params.get("following").is_some() {
        query = query.filter(
            crates::id.eq_any(
                follows::table
                    .select(follows::crate_id)
                    .filter(follows::user_id.eq(req.user()?.id)),
            ),
        );
    }

    if sort == "downloads" {
        query = query.then_order_by(crates::downloads.desc())
    } else if sort == "recent-downloads" {
        query = query.then_order_by(recent_crate_downloads::downloads.desc().nulls_last())
    } else if sort == "recent-updates" {
        query = query.order(crates::updated_at.desc());
    } else {
        query = query.then_order_by(crates::name.asc())
    }

    // The database query returns a tuple within a tuple, with the root
    // tuple containing 3 items.
    let data = query
        .paginate(limit, offset)
        .load::<((Crate, bool, Option<i64>), i64)>(&*conn)?;
    let total = data.first().map(|&(_, t)| t).unwrap_or(0);
    let perfect_matches = data.iter().map(|&((_, b, _), _)| b).collect::<Vec<_>>();
    let recent_downloads = data
        .iter()
        .map(|&((_, _, s), _)| s.unwrap_or(0))
        .collect::<Vec<_>>();
    let crates = data.into_iter().map(|((c, _, _), _)| c).collect::<Vec<_>>();

    let versions = crates
        .versions()
        .load::<Version>(&*conn)?
        .grouped_by(&crates)
        .into_iter()
        .map(|versions| Version::max(versions.into_iter().map(|v| v.num)));

    let badges = CrateBadge::belonging_to(&crates)
        .select((badges::crate_id, badges::all_columns))
        .load::<CrateBadge>(&*conn)?
        .grouped_by(&crates)
        .into_iter()
        .map(|badges| badges.into_iter().map(|cb| cb.badge).collect());

    let crates = versions
        .zip(crates)
        .zip(perfect_matches)
        .zip(recent_downloads)
        .zip(badges)
        .map(
            |((((max_version, krate), perfect_match), recent_downloads), badges)| {
                krate.minimal_encodable(
                    &max_version,
                    Some(badges),
                    perfect_match,
                    Some(recent_downloads),
                )
            },
        )
        .collect();

    #[derive(Serialize)]
    struct R {
        crates: Vec<EncodableCrate>,
        meta: Meta,
    }
    #[derive(Serialize)]
    struct Meta {
        total: i64,
    }

    Ok(req.json(&R {
        crates,
        meta: Meta { total },
    }))
}
<commit_msg>Optimize our most time consuming query<commit_after>//! Endpoint for searching and discovery functionality

use diesel::sql_types::{NotNull, Nullable};
use diesel_full_text_search::*;

use crate::controllers::helpers::Paginate;
use crate::controllers::prelude::*;
use crate::models::{Crate, CrateBadge, CrateVersions, OwnerKind, Version};
use crate::schema::*;
use crate::views::EncodableCrate;

use crate::models::krate::{canon_crate_name, ALL_COLUMNS};

/// Handles the `GET /crates` route.
/// Returns a list of crates. Called in a variety of scenarios in the
/// front end, including:
/// - Alphabetical listing of crates
/// - List of crates under a specific owner
/// - Listing a user's followed crates
///
/// Notes:
/// The different use cases this function covers is handled through passing
/// in parameters in the GET request.
///
/// We would like to stop adding functionality in here. It was built like
/// this to keep the number of database queries low, though given Rust's
/// low performance overhead, this is a soft goal to have, and can afford
/// more database transactions if it aids understandability.
///
/// All of the edge cases for this function are not currently covered
/// in testing, and if they fail, it is difficult to determine what
/// caused the break. In the future, we should look at splitting this
/// function out to cover the different use cases, and create unit tests
/// for them.
pub fn search(req: &mut dyn Request) -> CargoResult<Response> {
    use diesel::sql_types::Bool;

    let conn = req.db_conn()?;
    let (offset, limit) = req.pagination(10, 100)?;
    let params = req.query();
    let sort = params
        .get("sort")
        .map(|s| &**s)
        .unwrap_or("recent-downloads");
    let mut has_filter = false;

    let selection = (
        ALL_COLUMNS,
        false.into_sql::<Bool>(),
        recent_crate_downloads::downloads.nullable(),
    );
    let mut query = crates::table
        .left_join(recent_crate_downloads::table)
        .select(selection)
        .into_boxed();

    if let Some(q_string) = params.get("q") {
        has_filter = true;
        if !q_string.is_empty() {
            let sort = params.get("sort").map(|s| &**s).unwrap_or("relevance");
            let q = plainto_tsquery(q_string);
            query = query.filter(
                q.matches(crates::textsearchable_index_col)
                    .or(Crate::with_name(q_string)),
            );

            query = query.select((
                ALL_COLUMNS,
                Crate::with_name(q_string),
                recent_crate_downloads::downloads.nullable(),
            ));
            query = query.order(Crate::with_name(q_string).desc());

            if sort == "relevance" {
                let rank = ts_rank_cd(crates::textsearchable_index_col, q);
                query = query.then_order_by(rank.desc())
            }
        }
    }

    if let Some(cat) = params.get("category") {
        has_filter = true;
        query = query.filter(
            crates::id.eq_any(
                crates_categories::table
                    .select(crates_categories::crate_id)
                    .inner_join(categories::table)
                    .filter(
                        categories::slug
                            .eq(cat)
                            .or(categories::slug.like(format!("{}::%", cat))),
                    ),
            ),
        );
    }

    if let Some(kw) = params.get("keyword") {
        has_filter = true;
        query = query.filter(
            crates::id.eq_any(
                crates_keywords::table
                    .select(crates_keywords::crate_id)
                    .inner_join(keywords::table)
                    .filter(crate::lower(keywords::keyword).eq(crate::lower(kw))),
            ),
        );
    } else if let Some(letter) = params.get("letter") {
        has_filter = true;
        let pattern = format!(
            "{}%",
            letter
                .chars()
                .next()
                .unwrap()
                .to_lowercase()
                .collect::<String>()
        );
        query = query.filter(canon_crate_name(crates::name).like(pattern));
    } else if let Some(user_id) = params.get("user_id").and_then(|s| s.parse::<i32>().ok()) {
        has_filter = true;
        query = query.filter(
            crates::id.eq_any(
                crate_owners::table
                    .select(crate_owners::crate_id)
                    .filter(crate_owners::owner_id.eq(user_id))
                    .filter(crate_owners::deleted.eq(false))
                    .filter(crate_owners::owner_kind.eq(OwnerKind::User as i32)),
            ),
        );
    } else if let Some(team_id) = params.get("team_id").and_then(|s| s.parse::<i32>().ok()) {
        has_filter = true;
        query = query.filter(
            crates::id.eq_any(
                crate_owners::table
                    .select(crate_owners::crate_id)
                    .filter(crate_owners::owner_id.eq(team_id))
                    .filter(crate_owners::deleted.eq(false))
                    .filter(crate_owners::owner_kind.eq(OwnerKind::Team as i32)),
            ),
        );
    } else if params.get("following").is_some() {
        has_filter = true;
        query = query.filter(
            crates::id.eq_any(
                follows::table
                    .select(follows::crate_id)
                    .filter(follows::user_id.eq(req.user()?.id)),
            ),
        );
    }

    if sort == "downloads" {
        query = query.then_order_by(crates::downloads.desc())
    } else if sort == "recent-downloads" {
        query = query.then_order_by(recent_crate_downloads::downloads.desc().nulls_last())
    } else if sort == "recent-updates" {
        query = query.order(crates::updated_at.desc());
    } else {
        query = query.then_order_by(crates::name.asc())
    }

    // The database query returns a tuple within a tuple, with the root
    // tuple containing 3 items.
    let data = if has_filter {
        query
            .paginate(limit, offset)
            .load::<((Crate, bool, Option<i64>), i64)>(&*conn)?
    } else {
        sql_function!(fn coalesce<T: NotNull>(value: Nullable<T>, default: T) -> T);
        query
            .select((
                // FIXME: Use `query.selection()` if that feature ends up in
                // Diesel 2.0
                selection,
                coalesce(crates::table.count().single_value(), 0),
            ))
            .limit(limit)
            .offset(offset)
            .load(&*conn)?
    };
    let total = data.first().map(|&(_, t)| t).unwrap_or(0);
    let perfect_matches = data.iter().map(|&((_, b, _), _)| b).collect::<Vec<_>>();
    let recent_downloads = data
        .iter()
        .map(|&((_, _, s), _)| s.unwrap_or(0))
        .collect::<Vec<_>>();
    let crates = data.into_iter().map(|((c, _, _), _)| c).collect::<Vec<_>>();

    let versions = crates
        .versions()
        .load::<Version>(&*conn)?
        .grouped_by(&crates)
        .into_iter()
        .map(|versions| Version::max(versions.into_iter().map(|v| v.num)));

    let badges = CrateBadge::belonging_to(&crates)
        .select((badges::crate_id, badges::all_columns))
        .load::<CrateBadge>(&*conn)?
        .grouped_by(&crates)
        .into_iter()
        .map(|badges| badges.into_iter().map(|cb| cb.badge).collect());

    let crates = versions
        .zip(crates)
        .zip(perfect_matches)
        .zip(recent_downloads)
        .zip(badges)
        .map(
            |((((max_version, krate), perfect_match), recent_downloads), badges)| {
                krate.minimal_encodable(
                    &max_version,
                    Some(badges),
                    perfect_match,
                    Some(recent_downloads),
                )
            },
        )
        .collect();

    #[derive(Serialize)]
    struct R {
        crates: Vec<EncodableCrate>,
        meta: Meta,
    }
    #[derive(Serialize)]
    struct Meta {
        total: i64,
    }

    Ok(req.json(&R {
        crates,
        meta: Meta { total },
    }))
}
<|endoftext|>


<commit_before>
pub mod raw {
   use libc::{c_int, c_char, c_uint};

   use apr::raw::{apr_pool_t, apr_time_t, apr_array_header_t, apr_off_t, apr_thread_mutex_t,
      apr_int64_t, apr_table_t, apr_bucket_brigade, apr_uri_t, apr_finfo_t, apr_sockaddr_t};

   use util_filter::raw::{ap_filter_t};

   pub const OK:        c_int = 0;
   pub const DECLINED:  c_int = -1;
   pub const DONE:      c_int = -2;
   pub const SUSPENDED: c_int = -3;

   #[repr(C)]
   pub struct request_rec {
      pub pool: *mut apr_pool_t,
      pub connection: *mut conn_rec,
      pub server: *mut server_rec,
      pub next: *mut request_rec,
      pub prev: *mut request_rec,
      pub main: *mut request_rec,
      pub the_request: *mut c_char,
      pub assbackwards: c_int,
      pub proxyreq: c_int,
      pub header_only: c_int,
      pub proto_num: c_int,
      pub protocol: *mut c_char,
      pub hostname: *const c_char,
      pub request_time: apr_time_t,
      pub status_line: *const c_char,
      pub status: c_int,
      pub method_number: c_int,
      pub method: *const c_char,
      pub allowed: apr_int64_t,
      pub allowed_xmethods: *mut apr_array_header_t,
      pub allowed_methods: *mut ap_method_list_t,
      pub sent_bodyct: apr_off_t,
      pub bytes_sent: apr_off_t,
      pub mtime: apr_time_t,
      pub range: *const c_char,
      pub clength: apr_off_t,
      pub chunked: c_int,
      pub read_body: c_int,
      pub read_chunked: c_int,
      pub expecting_100: c_uint,
      pub kept_body: *mut apr_bucket_brigade,
      pub body_table: *mut apr_table_t,
      pub remaining: apr_off_t,
      pub read_length: apr_off_t,
      pub headers_in: *mut apr_table_t,
      pub headers_out: *mut apr_table_t,
      pub err_headers_out: *mut apr_table_t,
      pub subprocess_env: *mut apr_table_t,
      pub notes: *mut apr_table_t,
      pub content_type: *const c_char,
      pub handler: *const c_char,
      pub content_encoding: *const c_char,
      pub content_languages: *mut apr_array_header_t,
      pub vlist_validator: *mut c_char,
      pub user: *mut c_char,
      pub ap_auth_type: *mut c_char,
      pub unparsed_uri: *mut c_char,
      pub uri: *mut c_char,
      pub filename: *mut c_char,
      pub canonical_filename: *mut c_char,
      pub path_info: *mut c_char,
      pub args: *mut c_char,
      pub used_path_info: c_int,
      pub eos_sent: c_int,
      pub per_dir_config: *mut ap_conf_vector_t,
      pub request_config: *mut ap_conf_vector_t,
      pub log: *const ap_logconf,
      pub log_id: *const c_char,
      pub htaccess: *const htaccess_result,
      pub output_filters: *mut ap_filter_t,
      pub input_filters: *mut ap_filter_t,
      pub proto_output_filters: *mut ap_filter_t,
      pub proto_input_filters: *mut ap_filter_t,
      pub no_cache: c_int,
      pub no_local_copy: c_int,
      pub invoke_mtx: *mut apr_thread_mutex_t,
      pub parsed_uri: apr_uri_t,
      pub finfo: apr_finfo_t,
      pub useragent_addr: *mut apr_sockaddr_t,
      pub useragent_ip: *mut c_char,
      pub trailers_in: *mut apr_table_t,
      pub trailers_out: *mut apr_table_t,
   }

   #[repr(C)]
   pub struct ap_logconf {
       pub module_levels: *mut c_char,
       pub level: c_int,
   }

   #[repr(C)]
   pub struct ap_conf_vector_t;

   #[repr(C)]
   pub struct ap_method_list_t;

   #[repr(C)]
   pub struct conn_rec;

   #[repr(C)]
   pub struct htaccess_result;

   #[repr(C)]
   pub struct process_rec;

   #[repr(C)]
   pub struct server_rec;

}

struct Wrapper<'a, T: 'a> {
   raw: &'a mut T
}

impl<'a, T> Wrapper<'a, T> {
   pub fn from_raw_ptr(ptr: *mut T) -> Option<Self> {
      if ptr.is_null() {
         None
      } else {
         let raw: &mut T = unsafe { &mut *ptr };
         Some(
            Wrapper::<T> {
               raw: raw
            }
         )
      }
   }
}

pub type Request<'a> = Wrapper<'a, raw::request_rec>;

use std::str;
use std::ffi::CStr;

macro_rules! str_getter {
   ( $field:ident ) => {
      pub fn $field(&self) -> Option<&'a str> {
         let ptr = self.raw.$field;
         if ptr.is_null() {
            return None
         }

         let data = unsafe { CStr::from_ptr(ptr) }.to_bytes();
         match str::from_utf8(data) {
            Ok(s) => Some(s),
            Err(_) => None
         }
      }
   }
}

impl<'a> Request<'a> {
   str_getter!(the_request);
   str_getter!(protocol);
   str_getter!(hostname);
   str_getter!(status_line);
   str_getter!(method);
   str_getter!(range);
   str_getter!(content_type);
   str_getter!(handler);
   str_getter!(content_encoding);
   str_getter!(vlist_validator);
   str_getter!(user);
   str_getter!(ap_auth_type);
   str_getter!(unparsed_uri);
   str_getter!(uri);
   str_getter!(filename);
   str_getter!(canonical_filename);
   str_getter!(path_info);
   str_getter!(args);
   str_getter!(log_id);
   str_getter!(useragent_ip);
}
<commit_msg>Not using macros for wrapper getter methods, since they are not that readable<commit_after>
pub mod raw {
   use libc::{c_int, c_char, c_uint};

   use apr::raw::{apr_pool_t, apr_time_t, apr_array_header_t, apr_off_t, apr_thread_mutex_t,
      apr_int64_t, apr_table_t, apr_bucket_brigade, apr_uri_t, apr_finfo_t, apr_sockaddr_t};

   use util_filter::raw::{ap_filter_t};

   pub const OK:        c_int = 0;
   pub const DECLINED:  c_int = -1;
   pub const DONE:      c_int = -2;
   pub const SUSPENDED: c_int = -3;

   #[repr(C)]
   pub struct request_rec {
      pub pool: *mut apr_pool_t,
      pub connection: *mut conn_rec,
      pub server: *mut server_rec,
      pub next: *mut request_rec,
      pub prev: *mut request_rec,
      pub main: *mut request_rec,
      pub the_request: *mut c_char,
      pub assbackwards: c_int,
      pub proxyreq: c_int,
      pub header_only: c_int,
      pub proto_num: c_int,
      pub protocol: *mut c_char,
      pub hostname: *const c_char,
      pub request_time: apr_time_t,
      pub status_line: *const c_char,
      pub status: c_int,
      pub method_number: c_int,
      pub method: *const c_char,
      pub allowed: apr_int64_t,
      pub allowed_xmethods: *mut apr_array_header_t,
      pub allowed_methods: *mut ap_method_list_t,
      pub sent_bodyct: apr_off_t,
      pub bytes_sent: apr_off_t,
      pub mtime: apr_time_t,
      pub range: *const c_char,
      pub clength: apr_off_t,
      pub chunked: c_int,
      pub read_body: c_int,
      pub read_chunked: c_int,
      pub expecting_100: c_uint,
      pub kept_body: *mut apr_bucket_brigade,
      pub body_table: *mut apr_table_t,
      pub remaining: apr_off_t,
      pub read_length: apr_off_t,
      pub headers_in: *mut apr_table_t,
      pub headers_out: *mut apr_table_t,
      pub err_headers_out: *mut apr_table_t,
      pub subprocess_env: *mut apr_table_t,
      pub notes: *mut apr_table_t,
      pub content_type: *const c_char,
      pub handler: *const c_char,
      pub content_encoding: *const c_char,
      pub content_languages: *mut apr_array_header_t,
      pub vlist_validator: *mut c_char,
      pub user: *mut c_char,
      pub ap_auth_type: *mut c_char,
      pub unparsed_uri: *mut c_char,
      pub uri: *mut c_char,
      pub filename: *mut c_char,
      pub canonical_filename: *mut c_char,
      pub path_info: *mut c_char,
      pub args: *mut c_char,
      pub used_path_info: c_int,
      pub eos_sent: c_int,
      pub per_dir_config: *mut ap_conf_vector_t,
      pub request_config: *mut ap_conf_vector_t,
      pub log: *const ap_logconf,
      pub log_id: *const c_char,
      pub htaccess: *const htaccess_result,
      pub output_filters: *mut ap_filter_t,
      pub input_filters: *mut ap_filter_t,
      pub proto_output_filters: *mut ap_filter_t,
      pub proto_input_filters: *mut ap_filter_t,
      pub no_cache: c_int,
      pub no_local_copy: c_int,
      pub invoke_mtx: *mut apr_thread_mutex_t,
      pub parsed_uri: apr_uri_t,
      pub finfo: apr_finfo_t,
      pub useragent_addr: *mut apr_sockaddr_t,
      pub useragent_ip: *mut c_char,
      pub trailers_in: *mut apr_table_t,
      pub trailers_out: *mut apr_table_t,
   }

   #[repr(C)]
   pub struct ap_logconf {
       pub module_levels: *mut c_char,
       pub level: c_int,
   }

   #[repr(C)]
   pub struct ap_conf_vector_t;

   #[repr(C)]
   pub struct ap_method_list_t;

   #[repr(C)]
   pub struct conn_rec;

   #[repr(C)]
   pub struct htaccess_result;

   #[repr(C)]
   pub struct process_rec;

   #[repr(C)]
   pub struct server_rec;

}

use std::str;
use std::ffi::CStr;
use libc::c_char;

struct Wrapper<'a, T: 'a> {
   raw: &'a mut T
}

impl<'a, T> Wrapper<'a, T> {
   pub fn from_raw_ptr(ptr: *mut T) -> Option<Self> {
      if ptr.is_null() {
         None
      } else {
         let raw: &mut T = unsafe { &mut *ptr };
         Some(
            Wrapper::<T> {
               raw: raw
            }
         )
      }
   }

   #[inline]
   fn string_value(&self, ptr: *const c_char) -> Option<&'a str> {
      if ptr.is_null() {
         return None
      }

      let data = unsafe { CStr::from_ptr(ptr) }.to_bytes();
      match str::from_utf8(data) {
         Ok(s) => Some(s),
         Err(_) => None
      }
   }
}

pub type Request<'a> = Wrapper<'a, raw::request_rec>;

impl<'a> Request<'a> {
   pub fn the_request(&self) -> Option<&'a str> {
      self.string_value(self.raw.the_request)
   }

   pub fn protocol(&self) -> Option<&'a str> {
      self.string_value(self.raw.protocol)
   }

   pub fn hostname(&self) -> Option<&'a str> {
      self.string_value(self.raw.hostname)
   }

   pub fn status_line(&self) -> Option<&'a str> {
      self.string_value(self.raw.status_line)
   }

   pub fn method(&self) -> Option<&'a str> {
      self.string_value(self.raw.method)
   }

   pub fn range(&self) -> Option<&'a str> {
      self.string_value(self.raw.range)
   }

   pub fn content_type(&self) -> Option<&'a str> {
      self.string_value(self.raw.content_type)
   }

   pub fn handler(&self) -> Option<&'a str> {
      self.string_value(self.raw.handler)
   }

   pub fn content_encoding(&self) -> Option<&'a str> {
      self.string_value(self.raw.content_encoding)
   }

   pub fn vlist_validator(&self) -> Option<&'a str> {
      self.string_value(self.raw.vlist_validator)
   }

   pub fn user(&self) -> Option<&'a str> {
      self.string_value(self.raw.user)
   }

   pub fn ap_auth_type(&self) -> Option<&'a str> {
      self.string_value(self.raw.ap_auth_type)
   }

   pub fn unparsed_uri(&self) -> Option<&'a str> {
      self.string_value(self.raw.unparsed_uri)
   }

   pub fn uri(&self) -> Option<&'a str> {
      self.string_value(self.raw.uri)
   }

   pub fn filename(&self) -> Option<&'a str> {
      self.string_value(self.raw.filename)
   }

   pub fn canonical_filename(&self) -> Option<&'a str> {
      self.string_value(self.raw.canonical_filename)
   }

   pub fn path_info(&self) -> Option<&'a str> {
      self.string_value(self.raw.path_info)
   }

   pub fn args(&self) -> Option<&'a str> {
      self.string_value(self.raw.args)
   }

   pub fn log_id(&self) -> Option<&'a str> {
      self.string_value(self.raw.log_id)
   }

   pub fn useragent_ip(&self) -> Option<&'a str> {
      self.string_value(self.raw.useragent_ip)
   }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Buffer whose content is read-written by the GPU only.
//!
//! Each access from the CPU or from the GPU locks the whole buffer for either reading or writing.
//! You can read the buffer multiple times simultaneously from multiple queues. Trying to read and
//! write simultaneously, or write and write simultaneously will block with a semaphore.

use smallvec::SmallVec;
use std::marker::PhantomData;
use std::mem;
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;

use buffer::BufferUsage;
use buffer::sys::BufferCreationError;
use buffer::sys::SparseLevel;
use buffer::sys::UnsafeBuffer;
use buffer::traits::BufferAccess;
use buffer::traits::BufferInner;
use buffer::traits::TypedBufferAccess;
use device::Device;
use device::DeviceOwned;
use device::Queue;
use instance::QueueFamily;
use memory::pool::AllocLayout;
use memory::pool::MemoryPool;
use memory::pool::MemoryPoolAlloc;
use memory::pool::StdMemoryPool;
use sync::AccessError;
use sync::Sharing;

use OomError;
use SafeDeref;

/// Buffer whose content is accessible by the CPU.
#[derive(Debug)]
pub struct DeviceLocalBuffer<T: ?Sized, A = Arc<StdMemoryPool>>
    where A: MemoryPool
{
    // Inner content.
    inner: UnsafeBuffer,

    // The memory held by the buffer.
    memory: A::Alloc,

    // Queue families allowed to access this buffer.
    queue_families: SmallVec<[u32; 4]>,

    // Number of times this buffer is locked on the GPU side.
    gpu_lock: AtomicUsize,

    // Necessary to make it compile.
    marker: PhantomData<Box<T>>,
}

impl<T> DeviceLocalBuffer<T> {
    /// Builds a new buffer. Only allowed for sized data.
    #[inline]
    pub fn new<'a, I>(device: Arc<Device>, usage: BufferUsage, queue_families: I)
                      -> Result<Arc<DeviceLocalBuffer<T>>, OomError>
        where I: IntoIterator<Item = QueueFamily<'a>>
    {
        unsafe { DeviceLocalBuffer::raw(device, mem::size_of::<T>(), usage, queue_families) }
    }
}

impl<T> DeviceLocalBuffer<[T]> {
    /// Builds a new buffer. Can be used for arrays.
    #[inline]
    pub fn array<'a, I>(device: Arc<Device>, len: usize, usage: BufferUsage, queue_families: I)
                        -> Result<Arc<DeviceLocalBuffer<[T]>>, OomError>
        where I: IntoIterator<Item = QueueFamily<'a>>
    {
        unsafe { DeviceLocalBuffer::raw(device, len * mem::size_of::<T>(), usage, queue_families) }
    }
}

impl<T: ?Sized> DeviceLocalBuffer<T> {
    /// Builds a new buffer without checking the size.
    ///
    /// # Safety
    ///
    /// You must ensure that the size that you pass is correct for `T`.
    ///
    pub unsafe fn raw<'a, I>(device: Arc<Device>, size: usize, usage: BufferUsage,
                             queue_families: I)
                             -> Result<Arc<DeviceLocalBuffer<T>>, OomError>
        where I: IntoIterator<Item = QueueFamily<'a>>
    {
        let queue_families = queue_families
            .into_iter()
            .map(|f| f.id())
            .collect::<SmallVec<[u32; 4]>>();

        let (buffer, mem_reqs) = {
            let sharing = if queue_families.len() >= 2 {
                Sharing::Concurrent(queue_families.iter().cloned())
            } else {
                Sharing::Exclusive
            };

            match UnsafeBuffer::new(device.clone(), size, usage, sharing, SparseLevel::none()) {
                Ok(b) => b,
                Err(BufferCreationError::OomError(err)) => return Err(err),
                Err(_) => unreachable!(),        // We don't use sparse binding, therefore the other
                // errors can't happen
            }
        };

        let mem_ty = {
            let device_local = device
                .physical_device()
                .memory_types()
                .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0)
                .filter(|t| t.is_device_local());
            let any = device
                .physical_device()
                .memory_types()
                .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0);
            device_local.chain(any).next().unwrap()
        };

        let mem = MemoryPool::alloc(&Device::standard_pool(&device),
                                    mem_ty,
                                    mem_reqs.size,
                                    mem_reqs.alignment,
                                    AllocLayout::Linear)?;
        debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
        buffer.bind_memory(mem.memory(), mem.offset())?;

        Ok(Arc::new(DeviceLocalBuffer {
                        inner: buffer,
                        memory: mem,
                        queue_families: queue_families,
                        gpu_lock: AtomicUsize::new(0),
                        marker: PhantomData,
                    }))
    }
}

impl<T: ?Sized, A> DeviceLocalBuffer<T, A>
    where A: MemoryPool
{
    /// Returns the device used to create this buffer.
    #[inline]
    pub fn device(&self) -> &Arc<Device> {
        self.inner.device()
    }

    /// Returns the queue families this buffer can be used on.
    // TODO: use a custom iterator
    #[inline]
    pub fn queue_families(&self) -> Vec<QueueFamily> {
        self.queue_families
            .iter()
            .map(|&num| {
                     self.device()
                         .physical_device()
                         .queue_family_by_id(num)
                         .unwrap()
                 })
            .collect()
    }
}

unsafe impl<T: ?Sized, A> DeviceOwned for DeviceLocalBuffer<T, A>
    where A: MemoryPool
{
    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.inner.device()
    }
}

unsafe impl<T: ?Sized, A> BufferAccess for DeviceLocalBuffer<T, A>
    where T: 'static + Send + Sync,
          A: MemoryPool + 'static
{
    #[inline]
    fn inner(&self) -> BufferInner {
        BufferInner {
            buffer: &self.inner,
            offset: 0,
        }
    }

    #[inline]
    fn size(&self) -> usize {
        self.inner.size()
    }

    #[inline]
    fn conflict_key(&self, self_offset: usize, self_size: usize) -> u64 {
        self.inner.key()
    }

    #[inline]
    fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> {
        // FIXME: not implemented correctly
        /*let val = self.gpu_lock.fetch_add(1, Ordering::SeqCst);
        if val == 1 {
            true
        } else {
            self.gpu_lock.fetch_sub(1, Ordering::SeqCst);
            false
        }*/
        Ok(())
    }

    #[inline]
    unsafe fn increase_gpu_lock(&self) {
        // FIXME: not implemented correctly
        /*let val = self.gpu_lock.fetch_add(1, Ordering::SeqCst);
        debug_assert!(val >= 1);*/
    }

    #[inline]
    unsafe fn unlock(&self) {
        // TODO:
    }
}

unsafe impl<T: ?Sized, A> TypedBufferAccess for DeviceLocalBuffer<T, A>
    where T: 'static + Send + Sync,
          A: MemoryPool + 'static
{
    type Content = T;
}
<commit_msg>Correctly implement DeviceLocalBuffer<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Buffer whose content is read-written by the GPU only.
//!
//! Each access from the CPU or from the GPU locks the whole buffer for either reading or writing.
//! You can read the buffer multiple times simultaneously from multiple queues. Trying to read and
//! write simultaneously, or write and write simultaneously will block with a semaphore.

use smallvec::SmallVec;
use std::marker::PhantomData;
use std::mem;
use std::sync::Arc;
use std::sync::Mutex;

use buffer::BufferUsage;
use buffer::sys::BufferCreationError;
use buffer::sys::SparseLevel;
use buffer::sys::UnsafeBuffer;
use buffer::traits::BufferAccess;
use buffer::traits::BufferInner;
use buffer::traits::TypedBufferAccess;
use device::Device;
use device::DeviceOwned;
use device::Queue;
use instance::QueueFamily;
use memory::pool::AllocLayout;
use memory::pool::MemoryPool;
use memory::pool::MemoryPoolAlloc;
use memory::pool::StdMemoryPoolAlloc;
use sync::AccessError;
use sync::Sharing;

use OomError;

/// Buffer whose content is in device-local memory.
///
/// This buffer type is useful in order to store intermediary data. For example you execute a
/// compute shader that writes to this buffer, then read the content of the buffer in a following
/// compute or graphics pipeline.
///
/// The `DeviceLocalBuffer` will be in device-local memory, unless the device doesn't provide any
/// device-local memory.
#[derive(Debug)]
pub struct DeviceLocalBuffer<T: ?Sized, A = StdMemoryPoolAlloc> {
    // Inner content.
    inner: UnsafeBuffer,

    // The memory held by the buffer.
    memory: A,

    // Queue families allowed to access this buffer.
    queue_families: SmallVec<[u32; 4]>,

    // Number of times this buffer is locked on the GPU side.
    gpu_lock: Mutex<GpuAccess>,

    // Necessary to make it compile.
    marker: PhantomData<Box<T>>,
}

#[derive(Debug, Copy, Clone)]
enum GpuAccess {
    None,
    NonExclusive { num: u32 },
    Exclusive { num: u32 },
}

impl<T> DeviceLocalBuffer<T> {
    /// Builds a new buffer. Only allowed for sized data.
    // TODO: unsafe because uninitialized data
    #[inline]
    pub fn new<'a, I>(device: Arc<Device>, usage: BufferUsage, queue_families: I)
                      -> Result<Arc<DeviceLocalBuffer<T>>, OomError>
        where I: IntoIterator<Item = QueueFamily<'a>>
    {
        unsafe { DeviceLocalBuffer::raw(device, mem::size_of::<T>(), usage, queue_families) }
    }
}

impl<T> DeviceLocalBuffer<[T]> {
    /// Builds a new buffer. Can be used for arrays.
    // TODO: unsafe because uninitialized data
    #[inline]
    pub fn array<'a, I>(device: Arc<Device>, len: usize, usage: BufferUsage, queue_families: I)
                        -> Result<Arc<DeviceLocalBuffer<[T]>>, OomError>
        where I: IntoIterator<Item = QueueFamily<'a>>
    {
        unsafe { DeviceLocalBuffer::raw(device, len * mem::size_of::<T>(), usage, queue_families) }
    }
}

impl<T: ?Sized> DeviceLocalBuffer<T> {
    /// Builds a new buffer without checking the size.
    ///
    /// # Safety
    ///
    /// You must ensure that the size that you pass is correct for `T`.
    ///
    pub unsafe fn raw<'a, I>(device: Arc<Device>, size: usize, usage: BufferUsage,
                             queue_families: I)
                             -> Result<Arc<DeviceLocalBuffer<T>>, OomError>
        where I: IntoIterator<Item = QueueFamily<'a>>
    {
        let queue_families = queue_families
            .into_iter()
            .map(|f| f.id())
            .collect::<SmallVec<[u32; 4]>>();

        let (buffer, mem_reqs) = {
            let sharing = if queue_families.len() >= 2 {
                Sharing::Concurrent(queue_families.iter().cloned())
            } else {
                Sharing::Exclusive
            };

            match UnsafeBuffer::new(device.clone(), size, usage, sharing, SparseLevel::none()) {
                Ok(b) => b,
                Err(BufferCreationError::OomError(err)) => return Err(err),
                Err(_) => unreachable!(),        // We don't use sparse binding, therefore the other
                // errors can't happen
            }
        };

        let mem_ty = {
            let device_local = device
                .physical_device()
                .memory_types()
                .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0)
                .filter(|t| t.is_device_local());
            let any = device
                .physical_device()
                .memory_types()
                .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0);
            device_local.chain(any).next().unwrap()
        };

        let mem = MemoryPool::alloc(&Device::standard_pool(&device),
                                    mem_ty,
                                    mem_reqs.size,
                                    mem_reqs.alignment,
                                    AllocLayout::Linear)?;
        debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
        buffer.bind_memory(mem.memory(), mem.offset())?;

        Ok(Arc::new(DeviceLocalBuffer {
                        inner: buffer,
                        memory: mem,
                        queue_families: queue_families,
                        gpu_lock: Mutex::new(GpuAccess::None),
                        marker: PhantomData,
                    }))
    }
}

impl<T: ?Sized, A> DeviceLocalBuffer<T, A> {
    /// Returns the queue families this buffer can be used on.
    // TODO: use a custom iterator
    #[inline]
    pub fn queue_families(&self) -> Vec<QueueFamily> {
        self.queue_families
            .iter()
            .map(|&num| {
                     self.device()
                         .physical_device()
                         .queue_family_by_id(num)
                         .unwrap()
                 })
            .collect()
    }
}

unsafe impl<T: ?Sized, A> DeviceOwned for DeviceLocalBuffer<T, A> {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.inner.device()
    }
}

unsafe impl<T: ?Sized, A> BufferAccess for DeviceLocalBuffer<T, A>
    where T: 'static + Send + Sync
{
    #[inline]
    fn inner(&self) -> BufferInner {
        BufferInner {
            buffer: &self.inner,
            offset: 0,
        }
    }

    #[inline]
    fn size(&self) -> usize {
        self.inner.size()
    }

    #[inline]
    fn conflict_key(&self, self_offset: usize, self_size: usize) -> u64 {
        self.inner.key()
    }

    #[inline]
    fn try_gpu_lock(&self, exclusive: bool, _: &Queue) -> Result<(), AccessError> {
        let mut lock = self.gpu_lock.lock().unwrap();
        match &mut *lock {
            a @ &mut GpuAccess::None => {
                if exclusive {
                    *a = GpuAccess::Exclusive { num: 1 };
                } else {
                    *a = GpuAccess::NonExclusive { num: 1 };
                }

                Ok(())
            },
            &mut GpuAccess::NonExclusive { ref mut num } => {
                if exclusive {
                    Err(AccessError::AlreadyInUse)
                } else {
                    *num += 1;
                    Ok(())
                }
            },
            &mut GpuAccess::Exclusive { ref mut num } => {
                Err(AccessError::AlreadyInUse)
            }
        }
    }

    #[inline]
    unsafe fn increase_gpu_lock(&self) {
        let mut lock = self.gpu_lock.lock().unwrap();
        match *lock {
            GpuAccess::None => panic!(),
            GpuAccess::NonExclusive { ref mut num } => {
                debug_assert!(*num >= 1);
                *num += 1;
            },
            GpuAccess::Exclusive { ref mut num } => {
                debug_assert!(*num >= 1);
                *num += 1;
            }
        }
    }

    #[inline]
    unsafe fn unlock(&self) {
        let mut lock = self.gpu_lock.lock().unwrap();
        match *lock {
            GpuAccess::None => panic!("Tried to unlock a buffer that isn't locked"),
            GpuAccess::NonExclusive { ref mut num } => {
                assert!(*num >= 1);
                *num -= 1;
            },
            GpuAccess::Exclusive { ref mut num } => {
                assert!(*num >= 1);
                *num -= 1;
            },
        }
    }
}

unsafe impl<T: ?Sized, A> TypedBufferAccess for DeviceLocalBuffer<T, A>
    where T: 'static + Send + Sync
{
    type Content = T;
}
<|endoftext|>