use std::{error,fmt};
use std::num::ParseFloatError;
use std::vec::Vec;
use std::collections::BTreeMap;
pub mod metric_parser;
pub mod service_check_parser;
#[derive(Debug,PartialEq)]
pub enum ParseError {
EmptyInput,
IncompleteInput,
NoName,
ValueNotFloat,
SampleRateNotFloat,
UnknownMetricType,
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ParseError::EmptyInput => write!(f, "Empty input"),
ParseError::IncompleteInput => write!(f, "Incomplete input"),
ParseError::NoName => write!(f, "No name in input"),
ParseError::ValueNotFloat => write!(f, "Value is not a float"),
ParseError::SampleRateNotFloat => write!(f, "Sample rate is not a float"),
ParseError::UnknownMetricType => write!(f, "Unknown metric type")
}
}
}
impl error::Error for ParseError {
fn description(&self) -> &str {
"description() is deprecated; use Display"
}
}
#[derive(Debug,PartialEq)]
pub struct Parser {
chars: Vec<char>,
len: usize,
pos: usize
}
impl Parser {
pub fn new(buf: String) -> Parser {
let chars: Vec<char> = buf.chars().collect();
let len = chars.len();
Parser {
chars: chars,
len: len,
pos: 0
}
}
fn take_until(&mut self, to_match: Vec<char>) -> String {
let mut chars = Vec::new();
loop {
if self.pos >= self.len {
break
}
let current_char = self.chars[self.pos];
self.pos += 1;
if to_match.contains(¤t_char) {
break
} else {
chars.push(current_char);
}
}
chars.into_iter().collect()
}
fn take_float_until(&mut self, to_match: Vec<char>) -> Result<f64, ParseFloatError> {
let string = self.take_until(to_match);
string.parse()
}
fn peek(&mut self) -> Option<char> {
if self.pos == self.len {
None
} else {
Some(self.chars[self.pos])
}
}
fn last(&mut self) -> Option<char> {
if self.pos == 0 {
None
} else {
Some(self.chars[self.pos - 1 ])
}
}
fn skip(&mut self) {
self.pos += 1;
}
fn parse_tags(&mut self) -> BTreeMap<String, String> {
let mut tags = BTreeMap::new();
self.skip();
loop {
if Some('|') == self.last() {
break
}
let tag = self.take_until(vec![',', '|']);
if tag.is_empty() {
break
}
let mut split= tag.split(":");
match split.next() {
Some(key) => {
let parts: Vec<&str> = split.collect();
tags.insert(key.to_owned(), parts.join(":"))
},
None => break
};
}
tags
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use super::Parser;
#[test]
fn test_take_until() {
let mut parser = Parser::new("this is a string".to_string());
assert_eq!(parser.take_until(vec![' ']), "this");
assert_eq!(parser.pos, 5);
assert_eq!(parser.take_until(vec!['.']), "is a string");
assert_eq!(parser.pos, 16);
}
#[test]
fn test_take_float_until() {
let mut parser = Parser::new("10.01|number|string".to_string());
assert_eq!(parser.take_float_until(vec!['|']), Ok(10.01));
assert_eq!(parser.pos, 6);
assert!(parser.take_float_until(vec!['|']).is_err());
assert_eq!(parser.pos, 13);
}
#[test]
fn test_peek() {
let mut parser = Parser::new("this is a string".to_string());
parser.pos = 10;
assert_eq!(parser.peek(), Some('s'));
assert_eq!(parser.pos, 10);
parser.pos = 16;
assert_eq!(parser.peek(), None);
}
#[test]
fn test_last() {
let mut parser = Parser::new("abcdef".to_string());
parser.pos = 0;
assert_eq!(parser.last(), None);
assert_eq!(parser.pos, 0);
parser.pos = 3;
assert_eq!(parser.last(), Some('c'));
}
#[test]
fn test_skip() {
let mut parser = Parser::new("foo#bar".to_string());
parser.pos = 3;
parser.skip();
assert_eq!(parser.pos, 4);
}
#[test]
fn test_parse_tags() {
let mut parser = Parser::new("#hostname:frontend1,redis_instance:10.0.0.16:6379,namespace:web".to_string());
let mut tags = BTreeMap::new();
tags.insert("hostname".to_string(), "frontend1".to_string());
tags.insert("redis_instance".to_string(), "10.0.0.16:6379".to_string());
tags.insert("namespace".to_string(), "web".to_string());
assert_eq!(parser.parse_tags(), tags);
}
}