ticker/src/ics/parser.rs

96 lines
2.8 KiB
Rust
Raw Normal View History

2019-10-06 23:28:39 +02:00
use std::mem::replace;
use std::collections::HashMap;
2019-10-07 00:12:31 +02:00
use super::{Object, Props};
2019-10-06 23:28:39 +02:00
use super::tokenizer::{Tokenizer, Token};
pub struct Parser {
tokenizer: Tokenizer,
object_name: Option<String>,
current_key: Option<String>,
2019-10-07 00:12:31 +02:00
current_prop: Option<String>,
props: Props,
content: HashMap<String, (Props, String)>,
2019-10-06 23:28:39 +02:00
}
impl Parser {
pub fn new() -> Self {
Parser {
tokenizer: Tokenizer::new(),
object_name: None,
current_key: None,
2019-10-07 00:12:31 +02:00
current_prop: None,
props: vec![],
2019-10-06 23:28:39 +02:00
content: HashMap::new(),
}
}
pub fn feed<F>(&mut self, input: &'_ [u8], mut f: F)
where
F: FnMut(Object),
{
let current_key = &mut self.current_key;
2019-10-07 00:12:31 +02:00
let current_prop = &mut self.current_prop;
let props = &mut self.props;
2019-10-06 23:28:39 +02:00
let object_name = &mut self.object_name;
let content = &mut self.content;
self.tokenizer.feed(input, |token| {
match token {
Token::Key(key) => *current_key = Some(key),
2019-10-07 00:12:31 +02:00
Token::PropName(name) => *current_prop = Some(name),
Token::PropValue(value) => {
current_prop.take().map(|name| {
props.push((name, value));
});
}
2019-10-06 23:28:39 +02:00
Token::Value(value) => {
fn compare(s1: &Option<String>, s2: &str) -> bool {
s1.as_ref().map(|s1| s1 == s2).unwrap_or(s2.len() == 0)
}
if compare(current_key, "BEGIN") {
*object_name = Some(value);
content.clear();
} else if compare(current_key, "END") {
let object_name = replace(object_name, None);
let content = replace(content, HashMap::new());
object_name.map(|name| f(Object { name, content }));
} else {
2019-10-07 00:12:31 +02:00
let props = replace(props, vec![]);
2019-10-06 23:28:39 +02:00
let key = replace(current_key, None);
2019-10-07 00:12:31 +02:00
key.map(|key| content.insert(key, (props, value)));
2019-10-06 23:28:39 +02:00
}
}
}
});
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn parse_event() {
let mut p = Parser::new();
let mut obj = None;
p.feed(b"BEGIN:VEVENT
SUMMARY:Test event
DTSTART:19700101
END:VEVENT
", |o| {
assert!(obj.is_none());
obj = Some(o);
});
assert_eq!(obj, Some(Object {
name: "VEVENT".to_owned(),
content: [("SUMMARY", "Test event"),
("DTSTART", "19700101")]
.iter()
.cloned()
2019-10-07 00:12:31 +02:00
.map(|(k, v)| (k.to_owned(), (vec![], v.to_owned())))
2019-10-06 23:28:39 +02:00
.collect(),
}));
}
}