summaryrefslogtreecommitdiffstats
path: root/src/types.rs
blob: 0fca609d013ed9462516cac96858d866f6207692 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use constants::MessageType;

#[deriving(PartialEq, Eq, Show)]
pub struct Message {
    from: Option<String>,
    message_type: MessageType,
    params: Vec<String>,
}

impl Message {
    fn new (from: Option<String>, message_type: MessageType, params: Vec<String>) -> Message {
        Message { from: from, message_type: message_type, params: params }
    }

    pub fn parse (msg: &str) -> Result<Message, &'static str> {
        let message_parser = regex!(r"^(?::([^ ]+) )?([A-Z]+|[0-9]{3}) ([^\r\n\0]*)\r\n$");
        match message_parser.captures(msg) {
            Some(captures) => {
                let from = captures.at(1);
                let from = if from.len() > 0 { Some(from.to_string()) } else { None };
                let command = captures.at(2);

                let params = Message::parse_params(captures.at(3));

                match from_str(command) {
                    Some(c) => Ok(Message::new(from, c, params)),
                    None => Err("command parsing failed"),
                }
            },
            None => Err("message parsing failed"),
        }
    }

    pub fn from (&self) -> &Option<String> {
        &self.from
    }

    pub fn message_type (&self) -> &MessageType {
        &self.message_type
    }

    pub fn params (&self) -> &Vec<String> {
        &self.params
    }

    fn parse_params(params: &str) -> Vec<String> {
        let mut offset = 0;
        let len = params.len();
        let mut ret = vec![];

        loop {
            if offset >= len {
                return ret;
            }

            let remaining = params.slice(offset, len);

            if remaining.starts_with(":") {
                ret.push(remaining.to_string());
                return ret;
            }

            match remaining.find(' ') {
                Some(next) => {
                    ret.push(remaining.slice(0, next).to_string());
                    offset = next + 1;
                },
                None => {
                    ret.push(remaining.to_string());
                    return ret;
                }
            }
        }
    }
}

#[test]
fn test_message_parser () {
    use constants::*;

    {
        let msg = "PASS secretpasswordhere\r\n";
        assert_eq!(
            Message::parse(msg),
            Ok(
                Message {
                    from: None,
                    message_type: CommandMessage(Pass),
                    params: vec!["secretpasswordhere".to_string()],
                }
            )
        );
    }
}