<commit_before>/*
Copyright (c) 2016 Antoine Wacheux

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

extern crate liquid;
extern crate regex_syntax;

use liquidobject::LiquidObject;
use self::liquid::Value;
use std::collections::HashMap;
use self::regex_syntax::Expr;

pub struct LexicalUnit {
    name : String,
    regex : String,
}

impl LiquidObject for LexicalUnit {
    fn to_liquid_object(&self) -> Value {
        let mut result = HashMap::new();
        result.insert("name".to_owned(), liquid::Value::Str(self.name.clone()));
        result.insert("regex".to_owned(), liquid::Value::Str(self.regex.clone()));
        Value::Object(result)
    }
}

pub struct ProductionRule {
    name: String,
    rule: Vec<RuleComponent>
}

pub enum RuleComponent {
    ProductionRule(String),
    LexicalUnit(String)
}

pub struct Grammar {
    lexical_units : Vec<LexicalUnit>,
    production_rules: Vec<ProductionRule>,
    axiom : String
}

impl Grammar {
    fn new() -> Grammar {
        Grammar {
            lexical_units: Vec::new(),
            production_rules: Vec::new(),
            axiom: String::new(),
        }
    }

    fn get_production_rule(&self, name: &String) -> Vec<&ProductionRule> {
        self.production_rules.iter().filter(|x| x.name == *name).collect()
    }

    pub fn get_all_production_rules_name(&self) -> Vec<String> {
        let uniq_filter = {
            let mut found : Vec<String> = Vec::new();

            move |x:&String| {
                if found.iter().find(|elem| *elem == x).is_some() {
                    false
                }
                else {
                    found.push(x.clone());
                    true
                }
            }
        };

        self.production_rules.iter().map(|x| x.name.clone()).filter(uniq_filter).collect()
    }

    pub fn get_all_lexical_units(&self) -> &Vec<LexicalUnit> {
        &self.lexical_units
    }
}

//Methods
//get_rule(name: String&) -> Vec<ProductionRule>&

struct ParserContext {
    src: String,
    row: u32,
    col: u32,
    str_pos: usize,
}

impl ParserContext {
    fn new(src : String) -> ParserContext {
        ParserContext {
            src: src,
            row: 1,
            col: 0,
            str_pos: 0
        }
    }

    fn next_word(&mut self) -> Option<String> {
        let mut iter = self.src.chars();
        let mut ch = iter.nth(self.str_pos);
        let mut c : char;
        let mut read_word = String::new();

        match ch {
            None => { return None },
            Some(chr) => { c = chr}
        }

        macro_rules! next_char {
            ($err:stmt) => ({
                self.str_pos += 1;
                ch = iter.next();

                match ch {
                    None => { $err },
                    Some(chr) => { c = chr }
                }

                match c { //TODO : Implement a better line/column tracking and forward this information in the word
                    ' ' | '\t' => self.col += 1,
                    '\n' => self.row += 1,
                    _ => {}
                }
            });
        }

        while (c == ' ') || (c == '\n') || (c == '\t') { //We skip leading whitespaces
            next_char! { return None }
        }

        while !((c == ' ') || (c == '\n') || (c == '\t')) {
            read_word.push(c);
            next_char! { return Some(read_word) }
        }

        Some(read_word)
    }

    pub fn read_line(&mut self) -> Option<String> {
        let mut iter = self.src.chars();
        let mut ch = iter.nth(self.str_pos);
        let mut c : char;
        let mut read_word = String::new();

        match ch {
            None => { return None },
            Some(chr) => { c = chr}
        }

        macro_rules! next_char {
            ($err:stmt) => ({
                self.str_pos += 1;
                ch = iter.next();

                match ch {
                    None => { $err },
                    Some(chr) => { c = chr }
                }

                match c {
                    ' ' | '\t' => self.col += 1,
                    '\n' => self.row += 1,
                    _ => {}
                }
            });
        }

        while (c == ' ') || (c == '\n') || (c == '\t') { //We skip leading whitespaces
            next_char! { return None }
        }

        while c != '\n' {
            read_word.push(c);
            next_char! { return Some(read_word) }
        }

        Some(read_word)
    }
}

struct GrammarParser {
    ctx: ParserContext,
    grammar: Grammar,
    auto_counter: u32, //Counter for generating automatic lexical unit name
}

impl GrammarParser {
    fn new(src: String) -> GrammarParser {
        GrammarParser {
            ctx : ParserContext::new(src),
            grammar: Grammar::new(),
            auto_counter: 1,
        }
    }

    fn get_resulting_grammar(self) -> Grammar {
        self.grammar
    }

    fn parse(&mut self) -> Result<(), String> {
        while let Some(str_word) = self.ctx.next_word() {
            match str_word.chars().next().unwrap() {
                'A'...'Z' => {
                    match self.parse_grammar_rule(str_word) {
                        Ok(()) => {},
                        Err(msg) => {return Err(msg)},
                    }
                },
                'a'...'z' => {
                    match self.parse_lexical_unit(str_word) {
                        Ok(()) => {},
                        Err(msg) => {return Err(msg)},
                    }
                }
                _ => { return Err(String::from("Unknown token : ") + str_word.as_str())}
            }
        }

        Ok(())
    }

    fn parse_grammar_rule(&mut self, name : String) -> Result<(), String> {
        match self.ctx.next_word() {
            None => {return Err(format!("line {} : Expected '->'", self.ctx.row))},
            Some(token) => {
                if token != "->" {
                    return Err(format!("line {} : Expected '->' found '{}'", self.ctx.row, token));
                }
            }
        }

        if let Some(rule_str) = self.ctx.read_line() {
            let mut production_rule = ProductionRule{
                name: name,
                rule: Vec::new()
            };

            for comp_str in rule_str.split_whitespace() {
                let component_str = String::from(comp_str);
                let component = match component_str.chars().next().unwrap() {
                    'a'...'z' => {RuleComponent::LexicalUnit(component_str.to_owned())},
                    'A'...'Z' => {RuleComponent::ProductionRule(component_str.to_owned())},
                    '\'' => {
                        let s = component_str.len();
                        if s < 3 {
                            return Err(format!("line {} : The regex in a production rule cannot be empty.", self.ctx.row));
                        }

                        let regex = unsafe {component_str.slice_unchecked(1, s-1) }; //TODO : Remove this unsafe
                        let name = format!("auto_{}", self.auto_counter);
                        self.auto_counter += 1;

                        if self.grammar.lexical_units.iter().find(|rule| rule.name == name).is_none() {
                            self.grammar.lexical_units.push(LexicalUnit{name:name.clone(), regex:String::from(regex)});
                        }

                        RuleComponent::LexicalUnit(name)
                    },
                    _ => {return Err(format!("line {}: '{}' is unexpected", self.ctx.row, component_str))}
                };

                production_rule.rule.push(component);
            }
            self.grammar.production_rules.push(production_rule);
            Ok(())
        }
        else {
            Err(format!("A production rule cannot be empty"))
        }
    }

    fn parse_lexical_unit(&mut self, name : String) -> Result<(), String> {
        //Let's read the ':'
        match self.ctx.next_word() {
            None => {return Err(format!("line {} : Expected ':'", self.ctx.row))},
            Some(token) => {
                if token != ":" {
                    return Err(format!("line {} : Expected ':' found '{}'", self.ctx.row, token));
                }
            }
        }

        //Ok, now the regex
        if let Some(regex) = self.ctx.read_line() {
            let lexical_rule = LexicalUnit {
                name: name,
                regex: regex,
            };

            if self.grammar.lexical_units.iter().find(|rule| rule.name == lexical_rule.name).is_some() {
                return Err(format!("line {} : the lexical unit {} has already been declared", self.ctx.row, lexical_rule.name));
            }

            //TODO : Implement a check by regex too
            //TODO : Check that the regex is syntacticly corret (https://crates.io/crates/regex-syntax)
            self.grammar.lexical_units.push(lexical_rule);
            Ok(())
        }
        else {
            Err(format!("line {} : The regex is expected after ':' but nothing was found", self.ctx.row))
        }
    }

    fn check_consistency(& self) -> Result<(), String> {
        for production_rule in &self.grammar.production_rules {
            for rule_component in &production_rule.rule {
                match rule_component {
                    &RuleComponent::ProductionRule(ref name) => {
                        if self.grammar.production_rules.iter().find(|r| *name == r.name).is_none() {
                            return Err(format!("The production rule '{}' does not exist", name));
                        }
                    },
                    &RuleComponent::LexicalUnit(ref name) => {
                        if self.grammar.lexical_units.iter().find(|lu| *name  == lu.name).is_none() {
                            return Err(format!("The lexical unit '{}' does not exist", name));
                        }
                    },
                }
            }
        }

        Ok(())
    }

    fn check_for_left_recursion(& self) -> Result<(), String> {
        self.grammar.production_rules.iter()
            .map(|p| self.begin_with_production_rule(&p.name, &p.name))
            .fold(Ok(()), |acc, x| acc.and(x))
    }

    fn check_regexs(&self) -> Result<(), String> {
        let regex_check_job = |regex: &LexicalUnit| match Expr::parse(&regex.regex) {
                Ok(_) => Ok(()),
                Err(err) => Err(format!("Error in regex {} : {}", regex.name, err)),
            };

        self.grammar.lexical_units.iter().map(regex_check_job).fold(Ok(()), |acc, x| acc.and(x))
    }

    fn begin_with_production_rule(& self, name: &String, production_to_look_in: &String) -> Result<(), String> {
        for p in self.grammar.get_production_rule(production_to_look_in) {
            let first_component = &p.rule.iter().next().unwrap();
            if let &RuleComponent::ProductionRule(ref p_name) = *first_component {
                if name == p_name {
                    return Err(format!("Left recursion for the production rule {}", name));
                }
                else {
                    return self.begin_with_production_rule(name, p_name);
                }
            }
        }

        Ok(())
    }
}

pub fn parse_grammar(src : String) -> Result<Grammar, String> {
    let mut parser = GrammarParser::new(src);

    if let Err(msg) = parser.parse() {
        return Err(msg);
    }

    if let Err(msg) = parser.check_consistency() {
        return Err(msg);
    }

    if let Err(msg) = parser.check_for_left_recursion() {
        return Err(msg);
    }

    if let Err(msg) = parser.check_regexs() {
        return Err(msg);
    }

    Ok(parser.get_resulting_grammar())
}

#[test]
fn test_next_word() {
    let mut ctx = ParserContext::new("test and run".to_owned());

    assert_eq!(ctx.next_word().unwrap(), "test");
    assert_eq!(ctx.next_word().unwrap(), "and");
    assert_eq!(ctx.next_word().unwrap(), "run");
}

#[test]
fn test_read_line() {
    let mut ctx = ParserContext::new("line 1\nline2\nline3\nline4".to_owned());
    ctx.next_word();

    assert_eq!(ctx.read_line().unwrap(), "1");
    assert_eq!(ctx.read_line().unwrap(), "line2");
    assert_eq!(ctx.read_line().unwrap(), "line3");
    assert_eq!(ctx.read_line().unwrap(), "line4");
}
<commit_msg>Remove a slicing unsafe block<commit_after>/*
Copyright (c) 2016 Antoine Wacheux

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

extern crate liquid;
extern crate regex_syntax;

use liquidobject::LiquidObject;
use self::liquid::Value;
use std::collections::HashMap;
use self::regex_syntax::Expr;

pub struct LexicalUnit {
    name : String,
    regex : String,
}

impl LiquidObject for LexicalUnit {
    fn to_liquid_object(&self) -> Value {
        let mut result = HashMap::new();
        result.insert("name".to_owned(), liquid::Value::Str(self.name.clone()));
        result.insert("regex".to_owned(), liquid::Value::Str(self.regex.clone()));
        Value::Object(result)
    }
}

pub struct ProductionRule {
    name: String,
    rule: Vec<RuleComponent>
}

pub enum RuleComponent {
    ProductionRule(String),
    LexicalUnit(String)
}

pub struct Grammar {
    lexical_units : Vec<LexicalUnit>,
    production_rules: Vec<ProductionRule>,
    axiom : String
}

impl Grammar {
    fn new() -> Grammar {
        Grammar {
            lexical_units: Vec::new(),
            production_rules: Vec::new(),
            axiom: String::new(),
        }
    }

    fn get_production_rule(&self, name: &String) -> Vec<&ProductionRule> {
        self.production_rules.iter().filter(|x| x.name == *name).collect()
    }

    pub fn get_all_production_rules_name(&self) -> Vec<String> {
        let uniq_filter = {
            let mut found : Vec<String> = Vec::new();

            move |x:&String| {
                if found.iter().find(|elem| *elem == x).is_some() {
                    false
                }
                else {
                    found.push(x.clone());
                    true
                }
            }
        };

        self.production_rules.iter().map(|x| x.name.clone()).filter(uniq_filter).collect()
    }

    pub fn get_all_lexical_units(&self) -> &Vec<LexicalUnit> {
        &self.lexical_units
    }
}

//Methods
//get_rule(name: String&) -> Vec<ProductionRule>&

struct ParserContext {
    src: String,
    row: u32,
    col: u32,
    str_pos: usize,
}

impl ParserContext {
    fn new(src : String) -> ParserContext {
        ParserContext {
            src: src,
            row: 1,
            col: 0,
            str_pos: 0
        }
    }

    fn next_word(&mut self) -> Option<String> {
        let mut iter = self.src.chars();
        let mut ch = iter.nth(self.str_pos);
        let mut c : char;
        let mut read_word = String::new();

        match ch {
            None => { return None },
            Some(chr) => { c = chr}
        }

        macro_rules! next_char {
            ($err:stmt) => ({
                self.str_pos += 1;
                ch = iter.next();

                match ch {
                    None => { $err },
                    Some(chr) => { c = chr }
                }

                match c { //TODO : Implement a better line/column tracking and forward this information in the word
                    ' ' | '\t' => self.col += 1,
                    '\n' => self.row += 1,
                    _ => {}
                }
            });
        }

        while (c == ' ') || (c == '\n') || (c == '\t') { //We skip leading whitespaces
            next_char! { return None }
        }

        while !((c == ' ') || (c == '\n') || (c == '\t')) {
            read_word.push(c);
            next_char! { return Some(read_word) }
        }

        Some(read_word)
    }

    pub fn read_line(&mut self) -> Option<String> {
        let mut iter = self.src.chars();
        let mut ch = iter.nth(self.str_pos);
        let mut c : char;
        let mut read_word = String::new();

        match ch {
            None => { return None },
            Some(chr) => { c = chr}
        }

        macro_rules! next_char {
            ($err:stmt) => ({
                self.str_pos += 1;
                ch = iter.next();

                match ch {
                    None => { $err },
                    Some(chr) => { c = chr }
                }

                match c {
                    ' ' | '\t' => self.col += 1,
                    '\n' => self.row += 1,
                    _ => {}
                }
            });
        }

        while (c == ' ') || (c == '\n') || (c == '\t') { //We skip leading whitespaces
            next_char! { return None }
        }

        while c != '\n' {
            read_word.push(c);
            next_char! { return Some(read_word) }
        }

        Some(read_word)
    }
}

struct GrammarParser {
    ctx: ParserContext,
    grammar: Grammar,
    auto_counter: u32, //Counter for generating automatic lexical unit name
}

impl GrammarParser {
    fn new(src: String) -> GrammarParser {
        GrammarParser {
            ctx : ParserContext::new(src),
            grammar: Grammar::new(),
            auto_counter: 1,
        }
    }

    fn get_resulting_grammar(self) -> Grammar {
        self.grammar
    }

    fn parse(&mut self) -> Result<(), String> {
        while let Some(str_word) = self.ctx.next_word() {
            match str_word.chars().next().unwrap() {
                'A'...'Z' => {
                    match self.parse_grammar_rule(str_word) {
                        Ok(()) => {},
                        Err(msg) => {return Err(msg)},
                    }
                },
                'a'...'z' => {
                    match self.parse_lexical_unit(str_word) {
                        Ok(()) => {},
                        Err(msg) => {return Err(msg)},
                    }
                }
                _ => { return Err(String::from("Unknown token : ") + str_word.as_str())}
            }
        }

        Ok(())
    }

    fn parse_grammar_rule(&mut self, name : String) -> Result<(), String> {
        match self.ctx.next_word() {
            None => {return Err(format!("line {} : Expected '->'", self.ctx.row))},
            Some(token) => {
                if token != "->" {
                    return Err(format!("line {} : Expected '->' found '{}'", self.ctx.row, token));
                }
            }
        }

        if let Some(rule_str) = self.ctx.read_line() {
            let mut production_rule = ProductionRule{
                name: name,
                rule: Vec::new()
            };

            for comp_str in rule_str.split_whitespace() {
                let component_str = String::from(comp_str);
                let component = match component_str.chars().next().unwrap() {
                    'a'...'z' => {RuleComponent::LexicalUnit(component_str.to_owned())},
                    'A'...'Z' => {RuleComponent::ProductionRule(component_str.to_owned())},
                    '\'' => {
                        let s = component_str.len();
                        if s < 3 {
                            return Err(format!("line {} : The regex in a production rule cannot be empty.", self.ctx.row));
                        }

                        let regex : String = component_str.chars()
                                                    .enumerate()
                                                    .filter(|&(i, _)| 0 < i && i < s-1)
                                                    .map(|(_, c)| c)
                                                    .collect();

                        let name = format!("auto_{}", self.auto_counter);
                        self.auto_counter += 1;

                        if self.grammar.lexical_units.iter().find(|rule| rule.name == name).is_none() {
                            self.grammar.lexical_units.push(LexicalUnit{name:name.clone(), regex:String::from(regex)});
                        }

                        RuleComponent::LexicalUnit(name)
                    },
                    _ => {return Err(format!("line {}: '{}' is unexpected", self.ctx.row, component_str))}
                };

                production_rule.rule.push(component);
            }
            self.grammar.production_rules.push(production_rule);
            Ok(())
        }
        else {
            Err(format!("A production rule cannot be empty"))
        }
    }

    fn parse_lexical_unit(&mut self, name : String) -> Result<(), String> {
        //Let's read the ':'
        match self.ctx.next_word() {
            None => {return Err(format!("line {} : Expected ':'", self.ctx.row))},
            Some(token) => {
                if token != ":" {
                    return Err(format!("line {} : Expected ':' found '{}'", self.ctx.row, token));
                }
            }
        }

        //Ok, now the regex
        if let Some(regex) = self.ctx.read_line() {
            let lexical_rule = LexicalUnit {
                name: name,
                regex: regex,
            };

            if self.grammar.lexical_units.iter().find(|rule| rule.name == lexical_rule.name).is_some() {
                return Err(format!("line {} : the lexical unit {} has already been declared", self.ctx.row, lexical_rule.name));
            }

            //TODO : Implement a check by regex too
            //TODO : Check that the regex is syntacticly corret (https://crates.io/crates/regex-syntax)
            self.grammar.lexical_units.push(lexical_rule);
            Ok(())
        }
        else {
            Err(format!("line {} : The regex is expected after ':' but nothing was found", self.ctx.row))
        }
    }

    fn check_consistency(& self) -> Result<(), String> {
        for production_rule in &self.grammar.production_rules {
            for rule_component in &production_rule.rule {
                match rule_component {
                    &RuleComponent::ProductionRule(ref name) => {
                        if self.grammar.production_rules.iter().find(|r| *name == r.name).is_none() {
                            return Err(format!("The production rule '{}' does not exist", name));
                        }
                    },
                    &RuleComponent::LexicalUnit(ref name) => {
                        if self.grammar.lexical_units.iter().find(|lu| *name  == lu.name).is_none() {
                            return Err(format!("The lexical unit '{}' does not exist", name));
                        }
                    },
                }
            }
        }

        Ok(())
    }

    fn check_for_left_recursion(& self) -> Result<(), String> {
        self.grammar.production_rules.iter()
            .map(|p| self.begin_with_production_rule(&p.name, &p.name))
            .fold(Ok(()), |acc, x| acc.and(x))
    }

    fn check_regexs(&self) -> Result<(), String> {
        let regex_check_job = |regex: &LexicalUnit| match Expr::parse(&regex.regex) {
                Ok(_) => Ok(()),
                Err(err) => Err(format!("Error in regex {} : {}", regex.name, err)),
            };

        self.grammar.lexical_units.iter().map(regex_check_job).fold(Ok(()), |acc, x| acc.and(x))
    }

    fn begin_with_production_rule(& self, name: &String, production_to_look_in: &String) -> Result<(), String> {
        for p in self.grammar.get_production_rule(production_to_look_in) {
            let first_component = &p.rule.iter().next().unwrap();
            if let &RuleComponent::ProductionRule(ref p_name) = *first_component {
                if name == p_name {
                    return Err(format!("Left recursion for the production rule {}", name));
                }
                else {
                    return self.begin_with_production_rule(name, p_name);
                }
            }
        }

        Ok(())
    }
}

pub fn parse_grammar(src : String) -> Result<Grammar, String> {
    let mut parser = GrammarParser::new(src);

    if let Err(msg) = parser.parse() {
        return Err(msg);
    }

    if let Err(msg) = parser.check_consistency() {
        return Err(msg);
    }

    if let Err(msg) = parser.check_for_left_recursion() {
        return Err(msg);
    }

    if let Err(msg) = parser.check_regexs() {
        return Err(msg);
    }

    Ok(parser.get_resulting_grammar())
}

#[test]
fn test_next_word() {
    let mut ctx = ParserContext::new("test and run".to_owned());

    assert_eq!(ctx.next_word().unwrap(), "test");
    assert_eq!(ctx.next_word().unwrap(), "and");
    assert_eq!(ctx.next_word().unwrap(), "run");
}

#[test]
fn test_read_line() {
    let mut ctx = ParserContext::new("line 1\nline2\nline3\nline4".to_owned());
    ctx.next_word();

    assert_eq!(ctx.read_line().unwrap(), "1");
    assert_eq!(ctx.read_line().unwrap(), "line2");
    assert_eq!(ctx.read_line().unwrap(), "line3");
    assert_eq!(ctx.read_line().unwrap(), "line4");
}
<|endoftext|>


<commit_before>/**
 * At the moment, this is a partial hashmap implementation, not yet fit for
 * use, but useful as a stress test for rustboot.
 */

type hashfn[K] = fn(&K) -> uint;
type eqfn[K] = fn(&K, &K) -> bool;

state type hashmap[K, V] = state obj {
                                 fn size() -> uint;
                                 fn insert(&K key, &V val) -> bool;
                                 fn contains_key(&K key) -> bool;
                                 fn get(&K key) -> V;
                                 fn find(&K key) -> option.t[V];
                                 fn remove(&K key) -> option.t[V];
                                 fn rehash();
                                 iter items() -> @tup(K,V);
};

fn mk_hashmap[K, V](&hashfn[K] hasher, &eqfn[K] eqer) -> hashmap[K, V] {

    let uint initial_capacity = 32u; // 2^5
    let util.rational load_factor = rec(num=3, den=4);

    tag bucket[K, V] {
        nil;
        deleted;
        some(K, V);
    }

    fn make_buckets[K, V](uint nbkts) -> vec[mutable bucket[K, V]] {
        ret _vec.init_elt_mut[bucket[K, V]](nil[K, V], nbkts);
    }

    // Derive two hash functions from the one given by taking the upper
    // half and lower half of the uint bits.  Our bucket probing
    // sequence is then defined by
    //
    //   hash(key, i) := hashl(key) + i * hashr(key)   for i = 0, 1, 2, ...
    //
    // Tearing the hash function apart this way is kosher in practice
    // as, assuming 32-bit uints, the table would have to be at 2^32
    // buckets before the resulting pair of hash functions no longer
    // probes all buckets for a fixed key.  Note that hashr is made to
    // output odd numbers (hence coprime to the number of nbkts, which
    // is always a power of 2), so that all buckets are probed for a
    // fixed key.

    fn hashl[K](&hashfn[K] hasher, uint nbkts, &K key) -> uint {
        ret (hasher(key) >>> (sys.rustrt.size_of[uint]() * 8u / 2u));
    }

    fn hashr[K](&hashfn[K] hasher, uint nbkts, &K key) -> uint {
        ret ((((~ 0u) >>> (sys.rustrt.size_of[uint]() * 8u / 2u))
              & hasher(key)) * 2u + 1u);
    }

    fn hash[K](&hashfn[K] hasher, uint nbkts, &K key, uint i) -> uint {
        ret (hashl[K](hasher, nbkts, key)
             + i * hashr[K](hasher, nbkts, key)) % nbkts;
    }

    /**
     * We attempt to never call this with a full table.  If we do, it
     * will fail.
     */
    fn insert_common[K, V](&hashfn[K] hasher,
                                  &eqfn[K] eqer,
                                  vec[mutable bucket[K, V]] bkts,
                                  uint nbkts,
                                  &K key,
                                  &V val)
        -> bool
        {
            let uint i = 0u;
            while (i < nbkts) {
                let uint j = hash[K](hasher, nbkts, key, i);
                alt (bkts.(j)) {
                    case (some[K, V](?k, _)) {
                        if (eqer(key, k)) {
                            bkts.(j) = some[K, V](k, val);
                            ret false;
                        }
                        i += 1u;
                    }
                    case (_) {
                        bkts.(j) = some[K, V](key, val);
                        ret true;
                    }
                }
            }
            fail; // full table
        }

    fn find_common[K, V](&hashfn[K] hasher,
                         &eqfn[K] eqer,
                         vec[mutable bucket[K, V]] bkts,
                         uint nbkts,
                         &K key)
        -> option.t[V]
        {
            let uint i = 0u;
            while (i < nbkts) {
                let uint j = (hash[K](hasher, nbkts, key, i));
                alt (bkts.(j)) {
                    case (some[K, V](?k, ?v)) {
                        if (eqer(key, k)) {
                            ret option.some[V](v);
                        }
                    }
                    case (nil[K, V]) {
                        ret option.none[V];
                    }
                    case (deleted[K, V]) { }
                }
                i += 1u;
            }
            ret option.none[V];
        }

   fn rehash[K, V](&hashfn[K] hasher,
                          &eqfn[K] eqer,
                          vec[mutable bucket[K, V]] oldbkts, uint noldbkts,
                          vec[mutable bucket[K, V]] newbkts, uint nnewbkts)
        {
            for (bucket[K, V] b in oldbkts) {
                alt (b) {
                    case (some[K, V](?k, ?v)) {
                        insert_common[K, V](hasher, eqer, newbkts,
                                            nnewbkts, k, v);
                    }
                    case (_) { }
                }
            }
        }

    state obj hashmap[K, V](hashfn[K] hasher,
                            eqfn[K] eqer,
                            mutable vec[mutable bucket[K, V]] bkts,
                            mutable uint nbkts,
                            mutable uint nelts,
                            util.rational lf)
        {
            fn size() -> uint { ret nelts; }

            fn insert(&K key, &V val) -> bool {
                let util.rational load = rec(num=(nelts + 1u) as int,
                                             den=nbkts as int);
                if (!util.rational_leq(load, lf)) {
                    let uint nnewbkts = _uint.next_power_of_two(nbkts + 1u);
                    let vec[mutable bucket[K, V]] newbkts =
                        make_buckets[K, V](nnewbkts);
                    rehash[K, V](hasher, eqer, bkts, nbkts,
                                 newbkts, nnewbkts);
                    bkts = newbkts;
                    nbkts = nnewbkts;
                }

                if (insert_common[K, V](hasher, eqer, bkts,
                                        nbkts, key, val)) {
                    nelts += 1u;
                    ret true;
                }
                ret false;
            }

            fn contains_key(&K key) -> bool {
                alt (find_common[K, V](hasher, eqer, bkts, nbkts, key)) {
                    case (option.some[V](_)) { ret true; }
                    case (_) { ret false; }
                }
                fail;   // FIXME: remove me when exhaustiveness checking works
            }

            fn get(&K key) -> V {
                alt (find_common[K, V](hasher, eqer, bkts, nbkts, key)) {
                    case (option.some[V](?val)) { ret val; }
                    case (_) { fail; }
                }
                fail;   // FIXME: remove me when exhaustiveness checking works
            }

            fn find(&K key) -> option.t[V] {
                // FIXME: should be 'be' but parametric tail-calls don't
                // work at the moment.
                ret find_common[K, V](hasher, eqer, bkts, nbkts, key);
            }

            fn remove(&K key) -> option.t[V] {
                let uint i = 0u;
                while (i < nbkts) {
                    let uint j = (hash[K](hasher, nbkts, key, i));
                    alt (bkts.(j)) {
                        case (some[K, V](?k, ?v)) {
                            if (eqer(key, k)) {
                                bkts.(j) = deleted[K, V];
                                nelts -= 1u;
                                ret option.some[V](v);
                            }
                        }
                        case (deleted[K, V]) { }
                        case (nil[K, V]) {
                            ret option.none[V];
                        }
                    }
                    i += 1u;
                }
                ret option.none[V];
            }

            fn rehash() {
                let vec[mutable bucket[K, V]] newbkts =
                    make_buckets[K, V](nbkts);
                rehash[K, V](hasher, eqer, bkts, nbkts, newbkts, nbkts);
                bkts = newbkts;
            }

            iter items() -> @tup(K,V) {
                for (bucket[K,V] b in bkts) {
                    alt (b) {
                        case(some[K,V](?k,?v)) {
                            put @tup(k,v);
                        }
                        case (_) { }
                    }
                }
            }
        }

    let vec[mutable bucket[K, V]] bkts =
        make_buckets[K, V](initial_capacity);

    ret hashmap[K, V](hasher, eqer, bkts, initial_capacity, 0u, load_factor);
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Minimize calls to hash function in map.rs<commit_after>/**
 * At the moment, this is a partial hashmap implementation, not yet fit for
 * use, but useful as a stress test for rustboot.
 */

type hashfn[K] = fn(&K) -> uint;
type eqfn[K] = fn(&K, &K) -> bool;

state type hashmap[K, V] = state obj {
                                 fn size() -> uint;
                                 fn insert(&K key, &V val) -> bool;
                                 fn contains_key(&K key) -> bool;
                                 fn get(&K key) -> V;
                                 fn find(&K key) -> option.t[V];
                                 fn remove(&K key) -> option.t[V];
                                 fn rehash();
                                 iter items() -> @tup(K,V);
};

fn mk_hashmap[K, V](&hashfn[K] hasher, &eqfn[K] eqer) -> hashmap[K, V] {

    let uint initial_capacity = 32u; // 2^5
    let util.rational load_factor = rec(num=3, den=4);

    tag bucket[K, V] {
        nil;
        deleted;
        some(K, V);
    }

    fn make_buckets[K, V](uint nbkts) -> vec[mutable bucket[K, V]] {
        ret _vec.init_elt_mut[bucket[K, V]](nil[K, V], nbkts);
    }

    // Derive two hash functions from the one given by taking the upper
    // half and lower half of the uint bits.  Our bucket probing
    // sequence is then defined by
    //
    //   hash(key, i) := hashl(key) + i * hashr(key)   for i = 0, 1, 2, ...
    //
    // Tearing the hash function apart this way is kosher in practice
    // as, assuming 32-bit uints, the table would have to be at 2^32
    // buckets before the resulting pair of hash functions no longer
    // probes all buckets for a fixed key.  Note that hashr is made to
    // output odd numbers (hence coprime to the number of nbkts, which
    // is always a power of 2), so that all buckets are probed for a
    // fixed key.

    fn hashl(uint n, uint nbkts) -> uint {
        ret (n >>> 16u);
    }

    fn hashr(uint n, uint nbkts) -> uint {
        ret ((((~ 0u) >>> 16u) & n) * 2u + 1u);
    }

    fn hash(uint h, uint nbkts, uint i) -> uint {
        ret (hashl(h, nbkts) + i * hashr(h, nbkts)) % nbkts;
    }

    /**
     * We attempt to never call this with a full table.  If we do, it
     * will fail.
     */
    fn insert_common[K, V](&hashfn[K] hasher,
                                  &eqfn[K] eqer,
                                  vec[mutable bucket[K, V]] bkts,
                                  uint nbkts,
                                  &K key,
                                  &V val)
        -> bool
        {
            let uint i = 0u;
            let uint h = hasher(key);
            while (i < nbkts) {
                let uint j = hash(h, nbkts, i);
                alt (bkts.(j)) {
                    case (some[K, V](?k, _)) {
                        if (eqer(key, k)) {
                            bkts.(j) = some[K, V](k, val);
                            ret false;
                        }
                        i += 1u;
                    }
                    case (_) {
                        bkts.(j) = some[K, V](key, val);
                        ret true;
                    }
                }
            }
            fail; // full table
        }

    fn find_common[K, V](&hashfn[K] hasher,
                         &eqfn[K] eqer,
                         vec[mutable bucket[K, V]] bkts,
                         uint nbkts,
                         &K key)
        -> option.t[V]
        {
            let uint i = 0u;
            let uint h = hasher(key);
            while (i < nbkts) {
                let uint j = (hash(h, nbkts, i));
                alt (bkts.(j)) {
                    case (some[K, V](?k, ?v)) {
                        if (eqer(key, k)) {
                            ret option.some[V](v);
                        }
                    }
                    case (nil[K, V]) {
                        ret option.none[V];
                    }
                    case (deleted[K, V]) { }
                }
                i += 1u;
            }
            ret option.none[V];
        }

   fn rehash[K, V](&hashfn[K] hasher,
                          &eqfn[K] eqer,
                          vec[mutable bucket[K, V]] oldbkts, uint noldbkts,
                          vec[mutable bucket[K, V]] newbkts, uint nnewbkts)
        {
            for (bucket[K, V] b in oldbkts) {
                alt (b) {
                    case (some[K, V](?k, ?v)) {
                        insert_common[K, V](hasher, eqer, newbkts,
                                            nnewbkts, k, v);
                    }
                    case (_) { }
                }
            }
        }

    state obj hashmap[K, V](hashfn[K] hasher,
                            eqfn[K] eqer,
                            mutable vec[mutable bucket[K, V]] bkts,
                            mutable uint nbkts,
                            mutable uint nelts,
                            util.rational lf)
        {
            fn size() -> uint { ret nelts; }

            fn insert(&K key, &V val) -> bool {
                let util.rational load = rec(num=(nelts + 1u) as int,
                                             den=nbkts as int);
                if (!util.rational_leq(load, lf)) {
                    let uint nnewbkts = _uint.next_power_of_two(nbkts + 1u);
                    let vec[mutable bucket[K, V]] newbkts =
                        make_buckets[K, V](nnewbkts);
                    rehash[K, V](hasher, eqer, bkts, nbkts,
                                 newbkts, nnewbkts);
                    bkts = newbkts;
                    nbkts = nnewbkts;
                }

                if (insert_common[K, V](hasher, eqer, bkts,
                                        nbkts, key, val)) {
                    nelts += 1u;
                    ret true;
                }
                ret false;
            }

            fn contains_key(&K key) -> bool {
                alt (find_common[K, V](hasher, eqer, bkts, nbkts, key)) {
                    case (option.some[V](_)) { ret true; }
                    case (_) { ret false; }
                }
                fail;   // FIXME: remove me when exhaustiveness checking works
            }

            fn get(&K key) -> V {
                alt (find_common[K, V](hasher, eqer, bkts, nbkts, key)) {
                    case (option.some[V](?val)) { ret val; }
                    case (_) { fail; }
                }
                fail;   // FIXME: remove me when exhaustiveness checking works
            }

            fn find(&K key) -> option.t[V] {
                // FIXME: should be 'be' but parametric tail-calls don't
                // work at the moment.
                ret find_common[K, V](hasher, eqer, bkts, nbkts, key);
            }

            fn remove(&K key) -> option.t[V] {
                let uint i = 0u;
                let uint h = hasher(key);
                while (i < nbkts) {
                    let uint j = (hash(h, nbkts, i));
                    alt (bkts.(j)) {
                        case (some[K, V](?k, ?v)) {
                            if (eqer(key, k)) {
                                bkts.(j) = deleted[K, V];
                                nelts -= 1u;
                                ret option.some[V](v);
                            }
                        }
                        case (deleted[K, V]) { }
                        case (nil[K, V]) {
                            ret option.none[V];
                        }
                    }
                    i += 1u;
                }
                ret option.none[V];
            }

            fn rehash() {
                let vec[mutable bucket[K, V]] newbkts =
                    make_buckets[K, V](nbkts);
                rehash[K, V](hasher, eqer, bkts, nbkts, newbkts, nbkts);
                bkts = newbkts;
            }

            iter items() -> @tup(K,V) {
                for (bucket[K,V] b in bkts) {
                    alt (b) {
                        case(some[K,V](?k,?v)) {
                            put @tup(k,v);
                        }
                        case (_) { }
                    }
                }
            }
        }

    let vec[mutable bucket[K, V]] bkts =
        make_buckets[K, V](initial_capacity);

    ret hashmap[K, V](hasher, eqer, bkts, initial_capacity, 0u, load_factor);
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>
use std;
use std::num::FromPrimitive;
use ffi::*;
use {
    Face,
    FtResult,
};

pub struct Library {
    raw: FT_Library,
}

impl Library {
    pub fn init() -> FtResult<Library> {
        unsafe {
            let mut library = std::ptr::mut_null();
            let err = FT_Init_FreeType(&mut library);
            if err == 0 {
                Ok(Library {
                    raw: library,
                })
            } else {
                Err(FromPrimitive::from_i32(err).unwrap())
            }
        }
    }

    pub fn new_face(&self, filepathname: &str, face_index: FT_Long) -> FtResult<Face> {
        unsafe {
            let mut face = std::ptr::mut_null();
            let err = FT_New_Face(self.raw, filepathname.as_slice().as_ptr(), face_index, &mut face);
            if err == FT_Err_Ok {
                Ok(Face::from_raw(face))
            } else {
                Err(FromPrimitive::from_i32(err).unwrap())
            }
        }
    }

    pub fn new_memory_face(&self, buffer: &[u8], face_index: FT_Long) -> FtResult<Face> {
        unsafe {
            let mut face = std::ptr::mut_null();
            let err = FT_New_Memory_Face(self.raw, buffer.as_ptr(), buffer.len() as i64, face_index, &mut face);
            if err == FT_Err_Ok {
                Ok(Face::from_raw(face))
            } else {
                Err(FromPrimitive::from_i32(err).unwrap())
            }
        }
    }

    pub fn raw(&self) -> FT_Library {
        self.raw
    }
}

impl Drop for Library {
    fn drop(&mut self) {
        /*
        unsafe {
            let err = FT_Done_FreeType(self.raw);
            if err != 0 {
                std::io::println(format!("Failed to drop Library. Error Code: {}", err).as_slice());
            }
        }
        */
    }
}

<commit_msg>Change `as i64` to `as FT_Long`<commit_after>
use std;
use std::num::FromPrimitive;
use ffi::*;
use {
    Face,
    FtResult,
};

pub struct Library {
    raw: FT_Library,
}

impl Library {
    pub fn init() -> FtResult<Library> {
        unsafe {
            let mut library = std::ptr::mut_null();
            let err = FT_Init_FreeType(&mut library);
            if err == 0 {
                Ok(Library {
                    raw: library,
                })
            } else {
                Err(FromPrimitive::from_i32(err).unwrap())
            }
        }
    }

    pub fn new_face(&self, filepathname: &str, face_index: FT_Long) -> FtResult<Face> {
        unsafe {
            let mut face = std::ptr::mut_null();
            let err = FT_New_Face(self.raw, filepathname.as_slice().as_ptr(), face_index, &mut face);
            if err == FT_Err_Ok {
                Ok(Face::from_raw(face))
            } else {
                Err(FromPrimitive::from_i32(err).unwrap())
            }
        }
    }

    pub fn new_memory_face(&self, buffer: &[u8], face_index: FT_Long) -> FtResult<Face> {
        unsafe {
            let mut face = std::ptr::mut_null();
            let err = FT_New_Memory_Face(self.raw, buffer.as_ptr(), buffer.len() as FT_Long, face_index, &mut face);
            if err == FT_Err_Ok {
                Ok(Face::from_raw(face))
            } else {
                Err(FromPrimitive::from_i32(err).unwrap())
            }
        }
    }

    pub fn raw(&self) -> FT_Library {
        self.raw
    }
}

impl Drop for Library {
    fn drop(&mut self) {
        /*
        unsafe {
            let err = FT_Done_FreeType(self.raw);
            if err != 0 {
                std::io::println(format!("Failed to drop Library. Error Code: {}", err).as_slice());
            }
        }
        */
    }
}

<|endoftext|>


<commit_before>use std::str;
use std::io::{IoResult, MemWriter, MemReader};
use std::mem;

use types::Oid;

pub static PROTOCOL_VERSION: i32 = 0x0003_0000;
pub static CANCEL_CODE: i32 = 80877102;
pub static SSL_CODE: i32 = 80877103;

pub enum BackendMessage {
    AuthenticationCleartextPassword,
    AuthenticationGSS,
    AuthenticationKerberosV5,
    AuthenticationMD5Password {
        pub salt: [u8, ..4]
    },
    AuthenticationOk,
    AuthenticationSCMCredential,
    AuthenticationSSPI,
    BackendKeyData {
        pub process_id: i32,
        pub secret_key: i32
    },
    BindComplete,
    CloseComplete,
    CommandComplete {
        pub tag: ~str
    },
    DataRow {
        pub row: Vec<Option<Vec<u8>>>
    },
    EmptyQueryResponse,
    ErrorResponse {
        pub fields: Vec<(u8, ~str)>
    },
    NoData,
    NoticeResponse {
        pub fields: Vec<(u8, ~str)>
    },
    NotificationResponse {
        pub pid: i32,
        pub channel: ~str,
        pub payload: ~str
    },
    ParameterDescription {
        pub types: Vec<Oid>
    },
    ParameterStatus {
        pub parameter: ~str,
        pub value: ~str
    },
    ParseComplete,
    PortalSuspended,
    ReadyForQuery {
        pub state: u8
    },
    RowDescription {
        pub descriptions: Vec<RowDescriptionEntry>
    }
}

pub struct RowDescriptionEntry {
    pub name: ~str,
    pub table_oid: Oid,
    pub column_id: i16,
    pub type_oid: Oid,
    pub type_size: i16,
    pub type_modifier: i32,
    pub format: i16
}

pub enum FrontendMessage<'a> {
    Bind {
        pub portal: &'a str,
        pub statement: &'a str,
        pub formats: &'a [i16],
        pub values: &'a [Option<Vec<u8>>],
        pub result_formats: &'a [i16]
    },
    CancelRequest {
        pub code: i32,
        pub process_id: i32,
        pub secret_key: i32,
    },
    Close {
        pub variant: u8,
        pub name: &'a str
    },
    Describe {
        pub variant: u8,
        pub name: &'a str
    },
    Execute {
        pub portal: &'a str,
        pub max_rows: i32
    },
    Parse {
        pub name: &'a str,
        pub query: &'a str,
        pub param_types: &'a [i32]
    },
    PasswordMessage {
        pub password: &'a str
    },
    Query {
        pub query: &'a str
    },
    SslRequest {
        pub code: i32
    },
    StartupMessage {
        pub version: i32,
        pub parameters: &'a [(~str, ~str)]
    },
    Sync,
    Terminate
}

#[doc(hidden)]
trait WriteCStr {
    fn write_cstr(&mut self, s: &str) -> IoResult<()>;
}

impl<W: Writer> WriteCStr for W {
    fn write_cstr(&mut self, s: &str) -> IoResult<()> {
        try!(self.write(s.as_bytes()));
        self.write_u8(0)
    }
}

#[doc(hidden)]
pub trait WriteMessage {
    fn write_message(&mut self, &FrontendMessage) -> IoResult<()> ;
}

impl<W: Writer> WriteMessage for W {
    fn write_message(&mut self, message: &FrontendMessage) -> IoResult<()> {
        debug!("Writing message {:?}", message);
        let mut buf = MemWriter::new();
        let mut ident = None;

        match *message {
            Bind { portal, statement, formats, values, result_formats } => {
                ident = Some('B');
                try!(buf.write_cstr(portal));
                try!(buf.write_cstr(statement));

                try!(buf.write_be_i16(formats.len() as i16));
                for format in formats.iter() {
                    try!(buf.write_be_i16(*format));
                }

                try!(buf.write_be_i16(values.len() as i16));
                for value in values.iter() {
                    match *value {
                        None => {
                            try!(buf.write_be_i32(-1));
                        }
                        Some(ref value) => {
                            try!(buf.write_be_i32(value.len() as i32));
                            try!(buf.write(value.as_slice()));
                        }
                    }
                }

                try!(buf.write_be_i16(result_formats.len() as i16));
                for format in result_formats.iter() {
                    try!(buf.write_be_i16(*format));
                }
            }
            CancelRequest { code, process_id, secret_key } => {
                try!(buf.write_be_i32(code));
                try!(buf.write_be_i32(process_id));
                try!(buf.write_be_i32(secret_key));
            }
            Close { variant, name } => {
                ident = Some('C');
                try!(buf.write_u8(variant));
                try!(buf.write_cstr(name));
            }
            Describe { variant, name } => {
                ident = Some('D');
                try!(buf.write_u8(variant));
                try!(buf.write_cstr(name));
            }
            Execute { portal, max_rows } => {
                ident = Some('E');
                try!(buf.write_cstr(portal));
                try!(buf.write_be_i32(max_rows));
            }
            Parse { name, query, param_types } => {
                ident = Some('P');
                try!(buf.write_cstr(name));
                try!(buf.write_cstr(query));
                try!(buf.write_be_i16(param_types.len() as i16));
                for ty in param_types.iter() {
                    try!(buf.write_be_i32(*ty));
                }
            }
            PasswordMessage { password } => {
                ident = Some('p');
                try!(buf.write_cstr(password));
            }
            Query { query } => {
                ident = Some('Q');
                try!(buf.write_cstr(query));
            }
            StartupMessage { version, parameters } => {
                try!(buf.write_be_i32(version));
                for &(ref k, ref v) in parameters.iter() {
                    try!(buf.write_cstr(k.as_slice()));
                    try!(buf.write_cstr(v.as_slice()));
                }
                try!(buf.write_u8(0));
            }
            SslRequest { code } => try!(buf.write_be_i32(code)),
            Sync => {
                ident = Some('S');
            }
            Terminate => {
                ident = Some('X');
            }
        }

        match ident {
            Some(ident) => try!(self.write_u8(ident as u8)),
            None => ()
        }

        let buf = buf.unwrap();
        // add size of length value
        try!(self.write_be_i32((buf.len() + mem::size_of::<i32>()) as i32));
        try!(self.write(buf.as_slice()));

        Ok(())
    }
}

#[doc(hidden)]
trait ReadCStr {
    fn read_cstr(&mut self) -> IoResult<~str>;
}

impl<R: Buffer> ReadCStr for R {
    fn read_cstr(&mut self) -> IoResult<~str> {
        let mut buf = try!(self.read_until(0));
        buf.pop();
        // FIXME
        Ok(str::from_utf8(buf.as_slice()).unwrap().to_owned())
    }
}

#[doc(hidden)]
pub trait ReadMessage {
    fn read_message(&mut self) -> IoResult<BackendMessage>;
}

impl<R: Reader> ReadMessage for R {
    fn read_message(&mut self) -> IoResult<BackendMessage> {
        debug!("Reading message");

        let ident = try!(self.read_u8());
        // subtract size of length value
        let len = try!(self.read_be_i32()) as uint - mem::size_of::<i32>();
        let mut buf = MemReader::new(try!(self.read_exact(len)));

        let ret = match ident as char {
            '1' => ParseComplete,
            '2' => BindComplete,
            '3' => CloseComplete,
            'A' => NotificationResponse {
                pid: try!(buf.read_be_i32()),
                channel: try!(buf.read_cstr()),
                payload: try!(buf.read_cstr())
            },
            'C' => CommandComplete { tag: try!(buf.read_cstr()) },
            'D' => try!(read_data_row(&mut buf)),
            'E' => ErrorResponse { fields: try!(read_fields(&mut buf)) },
            'I' => EmptyQueryResponse,
            'K' => BackendKeyData {
                process_id: try!(buf.read_be_i32()),
                secret_key: try!(buf.read_be_i32())
            },
            'n' => NoData,
            'N' => NoticeResponse { fields: try!(read_fields(&mut buf)) },
            'R' => try!(read_auth_message(&mut buf)),
            's' => PortalSuspended,
            'S' => ParameterStatus {
                parameter: try!(buf.read_cstr()),
                value: try!(buf.read_cstr())
            },
            't' => try!(read_parameter_description(&mut buf)),
            'T' => try!(read_row_description(&mut buf)),
            'Z' => ReadyForQuery { state: try!(buf.read_u8()) },
            ident => fail!("Unknown message identifier `{}`", ident)
        };
        debug!("Read message {:?}", ret);
        Ok(ret)
    }
}

fn read_fields(buf: &mut MemReader) -> IoResult<Vec<(u8, ~str)>> {
    let mut fields = Vec::new();
    loop {
        let ty = try!(buf.read_u8());
        if ty == 0 {
            break;
        }

        fields.push((ty, try!(buf.read_cstr())));
    }

    Ok(fields)
}

fn read_data_row(buf: &mut MemReader) -> IoResult<BackendMessage> {
    let len = try!(buf.read_be_i16()) as uint;
    let mut values = Vec::with_capacity(len);

    for _ in range(0, len) {
        let val = match try!(buf.read_be_i32()) {
            -1 => None,
            len => Some(try!(buf.read_exact(len as uint)))
        };
        values.push(val);
    }

    Ok(DataRow { row: values })
}

fn read_auth_message(buf: &mut MemReader) -> IoResult<BackendMessage> {
    Ok(match try!(buf.read_be_i32()) {
        0 => AuthenticationOk,
        2 => AuthenticationKerberosV5,
        3 => AuthenticationCleartextPassword,
        5 => {
            let mut salt = [0, 0, 0, 0];
            try!(buf.fill(salt));
            AuthenticationMD5Password { salt: salt }
        },
        6 => AuthenticationSCMCredential,
        7 => AuthenticationGSS,
        9 => AuthenticationSSPI,
        val => fail!("Invalid authentication identifier `{}`", val)
    })
}

fn read_parameter_description(buf: &mut MemReader) -> IoResult<BackendMessage> {
    let len = try!(buf.read_be_i16()) as uint;
    let mut types = Vec::with_capacity(len);

    for _ in range(0, len) {
        types.push(try!(buf.read_be_u32()));
    }

    Ok(ParameterDescription { types: types })
}

fn read_row_description(buf: &mut MemReader) -> IoResult<BackendMessage> {
    let len = try!(buf.read_be_i16()) as uint;
    let mut types = Vec::with_capacity(len);

    for _ in range(0, len) {
        types.push(RowDescriptionEntry {
            name: try!(buf.read_cstr()),
            table_oid: try!(buf.read_be_u32()),
            column_id: try!(buf.read_be_i16()),
            type_oid: try!(buf.read_be_u32()),
            type_size: try!(buf.read_be_i16()),
            type_modifier: try!(buf.read_be_i32()),
            format: try!(buf.read_be_i16())
        });
    }

    Ok(RowDescription { descriptions: types })
}
<commit_msg>Remove the last remaining {:?}<commit_after>use std::str;
use std::io::{IoResult, MemWriter, MemReader};
use std::mem;

use types::Oid;

pub static PROTOCOL_VERSION: i32 = 0x0003_0000;
pub static CANCEL_CODE: i32 = 80877102;
pub static SSL_CODE: i32 = 80877103;

pub enum BackendMessage {
    AuthenticationCleartextPassword,
    AuthenticationGSS,
    AuthenticationKerberosV5,
    AuthenticationMD5Password {
        pub salt: [u8, ..4]
    },
    AuthenticationOk,
    AuthenticationSCMCredential,
    AuthenticationSSPI,
    BackendKeyData {
        pub process_id: i32,
        pub secret_key: i32
    },
    BindComplete,
    CloseComplete,
    CommandComplete {
        pub tag: ~str
    },
    DataRow {
        pub row: Vec<Option<Vec<u8>>>
    },
    EmptyQueryResponse,
    ErrorResponse {
        pub fields: Vec<(u8, ~str)>
    },
    NoData,
    NoticeResponse {
        pub fields: Vec<(u8, ~str)>
    },
    NotificationResponse {
        pub pid: i32,
        pub channel: ~str,
        pub payload: ~str
    },
    ParameterDescription {
        pub types: Vec<Oid>
    },
    ParameterStatus {
        pub parameter: ~str,
        pub value: ~str
    },
    ParseComplete,
    PortalSuspended,
    ReadyForQuery {
        pub state: u8
    },
    RowDescription {
        pub descriptions: Vec<RowDescriptionEntry>
    }
}

pub struct RowDescriptionEntry {
    pub name: ~str,
    pub table_oid: Oid,
    pub column_id: i16,
    pub type_oid: Oid,
    pub type_size: i16,
    pub type_modifier: i32,
    pub format: i16
}

pub enum FrontendMessage<'a> {
    Bind {
        pub portal: &'a str,
        pub statement: &'a str,
        pub formats: &'a [i16],
        pub values: &'a [Option<Vec<u8>>],
        pub result_formats: &'a [i16]
    },
    CancelRequest {
        pub code: i32,
        pub process_id: i32,
        pub secret_key: i32,
    },
    Close {
        pub variant: u8,
        pub name: &'a str
    },
    Describe {
        pub variant: u8,
        pub name: &'a str
    },
    Execute {
        pub portal: &'a str,
        pub max_rows: i32
    },
    Parse {
        pub name: &'a str,
        pub query: &'a str,
        pub param_types: &'a [i32]
    },
    PasswordMessage {
        pub password: &'a str
    },
    Query {
        pub query: &'a str
    },
    SslRequest {
        pub code: i32
    },
    StartupMessage {
        pub version: i32,
        pub parameters: &'a [(~str, ~str)]
    },
    Sync,
    Terminate
}

#[doc(hidden)]
trait WriteCStr {
    fn write_cstr(&mut self, s: &str) -> IoResult<()>;
}

impl<W: Writer> WriteCStr for W {
    fn write_cstr(&mut self, s: &str) -> IoResult<()> {
        try!(self.write(s.as_bytes()));
        self.write_u8(0)
    }
}

#[doc(hidden)]
pub trait WriteMessage {
    fn write_message(&mut self, &FrontendMessage) -> IoResult<()> ;
}

impl<W: Writer> WriteMessage for W {
    fn write_message(&mut self, message: &FrontendMessage) -> IoResult<()> {
        let mut buf = MemWriter::new();
        let mut ident = None;

        match *message {
            Bind { portal, statement, formats, values, result_formats } => {
                ident = Some('B');
                try!(buf.write_cstr(portal));
                try!(buf.write_cstr(statement));

                try!(buf.write_be_i16(formats.len() as i16));
                for format in formats.iter() {
                    try!(buf.write_be_i16(*format));
                }

                try!(buf.write_be_i16(values.len() as i16));
                for value in values.iter() {
                    match *value {
                        None => {
                            try!(buf.write_be_i32(-1));
                        }
                        Some(ref value) => {
                            try!(buf.write_be_i32(value.len() as i32));
                            try!(buf.write(value.as_slice()));
                        }
                    }
                }

                try!(buf.write_be_i16(result_formats.len() as i16));
                for format in result_formats.iter() {
                    try!(buf.write_be_i16(*format));
                }
            }
            CancelRequest { code, process_id, secret_key } => {
                try!(buf.write_be_i32(code));
                try!(buf.write_be_i32(process_id));
                try!(buf.write_be_i32(secret_key));
            }
            Close { variant, name } => {
                ident = Some('C');
                try!(buf.write_u8(variant));
                try!(buf.write_cstr(name));
            }
            Describe { variant, name } => {
                ident = Some('D');
                try!(buf.write_u8(variant));
                try!(buf.write_cstr(name));
            }
            Execute { portal, max_rows } => {
                ident = Some('E');
                try!(buf.write_cstr(portal));
                try!(buf.write_be_i32(max_rows));
            }
            Parse { name, query, param_types } => {
                ident = Some('P');
                try!(buf.write_cstr(name));
                try!(buf.write_cstr(query));
                try!(buf.write_be_i16(param_types.len() as i16));
                for ty in param_types.iter() {
                    try!(buf.write_be_i32(*ty));
                }
            }
            PasswordMessage { password } => {
                ident = Some('p');
                try!(buf.write_cstr(password));
            }
            Query { query } => {
                ident = Some('Q');
                try!(buf.write_cstr(query));
            }
            StartupMessage { version, parameters } => {
                try!(buf.write_be_i32(version));
                for &(ref k, ref v) in parameters.iter() {
                    try!(buf.write_cstr(k.as_slice()));
                    try!(buf.write_cstr(v.as_slice()));
                }
                try!(buf.write_u8(0));
            }
            SslRequest { code } => try!(buf.write_be_i32(code)),
            Sync => {
                ident = Some('S');
            }
            Terminate => {
                ident = Some('X');
            }
        }

        match ident {
            Some(ident) => try!(self.write_u8(ident as u8)),
            None => ()
        }

        let buf = buf.unwrap();
        // add size of length value
        try!(self.write_be_i32((buf.len() + mem::size_of::<i32>()) as i32));
        try!(self.write(buf.as_slice()));

        Ok(())
    }
}

#[doc(hidden)]
trait ReadCStr {
    fn read_cstr(&mut self) -> IoResult<~str>;
}

impl<R: Buffer> ReadCStr for R {
    fn read_cstr(&mut self) -> IoResult<~str> {
        let mut buf = try!(self.read_until(0));
        buf.pop();
        // FIXME
        Ok(str::from_utf8(buf.as_slice()).unwrap().to_owned())
    }
}

#[doc(hidden)]
pub trait ReadMessage {
    fn read_message(&mut self) -> IoResult<BackendMessage>;
}

impl<R: Reader> ReadMessage for R {
    fn read_message(&mut self) -> IoResult<BackendMessage> {
        let ident = try!(self.read_u8());
        // subtract size of length value
        let len = try!(self.read_be_i32()) as uint - mem::size_of::<i32>();
        let mut buf = MemReader::new(try!(self.read_exact(len)));

        let ret = match ident as char {
            '1' => ParseComplete,
            '2' => BindComplete,
            '3' => CloseComplete,
            'A' => NotificationResponse {
                pid: try!(buf.read_be_i32()),
                channel: try!(buf.read_cstr()),
                payload: try!(buf.read_cstr())
            },
            'C' => CommandComplete { tag: try!(buf.read_cstr()) },
            'D' => try!(read_data_row(&mut buf)),
            'E' => ErrorResponse { fields: try!(read_fields(&mut buf)) },
            'I' => EmptyQueryResponse,
            'K' => BackendKeyData {
                process_id: try!(buf.read_be_i32()),
                secret_key: try!(buf.read_be_i32())
            },
            'n' => NoData,
            'N' => NoticeResponse { fields: try!(read_fields(&mut buf)) },
            'R' => try!(read_auth_message(&mut buf)),
            's' => PortalSuspended,
            'S' => ParameterStatus {
                parameter: try!(buf.read_cstr()),
                value: try!(buf.read_cstr())
            },
            't' => try!(read_parameter_description(&mut buf)),
            'T' => try!(read_row_description(&mut buf)),
            'Z' => ReadyForQuery { state: try!(buf.read_u8()) },
            ident => fail!("Unknown message identifier `{}`", ident)
        };
        Ok(ret)
    }
}

fn read_fields(buf: &mut MemReader) -> IoResult<Vec<(u8, ~str)>> {
    let mut fields = Vec::new();
    loop {
        let ty = try!(buf.read_u8());
        if ty == 0 {
            break;
        }

        fields.push((ty, try!(buf.read_cstr())));
    }

    Ok(fields)
}

fn read_data_row(buf: &mut MemReader) -> IoResult<BackendMessage> {
    let len = try!(buf.read_be_i16()) as uint;
    let mut values = Vec::with_capacity(len);

    for _ in range(0, len) {
        let val = match try!(buf.read_be_i32()) {
            -1 => None,
            len => Some(try!(buf.read_exact(len as uint)))
        };
        values.push(val);
    }

    Ok(DataRow { row: values })
}

fn read_auth_message(buf: &mut MemReader) -> IoResult<BackendMessage> {
    Ok(match try!(buf.read_be_i32()) {
        0 => AuthenticationOk,
        2 => AuthenticationKerberosV5,
        3 => AuthenticationCleartextPassword,
        5 => {
            let mut salt = [0, 0, 0, 0];
            try!(buf.fill(salt));
            AuthenticationMD5Password { salt: salt }
        },
        6 => AuthenticationSCMCredential,
        7 => AuthenticationGSS,
        9 => AuthenticationSSPI,
        val => fail!("Invalid authentication identifier `{}`", val)
    })
}

fn read_parameter_description(buf: &mut MemReader) -> IoResult<BackendMessage> {
    let len = try!(buf.read_be_i16()) as uint;
    let mut types = Vec::with_capacity(len);

    for _ in range(0, len) {
        types.push(try!(buf.read_be_u32()));
    }

    Ok(ParameterDescription { types: types })
}

fn read_row_description(buf: &mut MemReader) -> IoResult<BackendMessage> {
    let len = try!(buf.read_be_i16()) as uint;
    let mut types = Vec::with_capacity(len);

    for _ in range(0, len) {
        types.push(RowDescriptionEntry {
            name: try!(buf.read_cstr()),
            table_oid: try!(buf.read_be_u32()),
            column_id: try!(buf.read_be_i16()),
            type_oid: try!(buf.read_be_u32()),
            type_size: try!(buf.read_be_i16()),
            type_modifier: try!(buf.read_be_i32()),
            format: try!(buf.read_be_i16())
        });
    }

    Ok(RowDescription { descriptions: types })
}
<|endoftext|>


<commit_before>//! Message handling.
//! This module defines the functions used to handle messages the bot reads that are not commands -
//! ie it determines if a message matches any greps, and figures out who to tag.

use {Grep, TIMEOUT};

use std::collections::{HashSet, HashMap};
use std::time::{Duration, Instant};

use discord::model::{ChannelId, Message, UserId};

pub fn handle(message: &Message,
              greps: &HashSet<Grep>,
              timeouts: &mut HashMap<(UserId, ChannelId), Instant>)
              -> Option<String> {
    let users: HashSet<_> = greps
        .iter()
        .filter(|&&Grep(ref regex, _)| regex.is_match(&message.content))
        .map(|&Grep(_, id)| id)
        .filter(|&id| id != message.author.id)
        .filter(|&id| match timeouts.get(&(id, message.channel_id)) {
                    Some(instant) => instant.elapsed() > Duration::from_secs(TIMEOUT),
                    None => true,
                })
        .collect();

    if !users.is_empty() {
        Some(users
                 .into_iter()
                 .inspect(|&id| { timeouts.insert((id, message.channel_id), Instant::now()); })
                 .fold("Hey!".into(),
                       |string, id| format!("{} {}", string, id.mention())))
    } else {
        None
    }
}
<commit_msg>documented some things<commit_after>//! Message handling.
//! This module defines the functions used to handle messages the bot reads that are not commands -
//! ie it determines if a message matches any greps, and figures out who to tag.

use {Grep, TIMEOUT};

use std::collections::{HashSet, HashMap};
use std::time::{Duration, Instant};

use discord::model::{ChannelId, Message, UserId};

/// Message parser. Recieves a message and determines if it matches any greps that were not tagged
/// within the time frame specified. If it does, generate a reply to the message that tags all
/// users whos greps matched the message.
pub fn handle(message: &Message,
                      greps: &HashSet<Grep>,
                      timeouts: &mut HashMap<(UserId, ChannelId), Instant>)
                      -> Option<String> {
    let users: HashSet<_> = greps
        .iter()
        .filter(|&&Grep(ref regex, _)| regex.is_match(&message.content))
        .map(|&Grep(_, id)| id)
        .filter(|&id| id != message.author.id)
        .filter(|&id| match timeouts.get(&(id, message.channel_id)) {
                    Some(instant) => instant.elapsed() > Duration::from_secs(TIMEOUT),
                    None => true,
                })
        .collect();

    if !users.is_empty() {
        Some(users
                 .into_iter()
                 .inspect(|&id| { timeouts.insert((id, message.channel_id), Instant::now()); })
                 .fold("Hey!".into(),
                       |string, id| format!("{} {}", string, id.mention())))
    } else {
        None
    }
}
<|endoftext|>


<commit_before>use ::expr::Expr;

#[cfg(test)] mod spec;

#[derive(PartialEq,Debug,Clone)]
enum State {
    State{edge: Option<char>, out: Edge},
    Split{s1: Option<char>, out1: Edge, s2: Option<char>, out2: Edge}
}

#[derive(PartialEq,Debug,Clone)]
enum Edge {
    Id(usize),
    Detached,
    End
}

impl State {
    pub fn state(edge: Option<char>, out: Edge) -> State {
        State::State{edge: edge, out: out}
    }

    pub fn split(s1: Option<char>, out1: Edge, s2: Option<char>, out2: Edge) -> State {
        State::Split{s1: s1,
                     out1: out1,
                     s2: s2,
                     out2: out2}
    }
}

#[derive(Debug)]
struct NFA {
    states: Vec<State>
}

impl NFA {

    pub fn new() -> NFA {
        NFA {
            states: Vec::new()
        }
    }

    pub fn from_expr(expr: &Expr) -> NFA {
        let mut nfa = Self::new();

        let id = nfa.build_expr(expr);
        nfa.update_outputs(id, Edge::End);
        nfa
    }

    fn build_expr(&mut self, expr: &Expr) -> usize {
        let id = match expr {
            &Expr::Single(c) => {
                let new_state_id = self.states.len();
                let s = State::state(Some(c), Edge::Detached);
                self.states.push(s);
                self.states.len() - 1
            },
            &Expr::Sequence(ref a, ref b) => {
                let left_id = self.build_expr(a);
                let right_id = self.build_expr(b);
                self.update_outputs(left_id, Edge::Id(right_id));
                left_id
            },
            &Expr::Optional(ref expr) => {
                let expr_id = self.build_expr(expr);
                let s = State::split(None, Edge::Id(expr_id), None, Edge::Detached);
                self.states.push(s);
                self.states.len() - 1
            }
            _ => panic!()
        };

        id
    }

    fn update_outputs(&mut self, start_id: usize, new_edge: Edge) {
        let state = self.states[start_id].clone();
        let state = match state {
            State::State{edge, ref out} => {
                State::state(edge,
                             self.replace_edge(out.clone(), new_edge)
)
            },
            State::Split{s1, ref out1, s2, ref out2} => {
                State::split(s1,
                             self.replace_edge(out1.clone(), new_edge.clone()),
                             s2,
                             self.replace_edge(out2.clone(), new_edge.clone()))
            }
        };
        self.states[start_id] = state;
    }

    fn replace_edge(&mut self, edge: Edge, replacement: Edge) -> Edge {
        match &edge {
            &Edge::Detached => {
                replacement
            },
            &Edge::Id(id) => {
                self.update_outputs(id, replacement);
                edge
            },
            _ => edge
        }
    }
}

<commit_msg>build option NFA<commit_after>use ::expr::Expr;

#[cfg(test)] mod spec;

#[derive(PartialEq,Debug,Clone)]
enum State {
    State{edge: Option<char>, out: Edge},
    Split{s1: Option<char>, out1: Edge, s2: Option<char>, out2: Edge}
}

#[derive(PartialEq,Debug,Clone)]
enum Edge {
    Id(usize),
    Detached,
    End
}

impl State {
    pub fn state(edge: Option<char>, out: Edge) -> State {
        State::State{edge: edge, out: out}
    }

    pub fn split(s1: Option<char>, out1: Edge, s2: Option<char>, out2: Edge) -> State {
        State::Split{s1: s1,
                     out1: out1,
                     s2: s2,
                     out2: out2}
    }
}

#[derive(Debug)]
struct NFA {
    start: usize,
    states: Vec<State>
}

impl NFA {

    pub fn new() -> NFA {
        NFA {
            start: 0,
            states: Vec::new()
        }
    }

    pub fn from_expr(expr: &Expr) -> NFA {
        let mut nfa = Self::new();

        nfa.start = nfa.build_expr(expr);
        nfa.update_outputs(id, Edge::End);
        nfa
    }

    fn build_expr(&mut self, expr: &Expr) -> usize {
        let id = match expr {
            &Expr::Single(c) => {
                let new_state_id = self.states.len();
                let s = State::state(Some(c), Edge::Detached);
                self.states.push(s);
                self.states.len() - 1
            },
            &Expr::Sequence(ref a, ref b) => {
                let left_id = self.build_expr(a);
                let right_id = self.build_expr(b);
                self.update_outputs(left_id, Edge::Id(right_id));
                left_id
            },
            &Expr::Optional(ref expr) => {
                let expr_id = self.build_expr(expr);
                let s = State::split(None, Edge::Id(expr_id), None, Edge::Detached);
                self.states.push(s);
                self.states.len() - 1
            }
            _ => panic!()
        };

        id
    }

    fn update_outputs(&mut self, start_id: usize, new_edge: Edge) {
        let state = self.states[start_id].clone();
        let state = match state {
            State::State{edge, ref out} => {
                State::state(edge,
                             self.replace_edge(out.clone(), new_edge)
)
            },
            State::Split{s1, ref out1, s2, ref out2} => {
                State::split(s1,
                             self.replace_edge(out1.clone(), new_edge.clone()),
                             s2,
                             self.replace_edge(out2.clone(), new_edge.clone()))
            }
        };
        self.states[start_id] = state;
    }

    fn replace_edge(&mut self, edge: Edge, replacement: Edge) -> Edge {
        match &edge {
            &Edge::Detached => {
                replacement
            },
            &Edge::Id(id) => {
                self.update_outputs(id, replacement);
                edge
            },
            _ => edge
        }
    }
}

<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::collections::BTreeMap;
use rustc_serialize::json::{Json, ToJson};

/// Each remote notification includes a payload.
/// The payload contains information about how the system should alert the user as well
/// as any custom data you provide.
pub struct Payload<'a> {
    pub aps: APS<'a>,
}

pub struct APS {
    pub alert: Option<APSAlert>,

    // The number to display as the badge of the app icon.
    pub badge: Option<u32>,

    // The name of a sound file in the app bundle or in the Library/Sounds folder of
    // the app’s data container.
    pub sound: Option<String>,

    // Provide this key with a value of 1 to indicate that new content is available.
    pub content_available: Option<u32>,

    // Provide this key with a string value that represents the identifier property.
    pub category: Option<String>,
}

pub enum APSAlert {
    Plain(String),
    Localized(APSLocalizedAlert),
}

pub struct APSLocalizedAlert {
    pub title: String,
    pub body: String,
    pub title_loc_key: Option<String>,
    pub title_loc_args: Option<Vec<String>>,
    pub action_loc_key: Option<String>,
    pub loc_key: String,
    pub loc_args: Vec<String>,
    pub launch_image: Option<String>,
}

impl Payload {
    pub fn new<S>(alert: APSAlert, badge: u32, sound: S, category: Option<String>) -> Payload
        where S: Into<String>
    {
        Payload {
            aps: APS {
                alert: Some(alert),
                badge: badge,
                sound: Some(sound.into()),
                content_available: None,
                category: category,
            },
        }
    }

    pub fn new_action_notification<S>(alert: APSAlert, badge: Option<u32>, sound: S, category: S) -> Payload<'a>
        where S: Into<Cow<'a, str>>
    {
        Payload {
            aps: APS {
                alert: Some(alert),
                badge: badge,
                sound: Some(sound.into()),
                content_available: None,
                category: Some(category.into()),
            },
        }
    }

    pub fn new_silent_notification() -> Payload<'a> {
        Payload {
            aps: APS {
                alert: None,
                badge: None,
                sound: None,
                content_available: Some(1),
                category: None,
            },
        }
    }

    pub fn to_string(&self) -> String {
        self.to_json().to_string()
    }

    pub fn len(&self) -> usize {
        self.to_string().len()
    }
}

impl<'a> ToJson for Payload<'a> {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("aps".to_string(), self.aps.to_json());
        Json::Object(d)
    }
}

/// The APS can contain one or more properties that specify the following user notification types:
/// - an alert message to display to the user
/// - a number to badge the app icon with
/// - a sound to play
pub struct APS<'a> {
    /// If this property is included, the system displays a standard alert or a banner,
    /// based on the user’s setting.
    pub alert: Option<APSAlert>,

    /// The number to display as the badge of the app icon.
    pub badge: Option<u32>,

    /// The name of a sound file in the app bundle or in the Library/Sounds folder of
    /// the app’s data container.
    pub sound: Option<Cow<'a, str>>,

    /// Provide this key with a value of 1 to indicate that new content is available.
    pub content_available: Option<u32>,

    /// Provide this key with a string value that represents the identifier property.
    pub category: Option<Cow<'a, str>>,
}

impl<'a> ToJson for APS<'a> {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        match self.alert {
            Some(APSAlert::Plain(ref s)) => {
                d.insert("alert".to_string(), s.to_json());
            }
            Some(APSAlert::Localized(ref l)) => {
                d.insert("alert".to_string(), l.to_json());
            }
            None => {}
        };
        if let Some(ref badge) = self.badge {
            d.insert("badge".to_string(), badge.to_json());
        }
        if let Some(ref sound) = self.sound {
            d.insert("sound".to_string(), sound.to_json());
        }
        if let Some(ref content_available) = self.content_available {
            d.insert("content-available".to_string(), content_available.to_json());
        }
        if let Some(ref category) = self.category {
            d.insert("category".to_string(), category.to_json());
        }
        Json::Object(d)
    }
}

/// Can specify a string or a dictionary as the value of alert.
pub enum APSAlert {
    Plain(String),
    Localized(APSLocalizedAlert),
}

/// Child properties of the alert property.
pub struct APSLocalizedAlert {
    /// A short string describing the purpose of the notification
    pub title: String,

    /// The text of the alert message.
    pub body: String,

    /// The key to a title string in the Localizable.strings file for the current localization.
    pub title_loc_key: Option<String>,

    /// Variable string values to appear in place of the format specifiers in title-loc-key.
    pub title_loc_args: Option<Vec<String>>,

    /// If a string is specified, the system displays an alert that includes the Close and View buttons.
    pub action_loc_key: Option<String>,

    /// A key to an alert-message string in a Localizable.strings file for the current localization.
    pub loc_key: String,

    /// Variable string values to appear in place of the format specifiers in loc-key.
    pub loc_args: Vec<String>,

    /// The filename of an image file in the app bundle.
    /// The image is used as the launch image when users tap the action button or move the action slider.
    pub launch_image: Option<String>,
}

impl ToJson for APSLocalizedAlert {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();

        d.insert("title".to_string(), self.title.to_json());
        d.insert("body".to_string(), self.body.to_json());

        if let Some(ref title_loc_key) = self.title_loc_key {
            d.insert("title-loc-key".to_string(), title_loc_key.to_json());
        } else {
            d.insert("title-loc-key".to_string(), Json::Null);
        }

        if let Some(ref title_loc_args) = self.title_loc_args {
            d.insert("title-loc-args".to_string(), title_loc_args.to_json());
        } else {
            d.insert("title-loc-args".to_string(), Json::Null);
        }

        if let Some(ref action_loc_key) = self.action_loc_key {
            d.insert("action-loc-key".to_string(), action_loc_key.to_json());
        } else {
            d.insert("action-loc-key".to_string(), Json::Null);
        }

        d.insert("loc-key".to_string(), self.loc_key.to_json());
        d.insert("loc-args".to_string(), self.loc_args.to_json());

        if let Some(ref launch_image) = self.launch_image {
            d.insert("launch-image".to_string(), launch_image.to_json());
        }

        Json::Object(d)
    }
}
<commit_msg>Make loc-key and loc-args optional<commit_after>use std::borrow::Cow;
use std::collections::BTreeMap;
use rustc_serialize::json::{Json, ToJson};

/// Each remote notification includes a payload.
/// The payload contains information about how the system should alert the user as well
/// as any custom data you provide.
pub struct Payload<'a> {
    pub aps: APS<'a>,
}

pub struct APS {
    pub alert: Option<APSAlert>,

    // The number to display as the badge of the app icon.
    pub badge: Option<u32>,

    // The name of a sound file in the app bundle or in the Library/Sounds folder of
    // the app’s data container.
    pub sound: Option<String>,

    // Provide this key with a value of 1 to indicate that new content is available.
    pub content_available: Option<u32>,

    // Provide this key with a string value that represents the identifier property.
    pub category: Option<String>,
}

pub enum APSAlert {
    Plain(String),
    Localized(APSLocalizedAlert),
}

pub struct APSLocalizedAlert {
    pub title: String,
    pub body: String,
    pub title_loc_key: Option<String>,
    pub title_loc_args: Option<Vec<String>>,
    pub action_loc_key: Option<String>,
    pub loc_key: Option<String>,
    pub loc_args: Option<Vec<String>>,
    pub launch_image: Option<String>,
}

impl Payload {
    pub fn new<S>(alert: APSAlert, badge: u32, sound: S, category: Option<String>) -> Payload
        where S: Into<String>
    {
        Payload {
            aps: APS {
                alert: Some(alert),
                badge: badge,
                sound: Some(sound.into()),
                content_available: None,
                category: category,
            },
        }
    }

    pub fn new_action_notification<S>(alert: APSAlert, badge: Option<u32>, sound: S, category: S) -> Payload<'a>
        where S: Into<Cow<'a, str>>
    {
        Payload {
            aps: APS {
                alert: Some(alert),
                badge: badge,
                sound: Some(sound.into()),
                content_available: None,
                category: Some(category.into()),
            },
        }
    }

    pub fn new_silent_notification() -> Payload<'a> {
        Payload {
            aps: APS {
                alert: None,
                badge: None,
                sound: None,
                content_available: Some(1),
                category: None,
            },
        }
    }

    pub fn to_string(&self) -> String {
        self.to_json().to_string()
    }

    pub fn len(&self) -> usize {
        self.to_string().len()
    }
}

impl<'a> ToJson for Payload<'a> {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("aps".to_string(), self.aps.to_json());
        Json::Object(d)
    }
}

/// The APS can contain one or more properties that specify the following user notification types:
/// - an alert message to display to the user
/// - a number to badge the app icon with
/// - a sound to play
pub struct APS<'a> {
    /// If this property is included, the system displays a standard alert or a banner,
    /// based on the user’s setting.
    pub alert: Option<APSAlert>,

    /// The number to display as the badge of the app icon.
    pub badge: Option<u32>,

    /// The name of a sound file in the app bundle or in the Library/Sounds folder of
    /// the app’s data container.
    pub sound: Option<Cow<'a, str>>,

    /// Provide this key with a value of 1 to indicate that new content is available.
    pub content_available: Option<u32>,

    /// Provide this key with a string value that represents the identifier property.
    pub category: Option<Cow<'a, str>>,
}

impl<'a> ToJson for APS<'a> {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        match self.alert {
            Some(APSAlert::Plain(ref s)) => {
                d.insert("alert".to_string(), s.to_json());
            }
            Some(APSAlert::Localized(ref l)) => {
                d.insert("alert".to_string(), l.to_json());
            }
            None => {}
        };
        if let Some(ref badge) = self.badge {
            d.insert("badge".to_string(), badge.to_json());
        }
        if let Some(ref sound) = self.sound {
            d.insert("sound".to_string(), sound.to_json());
        }
        if let Some(ref content_available) = self.content_available {
            d.insert("content-available".to_string(), content_available.to_json());
        }
        if let Some(ref category) = self.category {
            d.insert("category".to_string(), category.to_json());
        }
        Json::Object(d)
    }
}

/// Can specify a string or a dictionary as the value of alert.
pub enum APSAlert {
    Plain(String),
    Localized(APSLocalizedAlert),
}

/// Child properties of the alert property.
pub struct APSLocalizedAlert {
    /// A short string describing the purpose of the notification
    pub title: String,

    /// The text of the alert message.
    pub body: String,

    /// The key to a title string in the Localizable.strings file for the current localization.
    pub title_loc_key: Option<String>,

    /// Variable string values to appear in place of the format specifiers in title-loc-key.
    pub title_loc_args: Option<Vec<String>>,

    /// If a string is specified, the system displays an alert that includes the Close and View buttons.
    pub action_loc_key: Option<String>,

    /// A key to an alert-message string in a Localizable.strings file for the current localization.
    pub loc_key: String,

    /// Variable string values to appear in place of the format specifiers in loc-key.
    pub loc_args: Vec<String>,

    /// The filename of an image file in the app bundle.
    /// The image is used as the launch image when users tap the action button or move the action slider.
    pub launch_image: Option<String>,
}

impl ToJson for APSLocalizedAlert {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();

        d.insert("title".to_string(), self.title.to_json());
        d.insert("body".to_string(), self.body.to_json());

        if let Some(ref title_loc_key) = self.title_loc_key {
            d.insert("title-loc-key".to_string(), title_loc_key.to_json());
        } else {
            d.insert("title-loc-key".to_string(), Json::Null);
        }

        if let Some(ref title_loc_args) = self.title_loc_args {
            d.insert("title-loc-args".to_string(), title_loc_args.to_json());
        } else {
            d.insert("title-loc-args".to_string(), Json::Null);
        }

        if let Some(ref action_loc_key) = self.action_loc_key {
            d.insert("action-loc-key".to_string(), action_loc_key.to_json());
        } else {
            d.insert("action-loc-key".to_string(), Json::Null);
        }

        if let Some(ref loc_key) = self.loc_key {
            d.insert("loc-key".to_string(), loc_key.to_json());
        } else {
            d.insert("loc-key".to_string(), Json::Null);
        }

        if let Some(ref loc_args) = self.loc_args {
            d.insert("loc-args".to_string(), loc_args.to_json());
        } else {
            d.insert("loc-args".to_string(), Json::Null);
        }

        if let Some(ref launch_image) = self.launch_image {
            d.insert("launch-image".to_string(), launch_image.to_json());
        }

        Json::Object(d)
    }
}
<|endoftext|>