actiondb 0.3.0

A safe and efficient unstructured text (log) parsing library.
use std::cmp::{Ord, Ordering};
use utils::common_prefix::CommonPrefix;

use matcher::trie::node::{Node, ParserNode};
use matcher::trie::{HasPattern, TrieOperations};
use matcher::Pattern;
use parsers::Parser;

#[derive(Debug, Clone)]
pub struct LiteralNode {
    literal: String,
    has_value: bool,
    pattern: Option<Pattern>,
    node: Option<Box<Node>>,
}

impl LiteralNode {
    pub fn new(literal: String) -> LiteralNode {
        LiteralNode{ literal: literal,
                     has_value: false,
                     pattern: None,
                     node: None}
    }

    pub fn from_str(literal: &str) -> LiteralNode {
        LiteralNode::new(literal.to_string())
    }

    pub fn literal(&self) -> &str {
        &self.literal[..]
    }

    pub fn has_value(&self) -> bool {
        self.has_value
    }

    pub fn set_has_value(&mut self, has_value: bool) {
        self.has_value = has_value;
    }

    pub fn set_node(&mut self, node: Option<Box<Node>>) {
        self.node = node;
    }

    pub fn node_mut(&mut self) -> Option<&mut Node> {
        match self.node {
            Some(ref mut boxed_node) => {
                Some(boxed_node)
            },
            None => None
        }
    }

    pub fn node(&self) -> Option<&Node> {
        match self.node {
            Some(ref boxed_node) => {
                Some(boxed_node)
            },
            None => None
        }
    }

    pub fn cmp_str(&self, other: &str) -> Ordering {
        if self.literal.is_empty() && other.is_empty() {
            Ordering::Equal
        } else if self.literal.is_empty() {
            Ordering::Less
        } else if other.is_empty() {
            Ordering::Greater
        } else {
            self.literal[0..1].cmp(&other[0..1])
        }
    }

    pub fn split(self,
                 common_prefix_len: usize,
                 literal: &str) -> LiteralNode {
        let LiteralNode{ literal: self_literal,
                         has_value: self_has_value,
                         pattern: self_pattern,
                         node: self_node} = self;

        let common_prefix = literal.rtrunc(literal.len() - common_prefix_len);
        trace!("split(): common_prefix = {}", common_prefix);
        let left_branch = literal.ltrunc(common_prefix_len);
        let right_branch = self_literal.ltrunc(common_prefix_len);
        trace!("split(): left_branch = {}", left_branch);
        trace!("split(): right_branch = {}", right_branch);

        let mut node_to_return = LiteralNode::from_str(common_prefix);

        let mut new_node = Box::new(Node::new());
        let mut left_node = LiteralNode::from_str(left_branch);
        left_node.set_has_value(true);
        let mut right_node = LiteralNode::from_str(right_branch);

        right_node.set_node(self_node);
        right_node.set_has_value(self_has_value);

        if let Some(pattern) = self_pattern {
            right_node.set_pattern(pattern);
        }

        new_node.add_literal_node(left_node);
        new_node.add_literal_node(right_node);
        node_to_return.set_node(Some(new_node));
        node_to_return
    }

    pub fn is_leaf(&self) -> bool {
        self.node.is_none()
    }

    fn compare_first_chars(&self, other : &LiteralNode) -> Ordering {
        self.cmp_str(other.literal())
    }
}

impl TrieOperations for LiteralNode {
    fn insert_literal(&mut self, literal: &str) -> &mut LiteralNode {
        if self.is_leaf() {
            self.node = Some(Box::new(Node::new()));
        }

        self.node.as_mut().unwrap().insert_literal(literal)
    }

    fn insert_parser(&mut self, parser: Box<Parser>) -> &mut ParserNode {
        if self.is_leaf() {
            self.node = Some(Box::new(Node::new()));
        }

        self.node.as_mut().unwrap().insert_parser(parser)
    }
}

impl HasPattern for LiteralNode {
    fn set_pattern(&mut self, pattern: Pattern) {
        self.pattern = Some(pattern);
    }

    fn pattern(&self) -> Option<&Pattern> {
        self.pattern.as_ref()
    }
}


impl Eq for LiteralNode {}

impl PartialEq for LiteralNode {
    fn eq(&self, other: &Self) -> bool {
        self.compare_first_chars(other) == Ordering::Equal
    }

    fn ne(&self, other: &Self) -> bool {
        self.compare_first_chars(other) != Ordering::Equal
    }
}

impl Ord for LiteralNode {
    fn cmp(&self, other: &Self) -> Ordering {
        self.compare_first_chars(other)
    }
}

impl PartialOrd for LiteralNode {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[cfg(test)]
mod test {
    use matcher::trie::node::LiteralNode;
    use std::cmp::Ordering;

    #[test]
    fn given_literal_node_when_its_leafness_is_checked_the_right_result_is_returned() {
        let aleph = LiteralNode::from_str("aleph");

        assert_eq!(aleph.is_leaf(), true);
    }

    #[test]
    fn given_literal_node_when_it_is_compared_to_an_other_literal_node_then_only_their_first_chars_are_checked() {
        let alpha = LiteralNode::new("alpha".to_string());
        let beta = LiteralNode::new("beta".to_string());
        let aleph = LiteralNode::from_str("aleph");
        let a = LiteralNode::from_str("a");
        let empty = LiteralNode::from_str("");

        assert_eq!(alpha.cmp(&beta), Ordering::Less);
        assert_eq!(alpha.cmp(&aleph), Ordering::Equal);
        assert_eq!(beta.cmp(&alpha), Ordering::Greater);
        assert_eq!(alpha.cmp(&empty), Ordering::Greater);
        assert_eq!(empty.cmp(&alpha), Ordering::Less);
        assert_eq!(empty.cmp(&a), Ordering::Less);
        assert_eq!(empty.cmp_str("a"), Ordering::Less);
        assert_eq!(a.cmp(&empty), Ordering::Greater);
    }
}