redirectionio 2.5.2

Redirection IO Library to handle matching rule, redirect and filtering headers and body.
use crate::regex_radix_tree::{Node, NodeItem, Storage, Trace};
use regex::{Regex, RegexBuilder};
use std::marker::PhantomData;

#[derive(Debug, Clone)]
pub struct Leaf<T: NodeItem, S: Storage<T>> {
    storage: S,
    level: u64,
    prefix: String,
    prefix_compiled: Option<Regex>,
    ignore_case: bool,
    phantom: PhantomData<T>,
}

impl<T: NodeItem, S: Storage<T>> Node<T, S> for Leaf<T, S> {
    fn insert(&mut self, item: T, _parent_prefix_size: u32) {
        self.ignore_case = self.ignore_case || item.case_insensitive();
        self.storage.push(item)
    }

    fn find(&self, value: &str) -> Vec<&S> {
        if self.is_match(value) {
            return vec![&self.storage];
        }

        Vec::new()
    }

    fn trace(&self, value: &str) -> Trace<T, S> {
        let matched = self.is_match(value);
        let storage = if matched { Some(self.storage.clone()) } else { None };

        Trace::new(self.prefix.clone(), matched, self.storage.len() as u64, Vec::new(), storage)
    }

    fn remove(&mut self, id: &str) -> bool {
        self.storage.remove(id)
    }

    fn regex(&self) -> &str {
        self.prefix.as_str()
    }

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

    fn is_empty(&self) -> bool {
        self.storage.is_empty()
    }

    fn can_insert_item(&self, _prefix: &str, item: &T) -> bool {
        item.regex() == self.prefix
    }

    fn incr_level(&mut self) {
        self.level += 1
    }

    fn cache(&mut self, limit: u64, level: u64) -> u64 {
        if self.level != level {
            return limit;
        }

        if limit == 0 {
            return limit;
        }

        self.prefix_compiled = self.create_regex();

        if self.prefix_compiled.is_some() {
            return limit - 1;
        }

        limit
    }

    fn box_clone(&self) -> Box<dyn Node<T, S>> {
        Box::new(self.clone())
    }
}

impl<T: NodeItem, S: Storage<T>> Leaf<T, S> {
    pub fn new(item: T, level: u64, ignore_case: bool) -> Leaf<T, S> {
        let mut storage = S::new(item.regex());
        let prefix = item.regex().to_string();

        storage.push(item);

        Leaf {
            prefix,
            storage,
            level,
            prefix_compiled: None,
            ignore_case,
            phantom: PhantomData,
        }
    }

    fn is_match(&self, value: &str) -> bool {
        match self.prefix_compiled.as_ref() {
            Some(regex) => regex.is_match(value),
            None => match self.create_regex() {
                None => false,
                Some(regex) => regex.is_match(value),
            },
        }
    }

    fn create_regex(&self) -> Option<Regex> {
        let regex = ["^", self.prefix.as_str(), "$"].join("");

        match RegexBuilder::new(regex.as_str()).case_insensitive(self.ignore_case).build() {
            Err(e) => {
                error!("Cannot create regex: {:?}", e);

                None
            }
            Ok(regex) => Some(regex),
        }
    }
}