From d9053d992dee9bb96d578d10045573438a6badc5 Mon Sep 17 00:00:00 2001 From: PoiScript Date: Wed, 24 Apr 2019 18:12:51 +0800 Subject: [PATCH] refactor(headline): store headline tags into Vec --- src/headline.rs | 314 ++++++++++++++++++++++-------------------------- src/parser.rs | 16 +-- src/tools.rs | 4 +- 3 files changed, 151 insertions(+), 183 deletions(-) diff --git a/src/headline.rs b/src/headline.rs index 9a11b27..ef5ea49 100644 --- a/src/headline.rs +++ b/src/headline.rs @@ -3,7 +3,7 @@ use jetscii::ByteSubstring; use memchr::{memchr, memchr2, memrchr}; -pub(crate) const DEFAULT_KEYWORDS: &[&str] = +pub(crate) const DEFAULT_TODO_KEYWORDS: &[&str] = &["TODO", "DONE", "NEXT", "WAITING", "LATER", "CANCELLED"]; #[cfg_attr(test, derive(PartialEq))] @@ -14,7 +14,7 @@ pub struct Headline<'a> { /// priority cookie pub priority: Option, /// headline tags, including the sparated colons - pub tags: Option<&'a str>, + pub tags: Vec<&'a str>, /// headline title pub title: &'a str, /// headline keyword @@ -48,7 +48,7 @@ impl<'a> Headline<'a> { keyword: None, priority: None, title: "", - tags: None, + tags: Vec::new(), }, off, end, @@ -86,12 +86,12 @@ impl<'a> Headline<'a> { let (title, tags) = if let Some(i) = memrchr(b' ', tail.as_bytes()) { let last = &tail[i + 1..]; if last.len() > 2 && last.starts_with(':') && last.ends_with(':') { - (tail[..i].trim(), Some(last)) + (tail[..i].trim(), last) } else { - (tail, None) + (tail, "") } } else { - (tail, None) + (tail, "") }; ( @@ -100,7 +100,7 @@ impl<'a> Headline<'a> { keyword, priority, title, - tags, + tags: tags.split(':').filter(|s| !s.is_empty()).collect(), }, off, end, @@ -138,173 +138,141 @@ impl<'a> Headline<'a> { /// checks if this headline is "archived" pub fn is_archived(&self) -> bool { - self.tags - .map(|tags| tags[1..].split_terminator(':').any(|t| t == "ARCHIVE")) - .unwrap_or(false) + self.tags.contains(&"ARCHIVE") } } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse() { - assert_eq!( - Headline::parse("**** TODO [#A] COMMENT Title :tag:a2%:", DEFAULT_KEYWORDS).0, - Headline { - level: 4, - priority: Some('A'), - keyword: Some("TODO"), - title: "COMMENT Title", - tags: Some(":tag:a2%:"), - }, - ); - assert_eq!( - Headline::parse("**** ToDO [#A] COMMENT Title :tag:a2%:", DEFAULT_KEYWORDS).0, - Headline { - level: 4, - priority: None, - tags: Some(":tag:a2%:"), - title: "ToDO [#A] COMMENT Title", - keyword: None, - }, - ); - assert_eq!( - Headline::parse("**** T0DO [#A] COMMENT Title :tag:a2%:", DEFAULT_KEYWORDS).0, - Headline { - level: 4, - priority: None, - tags: Some(":tag:a2%:"), - title: "T0DO [#A] COMMENT Title", - keyword: None, - }, - ); - assert_eq!( - Headline::parse("**** TODO [#1] COMMENT Title :tag:a2%:", DEFAULT_KEYWORDS).0, - Headline { - level: 4, - priority: None, - tags: Some(":tag:a2%:"), - title: "[#1] COMMENT Title", - keyword: Some("TODO") - }, - ); - assert_eq!( - Headline::parse("**** TODO [#a] COMMENT Title :tag:a2%:", DEFAULT_KEYWORDS).0, - Headline { - level: 4, - priority: None, - tags: Some(":tag:a2%:"), - title: "[#a] COMMENT Title", - keyword: Some("TODO") - }, - ); - assert_eq!( - Headline::parse("**** TODO [#A] COMMENT Title :tag:a2%", DEFAULT_KEYWORDS).0, - Headline { - level: 4, - priority: Some('A'), - tags: None, - title: "COMMENT Title :tag:a2%", - keyword: Some("TODO"), - }, - ); - assert_eq!( - Headline::parse("**** TODO [#A] COMMENT Title tag:a2%:", DEFAULT_KEYWORDS).0, - Headline { - level: 4, - priority: Some('A'), - tags: None, - title: "COMMENT Title tag:a2%:", - keyword: Some("TODO"), - }, - ); - assert_eq!( - Headline::parse("**** COMMENT Title tag:a2%:", DEFAULT_KEYWORDS).0, - Headline { - level: 4, - priority: None, - tags: None, - title: "COMMENT Title tag:a2%:", - keyword: None, - }, - ); - - assert_eq!( - Headline::parse("**** TODO [#A] COMMENT Title :tag:a2%:", &[]).0, - Headline { - level: 4, - priority: None, - keyword: None, - title: "TODO [#A] COMMENT Title", - tags: Some(":tag:a2%:"), - }, - ); - assert_eq!( - Headline::parse("**** TASK [#A] COMMENT Title :tag:a2%:", &["TASK"]).0, - Headline { - level: 4, - priority: Some('A'), - keyword: Some("TASK"), - title: "COMMENT Title", - tags: Some(":tag:a2%:"), - }, - ); - } - - #[test] - fn is_commented() { - assert!(Headline::parse("* COMMENT Title", DEFAULT_KEYWORDS) - .0 - .is_commented()); - assert!(!Headline::parse("* Title", DEFAULT_KEYWORDS) - .0 - .is_commented()); - assert!(!Headline::parse("* C0MMENT Title", DEFAULT_KEYWORDS) - .0 - .is_commented()); - assert!(!Headline::parse("* comment Title", DEFAULT_KEYWORDS) - .0 - .is_commented()); - } - - #[test] - fn is_archived() { - assert!(Headline::parse("* Title :ARCHIVE:", DEFAULT_KEYWORDS) - .0 - .is_archived()); - assert!(Headline::parse("* Title :tag:ARCHIVE:", DEFAULT_KEYWORDS) - .0 - .is_archived()); - assert!(Headline::parse("* Title :ARCHIVE:tag:", DEFAULT_KEYWORDS) - .0 - .is_archived()); - assert!(!Headline::parse("* Title", DEFAULT_KEYWORDS) - .0 - .is_commented()); - assert!(!Headline::parse("* Title :ARCHIVED:", DEFAULT_KEYWORDS) - .0 - .is_archived()); - assert!(!Headline::parse("* Title :ARCHIVES:", DEFAULT_KEYWORDS) - .0 - .is_archived()); - assert!(!Headline::parse("* Title :archive:", DEFAULT_KEYWORDS) - .0 - .is_archived()); - } - - #[test] - fn find_level() { - assert_eq!( - Headline::find_level( - r#" -** Title -* Title -** Title"#, - 1 - ), - 10 - ); - } +#[test] +fn parse() { + assert_eq!( + Headline::parse("**** TODO [#A] COMMENT Title :tag:a2%:", &["TODO"]).0, + Headline { + level: 4, + priority: Some('A'), + keyword: Some("TODO"), + title: "COMMENT Title", + tags: vec!["tag", "a2%"], + }, + ); + assert_eq!( + Headline::parse("**** ToDO [#A] COMMENT Title :tag:a2%:", &["TODO"]).0, + Headline { + level: 4, + priority: None, + tags: vec!["tag", "a2%"], + title: "ToDO [#A] COMMENT Title", + keyword: None, + }, + ); + assert_eq!( + Headline::parse("**** T0DO [#A] COMMENT Title :tag:a2%:", &["TODO"]).0, + Headline { + level: 4, + priority: None, + tags: vec!["tag", "a2%"], + title: "T0DO [#A] COMMENT Title", + keyword: None, + }, + ); + assert_eq!( + Headline::parse("**** TODO [#1] COMMENT Title :tag:a2%:", &["TODO"]).0, + Headline { + level: 4, + priority: None, + tags: vec!["tag", "a2%"], + title: "[#1] COMMENT Title", + keyword: Some("TODO") + }, + ); + assert_eq!( + Headline::parse("**** TODO [#a] COMMENT Title :tag:a2%:", &["TODO"]).0, + Headline { + level: 4, + priority: None, + tags: vec!["tag", "a2%"], + title: "[#a] COMMENT Title", + keyword: Some("TODO") + }, + ); + assert_eq!( + Headline::parse("**** TODO [#A] COMMENT Title :tag:a2%", &["TODO"]).0, + Headline { + level: 4, + priority: Some('A'), + tags: Vec::new(), + title: "COMMENT Title :tag:a2%", + keyword: Some("TODO"), + }, + ); + assert_eq!( + Headline::parse("**** TODO [#A] COMMENT Title tag:a2%:", &["TODO"]).0, + Headline { + level: 4, + priority: Some('A'), + tags: Vec::new(), + title: "COMMENT Title tag:a2%:", + keyword: Some("TODO"), + }, + ); + assert_eq!( + Headline::parse("**** COMMENT Title tag:a2%:", &["TODO"]).0, + Headline { + level: 4, + priority: None, + tags: Vec::new(), + title: "COMMENT Title tag:a2%:", + keyword: None, + }, + ); +} + +#[test] +fn parse_todo_keywords() { + assert_eq!( + Headline::parse("**** TODO [#A] COMMENT Title :tag:a2%:", &[]).0, + Headline { + level: 4, + priority: None, + keyword: None, + title: "TODO [#A] COMMENT Title", + tags: vec!["tag", "a2%"], + }, + ); + assert_eq!( + Headline::parse("**** TASK [#A] COMMENT Title :tag:a2%:", &["TASK"]).0, + Headline { + level: 4, + priority: Some('A'), + keyword: Some("TASK"), + title: "COMMENT Title", + tags: vec!["tag", "a2%"], + }, + ); +} + +#[test] +fn is_commented() { + assert!(Headline::parse("* COMMENT Title", &[]).0.is_commented()); + assert!(!Headline::parse("* Title", &[]).0.is_commented()); + assert!(!Headline::parse("* C0MMENT Title", &[]).0.is_commented()); + assert!(!Headline::parse("* comment Title", &[]).0.is_commented()); +} + +#[test] +fn is_archived() { + assert!(Headline::parse("* Title :ARCHIVE:", &[]).0.is_archived()); + assert!(Headline::parse("* Title :t:ARCHIVE:", &[]).0.is_archived()); + assert!(Headline::parse("* Title :ARCHIVE:t:", &[]).0.is_archived()); + assert!(!Headline::parse("* Title", &[]).0.is_commented()); + assert!(!Headline::parse("* Title :ARCHIVED:", &[]).0.is_archived()); + assert!(!Headline::parse("* Title :ARCHIVES:", &[]).0.is_archived()); + assert!(!Headline::parse("* Title :archive:", &[]).0.is_archived()); +} + +#[test] +fn find_level() { + assert_eq!( + Headline::find_level("\n** Title\n* Title\n** Title\n", 1), + "\n** Title\n".len() + ); } diff --git a/src/parser.rs b/src/parser.rs index a1ec5de..d7d0b32 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -144,7 +144,7 @@ pub struct Parser<'a> { off: usize, ele_buf: Option<(Event<'a>, usize, usize, usize)>, obj_buf: Option<(Event<'a>, usize, usize, usize)>, - keywords: &'a [&'a str], + todo_keywords: &'a [&'a str], } impl<'a> Parser<'a> { @@ -157,12 +157,12 @@ impl<'a> Parser<'a> { off: 0, ele_buf: None, obj_buf: None, - keywords: DEFAULT_KEYWORDS, + todo_keywords: DEFAULT_TODO_KEYWORDS, } } /// creates a new parser from string, with the specified keywords - pub fn with_keywrods(text: &'a str, keywords: &'a [&'a str]) -> Parser<'a> { + pub fn with_todo_keywrods(text: &'a str, todo_keywords: &'a [&'a str]) -> Parser<'a> { Parser { text, stack: Vec::new(), @@ -170,7 +170,7 @@ impl<'a> Parser<'a> { off: 0, ele_buf: None, obj_buf: None, - keywords, + todo_keywords, } } @@ -184,9 +184,9 @@ impl<'a> Parser<'a> { self.stack.len() } - /// set keywords - pub fn set_keywords(&mut self, keywords: &'a [&'a str]) { - self.keywords = keywords; + /// set todo keywords + pub fn set_todo_keywords(&mut self, todo_keywords: &'a [&'a str]) { + self.todo_keywords = todo_keywords; } /// set text @@ -210,7 +210,7 @@ impl<'a> Parser<'a> { } fn next_headline(&mut self, text: &'a str) -> Event<'a> { - let (hdl, off, end) = Headline::parse(text, self.keywords); + let (hdl, off, end) = Headline::parse(text, self.todo_keywords); self.push_stack(Container::Headline(self.off + off), end, end); self.off += off; Event::HeadlineBeg(hdl) diff --git a/src/tools.rs b/src/tools.rs index 4657b8a..85792d8 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -1,5 +1,5 @@ use crate::elements::{fn_def, Keyword}; -use crate::headline::{Headline, DEFAULT_KEYWORDS}; +use crate::headline::{Headline, DEFAULT_TODO_KEYWORDS}; use memchr::memchr; type Headlines<'a> = Vec>; @@ -13,7 +13,7 @@ pub fn metadata(src: &str) -> (Headlines<'_>, Keywords<'_>, Footnotes<'_>) { if line.starts_with('*') { let level = memchr(b' ', line.as_bytes()).unwrap_or_else(|| line.len()); if line.as_bytes()[0..level].iter().all(|&c| c == b'*') { - headlines.push(Headline::parse(line, DEFAULT_KEYWORDS).0) + headlines.push(Headline::parse(line, DEFAULT_TODO_KEYWORDS).0) } } else if line.starts_with("#+") { if let Some((key, _, value, _)) = Keyword::parse(line) {