<commit_before>use chrono::DateTime;
use chrono::prelude::{Local, Utc};
use errors::*;
use nom::{alphanumeric, ErrorKind, IResult};
use output::Output;
use std::str;

#[derive(Debug, PartialEq, Eq)]
struct Program {
    name: MacroOp,
    steps: Vec<Step>,
}

#[derive(Debug, PartialEq, Eq)]
struct Step {
    op: MacroOp,
    args: Vec<String>
}

#[derive(Debug, PartialEq, Eq)]
enum MacroOp {
    Add,
    Divide,
    Multiply,
    Name(String),
    Roll,
    Say,
    Subtract,
    Whisper,
}

#[derive(Debug, PartialEq, Eq)]
enum RollOp {
    Advantage,
    Comment,
    Disadvantage,
    NdD,
}

#[derive(Debug, PartialEq, Eq)]
enum SayOp {
    Message,
    To,
}

named!(arguments <&[u8], String>, alt!(
    string |
    quoted |
    single_quoted
));

/// Matches a command
named!(command <&[u8], MacroOp>, alt!(
    map!(ws!(tag!("!roll")),     |_| MacroOp::Roll)      |
    map!(ws!(tag!("!r")),        |_| MacroOp::Roll)      |
    map!(ws!(tag!("!say")),      |_| MacroOp::Say)       |
    map!(ws!(tag!("!s")),        |_| MacroOp::Say)       |
    map!(ws!(tag!("!whisper")),  |_| MacroOp::Whisper)   |
    map!(ws!(tag!("!w")),        |_| MacroOp::Whisper)
));

/// Matches a macro name
named!(name <&[u8], MacroOp>, ws!(do_parse!(
    tag!("#") >>
    name: map_res!(is_not!(" \t\r\n"), |r: &[u8]| String::from_utf8(r.to_vec())) >>
    (MacroOp::Name(name))
)));

/// Matches any type of operation
named!(op <&[u8], MacroOp>, alt!(
    name |
    command |
    ws!(primitive)
));

/// Matches primitive operations
named!(primitive <&[u8], MacroOp>, alt!(
    map!(tag!("*"), |_| MacroOp::Multiply)  |
    map!(tag!("+"), |_| MacroOp::Add)       |
    map!(tag!("-"), |_| MacroOp::Subtract)  |
    map!(tag!("/"), |_| MacroOp::Divide)
));

/// Parse the complete macro
named!(parse <&[u8], Program>, do_parse!(
    prog_name: name >>
    steps: many0!(parse_step) >>
    (Program {
        name: prog_name,
        steps: steps,
    })
));

/// Parse a step of the program
named!(parse_step <&[u8], Step>, do_parse!(
    op_type: op >>
    args: ws!(many0!(arguments)) >>
    (Step {
        op: op_type,
        args,
    })
));

/// Matches arguments in quotes ("")
named!(quoted <&[u8], String>, do_parse!(
    word: ws!(delimited!(tag!("\""),take_until!("\""), tag!("\""))) >>
    (String::from_utf8(word.to_vec()).unwrap())
));

/// Matches arguments in quotes ('')
named!(single_quoted <&[u8], String>, do_parse!(
    word: ws!(delimited!(tag!("'"),take_until!("'"), tag!("'"))) >>
    (String::from_utf8(word.to_vec()).unwrap())
));

/// Match alphanumeric values to strings
named!(string <&[u8], String>, do_parse!(
    word: ws!(alphanumeric) >>
    (String::from_utf8(word.to_vec()).unwrap())
));

#[test]
fn test_simple_parser() {
    let program = Program {
        name: MacroOp::Name(String::from("macro-name")),
        steps: vec![Step {
            op: MacroOp::Roll,
            args: vec![ "1d20".to_string() ],
        }],
    };
    let (_, result) = parse(b"#macro-name !roll 1d20").unwrap();
    assert_eq!(result, program);

    let program = Program {
        name: MacroOp::Name(String::from("macro-name-2")),
        steps: vec![Step {
            op: MacroOp::Say,
            args: vec![ "Hello, world!".to_string() ],
        }],
    };
    let (_, result) = parse(b"#macro-name-2 !say \"Hello, world!\"").unwrap();
    assert_eq!(result, program);
}

#[test]
fn test_complex_parser() {
    let program = Program {
        name: MacroOp::Name(String::from("macro-name")),
        steps: vec![
            Step {
                op: MacroOp::Roll,
                args: vec![ "1d20".to_string() ],
            },
            Step {
                op: MacroOp::Say,
                args: vec![ "Smite!".to_string() ],
            },
        ],
    };
    let (_, result) = parse(b"#macro-name !roll 1d20 !say \"Smite!\"").unwrap();
    assert_eq!(result, program);

    let program = Program {
        name: MacroOp::Name(String::from("macro-name-2")),
        steps: vec![
            Step {
                op: MacroOp::Roll,
                args: vec![ "3d8".to_string() ],
            },
            Step {
                op: MacroOp::Add,
                args: vec![ "3".to_string() ],
            },
            Step {
                op: MacroOp::Say,
                args: vec![ "Smite!".to_string() ],
            },
            Step {
                op: MacroOp::Roll,
                args: vec![ "1d20".to_string() ],
            },
        ],
    };
    let (_, result) = parse(b"#macro-name-2 !roll 3d8+3 !say \"Smite!\" !roll 1d20").unwrap();
    assert_eq!(result, program);
}

#[test]
fn test_name_parser() {
    let (_, result) = name(b"#macro_name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("macro_name")));

    let (_, result) = name(b"#macro-name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("macro-name")));

    let (_, result) = name(b"#123macro-name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("123macro-name")));

    let (_, result) = name(b"#Z123macro-name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("Z123macro-name")));

    let (_, result) = name(b"#macro-name cannot have spaces").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("macro-name")));

    let bad_result = name(b"macro_name");
    match bad_result {
        IResult::Error(e) => assert_eq!(e, ErrorKind::Tag),
        _ => assert_eq!(1, 0),
    }
}

#[test]
fn test_command_parser_roll() {
    let (_, result) = command(b"!roll 1d20").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!r 1d20").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!roll advantage").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!roll adv").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!roll disadvantage").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!roll dis").unwrap();
    assert_eq!(result, MacroOp::Roll);
}

#[test]
fn test_op_parser() {
    let (_, result) = op(b"    #test-macro   ").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("test-macro")));
    let (_, result) = command(b"    !roll 1d20 ").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"   !say \"Hello!\" ").unwrap();
    assert_eq!(result, MacroOp::Say);
    let (_, result) = command(b"   !whisper").unwrap();
    assert_eq!(result, MacroOp::Whisper);
}

#[test]
fn test_arguments_parser() {
    let (_, result) = arguments(b"\"hello\"").unwrap();
    assert_eq!(result, String::from("hello"));
    let (_, result) = arguments(b"   Hello  ").unwrap();
    assert_eq!(result, String::from("Hello"));
    let (_, result) = arguments(b"'   Single String Args'").unwrap();
    assert_eq!(result, String::from("Single String Args"));
}

#[test]
fn test_quoted_parser() {
    let (_, result) = quoted(b"\"hello\"").unwrap();
    assert_eq!(result, String::from("hello"));
    let (_, result) = quoted(b"\"   Hello  \"").unwrap();
    assert_eq!(result, String::from("Hello  "));
}

#[test]
fn test_single_quoted_parser() {
    let (_, result) = single_quoted(b"'test 123'").unwrap();
    assert_eq!(result, String::from("test 123"));
    let (_, result) = single_quoted(b"'   Single String Args'").unwrap();
    assert_eq!(result, String::from("Single String Args"));
}
<commit_msg>minor cleanup<commit_after>// use errors::*;
use nom::{alphanumeric, ErrorKind, IResult};

#[derive(Debug, PartialEq, Eq)]
struct Program {
    name: MacroOp,
    steps: Vec<Step>,
}

#[derive(Debug, PartialEq, Eq)]
struct Step {
    op: MacroOp,
    args: Vec<String>
}

#[derive(Debug, PartialEq, Eq)]
enum MacroOp {
    Add,
    Divide,
    Multiply,
    Name(String),
    Roll,
    Say,
    Subtract,
    Whisper,
}

#[derive(Debug, PartialEq, Eq)]
enum RollOp {
    Advantage,
    Comment,
    Disadvantage,
    NdD,
}

#[derive(Debug, PartialEq, Eq)]
enum SayOp {
    Message,
    To,
}

named!(arguments <&[u8], String>, alt!(
    string |
    quoted |
    single_quoted
));

/// Matches a command
named!(command <&[u8], MacroOp>, alt!(
    map!(ws!(tag!("!roll")),     |_| MacroOp::Roll)      |
    map!(ws!(tag!("!r")),        |_| MacroOp::Roll)      |
    map!(ws!(tag!("!say")),      |_| MacroOp::Say)       |
    map!(ws!(tag!("!s")),        |_| MacroOp::Say)       |
    map!(ws!(tag!("!whisper")),  |_| MacroOp::Whisper)   |
    map!(ws!(tag!("!w")),        |_| MacroOp::Whisper)
));

/// Matches a macro name
named!(name <&[u8], MacroOp>, ws!(do_parse!(
    tag!("#") >>
    name: map_res!(is_not!(" \t\r\n"), |r: &[u8]| String::from_utf8(r.to_vec())) >>
    (MacroOp::Name(name))
)));

/// Matches any type of operation
named!(op <&[u8], MacroOp>, alt!(
    name |
    command |
    ws!(primitive)
));

/// Matches primitive operations
named!(primitive <&[u8], MacroOp>, alt!(
    map!(tag!("*"), |_| MacroOp::Multiply)  |
    map!(tag!("+"), |_| MacroOp::Add)       |
    map!(tag!("-"), |_| MacroOp::Subtract)  |
    map!(tag!("/"), |_| MacroOp::Divide)
));

/// Parse the complete macro
named!(parse <&[u8], Program>, do_parse!(
    prog_name: name >>
    steps: many0!(parse_step) >>
    (Program {
        name: prog_name,
        steps: steps,
    })
));

/// Parse a step of the program
named!(parse_step <&[u8], Step>, do_parse!(
    op_type: op >>
    args: ws!(many0!(arguments)) >>
    (Step {
        op: op_type,
        args,
    })
));

/// Matches arguments in quotes ("")
named!(quoted <&[u8], String>, do_parse!(
    word: ws!(delimited!(tag!("\""),take_until!("\""), tag!("\""))) >>
    (String::from_utf8(word.to_vec()).unwrap())
));

/// Matches arguments in quotes ('')
named!(single_quoted <&[u8], String>, do_parse!(
    word: ws!(delimited!(tag!("'"),take_until!("'"), tag!("'"))) >>
    (String::from_utf8(word.to_vec()).unwrap())
));

/// Match alphanumeric values to strings
named!(string <&[u8], String>, do_parse!(
    word: ws!(alphanumeric) >>
    (String::from_utf8(word.to_vec()).unwrap())
));

#[test]
fn test_simple_parser() {
    let program = Program {
        name: MacroOp::Name(String::from("macro-name")),
        steps: vec![Step {
            op: MacroOp::Roll,
            args: vec![ "1d20".to_string() ],
        }],
    };
    let (_, result) = parse(b"#macro-name !roll 1d20").unwrap();
    assert_eq!(result, program);

    let program = Program {
        name: MacroOp::Name(String::from("macro-name-2")),
        steps: vec![Step {
            op: MacroOp::Say,
            args: vec![ "Hello, world!".to_string() ],
        }],
    };
    let (_, result) = parse(b"#macro-name-2 !say \"Hello, world!\"").unwrap();
    assert_eq!(result, program);
}

#[test]
fn test_complex_parser() {
    let program = Program {
        name: MacroOp::Name(String::from("macro-name")),
        steps: vec![
            Step {
                op: MacroOp::Roll,
                args: vec![ "1d20".to_string() ],
            },
            Step {
                op: MacroOp::Say,
                args: vec![ "Smite!".to_string() ],
            },
        ],
    };
    let (_, result) = parse(b"#macro-name !roll 1d20 !say \"Smite!\"").unwrap();
    assert_eq!(result, program);

    let program = Program {
        name: MacroOp::Name(String::from("macro-name-2")),
        steps: vec![
            Step {
                op: MacroOp::Roll,
                args: vec![ "3d8".to_string() ],
            },
            Step {
                op: MacroOp::Add,
                args: vec![ "3".to_string() ],
            },
            Step {
                op: MacroOp::Say,
                args: vec![ "Smite!".to_string() ],
            },
            Step {
                op: MacroOp::Roll,
                args: vec![ "1d20".to_string() ],
            },
        ],
    };
    let (_, result) = parse(b"#macro-name-2 !roll 3d8+3 !say \"Smite!\" !roll 1d20").unwrap();
    assert_eq!(result, program);
}

#[test]
fn test_name_parser() {
    let (_, result) = name(b"#macro_name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("macro_name")));

    let (_, result) = name(b"#macro-name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("macro-name")));

    let (_, result) = name(b"#123macro-name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("123macro-name")));

    let (_, result) = name(b"#Z123macro-name").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("Z123macro-name")));

    let (_, result) = name(b"#macro-name cannot have spaces").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("macro-name")));

    let bad_result = name(b"macro_name");
    match bad_result {
        IResult::Error(e) => assert_eq!(e, ErrorKind::Tag),
        _ => assert_eq!(1, 0),
    }
}

#[test]
fn test_command_parser_roll() {
    let (_, result) = command(b"!roll 1d20").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!r 1d20").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!roll advantage").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!roll adv").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!roll disadvantage").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"!roll dis").unwrap();
    assert_eq!(result, MacroOp::Roll);
}

#[test]
fn test_op_parser() {
    let (_, result) = op(b"    #test-macro   ").unwrap();
    assert_eq!(result, MacroOp::Name(String::from("test-macro")));
    let (_, result) = command(b"    !roll 1d20 ").unwrap();
    assert_eq!(result, MacroOp::Roll);
    let (_, result) = command(b"   !say \"Hello!\" ").unwrap();
    assert_eq!(result, MacroOp::Say);
    let (_, result) = command(b"   !whisper").unwrap();
    assert_eq!(result, MacroOp::Whisper);
}

#[test]
fn test_arguments_parser() {
    let (_, result) = arguments(b"\"hello\"").unwrap();
    assert_eq!(result, String::from("hello"));
    let (_, result) = arguments(b"   Hello  ").unwrap();
    assert_eq!(result, String::from("Hello"));
    let (_, result) = arguments(b"'   Single String Args'").unwrap();
    assert_eq!(result, String::from("Single String Args"));
}

#[test]
fn test_quoted_parser() {
    let (_, result) = quoted(b"\"hello\"").unwrap();
    assert_eq!(result, String::from("hello"));
    let (_, result) = quoted(b"\"   Hello  \"").unwrap();
    assert_eq!(result, String::from("Hello  "));
}

#[test]
fn test_single_quoted_parser() {
    let (_, result) = single_quoted(b"'test 123'").unwrap();
    assert_eq!(result, String::from("test 123"));
    let (_, result) = single_quoted(b"'   Single String Args'").unwrap();
    assert_eq!(result, String::from("Single String Args"));
}
<|endoftext|>


<commit_before>#[cfg(feature = "regex1")]
use regex::Regex;
use serde_json::{Value, Map, Number};
use url::percent_encoding::percent_decode;

use merge::merge;
use helpers::{create_array, push_item_to_array};

#[cfg(feature = "regex1")]
lazy_static! {
    static ref PARENT_REGEX: Regex = Regex::new(r"^([^\]\[]+)").unwrap();
    static ref CHILD_REGEX: Regex = Regex::new(r"(\[[^\]\[]*\])").unwrap();
}

type Object = Map<String, Value>;

#[derive(Debug)]
#[allow(missing_copy_implementations)]
pub enum ParseErrorKind {
    DecodingError,
    Other
}

#[derive(Debug)]
pub struct ParseError {
    pub kind: ParseErrorKind,
    pub message: String
}

pub type ParseResult<T> = Result<T,ParseError>;

pub fn decode_component(source: &str) -> Result<String,String> {
    let result = percent_decode(source.as_bytes()).decode_utf8_lossy().to_string();
    return Ok(result);
}

fn parse_pairs(body: &str) -> Vec<(&str, Option<&str>)> {

    let mut pairs = vec![];

    for part in body.split("&") {
        let separator = part.find("]=")
                            .and_then(|pos| Some(pos+1))
                            .or_else(|| part.find("="));

        match separator {
            None => pairs.push((part, None)),
            Some(pos) => {
                let key = &part[..pos];
                let val = &part[(pos + 1)..];
                pairs.push((key, Some(val)));
            }
        }
    }

    return pairs
}

#[cfg(feature = "regex1")]
fn parse_key(key: &str) -> ParseResult<Vec<String>> {
    let mut keys: Vec<String> = vec![];

    match PARENT_REGEX.captures(key) {
        Some(captures) => {
            match decode_component(captures.get(1).unwrap().as_str()) {
                Ok(decoded_key) => keys.push(decoded_key),
                Err(err_msg) => return Err(ParseError{ kind: ParseErrorKind::DecodingError, message: err_msg })
            }
        }
        None => ()
    };

    for captures in CHILD_REGEX.captures_iter(key) {
        match decode_component(captures.get(1).unwrap().as_str()) {
            Ok(decoded_key) => keys.push(decoded_key),
            Err(err_msg) => return Err(ParseError{ kind: ParseErrorKind::DecodingError, message: err_msg })
        }
    }

    Ok(keys)
}

#[cfg(not(feature = "regex1"))]
fn parse_key(key: &str) -> ParseResult<Vec<String>> {
    let mut keys: Vec<String> = vec![];

    match key.split(|c| c=='[' || c==']').next() {
        Some(parent) if !parent.is_empty() =>  {
            match decode_component(parent) {
                Ok(decoded_key) => keys.push(decoded_key),
                Err(err_msg) => return Err(ParseError{ kind: ParseErrorKind::DecodingError, message: err_msg })
            }
        }
        _ => ()
    }

    let mut prev_bracket = None;
    for (idx, ch) in key.char_indices() {
        match ch {
            '[' => prev_bracket = Some(idx),
            ']' => {
                if let Some(prev_idx) = prev_bracket {
                    prev_bracket = None;
                    let child = &key[prev_idx..=idx];
                    match decode_component(child) {
                        Ok(decoded_key) => keys.push(decoded_key),
                        Err(err_msg) => return Err(ParseError{ kind: ParseErrorKind::DecodingError, message: err_msg })
                    }
                }
            }
            _ => (),
        }
    }

    Ok(keys)
}

fn cleanup_key(key: &str) -> &str {
    if key.starts_with("[") && key.ends_with("]") {
        &key[1..(key.len()-1)]
    } else {
        key
    }
}

fn create_idx_merger(idx: u64, obj: Value) -> Value {
    let mut tree = Object::new();
    tree.insert("__idx".to_string(), Value::Number(Number::from(idx)));
    tree.insert("__object".to_string(), obj);
    return Value::Object(tree)
}

fn create_object_with_key(key: String, obj: Value) -> Value {
    let mut tree = Object::new();
    tree.insert(key, obj);
    return Value::Object(tree)
}

fn apply_object(keys: &[String], val: Value) -> Value {

    if keys.len() > 0 {
        let key = keys.get(0).unwrap();
        if key == "[]" {
            let mut new_array = create_array();
            let item = apply_object(&keys[1..], val);
            push_item_to_array(&mut new_array, item);
            return new_array;
        } else {
            let key = cleanup_key(key);
            let array_index = key.parse();

            match array_index {
                Ok(idx) => {
                    let result = apply_object(&keys[1..], val);
                    let item = create_idx_merger(idx, result);
                    return item;
                },
                Err(_) => {
                    return create_object_with_key(key.to_string(), apply_object(&keys[1..], val));
                }
            }
        }

    } else {
        return val;
    }
}

pub fn parse(params: &str) -> ParseResult<Value> {
    let tree = Object::new();
    let mut obj = Value::Object(tree);
    let decoded_params = match decode_component(params) {
        Ok(val) => val,
        Err(err) => return Err(ParseError{ kind: ParseErrorKind::DecodingError, message: err })
    };
    let pairs = parse_pairs(&decoded_params);
    for &(key, value) in pairs.iter() {
        let parse_key_res = try!(parse_key(key));
        let key_chain = &parse_key_res[0..];
        let decoded_value = match decode_component(value.unwrap_or("")) {
            Ok(val) => val,
            Err(err) => return Err(ParseError{ kind: ParseErrorKind::DecodingError, message: err })
        };
        let partial = apply_object(key_chain, Value::String(decoded_value));
        merge(&mut obj, &partial);
    }

    Ok(obj)
}

#[cfg(test)]
mod tests {
    use parse;
    use serde_json::{Value, to_string};

    fn eq_str(value: Value, string: &str) {
        assert_eq!(&to_string(&value).unwrap(), string)
    }

    #[test]
    fn it_parses_simple_string() {
        eq_str(parse("0=foo").unwrap(), r#"{"0":"foo"}"#);
        eq_str(parse("a[<=>]==23").unwrap(), r#"{"a":{"<=>":"=23"}}"#);
        eq_str(parse(" foo = bar = baz ").unwrap(), r#"{" foo ":" bar = baz "}"#);
    }

    #[test]
    fn it_parses_nested_string() {
        eq_str(parse("a[b][c][d][e][f][g][h]=i").unwrap(),
            r#"{"a":{"b":{"c":{"d":{"e":{"f":{"g":{"h":"i"}}}}}}}}"#);
    }

    #[test]
    fn it_parses_simple_array() {
        eq_str(parse("a=b&a=c&a=d&a=e").unwrap(),
            r#"{"a":["b","c","d","e"]}"#);
    }

    #[test]
    fn it_parses_explicit_array() {
        eq_str(parse("a[]=b&a[]=c&a[]=d").unwrap(),
            r#"{"a":["b","c","d"]}"#);
    }

    #[test]
    fn it_parses_nested_array() {
        eq_str(parse("a[b][]=c&a[b][]=d").unwrap(),
            r#"{"a":{"b":["c","d"]}}"#);
    }

    #[test]
    fn it_allows_to_specify_array_indexes() {
        eq_str(parse("a[0][]=c&a[1][]=d").unwrap(),
            r#"{"a":[["c"],["d"]]}"#);
    }

    #[test]
    fn it_transforms_arrays_to_object() {
        eq_str(parse("foo[0]=bar&foo[bad]=baz").unwrap(),
            r#"{"foo":{"0":"bar","bad":"baz"}}"#);

        eq_str(parse("foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb").unwrap(),
            r#"{"foo":[{"a":"a","b":"b"},{"a":"aa","b":"bb"}]}"#);
    }

    #[test]
    fn it_doesnt_produce_empty_keys() {
        eq_str(parse("_r=1&").unwrap(),
            r#"{"_r":"1"}"#);
    }

    #[test]
    fn it_supports_encoded_strings() {
        eq_str(parse("a[b%20c]=c%20d").unwrap(),
            r#"{"a":{"b c":"c d"}}"#);
    }

    #[test]
    fn it_parses_explicit_encoded_array() {
        eq_str(parse("a%5B%5D=b&a%5B%5D=c&a%5B%5D=d").unwrap(),
            r#"{"a":["b","c","d"]}"#);
    }
}
<commit_msg>test(parser): Add test case for intended behaviour<commit_after>#[cfg(feature = "regex1")]
use regex::Regex;
use serde_json::{Value, Map, Number};
use url::percent_encoding::percent_decode;

use merge::merge;
use helpers::{create_array, push_item_to_array};

#[cfg(feature = "regex1")]
lazy_static! {
    static ref PARENT_REGEX: Regex = Regex::new(r"^([^\]\[]+)").unwrap();
    static ref CHILD_REGEX: Regex = Regex::new(r"(\[[^\]\[]*\])").unwrap();
}

type Object = Map<String, Value>;

#[derive(Debug)]
#[allow(missing_copy_implementations)]
pub enum ParseErrorKind {
    DecodingError,
    Other
}

#[derive(Debug)]
pub struct ParseError {
    pub kind: ParseErrorKind,
    pub message: String
}

pub type ParseResult<T> = Result<T,ParseError>;

pub fn decode_component(source: &str) -> Result<String,String> {
    let result = percent_decode(source.as_bytes()).decode_utf8_lossy().to_string();
    return Ok(result);
}

fn parse_pairs(body: &str) -> Vec<(&str, Option<&str>)> {

    let mut pairs = vec![];

    for part in body.split("&") {
        let separator = part.find("]=")
                            .and_then(|pos| Some(pos+1))
                            .or_else(|| part.find("="));

        match separator {
            None => pairs.push((part, None)),
            Some(pos) => {
                let key = &part[..pos];
                let val = &part[(pos + 1)..];
                pairs.push((key, Some(val)));
            }
        }
    }

    return pairs
}

#[cfg(feature = "regex1")]
fn parse_key(key: &str) -> ParseResult<Vec<String>> {
    let mut keys: Vec<String> = vec![];

    match PARENT_REGEX.captures(key) {
        Some(captures) => {
            match decode_component(captures.get(1).unwrap().as_str()) {
                Ok(decoded_key) => keys.push(decoded_key),
                Err(err_msg) => return Err(ParseError{ kind: ParseErrorKind::DecodingError, message: err_msg })
            }
        }
        None => ()
    };

    for captures in CHILD_REGEX.captures_iter(key) {
        match decode_component(captures.get(1).unwrap().as_str()) {
            Ok(decoded_key) => keys.push(decoded_key),
            Err(err_msg) => return Err(ParseError{ kind: ParseErrorKind::DecodingError, message: err_msg })
        }
    }

    Ok(keys)
}

#[cfg(not(feature = "regex1"))]
fn parse_key(key: &str) -> ParseResult<Vec<String>> {
    let mut keys: Vec<String> = vec![];

    match key.split(|c| c=='[' || c==']').next() {
        Some(parent) if !parent.is_empty() =>  {
            match decode_component(parent) {
                Ok(decoded_key) => keys.push(decoded_key),
                Err(err_msg) => return Err(ParseError{ kind: ParseErrorKind::DecodingError, message: err_msg })
            }
        }
        _ => ()
    }

    let mut prev_bracket = None;
    for (idx, ch) in key.char_indices() {
        match ch {
            '[' => prev_bracket = Some(idx),
            ']' => {
                if let Some(prev_idx) = prev_bracket {
                    prev_bracket = None;
                    let child = &key[prev_idx..=idx];
                    match decode_component(child) {
                        Ok(decoded_key) => keys.push(decoded_key),
                        Err(err_msg) => return Err(ParseError{ kind: ParseErrorKind::DecodingError, message: err_msg })
                    }
                }
            }
            _ => (),
        }
    }

    Ok(keys)
}

fn cleanup_key(key: &str) -> &str {
    if key.starts_with("[") && key.ends_with("]") {
        &key[1..(key.len()-1)]
    } else {
        key
    }
}

fn create_idx_merger(idx: u64, obj: Value) -> Value {
    let mut tree = Object::new();
    tree.insert("__idx".to_string(), Value::Number(Number::from(idx)));
    tree.insert("__object".to_string(), obj);
    return Value::Object(tree)
}

fn create_object_with_key(key: String, obj: Value) -> Value {
    let mut tree = Object::new();
    tree.insert(key, obj);
    return Value::Object(tree)
}

fn apply_object(keys: &[String], val: Value) -> Value {

    if keys.len() > 0 {
        let key = keys.get(0).unwrap();
        if key == "[]" {
            let mut new_array = create_array();
            let item = apply_object(&keys[1..], val);
            push_item_to_array(&mut new_array, item);
            return new_array;
        } else {
            let key = cleanup_key(key);
            let array_index = key.parse();

            match array_index {
                Ok(idx) => {
                    let result = apply_object(&keys[1..], val);
                    let item = create_idx_merger(idx, result);
                    return item;
                },
                Err(_) => {
                    return create_object_with_key(key.to_string(), apply_object(&keys[1..], val));
                }
            }
        }

    } else {
        return val;
    }
}

pub fn parse(params: &str) -> ParseResult<Value> {
    let tree = Object::new();
    let mut obj = Value::Object(tree);
    let decoded_params = match decode_component(params) {
        Ok(val) => val,
        Err(err) => return Err(ParseError{ kind: ParseErrorKind::DecodingError, message: err })
    };
    let pairs = parse_pairs(&decoded_params);
    for &(key, value) in pairs.iter() {
        let parse_key_res = try!(parse_key(key));
        let key_chain = &parse_key_res[0..];
        let decoded_value = match decode_component(value.unwrap_or("")) {
            Ok(val) => val,
            Err(err) => return Err(ParseError{ kind: ParseErrorKind::DecodingError, message: err })
        };
        let partial = apply_object(key_chain, Value::String(decoded_value));
        merge(&mut obj, &partial);
    }

    Ok(obj)
}

#[cfg(test)]
mod tests {
    use parse;
    use serde_json::{Value, to_string};

    fn eq_str(value: Value, string: &str) {
        assert_eq!(&to_string(&value).unwrap(), string)
    }

    #[test]
    fn it_parses_simple_string() {
        eq_str(parse("0=foo").unwrap(), r#"{"0":"foo"}"#);
        eq_str(parse("a[<=>]==23").unwrap(), r#"{"a":{"<=>":"=23"}}"#);
        eq_str(parse(" foo = bar = baz ").unwrap(), r#"{" foo ":" bar = baz "}"#);
    }

    #[test]
    fn it_parses_nested_string() {
        eq_str(parse("a[b][c][d][e][f][g][h]=i").unwrap(),
            r#"{"a":{"b":{"c":{"d":{"e":{"f":{"g":{"h":"i"}}}}}}}}"#);
    }

    #[test]
    fn it_parses_simple_array() {
        eq_str(parse("a=b&a=c&a=d&a=e").unwrap(),
            r#"{"a":["b","c","d","e"]}"#);
    }

    #[test]
    fn it_parses_explicit_array() {
        eq_str(parse("a[]=b&a[]=c&a[]=d").unwrap(),
            r#"{"a":["b","c","d"]}"#);
    }

    #[test]
    fn it_parses_nested_array() {
        eq_str(parse("a[b][]=c&a[b][]=d").unwrap(),
            r#"{"a":{"b":["c","d"]}}"#);
    }

    #[test]
    fn it_allows_to_specify_array_indexes() {
        eq_str(parse("a[0][]=c&a[1][]=d").unwrap(),
            r#"{"a":[["c"],["d"]]}"#);
    }

    #[test]
    fn it_transforms_arrays_to_object() {
        eq_str(parse("foo[0]=bar&foo[bad]=baz").unwrap(),
            r#"{"foo":{"0":"bar","bad":"baz"}}"#);

        eq_str(parse("foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb").unwrap(),
            r#"{"foo":[{"a":"a","b":"b"},{"a":"aa","b":"bb"}]}"#);
    }

    #[test]
    fn it_transforms_standalone_keys() {
        eq_str(parse("foo=bar&baz").unwrap(),
            r#"{"foo":"bar","baz":null}"#);
    }

    #[test]
    fn it_doesnt_produce_empty_keys() {
        eq_str(parse("_r=1&").unwrap(),
            r#"{"_r":"1"}"#);
    }

    #[test]
    fn it_supports_encoded_strings() {
        eq_str(parse("a[b%20c]=c%20d").unwrap(),
            r#"{"a":{"b c":"c d"}}"#);
    }

    #[test]
    fn it_parses_explicit_encoded_array() {
        eq_str(parse("a%5B%5D=b&a%5B%5D=c&a%5B%5D=d").unwrap(),
            r#"{"a":["b","c","d"]}"#);
    }
}
<|endoftext|>


<commit_before>use super::lexer::{Lexer, LexToken, LexError};
use super::position::Position;
use std::io::prelude::*;

#[derive(Debug, PartialEq, Clone)]
pub enum ParseEvent {
    BeginFile,
    EndOfFile,
    NodeStart(String),
    NodeEnd,
}

#[derive(Debug)]
pub enum ParseError {
    InvalidState,
    NotYetImplemented,
    LexError(LexError),
    UnexpectedEndOfFile,
    UnexpectedToken(LexToken),
}

#[derive(Debug, PartialEq, Clone)]
pub enum ParseContext {
    Basefile,
    Node
}

type ContextStack = Vec<ParseContext>;
pub type ParseResult = Result<(ParseEvent, Position), (ParseError, Position)>;

pub struct Parser<R: Read> {
    context: ContextStack,
    ended: bool,
    lexer: Lexer<R>,
}

impl<R: Read> Parser<R> {
    pub fn parse(lexer: Lexer<R>) -> Self {
        Parser {
            context: ContextStack::new(),
            ended: false,
            lexer: lexer,
        }
    }

    fn lex_error(&mut self, error: LexError) -> Option<ParseResult> {
        self.yield_error(ParseError::LexError(error))
    }

    fn parse_context_file(&mut self) -> Option<ParseResult> {
        let next = self.lexer.next();
        if let Some(Ok(LexToken::Identifier(ident))) = next {
            let next = self.lexer.next();
            match next {
                Some(Ok(LexToken::OpenBrace)) => {
                    self.context.push(ParseContext::Node);
                    self.yield_state(ParseEvent::NodeStart(ident))
                }
                Some(Ok(tok)) =>
                    self.yield_error(ParseError::UnexpectedToken(tok)),
                Some(Err(err)) =>
                    self.yield_error(ParseError::LexError(err)),
                None =>
                    self.yield_error(ParseError::UnexpectedEndOfFile),
            }
        } else if let Some(Ok(tok)) = next {
            self.yield_error(ParseError::UnexpectedToken(tok))
        } else if let Some(Err(next)) = next {
            self.lex_error(next)
        } else {
            assert_eq!(self.context.pop(), Some(ParseContext::Basefile));
            self.ended = true;
            self.yield_state(ParseEvent::EndOfFile)
        }
    }

    fn parse_context_node(&mut self) -> Option<ParseResult> {
        let next = self.lexer.next();
        match next {
            Some(Ok(LexToken::CloseBrace)) => {
                assert_eq!(self.context.pop(), Some(ParseContext::Node));
                self.yield_state(ParseEvent::NodeEnd)
            },
            Some(Ok(tok)) => {
                self.yield_error(ParseError::NotYetImplemented)
            },
            Some(Err(err)) => {
                self.yield_error(ParseError::LexError(err))
            },
            None => {
                self.yield_error(ParseError::UnexpectedEndOfFile)
            }
        }
    }

    fn yield_state(&mut self, state: ParseEvent) -> Option<ParseResult> {
        Some(Ok((state, self.lexer.position.clone())))
    }

    fn yield_error(&mut self, error: ParseError) -> Option<ParseResult> {
        self.ended = true;
        Some(Err((error, self.lexer.position.clone())))
    }
}

impl<R: Read> Iterator for Parser<R> {
    type Item = ParseResult;

    fn next(&mut self) -> Option<Self::Item> {
        if self.ended { return None; }

        let current_state = self.context.pop();
        match current_state {
            None => {
                self.context.push(ParseContext::Basefile);
                self.yield_state(ParseEvent::BeginFile)
            }
            Some(ParseContext::Basefile) => {
                self.context.push(current_state.unwrap());
                self.parse_context_file()
            },
            Some(ParseContext::Node) => {
                self.context.push(current_state.unwrap());
                self.parse_context_node()
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::lexer::Lexer;
    use std::io::Cursor;

    #[test]
    fn handle_empty_file() {
        let file = Cursor::new("".as_bytes());
        let mut parser = Parser::parse(Lexer::lex(file));
        assert_eq!(parser.next().unwrap().unwrap().0, ParseEvent::BeginFile);
        assert_eq!(parser.next().unwrap().unwrap().0, ParseEvent::EndOfFile);
        assert!(parser.next().is_none());
    }

    #[test]
    fn handle_node() {
        let file = Cursor::new("node { }".as_bytes());
        let mut parser = Parser::parse(Lexer::lex(file));
        assert_eq!(parser.next().unwrap().unwrap().0, ParseEvent::BeginFile);
        println!("BeginFile");
        assert_eq!(parser.next().unwrap().unwrap().0, ParseEvent::NodeStart("node".to_string()));
        println!("NodeStart");
        assert_eq!(parser.next().unwrap().unwrap().0, ParseEvent::NodeEnd);
        println!("NodeEnd");
        assert_eq!(parser.next().unwrap().unwrap().0, ParseEvent::EndOfFile);
        println!("EndOfFile");
        assert!(parser.next().is_none());
    }
}
<commit_msg>Use booleans to represent whether a comma has been reached<commit_after>use super::lexer::{Lexer, LexToken, LexError};
use super::position::Position;
use std::io::prelude::*;

#[derive(Debug, PartialEq, Clone)]
pub enum ParseEvent {
    BeginFile,
    EndOfFile,
    NodeStart(String),
    NodeEnd,
}

#[derive(Debug)]
pub enum ParseError {
    InvalidState,
    NotYetImplemented,
    LexError(LexError),
    UnexpectedEndOfFile,
    UnexpectedToken(LexToken),
}

#[derive(Debug, PartialEq, Clone)]
pub enum ParseContext {
    Basefile,
    Node(bool)
}

type ContextStack = Vec<ParseContext>;
pub type ParseResult = Result<(ParseEvent, Position), (ParseError, Position)>;

pub struct Parser<R: Read> {
    context: ContextStack,
    ended: bool,
    lexer: Lexer<R>,
}

impl<R: Read> Parser<R> {
    pub fn parse(lexer: Lexer<R>) -> Self {
        Parser {
            context: ContextStack::new(),
            ended: false,
            lexer: lexer,
        }
    }

    fn lex_error(&mut self, error: LexError) -> Option<ParseResult> {
        self.yield_error(ParseError::LexError(error))
    }

    fn parse_context_file(&mut self) -> Option<ParseResult> {
        let next = self.lexer.next();
        if let Some(Ok(LexToken::Identifier(ident))) = next {
            let next = self.lexer.next();
            match next {
                Some(Ok(LexToken::OpenBrace)) => {
                    self.context.push(ParseContext::Node(true));
                    self.yield_state(ParseEvent::NodeStart(ident))
                }
                Some(Ok(tok)) =>
                    self.yield_error(ParseError::UnexpectedToken(tok)),
                Some(Err(err)) =>
                    self.yield_error(ParseError::LexError(err)),
                None =>
                    self.yield_error(ParseError::UnexpectedEndOfFile),
            }
        } else if let Some(Ok(tok)) = next {
            self.yield_error(ParseError::UnexpectedToken(tok))
        } else if let Some(Err(next)) = next {
            self.lex_error(next)
        } else {
            self.ended = true;
            self.yield_state(ParseEvent::EndOfFile)
        }
    }

    fn parse_context_node(&mut self) -> Option<ParseResult> {
        let next = self.lexer.next();
        match next {
            Some(Ok(LexToken::CloseBrace)) => {
                self.context.pop();
                self.yield_state(ParseEvent::NodeEnd)
            },
            Some(Ok(tok)) => {
                self.yield_error(ParseError::NotYetImplemented)
            },
            Some(Err(err)) => {
                self.yield_error(ParseError::LexError(err))
            },
            None => {
                self.yield_error(ParseError::UnexpectedEndOfFile)
            }
        }
    }

    fn parse_context_node_no_comma(&mut self) -> Option<ParseResult> {
        let next = self.lexer.next();
        None
    }

    fn yield_state(&mut self, state: ParseEvent) -> Option<ParseResult> {
        Some(Ok((state, self.lexer.position.clone())))
    }

    fn yield_error(&mut self, error: ParseError) -> Option<ParseResult> {
        self.ended = true;
        Some(Err((error, self.lexer.position.clone())))
    }
}

impl<R: Read> Iterator for Parser<R> {
    type Item = ParseResult;

    fn next(&mut self) -> Option<Self::Item> {
        if self.ended { return None; }

        let current_state = self.context.pop();
        println!("state: {:?}", current_state);
        match current_state {
            None => {
                self.context.push(ParseContext::Basefile);
                self.yield_state(ParseEvent::BeginFile)
            }
            Some(ParseContext::Basefile) => {
                self.context.push(current_state.unwrap());
                self.parse_context_file()
            },
            Some(ParseContext::Node(true)) => {
                self.context.push(current_state.unwrap());
                self.parse_context_node()
            },
            Some(ParseContext::Node(false)) => {
                self.context.push(current_state.unwrap());
                self.parse_context_node_no_comma()
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::lexer::Lexer;
    use std::io::Cursor;

    #[test]
    fn handle_empty_file() {
        let file = Cursor::new("".as_bytes());
        let mut parser = Parser::parse(Lexer::lex(file));
        assert_eq!(parser.next().unwrap().unwrap().0, ParseEvent::BeginFile);
        assert_eq!(parser.next().unwrap().unwrap().0, ParseEvent::EndOfFile);
        assert!(parser.next().is_none());
    }

    #[test]
    fn handle_node() {
        let file = Cursor::new("node { }".as_bytes());
        let mut parser = Parser::parse(Lexer::lex(file));
        assert_eq!(parser.next().unwrap().unwrap().0, ParseEvent::BeginFile);
        println!("BeginFile");
        assert_eq!(parser.next().unwrap().unwrap().0, ParseEvent::NodeStart("node".to_string()));
        println!("NodeStart");
        assert_eq!(parser.next().unwrap().unwrap().0, ParseEvent::NodeEnd);
        println!("NodeEnd");
        assert_eq!(parser.next().unwrap().unwrap().0, ParseEvent::EndOfFile);
        println!("EndOfFile");
        assert!(parser.next().is_none());
    }
}
<|endoftext|>


<commit_before>use std;
use std::io::{Error, ErrorKind};
use std::io::Read;
use std::net::TcpStream;

/// Reads bytes from the TCP stream until CRLF is found or the given limit is
/// reached.
pub fn read_line(stream: &TcpStream, limit: usize) -> std::io::Result<String> {
  let mut res = vec!();
  for (i, value) in stream.bytes().enumerate() {
    let byte = try!(value);
    if byte == '\n' as u8 {
      // The end of the line. Make sure it actually was CRLF.
      let second_to_last = res.pop().unwrap_or(0);
      if second_to_last != '\r' as u8 {
        return Err(Error::new(ErrorKind::Other, "LF without CR."));
      }

      return match String::from_utf8(res) {
        Ok(s) => Ok(s),
        Err(_) => Err(Error::new(ErrorKind::Other, "Invalid UTF8.")),
      }
    }

    // Add the byte to this line.
    res.push(byte);

    // Check the limit.
    if i == limit {
      return Err(Error::new(ErrorKind::Other, "Line too long."));
    }
  }

  Err(Error::new(ErrorKind::Other, "Unexpected end of stream."))
}

/// Reads four bytes from the stream and combines them together to form a 32 bit
/// unsigned integer.
pub fn read_u32(stream: &TcpStream) -> std::io::Result<u32> {
  let mut buf: Vec<u8> = vec![];
  let read = try!(stream.take(4).read_to_end(&mut buf));

  if read < 4 {
    return Err(Error::new(ErrorKind::Other, "Unexpected end of stream."));
  }

  Ok(buf.iter().enumerate().fold(0, |result, (i, value)| {
    result | ((*value as u32) << (3 - i) * 8)
  }))
}

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

  #[test]
  fn test_readline_basic() {
    let stream = create_tcp_stream("foo\r\n".as_bytes());
    assert_eq!(read_line(&stream, 255).unwrap(), "foo");
  }

  #[test]
  fn test_readline_empty_line() {
    let stream = create_tcp_stream("\r\n".as_bytes());
    assert_eq!(read_line(&stream, 255).unwrap(), "");
  }

  #[test]
  fn test_readline_multiple_lines_reads_only_first() {
    let stream = create_tcp_stream("bar\r\nfoo\r\n".as_bytes());
    assert_eq!(read_line(&stream, 255).unwrap(), "bar");
    assert_eq!(read_line(&stream, 255).unwrap(), "foo");
  }

  #[test]
  fn test_readline_line_too_long() {
    let stream = create_tcp_stream("barbazfoo\r\n".as_bytes());
    assert!(read_line(&stream, 5).is_err());
  }

  #[test]
  fn test_readline_line_at_limit() {
    let stream = create_tcp_stream("123\r\n".as_bytes());
    assert_eq!(read_line(&stream, 5).unwrap(), "123");
  }

  #[test]
  fn test_readline_no_cr_before_lf() {
    let stream = create_tcp_stream("barbazfoo\n".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_no_crlf() {
    let stream = create_tcp_stream("barbazfoo".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_no_lf() {
    let stream = create_tcp_stream("barb\razfoo".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_empty() {
    let stream = create_tcp_stream("".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_only_lf() {
    let stream = create_tcp_stream("\n".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_read_u32_basic() {
    let stream = create_tcp_stream(&[0x29, 0xb7, 0xf4, 0xaa, 0xff, 0xff]);
    assert_eq!(read_u32(&stream).unwrap(), 0x29b7f4aa);
  }

  #[test]
  fn test_read_u32_basic_zeroes() {
    let stream = create_tcp_stream(&[0x29, 0x00, 0xf4, 0xaa, 0xff, 0xff]);
    assert_eq!(read_u32(&stream).unwrap(), 0x2900f4aa);
  }

  #[test]
  fn test_read_u32_too_little_data() {
    let stream = create_tcp_stream(&[0x29, 0xb7, 0xf4]);
    assert!(read_u32(&stream).is_err());
  }
}
<commit_msg>Simplify the read_line() function.<commit_after>use std;
use std::io::prelude::*;
use std::io::{Error, ErrorKind};
use std::net::TcpStream;

/// Reads bytes from the TCP stream until CRLF is found or the given limit is
/// reached.
pub fn read_line(stream: &TcpStream, limit: usize) -> std::io::Result<String> {
  let mut result = String::with_capacity(limit);

  for byte in stream.take(limit as u64).bytes() {
    let raw = try!(byte);
    let chr = raw as char;

    result.push(chr);

    if chr == '\n' {
      break;
    }
  }

  if result.ends_with("\r\n") {
    // .truncate() panics if the new length is not at char boundary. Since we
    // know the string ends with \r\n, that won't happen.
    let new_len = result.len() - 2;
    result.truncate(new_len);

    // Return the result.
    Ok(result)
  } else if result.len() == limit {
    // The line did not fit in the given limit.
    Err(Error::new(ErrorKind::Other, "Line too long."))
  } else {
    // The line ending was invalid or missing.
    Err(Error::new(ErrorKind::Other, "The line must end in CRLF sequence."))
  }
}

/// Reads four bytes from the stream and combines them together to form a 32 bit
/// unsigned integer.
pub fn read_u32(stream: &TcpStream) -> std::io::Result<u32> {
  let mut buf: Vec<u8> = vec![];
  let read = try!(stream.take(4).read_to_end(&mut buf));

  if read < 4 {
    return Err(Error::new(ErrorKind::Other, "Unexpected end of stream."));
  }

  Ok(buf.iter().enumerate().fold(0, |result, (i, value)| {
    result | ((*value as u32) << (3 - i) * 8)
  }))
}

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

  #[test]
  fn test_readline_basic() {
    let stream = create_tcp_stream("foo\r\n".as_bytes());
    assert_eq!(read_line(&stream, 255).unwrap(), "foo");
  }

  #[test]
  fn test_readline_empty_line() {
    let stream = create_tcp_stream("\r\n".as_bytes());
    assert_eq!(read_line(&stream, 255).unwrap(), "");
  }

  #[test]
  fn test_readline_multiple_lines_reads_only_first() {
    let stream = create_tcp_stream("bar\r\nfoo\r\n".as_bytes());
    assert_eq!(read_line(&stream, 255).unwrap(), "bar");
    assert_eq!(read_line(&stream, 255).unwrap(), "foo");
  }

  #[test]
  fn test_readline_line_too_long() {
    let stream = create_tcp_stream("barbazfoo\r\n".as_bytes());
    assert!(read_line(&stream, 5).is_err());
  }

  #[test]
  fn test_readline_line_at_limit() {
    let stream = create_tcp_stream("123\r\n".as_bytes());
    assert_eq!(read_line(&stream, 5).unwrap(), "123");
  }

  #[test]
  fn test_readline_no_cr_before_lf() {
    let stream = create_tcp_stream("barbazfoo\n".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_no_crlf() {
    let stream = create_tcp_stream("barbazfoo".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_no_lf() {
    let stream = create_tcp_stream("barb\razfoo".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_empty() {
    let stream = create_tcp_stream("".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_readline_only_lf() {
    let stream = create_tcp_stream("\n".as_bytes());
    assert!(read_line(&stream, 255).is_err());
  }

  #[test]
  fn test_read_u32_basic() {
    let stream = create_tcp_stream(&[0x29, 0xb7, 0xf4, 0xaa, 0xff, 0xff]);
    assert_eq!(read_u32(&stream).unwrap(), 0x29b7f4aa);
  }

  #[test]
  fn test_read_u32_basic_zeroes() {
    let stream = create_tcp_stream(&[0x29, 0x00, 0xf4, 0xaa, 0xff, 0xff]);
    assert_eq!(read_u32(&stream).unwrap(), 0x2900f4aa);
  }

  #[test]
  fn test_read_u32_too_little_data() {
    let stream = create_tcp_stream(&[0x29, 0xb7, 0xf4]);
    assert!(read_u32(&stream).is_err());
  }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;

use controls::Control;
use protocol::LdapResultExt;

use lber::structures::Tag;

/// Common components of an LDAP operation result.
///
/// This structure faithfully replicates the components dictated by the standard,
/// and is distinctly C-like with its reliance on numeric codes for the indication
/// of outcome. It would be tempting to hide it behind an automatic `Result`-like
/// interface, but there are scenarios where this would preclude intentional
/// incorporation of error conditions into query design.
///
/// A series of helper methods to provide idiomatic error handling is planned.
///
/// __Note__: this structure will probably be extended with the vector of
/// response controls in version 0.5.x, since controls are always part of the
/// response message.
#[derive(Clone, Debug)]
pub struct LdapResult {
    /// Result code.
    ///
    /// Generally, the value of zero indicates successful completion, but there's
    /// a number of other non-error codes arising as a result of various operations.
    /// See [Section A.1 of RFC 4511](https://tools.ietf.org/html/rfc4511#appendix-A.1).
    pub rc: u32,
    /// Matched component DN, where applicable.
    pub matched: String,
    /// Additional diagnostic text.
    pub text: String,
    /// Referrals.
    ///
    /// In the current implementation, all referrals received during a Search
    /// operation will be accumulated in this vector.
    pub refs: Vec<HashSet<String>>,
    /// Response controls.
    ///
    /// Missing and empty controls are both represented by an empty vector.
    pub ctrls: Vec<Control>,
}

#[doc(hidden)]
impl From<Tag> for LdapResult {
    fn from(t: Tag) -> LdapResult {
        <LdapResultExt as From<Tag>>::from(t).0
    }
}
<commit_msg>Add Error impl, helpers to LdapResult<commit_after>use std::collections::HashSet;
use std::error::Error;
use std::fmt;
use std::io;
use std::result::Result;

use controls::Control;
use protocol::LdapResultExt;

use lber::structures::Tag;

/// Common components of an LDAP operation result.
///
/// This structure faithfully replicates the components dictated by the standard,
/// and is distinctly C-like with its reliance on numeric codes for the indication
/// of outcome. It would be tempting to hide it behind an automatic `Result`-like
/// interface, but there are scenarios where this would preclude intentional
/// incorporation of error conditions into query design. Instead, the struct
/// implements helper methods, [`success()`](#method.success) and [`non_error()`]
/// (#method.non_error), which may be used for ergonomic error handling when
/// simple condition checking suffices.
#[derive(Clone, Debug)]
pub struct LdapResult {
    /// Result code.
    ///
    /// Generally, the value of zero indicates successful completion, but there's
    /// a number of other non-error codes arising as a result of various operations.
    /// See [Section A.1 of RFC 4511](https://tools.ietf.org/html/rfc4511#appendix-A.1).
    pub rc: u32,
    /// Matched component DN, where applicable.
    pub matched: String,
    /// Additional diagnostic text.
    pub text: String,
    /// Referrals.
    ///
    /// In the current implementation, all referrals received during a Search
    /// operation will be accumulated in this vector.
    pub refs: Vec<HashSet<String>>,
    /// Response controls.
    ///
    /// Missing and empty controls are both represented by an empty vector.
    pub ctrls: Vec<Control>,
}

#[doc(hidden)]
impl From<Tag> for LdapResult {
    fn from(t: Tag) -> LdapResult {
        <LdapResultExt as From<Tag>>::from(t).0
    }
}

impl Error for LdapResult {
    fn description(&self) -> &'static str {
	match self.rc {
	    0 => "success",
	    1 => "operationsError",
	    2 => "protocolError",
	    3 => "timeLimitExceeded",
	    4 => "sizeLimitExceeded",
	    5 => "compareFalse",
	    6 => "compareTrue",
	    7 => "authMethodNotSupported",
	    8 => "strongerAuthRequired",
	    10 => "referral",
	    11 => "adminLimitExceeded",
	    12 => "unavailableCriticalExtension",
	    13 => "confidentialityRequired",
	    14 => "saslBindInProgress",
	    16 => "noSuchAttribute",
	    17 => "undefinedAttributeType",
	    18 => "inappropriateMatching",
	    19 => "constraintViolation",
	    20 => "attributeOrValueExists",
	    21 => "invalidAttributeSyntax",
	    32 => "noSuchObject",
	    33 => "aliasProblem",
	    34 => "invalidDNSyntax",
	    36 => "aliasDereferencingProblem",
	    48 => "inappropriateAuthentication",
	    49 => "invalidCredentials",
	    50 => "insufficientAccessRights",
	    51 => "busy",
	    52 => "unavailable",
	    53 => "unwillingToPerform",
	    54 => "loopDetect",
	    64 => "namingViolation",
	    65 => "objectClassViolation",
	    66 => "notAllowedOnNonLeaf",
	    67 => "notAllowedOnRDN",
	    68 => "entryAlreadyExists",
	    69 => "objectClassModsProhibited",
	    71 => "affectsMultipleDSAs",
	    80 => "other",
	    88 => "abandoned",
	    _ => "unknown",
	}
    }
}

impl fmt::Display for LdapResult {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
	write!(f,
	    "rc={} ({}), dn: \"{}\", text: \"{}\"",
	    self.rc,
	    self.description(),
	    self.matched,
            self.text
        )
    }
}

impl LdapResult {
    /// If the result code is zero, return the instance itself wrapped
    /// in `Ok()`, otherwise wrap the instance in an `io::Error`.
    pub fn success(self) -> Result<Self, io::Error> {
        if self.rc == 0 {
            Ok(self)
        } else {
            Err(io::Error::new(io::ErrorKind::Other, self))
        }
    }

    /// If the result code is 0 or 10 (referral), return the instance
    /// itself wrapped in `Ok()`, otherwise wrap the instance in an
    /// io::Error`.
    pub fn non_error(self) -> Result<Self, io::Error> {
        if self.rc == 0 || self.rc == 10 {
            Ok(self)
        } else {
            Err(io::Error::new(io::ErrorKind::Other, self))
        }
    }
}
<|endoftext|>


<commit_before>use std::result;
use std::convert::From;
use std::ffi::NulError;

#[derive(Debug, PartialEq)]
pub enum Error {
    InvalidCString(NulError),
    DatabaseError(String),
}

#[derive(Debug)]
pub enum ConnectionError {
    InvalidCString(NulError),
    BadConnection(String),
}

pub type Result<T> = result::Result<T, Error>;
pub type ConnectionResult<T> = result::Result<T, ConnectionError>;

impl From<NulError> for ConnectionError {
    fn from(e: NulError) -> Self {
        ConnectionError::InvalidCString(e)
    }
}

impl From<NulError> for Error {
    fn from(e: NulError) -> Self {
        Error::InvalidCString(e)
    }
}
<commit_msg>Implement `Error` for our errors<commit_after>use std::result;
use std::convert::From;
use std::error::Error as StdError;
use std::fmt::{self, Display, Write};
use std::ffi::NulError;

#[derive(Debug, PartialEq)]
pub enum Error {
    InvalidCString(NulError),
    DatabaseError(String),
}

#[derive(Debug)]
pub enum ConnectionError {
    InvalidCString(NulError),
    BadConnection(String),
}

pub type Result<T> = result::Result<T, Error>;
pub type ConnectionResult<T> = result::Result<T, ConnectionError>;

impl From<NulError> for ConnectionError {
    fn from(e: NulError) -> Self {
        ConnectionError::InvalidCString(e)
    }
}

impl From<NulError> for Error {
    fn from(e: NulError) -> Self {
        Error::InvalidCString(e)
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error::InvalidCString(ref nul_err) => nul_err.fmt(f),
            &Error::DatabaseError(ref s) => write!(f, "{}", &s),
        }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match self {
            &Error::InvalidCString(ref nul_err) => nul_err.description(),
            &Error::DatabaseError(ref s) => &s,
        }
    }
}

impl Display for ConnectionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &ConnectionError::InvalidCString(ref nul_err) => nul_err.fmt(f),
            &ConnectionError::BadConnection(ref s) => write!(f, "{}", &s),
        }
    }
}

impl StdError for ConnectionError {
    fn description(&self) -> &str {
        match self {
            &ConnectionError::InvalidCString(ref nul_err) => nul_err.description(),
            &ConnectionError::BadConnection(ref s) => &s,
        }
    }
}
<|endoftext|>


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

/// Equivalent to a `match` expression but for routes.
///
/// Here is an example usage:
///
/// ```no_run
/// # #[macro_use] extern crate rouille; fn main() {
/// # let request: rouille::Request = unsafe { std::mem::uninitialized() };
/// let _result = router!(request,
///     // first route
///     (GET) (/) => {
///         12
///     },
///
///     // ... other routes here ...
///
///     // default route
///     _ => 5
/// );
/// # }
/// ```
///
/// The macro will take each route one by one and execute the first one that matches, similar to a
/// `match` language construct. The whole `router!` expression then returns what the closure
/// returns, therefore they must all return the same type.
///
/// You can use parameters by putting them inside `{}`:
///
/// ```ignore
/// (GET) (/{id}/foo) => {
///     ...
/// },
/// ```
///
/// If you use parameters inside `{}`, then a field with the same name must exist in the closure's
/// parameters list. The parameters do not need be in the same order.
///
/// Each parameter gets parsed through the `FromStr` trait. If the parsing fails, the route is
/// ignored. If you get an error because the type of the parameter couldn't be inferred, you can
/// also specify the type inside the brackets:
///
/// ```ignore
/// (GET) (/{id: u32}/foo) => {
///     ...
/// },
/// ```
///
/// Some other things to note:
///
/// - The right of the `=>` must be a block (must be surrounded by `{` and `}`).
/// - The pattern of the URL and the closure must be inside parentheses. This is to bypass
///   limitations of Rust's macros system.
/// - The default handler (with `_`) must be present or will get a compilation error.
///
// FIXME: turn `: $pt:ident` into `ty`
// TODO: don't panic if parsing fails
#[macro_export]
macro_rules! router {
    ($request:expr, $(($method:ident) ($($pat:tt)+) => $value:block,)* _ => $def:expr) => {
        {
            let ref request = $request;

            // ignoring the GET parameters (everything after `?`)
            let request_url = request.url();
            let request_url = {
                let pos = request_url.find('?').unwrap_or(request_url.len());
                &request_url[..pos]
            };

            let mut ret = None;

            $({
                if ret.is_none() && request.method() == stringify!($method) {
                    ret = router!(__check_pattern request_url $value $($pat)+);
                }
            })+

            if let Some(ret) = ret {
                ret
            } else {
                $def
            }
        }
    };

    (__check_pattern $url:ident $value:block /{$p:ident} $($rest:tt)*) => (
        if !$url.starts_with('/') {
            None
        } else {
            let url = &$url[1..];
            let pat_end = url.find('/').unwrap_or(url.len());
            let rest_url = &url[pat_end..];

            if let Some($p) = url[0 .. pat_end].parse().ok() {
                router!(__check_pattern rest_url $value $($rest)*)
            } else {
                None
            }
        }
    );

    (__check_pattern $url:ident $value:block /{$p:ident: $t:ty} $($rest:tt)*) => (
        if !$url.starts_with('/') {
            None
        } else {
            let url = &$url[1..];
            let pat_end = url.find('/').unwrap_or(url.len());
            let rest_url = &url[pat_end..];

            if let Some($p) = url[0 .. pat_end].parse().ok() {
                let $p: $t = $p;
                router!(__check_pattern rest_url $value $($rest)*)
            } else {
                None
            }
        }
    );

    (__check_pattern $url:ident $value:block /$p:ident $($rest:tt)*) => (
        {
            let required = concat!("/", stringify!($p));
            if $url.starts_with(required) {
                let rest_url = &$url[required.len()..];
                router!(__check_pattern rest_url $value $($rest)*)
            } else {
                None
            }
        }
    );

    (__check_pattern $url:ident $value:block - $($rest:tt)*) => (
        {
            if $url.starts_with('-') {
                let rest_url = &$url[1..];
                router!(__check_pattern rest_url $value $($rest)*)
            } else {
                None
            }
        }
    );

    (__check_pattern $url:ident $value:block) => (
        if $url.len() == 0 { Some($value) } else { None }
    );

    (__check_pattern $url:ident $value:block /) => (
        if $url == "/" { Some($value) } else { None }
    );

    (__check_pattern $url:ident $value:block $p:ident $($rest:tt)*) => (
        {
            let required = stringify!($p);
            if $url.starts_with(required) {
                let rest_url = &$url[required.len()..];
                router!(__check_pattern rest_url $value $($rest)*)
            } else {
                None
            }
        }
    );
}
<commit_msg>Add some tests for the router<commit_after>// Copyright (c) 2016 The Rouille developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

/// Equivalent to a `match` expression but for routes.
///
/// Here is an example usage:
///
/// ```no_run
/// # #[macro_use] extern crate rouille; fn main() {
/// # let request: rouille::Request = unsafe { std::mem::uninitialized() };
/// let _result = router!(request,
///     // first route
///     (GET) (/) => {
///         12
///     },
///
///     // ... other routes here ...
///
///     // default route
///     _ => 5
/// );
/// # }
/// ```
///
/// The macro will take each route one by one and execute the first one that matches, similar to a
/// `match` language construct. The whole `router!` expression then returns what the closure
/// returns, therefore they must all return the same type.
///
/// You can use parameters by putting them inside `{}`:
///
/// ```ignore
/// (GET) (/{id}/foo) => {
///     ...
/// },
/// ```
///
/// If you use parameters inside `{}`, then a field with the same name must exist in the closure's
/// parameters list. The parameters do not need be in the same order.
///
/// Each parameter gets parsed through the `FromStr` trait. If the parsing fails, the route is
/// ignored. If you get an error because the type of the parameter couldn't be inferred, you can
/// also specify the type inside the brackets:
///
/// ```ignore
/// (GET) (/{id: u32}/foo) => {
///     ...
/// },
/// ```
///
/// Some other things to note:
///
/// - The right of the `=>` must be a block (must be surrounded by `{` and `}`).
/// - The pattern of the URL and the closure must be inside parentheses. This is to bypass
///   limitations of Rust's macros system.
/// - The default handler (with `_`) must be present or will get a compilation error.
///
// FIXME: turn `: $pt:ident` into `ty`
// TODO: don't panic if parsing fails
#[macro_export]
macro_rules! router {
    ($request:expr, $(($method:ident) ($($pat:tt)+) => $value:block,)* _ => $def:expr) => {
        {
            let ref request = $request;

            // ignoring the GET parameters (everything after `?`)
            let request_url = request.url();
            let request_url = {
                let pos = request_url.find('?').unwrap_or(request_url.len());
                &request_url[..pos]
            };

            let mut ret = None;

            $({
                if ret.is_none() && request.method() == stringify!($method) {
                    ret = router!(__check_pattern request_url $value $($pat)+);
                }
            })+

            if let Some(ret) = ret {
                ret
            } else {
                $def
            }
        }
    };

    (__check_pattern $url:ident $value:block /{$p:ident} $($rest:tt)*) => (
        if !$url.starts_with('/') {
            None
        } else {
            let url = &$url[1..];
            let pat_end = url.find('/').unwrap_or(url.len());
            let rest_url = &url[pat_end..];

            if let Some($p) = url[0 .. pat_end].parse().ok() {
                router!(__check_pattern rest_url $value $($rest)*)
            } else {
                None
            }
        }
    );

    (__check_pattern $url:ident $value:block /{$p:ident: $t:ty} $($rest:tt)*) => (
        if !$url.starts_with('/') {
            None
        } else {
            let url = &$url[1..];
            let pat_end = url.find('/').unwrap_or(url.len());
            let rest_url = &url[pat_end..];

            if let Some($p) = url[0 .. pat_end].parse().ok() {
                let $p: $t = $p;
                router!(__check_pattern rest_url $value $($rest)*)
            } else {
                None
            }
        }
    );

    (__check_pattern $url:ident $value:block /$p:ident $($rest:tt)*) => (
        {
            let required = concat!("/", stringify!($p));
            if $url.starts_with(required) {
                let rest_url = &$url[required.len()..];
                router!(__check_pattern rest_url $value $($rest)*)
            } else {
                None
            }
        }
    );

    (__check_pattern $url:ident $value:block - $($rest:tt)*) => (
        {
            if $url.starts_with('-') {
                let rest_url = &$url[1..];
                router!(__check_pattern rest_url $value $($rest)*)
            } else {
                None
            }
        }
    );

    (__check_pattern $url:ident $value:block) => (
        if $url.len() == 0 { Some($value) } else { None }
    );

    (__check_pattern $url:ident $value:block /) => (
        if $url == "/" { Some($value) } else { None }
    );

    (__check_pattern $url:ident $value:block $p:ident $($rest:tt)*) => (
        {
            let required = stringify!($p);
            if $url.starts_with(required) {
                let rest_url = &$url[required.len()..];
                router!(__check_pattern rest_url $value $($rest)*)
            } else {
                None
            }
        }
    );
}

#[cfg(test)]
mod tests {
    use Request;

    #[test]
    fn basic() {
        let request = Request::fake_http("GET", "/", vec![], vec![]);

        assert_eq!(1, router!(request,
            (GET) (/hello) => { 0 },
            (GET) (/{val:u32}) => { 0 },
            (GET) (/) => { 1 },
            _ => 0
        ));
    }

    #[test]
    fn dash() {
        let request = Request::fake_http("GET", "/a-b", vec![], vec![]);

        assert_eq!(1, router!(request,
            (GET) (/a/b) => { 0 },
            (GET) (/a_b) => { 0 },
            (GET) (/a-b) => { 1 },
            _ => 0
        ));
    }

    #[test]
    fn params() {
        let request = Request::fake_http("GET", "/hello/5", vec![], vec![]);

        assert_eq!(1, router!(request,
            (GET) (/hello/) => { 0 },
            (GET) (/hello/{id:u32}) => { if id == 5 { 1 } else { 0 } },
            (GET) (/hello/{id:String}) => { 0 },
            _ => 0
        ));
    }
}
<|endoftext|>


<commit_before>//!Router asigns handlers to paths and resolves them per request

#[cfg(test)]
use http::method;
use http::method::Method;
use regex::Regex;
use std::collections::hashmap::HashMap;
use request::Request;
use response::Response;

/// A Route is the basic data structure that stores both the path
/// and the handler that gets executed for the route.
/// The path can contain variable pattern such as `user/:userid/invoices`
pub struct Route {
    pub path: String,
    pub method: Method,
    pub handler: fn(request: &Request, response: &mut Response),
    pub variables: HashMap<String, uint>,
    matcher: Regex
}

impl Clone for Route {
    fn clone(&self) -> Route {
        Route {
            path: self.path.clone(),
            method: self.method.clone(),
            handler: self.handler,
            matcher: self.matcher.clone(),
            variables: self.variables.clone()
        }
    }
}

/// A RouteResult is what the router returns when `match_route` is called.
/// It contains the matched `route` and also a `params` property holding
/// a HashMap with the keys being the variable names and the value being the
/// evaluated string
pub struct RouteResult<'a> {
    pub route: &'a Route,
    pub params: HashMap<String, String>
}

/// The PathUtils collects some small helper methods that operate on the path
struct PathUtils;

// matches named variables (e.g. :userid)
static REGEX_VAR_SEQ: Regex                 = regex!(r":([a-zA-Z0-9_-]*)");
static VAR_SEQ:&'static str                 = "[a-zA-Z0-9_-]*";
static VAR_SEQ_WITH_SLASH:&'static str      = "[/a-zA-Z0-9_-]*";
static VAR_SEQ_WITH_CAPTURE:&'static str    = "([a-zA-Z0-9_-]*)";
// matches request params (e.g. ?foo=true&bar=false)
static REGEX_PARAM_SEQ:&'static str         = "[a-zA-Z0-9_=&?-]*";
static REGEX_START:&'static str             = "^";
static REGEX_END:&'static str               = "$";

impl PathUtils {
    fn create_regex (route_path: &str) -> Regex {

        let updated_path = route_path.to_string()
                                     // first mark all double wildcards for replacement. We can't directly replace them
                                     // since the replacement does contain the * symbol as well, which would get overwritten
                                     // with the next replace call
                                     .replace("**", "___DOUBLE_WILDCARD___")
                                     // then replace the regular wildcard symbols (*) with the appropriate regex
                                     .replace("*", VAR_SEQ)
                                     // now replace the previously marked double wild cards (**)
                                     .replace("___DOUBLE_WILDCARD___", VAR_SEQ_WITH_SLASH);

        // then replace the variable symbols (:variable) with the appropriate regex
        let result = REGEX_START.to_string()
                                .append(REGEX_VAR_SEQ.replace_all(updated_path.as_slice(), VAR_SEQ_WITH_CAPTURE).as_slice())
                                .append(REGEX_PARAM_SEQ)
                                .append(REGEX_END);

        match Regex::new(result.as_slice()) {
            Ok(re) => re,
            Err(err) => fail!("{}", err)
        }
    }

    fn get_variable_info (route_path: &str) -> HashMap<String, uint> {
        REGEX_VAR_SEQ.captures_iter(route_path)
             .enumerate()
             .map(|(i, matched)| (matched.at(1).to_string(), i))
             .collect()
    }
}

/// The Router's job is it to hold routes and to resolve them later against
/// concrete URLs

#[deriving(Clone)]
pub struct Router{
    pub routes: Vec<Route>,
}

impl Router {
    pub fn new () -> Router {
        Router {
            routes: Vec::new()
        }
    }

    pub fn add_route (&mut self, method: Method, path: String, handler: fn(request: &Request, response: &mut Response)) -> () {
        let matcher = PathUtils::create_regex(path.as_slice());
        let variable_infos = PathUtils::get_variable_info(path.as_slice());
        let route = Route {
            path: path,
            method: method,
            matcher: matcher,
            handler: handler,
            variables: variable_infos
        };
        self.routes.push(route);
    }

    pub fn match_route(&self, method: Method, path: String) -> Option<RouteResult> {
        self.routes.iter().find(|item| item.method == method && item.matcher.is_match(path.as_slice()))
            .and_then(|route| {
                match route.matcher.captures(path.as_slice()) {
                    Some(captures) => {
                        let mut map = HashMap::new();
                        for (name, pos) in route.variables.iter() {
                            map.insert(name.to_string(), captures.at(pos + 1).to_string());
                        }
                        Some(RouteResult {
                            route: route,
                            params: map
                        })
                    },
                    None => Some(RouteResult{
                        route: route,
                        params: HashMap::new()
                    })
                }
            })
    }
}

#[test]
fn creates_map_with_var_variable_infos () {
    let map = PathUtils::get_variable_info("foo/:uid/bar/:groupid");

    assert_eq!(map.len(), 2);
    assert_eq!(map.get(&"uid".to_string()), &0);
    assert_eq!(map.get(&"groupid".to_string()), &1);
}

#[test]
fn creates_regex_with_captures () {
    let regex = PathUtils::create_regex("foo/:uid/bar/:groupid");
    let caps = regex.captures("foo/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");

    let regex = PathUtils::create_regex("foo/*/:uid/bar/:groupid");
    let caps = regex.captures("foo/test/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");

    let regex = PathUtils::create_regex("foo/**/:uid/bar/:groupid");
    let caps = regex.captures("foo/test/another/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");
}

#[test]
fn creates_valid_regex_for_routes () {
    let regex1 = PathUtils::create_regex("foo/:uid/bar/:groupid");
    let regex2 = PathUtils::create_regex("foo/*/bar");
    let regex3 = PathUtils::create_regex("foo/**/bar");

    assert_eq!(regex1.is_match("foo/4711/bar/5490"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5490?foo=true&bar=false"), true);
    assert_eq!(regex1.is_match("foo/4711/bar"), false);
    assert_eq!(regex1.is_match("foo/4711/bar?foo=true&bar=false"), false);

    assert_eq!(regex2.is_match("foo/4711/bar"), true);
    assert_eq!(regex2.is_match("foo/4711/bar?foo=true&bar=false"), true);
    assert_eq!(regex2.is_match("foo/4711/4712/bar"), false);
    assert_eq!(regex2.is_match("foo/4711/4712/bar?foo=true&bar=false"), false);

    assert_eq!(regex3.is_match("foo/4711/bar"), true);
    assert_eq!(regex3.is_match("foo/4711/bar?foo=true&bar=false"), true);
    assert_eq!(regex3.is_match("foo/4711/4712/bar"), true);
    assert_eq!(regex3.is_match("foo/4711/4712/bar?foo=true&bar=false"), true);
}

#[test]
fn can_match_var_routes () {
    let route_store = &mut Router::new();

    fn handler (_request: &Request, response: &mut Response) -> () {
        let _ = response.origin.write("hello from foo".as_bytes());
    };

    route_store.add_route(method::Get, "/foo/:userid".to_string(), handler);
    route_store.add_route(method::Get, "/bar".to_string(), handler);

    let route_result = route_store.match_route(method::Get, "/foo/4711".to_string()).unwrap();
    let route = route_result.route;

    assert_eq!(route_result.params.get(&"userid".to_string()), &"4711".to_string());

    //assert the route has identified the variable
    assert_eq!(route.variables.len(), 1);
    assert_eq!(route.variables.get(&"userid".to_string()), &0);

    let route_result = route_store.match_route(method::Get, "/bar/4711".to_string());

    let result = match route_result {
        Some(_res) => true,
        None => false
    };

    assert_eq!(result, false);

    let route_result = route_store.match_route(method::Get, "/foo".to_string());

    let result = match route_result{
        Some(_res) => true,
        None => false
    };

    assert_eq!(result, false);
}
<commit_msg>=BG= allow router to match commas in dynamic URL segments and query parameters<commit_after>//!Router asigns handlers to paths and resolves them per request

#[cfg(test)]
use http::method;
use http::method::Method;
use regex::Regex;
use std::collections::hashmap::HashMap;
use request::Request;
use response::Response;

/// A Route is the basic data structure that stores both the path
/// and the handler that gets executed for the route.
/// The path can contain variable pattern such as `user/:userid/invoices`
pub struct Route {
    pub path: String,
    pub method: Method,
    pub handler: fn(request: &Request, response: &mut Response),
    pub variables: HashMap<String, uint>,
    matcher: Regex
}

impl Clone for Route {
    fn clone(&self) -> Route {
        Route {
            path: self.path.clone(),
            method: self.method.clone(),
            handler: self.handler,
            matcher: self.matcher.clone(),
            variables: self.variables.clone()
        }
    }
}

/// A RouteResult is what the router returns when `match_route` is called.
/// It contains the matched `route` and also a `params` property holding
/// a HashMap with the keys being the variable names and the value being the
/// evaluated string
pub struct RouteResult<'a> {
    pub route: &'a Route,
    pub params: HashMap<String, String>
}

/// The PathUtils collects some small helper methods that operate on the path
struct PathUtils;

// matches named variables (e.g. :userid)
static REGEX_VAR_SEQ: Regex                 = regex!(r":([,a-zA-Z0-9_-]*)");
static VAR_SEQ:&'static str                 = "[,a-zA-Z0-9_-]*";
static VAR_SEQ_WITH_SLASH:&'static str      = "[,/a-zA-Z0-9_-]*";
static VAR_SEQ_WITH_CAPTURE:&'static str    = "([,a-zA-Z0-9_-]*)";
// matches request params (e.g. ?foo=true&bar=false)
static REGEX_PARAM_SEQ:&'static str         = "[,a-zA-Z0-9_=&?-]*";
static REGEX_START:&'static str             = "^";
static REGEX_END:&'static str               = "$";

impl PathUtils {
    fn create_regex (route_path: &str) -> Regex {

        let updated_path = route_path.to_string()
                                     // first mark all double wildcards for replacement. We can't directly replace them
                                     // since the replacement does contain the * symbol as well, which would get overwritten
                                     // with the next replace call
                                     .replace("**", "___DOUBLE_WILDCARD___")
                                     // then replace the regular wildcard symbols (*) with the appropriate regex
                                     .replace("*", VAR_SEQ)
                                     // now replace the previously marked double wild cards (**)
                                     .replace("___DOUBLE_WILDCARD___", VAR_SEQ_WITH_SLASH);

        // then replace the variable symbols (:variable) with the appropriate regex
        let result = REGEX_START.to_string()
                                .append(REGEX_VAR_SEQ.replace_all(updated_path.as_slice(), VAR_SEQ_WITH_CAPTURE).as_slice())
                                .append(REGEX_PARAM_SEQ)
                                .append(REGEX_END);

        match Regex::new(result.as_slice()) {
            Ok(re) => re,
            Err(err) => fail!("{}", err)
        }
    }

    fn get_variable_info (route_path: &str) -> HashMap<String, uint> {
        REGEX_VAR_SEQ.captures_iter(route_path)
             .enumerate()
             .map(|(i, matched)| (matched.at(1).to_string(), i))
             .collect()
    }
}

/// The Router's job is it to hold routes and to resolve them later against
/// concrete URLs

#[deriving(Clone)]
pub struct Router{
    pub routes: Vec<Route>,
}

impl Router {
    pub fn new () -> Router {
        Router {
            routes: Vec::new()
        }
    }

    pub fn add_route (&mut self, method: Method, path: String, handler: fn(request: &Request, response: &mut Response)) -> () {
        let matcher = PathUtils::create_regex(path.as_slice());
        let variable_infos = PathUtils::get_variable_info(path.as_slice());
        let route = Route {
            path: path,
            method: method,
            matcher: matcher,
            handler: handler,
            variables: variable_infos
        };
        self.routes.push(route);
    }

    pub fn match_route(&self, method: Method, path: String) -> Option<RouteResult> {
        self.routes.iter().find(|item| item.method == method && item.matcher.is_match(path.as_slice()))
            .and_then(|route| {
                match route.matcher.captures(path.as_slice()) {
                    Some(captures) => {
                        let mut map = HashMap::new();
                        for (name, pos) in route.variables.iter() {
                            map.insert(name.to_string(), captures.at(pos + 1).to_string());
                        }
                        Some(RouteResult {
                            route: route,
                            params: map
                        })
                    },
                    None => Some(RouteResult{
                        route: route,
                        params: HashMap::new()
                    })
                }
            })
    }
}

#[test]
fn creates_map_with_var_variable_infos () {
    let map = PathUtils::get_variable_info("foo/:uid/bar/:groupid");

    assert_eq!(map.len(), 2);
    assert_eq!(map.get(&"uid".to_string()), &0);
    assert_eq!(map.get(&"groupid".to_string()), &1);
}

#[test]
fn creates_regex_with_captures () {
    let regex = PathUtils::create_regex("foo/:uid/bar/:groupid");
    let caps = regex.captures("foo/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");

    let regex = PathUtils::create_regex("foo/*/:uid/bar/:groupid");
    let caps = regex.captures("foo/test/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");

    let regex = PathUtils::create_regex("foo/**/:uid/bar/:groupid");
    let caps = regex.captures("foo/test/another/4711/bar/5490").unwrap();

    assert_eq!(caps.at(1), "4711");
    assert_eq!(caps.at(2), "5490");
}

#[test]
fn creates_valid_regex_for_routes () {
    let regex1 = PathUtils::create_regex("foo/:uid/bar/:groupid");
    let regex2 = PathUtils::create_regex("foo/*/bar");
    let regex3 = PathUtils::create_regex("foo/**/bar");

    assert_eq!(regex1.is_match("foo/4711/bar/5490"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5490?foo=true&bar=false"), true);
    assert_eq!(regex1.is_match("foo/4711/bar"), false);
    assert_eq!(regex1.is_match("foo/4711/bar?foo=true&bar=false"), false);

    assert_eq!(regex2.is_match("foo/4711/bar"), true);
    assert_eq!(regex2.is_match("foo/4711/bar?foo=true&bar=false"), true);
    assert_eq!(regex2.is_match("foo/4711/4712/bar"), false);
    assert_eq!(regex2.is_match("foo/4711/4712/bar?foo=true&bar=false"), false);

    assert_eq!(regex3.is_match("foo/4711/bar"), true);
    assert_eq!(regex3.is_match("foo/4711/bar?foo=true&bar=false"), true);
    assert_eq!(regex3.is_match("foo/4711/4712/bar"), true);
    assert_eq!(regex3.is_match("foo/4711/4712/bar?foo=true&bar=false"), true);

    //ensure that this works with commas too
    assert_eq!(regex1.is_match("foo/4711/bar/5490,1234"), true);
    assert_eq!(regex1.is_match("foo/4711/bar/5490,1234?foo=true&bar=false"), true);
    assert_eq!(regex1.is_match("foo/4711/bar"), false);
    assert_eq!(regex1.is_match("foo/4711/bar?foo=1,2,3&bar=false"), false);
}

#[test]
fn can_match_var_routes () {
    let route_store = &mut Router::new();

    fn handler (_request: &Request, response: &mut Response) -> () {
        let _ = response.origin.write("hello from foo".as_bytes());
    };

    route_store.add_route(method::Get, "/foo/:userid".to_string(), handler);
    route_store.add_route(method::Get, "/bar".to_string(), handler);

    let route_result = route_store.match_route(method::Get, "/foo/4711".to_string()).unwrap();
    let route = route_result.route;

    assert_eq!(route_result.params.get(&"userid".to_string()), &"4711".to_string());

    //assert the route has identified the variable
    assert_eq!(route.variables.len(), 1);
    assert_eq!(route.variables.get(&"userid".to_string()), &0);

    let route_result = route_store.match_route(method::Get, "/bar/4711".to_string());

    let result = match route_result {
        Some(_res) => true,
        None => false
    };

    assert_eq!(result, false);

    let route_result = route_store.match_route(method::Get, "/foo".to_string());

    let result = match route_result{
        Some(_res) => true,
        None => false
    };

    assert_eq!(result, false);

    //ensure that this will work with commas too
    let route_result = route_store.match_route(method::Get, "/foo/123,456".to_string());
    let result = match route_result {
        Some(_) => true,
        None => false
    };
    assert_eq!(result, true);
    let route_result = route_result.unwrap();
    let route = route_result.route;
    assert_eq!(route_result.params.get(&"userid".to_string()), &"123,456".to_string());
}
<|endoftext|>


<commit_before>//! A set of objects which automatically change their register or coil value when they go out of scope
//!
//! # Examples
//!
//! When the `auto` object goes out of scope and is dropped, the value of coil `10` is switched `On`:
//!
//! ```
//! # extern crate modbus;
//! # extern crate test_server;
//! # use test_server::start_dummy_server;
//! # fn main() {
//! use modbus::{Client, Coil};
//! use modbus::tcp;
//! use modbus::scoped::{ScopedCoil, CoilDropFunction};
//! # if cfg!(feature = "modbus-server-tests") {
//! # let (_s, port) = start_dummy_server();
//!
//! // let port = 502;
//! let mut client = tcp::Transport::new_with_port("127.0.0.1", port).unwrap();
//! {
//!    let mut auto = ScopedCoil::new(&mut client, 10, CoilDropFunction::On).unwrap();
//!    assert_eq!(auto.mut_transport().read_coils(10, 1).unwrap(), vec![Coil::Off]);
//! }
//! assert_eq!(client.read_coils(10, 1).unwrap(), vec![Coil::On]);
//! # }
//! # }
//! ```
//!
//! When the `auto` object goes out of scope and is dropped, the value of register `10` is modified by
//! function `fun`:
//!
//! ```
//! # extern crate modbus;
//! # extern crate test_server;
//! # use test_server::start_dummy_server;
//! # fn main() {
//! use modbus::{Client, Coil};
//! use modbus::tcp;
//! use modbus::scoped::{ScopedRegister, RegisterDropFunction};
//! # if cfg!(feature = "modbus-server-tests") {
//! # let (_s, port) = start_dummy_server();
//!
//! // let port = 502;
//! let mut client = tcp::Transport::new_with_port("127.0.0.1", port).unwrap();
//! {
//!     let fun = |v| v + 5;
//!     let mut auto = ScopedRegister::new(&mut client, 10, RegisterDropFunction::Fun(&fun)).unwrap();
//!     assert_eq!(auto.mut_transport().read_holding_registers(10, 1).unwrap(), vec![1]);
//! }
//! assert_eq!(client.read_holding_registers(10, 1).unwrap(), vec![6]);
//! # }
//! # }
//! ```

use {Coil, Transport, Client, Result};

/// Action to perform when the `ScopedCoil` is dropped.
pub enum CoilDropFunction {
    /// Set the coil to `Coil::On`
    On,
    /// Set the coil to `Coil::Off`
    Off,
    /// Toggle the current value.
    Toggle,
}

/// Action to perform when the `ScopedRegister` is dropped.
pub enum RegisterDropFunction<'a> {
    /// Set the register to zero value
    Zero,
    /// Increment the current register value by 1
    Increment,
    /// Decrement the current register value by 1
    Decrement,
    /// Set the register value to the given value.
    Value(u16),
    /// Execute the given function on the current value, setting the register with the result value.
    Fun(&'a Fn(u16) -> u16),
}

/// Auto object which modifies it's coil value depending on a given modification function if it
/// goes out of scope.
pub struct ScopedCoil<'a> {
    address: u16,
    fun: CoilDropFunction,
    transport: &'a mut Transport,
}

impl<'a> Drop for ScopedCoil<'a> {
    fn drop(&mut self) {
        // assume everything works as expected, we can't return an error
        let value = self.transport.read_coils(self.address, 1).unwrap()[0];
        let drop_value = match self.fun {
            CoilDropFunction::On => Coil::On,
            CoilDropFunction::Off => Coil::Off,
            CoilDropFunction::Toggle => if value == Coil::On {
                Coil::Off
            } else {
                Coil::On
            },
        };
        self.transport.write_single_coil(self.address, drop_value).unwrap()
    }
}

impl<'a> ScopedCoil<'a> {
    /// Create a new `ScopedCoil` object with `address` and drop function when the object goes
    /// out of scope.
    pub fn new(transport: &mut Transport,
               address: u16,
               fun: CoilDropFunction)
               -> Result<ScopedCoil> {
        Ok(ScopedCoil {
            address: address,
            fun: fun,
            transport: transport,
        })
    }

    pub fn mut_transport<'b>(&'b mut self) -> &'b mut Transport {
        self.transport
    }
}

/// Auto object which modifies it's register value depending on a given modification function if it
/// goes out of scope.
pub struct ScopedRegister<'a> {
    address: u16,
    fun: RegisterDropFunction<'a>,
    transport: &'a mut Transport,
}

impl<'a> Drop for ScopedRegister<'a> {
    fn drop(&mut self) {
        // assume everything works as expected, we can't return an error
        let value = self.transport.read_holding_registers(self.address, 1).unwrap()[0];
        let drop_value = match self.fun {
            RegisterDropFunction::Zero => 0u16,
            RegisterDropFunction::Increment => value + 1,
            RegisterDropFunction::Decrement => value - 1,
            RegisterDropFunction::Value(v) => v,
            RegisterDropFunction::Fun(f) => f(value),
        };
        self.transport.write_single_register(self.address, drop_value).unwrap()
    }
}

impl<'a> ScopedRegister<'a> {
    /// Create a new `ScopedRegister` object with `address` and drop function when the object goes
    /// out of scope.
    pub fn new<'b>(transport: &'b mut Transport,
                   address: u16,
                   fun: RegisterDropFunction<'b>)
                   -> Result<ScopedRegister<'b>> {
        Ok(ScopedRegister {
            address: address,
            fun: fun,
            transport: transport,
        })
    }

    pub fn mut_transport<'b>(&'b mut self) -> &'b mut Transport {
        self.transport
    }
}
<commit_msg>Remove unnecessary lifetime annotation<commit_after>//! A set of objects which automatically change their register or coil value when they go out of scope
//!
//! # Examples
//!
//! When the `auto` object goes out of scope and is dropped, the value of coil `10` is switched `On`:
//!
//! ```
//! # extern crate modbus;
//! # extern crate test_server;
//! # use test_server::start_dummy_server;
//! # fn main() {
//! use modbus::{Client, Coil};
//! use modbus::tcp;
//! use modbus::scoped::{ScopedCoil, CoilDropFunction};
//! # if cfg!(feature = "modbus-server-tests") {
//! # let (_s, port) = start_dummy_server();
//!
//! // let port = 502;
//! let mut client = tcp::Transport::new_with_port("127.0.0.1", port).unwrap();
//! {
//!    let mut auto = ScopedCoil::new(&mut client, 10, CoilDropFunction::On).unwrap();
//!    assert_eq!(auto.mut_transport().read_coils(10, 1).unwrap(), vec![Coil::Off]);
//! }
//! assert_eq!(client.read_coils(10, 1).unwrap(), vec![Coil::On]);
//! # }
//! # }
//! ```
//!
//! When the `auto` object goes out of scope and is dropped, the value of register `10` is modified by
//! function `fun`:
//!
//! ```
//! # extern crate modbus;
//! # extern crate test_server;
//! # use test_server::start_dummy_server;
//! # fn main() {
//! use modbus::{Client, Coil};
//! use modbus::tcp;
//! use modbus::scoped::{ScopedRegister, RegisterDropFunction};
//! # if cfg!(feature = "modbus-server-tests") {
//! # let (_s, port) = start_dummy_server();
//!
//! // let port = 502;
//! let mut client = tcp::Transport::new_with_port("127.0.0.1", port).unwrap();
//! {
//!     let fun = |v| v + 5;
//!     let mut auto = ScopedRegister::new(&mut client, 10, RegisterDropFunction::Fun(&fun)).unwrap();
//!     assert_eq!(auto.mut_transport().read_holding_registers(10, 1).unwrap(), vec![1]);
//! }
//! assert_eq!(client.read_holding_registers(10, 1).unwrap(), vec![6]);
//! # }
//! # }
//! ```

use {Coil, Transport, Client, Result};

/// Action to perform when the `ScopedCoil` is dropped.
pub enum CoilDropFunction {
    /// Set the coil to `Coil::On`
    On,
    /// Set the coil to `Coil::Off`
    Off,
    /// Toggle the current value.
    Toggle,
}

/// Action to perform when the `ScopedRegister` is dropped.
pub enum RegisterDropFunction<'a> {
    /// Set the register to zero value
    Zero,
    /// Increment the current register value by 1
    Increment,
    /// Decrement the current register value by 1
    Decrement,
    /// Set the register value to the given value.
    Value(u16),
    /// Execute the given function on the current value, setting the register with the result value.
    Fun(&'a Fn(u16) -> u16),
}

/// Auto object which modifies it's coil value depending on a given modification function if it
/// goes out of scope.
pub struct ScopedCoil<'a> {
    address: u16,
    fun: CoilDropFunction,
    transport: &'a mut Transport,
}

impl<'a> Drop for ScopedCoil<'a> {
    fn drop(&mut self) {
        // assume everything works as expected, we can't return an error
        let value = self.transport.read_coils(self.address, 1).unwrap()[0];
        let drop_value = match self.fun {
            CoilDropFunction::On => Coil::On,
            CoilDropFunction::Off => Coil::Off,
            CoilDropFunction::Toggle => if value == Coil::On {
                Coil::Off
            } else {
                Coil::On
            },
        };
        self.transport.write_single_coil(self.address, drop_value).unwrap()
    }
}

impl<'a> ScopedCoil<'a> {
    /// Create a new `ScopedCoil` object with `address` and drop function when the object goes
    /// out of scope.
    pub fn new(transport: &mut Transport,
               address: u16,
               fun: CoilDropFunction)
               -> Result<ScopedCoil> {
        Ok(ScopedCoil {
            address: address,
            fun: fun,
            transport: transport,
        })
    }

    pub fn mut_transport(&mut self) -> &mut Transport {
        self.transport
    }
}

/// Auto object which modifies it's register value depending on a given modification function if it
/// goes out of scope.
pub struct ScopedRegister<'a> {
    address: u16,
    fun: RegisterDropFunction<'a>,
    transport: &'a mut Transport,
}

impl<'a> Drop for ScopedRegister<'a> {
    fn drop(&mut self) {
        // assume everything works as expected, we can't return an error
        let value = self.transport.read_holding_registers(self.address, 1).unwrap()[0];
        let drop_value = match self.fun {
            RegisterDropFunction::Zero => 0u16,
            RegisterDropFunction::Increment => value + 1,
            RegisterDropFunction::Decrement => value - 1,
            RegisterDropFunction::Value(v) => v,
            RegisterDropFunction::Fun(f) => f(value),
        };
        self.transport.write_single_register(self.address, drop_value).unwrap()
    }
}

impl<'a> ScopedRegister<'a> {
    /// Create a new `ScopedRegister` object with `address` and drop function when the object goes
    /// out of scope.
    pub fn new<'b>(transport: &'b mut Transport,
                   address: u16,
                   fun: RegisterDropFunction<'b>)
                   -> Result<ScopedRegister<'b>> {
        Ok(ScopedRegister {
            address: address,
            fun: fun,
            transport: transport,
        })
    }

    pub fn mut_transport(&mut self) -> &mut Transport {
        self.transport
    }
}
<|endoftext|>