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
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! Log filtering facility: add the capability to filter out by regex log messages and/or colorize them.

use regex::Regex;
use serde_derive::Deserialize;
use std::default::Default;

/// This structure is a helper for toml deserialization.
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub(crate) struct Rule {
    pub pattern: String,
    pub exclude: Option<bool>,
    pub color: Option<String>,
}

/// This is our main way to filter out or colorize log messages.
#[derive(Clone, Debug)]
pub struct RuleFilter {
    pub pattern: Option<String>,
    exclude: bool,
    color: Option<String>,
    re: Regex,
}

impl RuleFilter {
    pub fn new(pattern: &str, exclude: bool, color: &str) -> Self {
        Self {
            pattern: Some(pattern.to_owned()),
            exclude,
            color: Some(color.to_owned()),
            re: Regex::new(&pattern).expect("Fail to init RuleFilter's regex."),
        }
    }
    /// Returns if we should exclude this log entry or not.
    pub fn exclude(&self) -> bool {
        self.exclude
    }

    /// Returns the color of the log entry.
    pub fn get_color(&self) -> String {
        match &self.color {
            Some(color) => color.clone(),
            None => String::default(),
        }
    }

    /// Returns true if we should log this sentence.
    pub fn should_log(&self, args: &str) -> bool {
        self.re.is_match(args) && !self.exclude
    }

    pub fn regex(&self) -> Regex {
        self.re.clone()
    }
    pub fn is_match(&self, args: &str) -> bool {
        self.re.is_match(args)
    }
}

impl Default for RuleFilter {
    fn default() -> Self {
        Self {
            pattern: Some(String::default()),
            exclude: false,
            color: None,
            re: Regex::new(&String::default()).expect("Fail to init RuleFilter's regex."),
        }
    }
}

impl From<Rule> for RuleFilter {
    fn from(rule: Rule) -> Self {
        let tf = RuleFilter::default();
        RuleFilter::new(
            &rule.pattern,
            rule.exclude.unwrap_or_else(|| tf.exclude()),
            &rule.color.unwrap_or_else(|| tf.get_color()),
        )
    }
}

impl From<RuleFilter> for Rule {
    fn from(rule_filter: RuleFilter) -> Self {
        Rule {
            pattern: rule_filter.pattern.unwrap_or_default(),
            exclude: Some(rule_filter.exclude),
            color: Some(rule_filter.color.unwrap_or_default()),
        }
    }
}

/// [RuleFilter] builder following the builder pattern.
pub struct RuleFilterBuilder {
    pattern: String,
    exclude: bool,
    color: Option<String>,
}

impl RuleFilterBuilder {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn set_pattern(&mut self, pattern: &str) -> &mut Self {
        self.pattern = pattern.to_owned();
        self
    }

    pub fn set_exclusion(&mut self, exclude: bool) -> &mut Self {
        self.exclude = exclude;
        self
    }

    pub fn set_color(&mut self, color: &str) -> &mut Self {
        self.color = Some(color.to_owned());
        self
    }

    pub fn build(&self) -> RuleFilter {
        let pattern = match self.pattern.len() {
            0 => None,
            _ => Some(self.pattern.clone()),
        };

        RuleFilter {
            pattern,
            exclude: self.exclude,
            color: self.color.clone(),
            re: Regex::new(&self.pattern).expect("Fail to init RuleFilter's regex."),
        }
    }
}

impl Default for RuleFilterBuilder {
    fn default() -> Self {
        Self {
            pattern: String::default(),
            exclude: false,
            color: None,
        }
    }
}

#[test]
fn should_log_test() {
    let rule_filter = RuleFilterBuilder::new()
        .set_pattern("foo")
        .set_exclusion(false)
        .build();

    assert_eq!(rule_filter.should_log("bar"), false);
    assert_eq!(rule_filter.should_log("xfooy"), true);
}

#[test]
fn is_match_test() {
    let rule_filter = RuleFilterBuilder::new()
        .set_pattern("foo")
        .set_exclusion(false)
        .build();

    assert_eq!(rule_filter.is_match("bar"), false);
    assert_eq!(rule_filter.is_match("xfooy"), true);
}