diff options
Diffstat (limited to 'src/org.rs')
-rw-r--r-- | src/org.rs | 139 |
1 files changed, 92 insertions, 47 deletions
@@ -1,77 +1,122 @@ use regex::Regex; - -#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Debug)] -pub enum Org<'a> { - Unknown, - Header(u16, &'a str), -} +use std::str; #[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)] -pub struct OrgNode<'a> { - pub raw: &'a str, - pub e: Org<'a>, +pub enum Org { + Unknown(String), + Header(u16, String), + Properties(Vec<(String, String)>) } -type OrgDocument<'a> = Vec<OrgNode<'a>>; +type OrgDocument = Vec<Org>; -struct Parser<'a> { - doc: OrgDocument<'a> -} - -impl<'a> Parser<'a> { - fn parse_line(&mut self, line: &'a str) { +macro_rules! matchers { + ( + $($id:ident <- $e:expr;)* + ) => { lazy_static! { - static ref header: Regex = Regex::new(r"(\*)+ (.*)").unwrap(); + $(static ref $id: Regex = Regex::new($e).unwrap();)* } + } +} + +matchers! { + header <- r"^(\*)+ (.*)"; + properties_start <- r"^\s*:PROPERTIES:\s*$"; + property <- r"^\s*:([^:]*): (.*)$"; + properties_end <- r"^\s*:END:\s*$"; +} + +struct Parser<I: Iterator<Item=String>> { + doc: OrgDocument, + iter: I, +} - if let Some(g) = header.captures(line) { - self.doc.push(OrgNode { - raw: line, - e: Org::Header( - g.get(1).unwrap().as_str().len() as u16, - g.get(2).unwrap().as_str() - )}); - } else { - self.doc.push(OrgNode { - raw: line, - e: Org::Unknown - }); +impl<I: Iterator<Item=String>> Parser<I> { + fn go(&mut self) { + while let Some(line) = self.iter.next() { + if let Some(g) = header.captures(&line) { + self.doc.push( + Org::Header( + g.get(1).unwrap().as_str().len() as u16, + g.get(2).unwrap().as_str().to_string())); + } else if properties_start.is_match(&line) { + &mut self.go_properties(line.clone()); + } else { + self.doc.push(Org::Unknown(line.to_string())); + } } } - pub fn parse(input: &'a str) -> OrgDocument<'a> { - let mut parser = Parser{doc: vec!()}; - for line in input.split('\n') { - parser.parse_line(line); + fn go_properties(&mut self, first_line: String) { + let mut fallback = vec!(first_line); + let mut properties = vec!(); + + while let Some(line) = self.iter.next() { + fallback.push(line.clone()); + + if let Some(g) = property.captures(&line) { + properties.push(( + g.get(1).unwrap().as_str().to_string(), + g.get(2).unwrap().as_str().to_string())); + } else if properties_end.is_match(&line) { + break; + } else { + self.doc.push(Org::Unknown(fallback.join("\n"))); + return; + } } - parser.doc + self.doc.push(Org::Properties(properties)); } } +pub fn parse(input: &str) -> OrgDocument { + let mut parser = Parser{ + doc: vec!(), + iter: input.split('\n').map(|line| line.to_string()) + }; + parser.go(); + parser.doc +} + #[cfg(test)] mod tests { use super::*; + fn s<S: AsRef<str>>(m: S) -> String { + m.as_ref().to_string() + } + #[test] fn parse_unknown() { let doc = "hello\nhello"; - assert_eq!(Parser::parse(doc), - vec!(OrgNode { - raw: "hello", - e: Org::Unknown - }, OrgNode { - raw: "hello", - e: Org::Unknown - })); + assert_eq!(parse(doc), + vec!(Org::Unknown("hello".to_string()), + Org::Unknown("hello".to_string()))); } #[test] fn parse_header() { let doc = "* hello"; - assert_eq!(Parser::parse(doc), - vec!(OrgNode { - raw: doc, - e: Org::Header(1, "hello") - })); + assert_eq!(parse(doc), + vec!(Org::Header(1, "hello".to_string()))); + assert_eq!(parse(" * hello"), + vec!(Org::Unknown(" * hello".to_string()))); + } + + #[test] + fn parse_properties() { + let doc = ":PROPERTIES: + :VERSION: 1.0 + :ANIMAL: dog + :END:"; + assert_eq!(parse(doc), + vec!(Org::Properties(vec!( + (s("VERSION"), s("1.0")), + (s("ANIMAL"), s("dog")))))); + let doc = ":PROPERTIES:\ninvalid\n:END:"; + assert_eq!(parse(doc), + vec!(Org::Unknown(s(":PROPERTIES:\ninvalid")), + Org::Unknown(s(":END:")))); } } |