use std::mem::replace; use std::collections::HashMap; use super::{Object, Props}; use super::tokenizer::{Tokenizer, Token}; pub struct Parser { tokenizer: Tokenizer, current_key: Option, current_prop: Option, props: Props, objects: Vec, } impl Parser { pub fn new() -> Self { Parser { tokenizer: Tokenizer::new(), current_key: None, current_prop: None, props: vec![], objects: vec![], } } pub fn feed(&mut self, input: &'_ [u8], mut f: F) where F: FnMut(Object), { let current_key = &mut self.current_key; let current_prop = &mut self.current_prop; let props = &mut self.props; let objects = &mut self.objects; self.tokenizer.feed(input, |token| { match token { Token::Key(key) => *current_key = Some(key), Token::PropName(name) => *current_prop = Some(name), Token::PropValue(value) => { current_prop.take().map(|name| { props.push((name, value)); }); } Token::Value(value) => { fn compare(s1: &Option, s2: &str) -> bool { s1.as_ref().map(|s1| s1 == s2).unwrap_or(s2.len() == 0) } if compare(current_key, "BEGIN") { objects.push(Object { name: value, content: HashMap::new(), children: vec![], }); } else if compare(current_key, "END") { while objects.len() > 1 { let object = objects.pop().unwrap(); let end = value == object.name; let objects_len = objects.len(); objects[objects_len - 1].children.push(Box::new(object)); if end { break; } } if objects.len() == 1 { f(objects.remove(0)); } } else if objects.len() > 0 { let props = replace(props, vec![]); let key = replace(current_key, None); let objects_len = objects.len(); let content = &mut objects[objects_len - 1].content; key.map(|key| content.insert(key, (props, 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(), (vec![], v.to_owned()))) .collect(), children: vec![], })); } #[test] fn parse_props() { let mut p = Parser::new(); let mut obj = None; p.feed(b"BEGIN:VEVENT SUMMARY:Test event DTSTART;TZID=Europe/Berlin:19700101 END:VEVENT ", |o| { assert!(obj.is_none()); obj = Some(o); }); assert_eq!(obj, Some(Object { name: "VEVENT".to_owned(), content: [ ("SUMMARY".to_owned(), (vec![], "Test event".to_owned())), ("DTSTART".to_owned(), (vec![("TZID".to_owned(), "Europe/Berlin".to_owned())], "19700101".to_owned())) ].iter().cloned().collect(), children: vec![], })); } #[test] fn parse_nested() { let mut p = Parser::new(); let mut obj = None; p.feed(b"BEGIN:VEVENT SUMMARY:Test event DTSTART:19700101 BEGIN:VALARM ACTION:NONE END:VALARM 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(), (vec![], v.to_owned()))) .collect(), children: vec![Box::new(Object { name: "VALARM".to_owned(), content: [("ACTION", "NONE")] .iter() .cloned() .map(|(k, v)| (k.to_owned(), (vec![], v.to_owned()))) .collect(), children: vec![], })], })); } }