use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Parser {
regex: regex::Regex,
is_logfmt: bool,
}
#[derive(Debug)]
#[allow(dead_code)]
pub enum ParserError {
InvalidParser(String),
}
impl Parser {
pub fn parse(s: &str) -> Result<Parser, ParserError> {
let mut parts = s.splitn(2, ' ');
let first = parts.next().ok_or(ParserError::InvalidParser(s.into()))?;
match first {
"logfmt" => return Ok(Parser::new_logfmt()),
"regex" => {
let rest = parts.next().ok_or(ParserError::InvalidParser(s.into()))?;
return Ok(Parser::new_from_regex(rest));
}
"pattern" => {
let rest = parts.next().ok_or(ParserError::InvalidParser(s.into()))?;
return Ok(Parser::new_from_pattern(rest));
}
_ => Err(ParserError::InvalidParser(s.into())),
}
}
pub fn new_from_pattern(linepattern: &str) -> Parser {
let mut repattern = "^".to_string();
let mut inpattern: bool = false;
let mut patternname = String::new();
for c in linepattern.chars() {
if c == '<' {
inpattern = true;
patternname.clear();
continue;
}
if c == '>' {
inpattern = false;
if patternname != "_" && patternname != "" {
repattern.push_str("(?P<");
repattern.push_str(&patternname);
repattern.push_str(">");
repattern.push_str(".*?");
repattern.push_str(")");
} else {
repattern.push_str("(");
repattern.push_str(".*?");
repattern.push_str(")");
}
continue;
}
if inpattern {
if is_special_for_re(c) {
repattern.push('\\');
}
patternname.push(c);
} else {
if is_special_for_re(c) {
repattern.push('\\');
}
repattern.push(c);
}
}
repattern.push_str("$");
let re = regex::Regex::new(&repattern).unwrap();
Parser {
regex: re,
is_logfmt: false,
}
}
pub fn new_logfmt() -> Parser {
let re = regex::Regex::new("(?P<key>[^ ]*?)=(?P<value>\".*?\"|[^ ]*)( |$)").unwrap();
Parser {
regex: re,
is_logfmt: true,
}
}
pub fn new_from_regex(regex: &str) -> Parser {
let re = regex::Regex::new(regex).unwrap();
Parser {
regex: re,
is_logfmt: false,
}
}
pub fn parse_line(&self, line: &str) -> HashMap<String, String> {
if self.is_logfmt {
return self.parse_logfmt(line);
} else {
return self.parse_regex(line);
}
}
fn parse_logfmt(&self, line: &str) -> HashMap<String, String> {
let mut data = HashMap::new();
for caps in self.regex.captures_iter(line) {
let key = caps["key"].to_string();
let value = caps["value"].to_string();
if value.starts_with('"') && value.ends_with('"') {
let value = value[1..value.len() - 1].to_string();
data.insert(key, value);
continue;
}
data.insert(key, value);
}
data
}
fn parse_regex(&self, line: &str) -> HashMap<String, String> {
let mut data = HashMap::new();
let caps = self.regex.captures(line);
match caps {
Some(caps) => {
for name in self.regex.capture_names().flatten() {
let value = caps[name].to_string();
data.insert(name.to_string(), value);
}
}
None => {}
}
data
}
}
fn is_special_for_re(c: char) -> bool {
match c {
'.' | '*' | '+' | '?' | '(' | ')' | '[' | ']' | '{' | '}' | '^' | '$' | '|' | '\\' => true,
_ => false,
}
}