romp 0.5.2

STOMP server and WebSockets platform
Documentation
//! Code for filtering messages, (not workflow filters). `xtomp` stores messages pre-filtered which is faster but only supports simple filters
//! `romp` runs the the filters on each message for each subscription which enables more elaborate filters.
//! `romp` requires that the header names follow `sanename.org` package name restrictions

use crate::message::stomp_message::StompMessage;

#[derive(Debug, PartialEq)]
enum Operator {
    Equal,
    NotEqual,
    NumericEquals,
    GreaterThan,
    LessThan,
}

#[derive(Debug)]
pub struct Filter {
    hdr: String,
    op: Operator,
    value: String,
}

impl Filter {

    /// Parse "name=value" no trim()ing of the input occurs
    // Parsing a string in rust is horrible always.
    // TODO This is shitty code fix it so it does no allocations and is not so fugly
    pub fn parse(definition: &String) -> Result<Filter, ()> {
        let mut state= 0;
        let mut hdr = String::new();
        let mut operator = String::new();
        let mut value= String::new();
        for c in definition.chars() {
            match state {
                0 => {
                    if (c >= 'a' && c <= 'z') || c == '-' {
                        hdr.push(c);
                    } else {
                        operator.push(c);
                        state += 1;
                    }
                },
                1 => {
                    if c == '=' || c == '!' || c == '>' || c == '<' {
                        operator.push(c);
                    } else {
                        value.push(c);
                        state += 1;
                    }
                }
                _ => {
                    value.push(c);
                }
            }
        }
        let op;
        match operator.as_str() {
            "=" => op = Operator::Equal,
            "==" => op = Operator::NumericEquals,
            "!=" => op = Operator::NotEqual,
            ">" => op = Operator::GreaterThan,
            "<" => op = Operator::LessThan,
            _ => op = Operator::Equal,
        }
        if op == Operator::GreaterThan || op == Operator::GreaterThan {
            if let Err(_) = value.parse::<i64>() {
                return Err(());
            }
        }
        Ok(Filter { hdr, op, value })
    }

    pub fn matches_message(&self, message: &StompMessage) -> bool {
        if let Some(to_match) = message.get_header(&self.hdr) {
            match self.op {
                Operator::Equal => return to_match == self.value.as_str(),
                Operator::NotEqual => return to_match != self.value.as_str(),
                Operator::NumericEquals => {
                    if let Ok(int) = to_match.parse::<i64>(){
                        return int == self.value.parse::<i64>().unwrap();
                    }
                    return false;
                },
                Operator::LessThan =>  {
                    if let Ok(int) = to_match.parse::<i64>(){
                        return int < self.value.parse::<i64>().unwrap();
                    }
                    return false;
                },
                Operator::GreaterThan =>  {
                    if let Ok(int) = to_match.parse::<i64>(){
                        return int > self.value.parse::<i64>().unwrap();
                    }
                    return false;
                },
            };
        }
        false
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::message::stomp_message::Ownership;

    #[test]
    pub fn test_string_equal() {
        let filter = Filter::parse(&String::from("grp=1")).unwrap();

        let mut message = StompMessage::new(Ownership::Destination);
        message.add_header("grp", "1");

        if ! filter.matches_message(&message) {
            panic!("grp=1");
        }

        let mut message = StompMessage::new(Ownership::Destination);
        message.add_header("grp", "2");

        if filter.matches_message(&message) {
            panic!("grp=2");
        }
    }


    #[test]
    pub fn test_numeric_equal() {
        let filter = Filter::parse(&String::from("grp==1")).unwrap();

        let mut message = StompMessage::new(Ownership::Destination);
        message.add_header("grp", "1");

        if ! filter.matches_message(&message) {
            panic!("grp==1");
        }

        let mut message = StompMessage::new(Ownership::Destination);
        message.add_header("grp", "2");

        if filter.matches_message(&message) {
            panic!("grp==2");
        }
    }


    #[test]
    pub fn test_numeric_gt() {
        let filter = Filter::parse(&String::from("grp>1")).unwrap();

        let mut message = StompMessage::new(Ownership::Destination);
        message.add_header("grp", "2");

        if ! filter.matches_message(&message) {
            panic!("grp>2");
        }

        let mut message = StompMessage::new(Ownership::Destination);
        message.add_header("grp", "1");

        if filter.matches_message(&message) {
            panic!("grp>1");
        }
    }

    #[test]
    pub fn test_numeric_lt() {
        let filter = Filter::parse(&String::from("grp<1")).unwrap();

        let mut message = StompMessage::new(Ownership::Destination);
        message.add_header("grp", "0");

        if ! filter.matches_message(&message) {
            panic!("grp<0");
        }

        let mut message = StompMessage::new(Ownership::Destination);
        message.add_header("grp", "1");

        if filter.matches_message(&message) {
            panic!("grp<1");
        }
    }
}