83 lines
2.3 KiB
Rust
83 lines
2.3 KiB
Rust
|
use std::mem::replace;
|
||
|
use std::collections::HashMap;
|
||
|
use super::Object;
|
||
|
use super::tokenizer::{Tokenizer, Token};
|
||
|
|
||
|
pub struct Parser {
|
||
|
tokenizer: Tokenizer,
|
||
|
object_name: Option<String>,
|
||
|
current_key: Option<String>,
|
||
|
content: HashMap<String, String>,
|
||
|
}
|
||
|
|
||
|
impl Parser {
|
||
|
pub fn new() -> Self {
|
||
|
Parser {
|
||
|
tokenizer: Tokenizer::new(),
|
||
|
object_name: None,
|
||
|
current_key: None,
|
||
|
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;
|
||
|
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),
|
||
|
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 {
|
||
|
let key = replace(current_key, None);
|
||
|
key.map(|key| content.insert(key, value));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[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()
|
||
|
.map(|(k, v)| (k.to_owned(), v.to_owned()))
|
||
|
.collect(),
|
||
|
}));
|
||
|
}
|
||
|
}
|