refactor(headline): store headline tags into Vec
This commit is contained in:
parent
56e289fb48
commit
d9053d992d
148
src/headline.rs
148
src/headline.rs
|
@ -3,7 +3,7 @@
|
||||||
use jetscii::ByteSubstring;
|
use jetscii::ByteSubstring;
|
||||||
use memchr::{memchr, memchr2, memrchr};
|
use memchr::{memchr, memchr2, memrchr};
|
||||||
|
|
||||||
pub(crate) const DEFAULT_KEYWORDS: &[&str] =
|
pub(crate) const DEFAULT_TODO_KEYWORDS: &[&str] =
|
||||||
&["TODO", "DONE", "NEXT", "WAITING", "LATER", "CANCELLED"];
|
&["TODO", "DONE", "NEXT", "WAITING", "LATER", "CANCELLED"];
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
|
@ -14,7 +14,7 @@ pub struct Headline<'a> {
|
||||||
/// priority cookie
|
/// priority cookie
|
||||||
pub priority: Option<char>,
|
pub priority: Option<char>,
|
||||||
/// headline tags, including the sparated colons
|
/// headline tags, including the sparated colons
|
||||||
pub tags: Option<&'a str>,
|
pub tags: Vec<&'a str>,
|
||||||
/// headline title
|
/// headline title
|
||||||
pub title: &'a str,
|
pub title: &'a str,
|
||||||
/// headline keyword
|
/// headline keyword
|
||||||
|
@ -48,7 +48,7 @@ impl<'a> Headline<'a> {
|
||||||
keyword: None,
|
keyword: None,
|
||||||
priority: None,
|
priority: None,
|
||||||
title: "",
|
title: "",
|
||||||
tags: None,
|
tags: Vec::new(),
|
||||||
},
|
},
|
||||||
off,
|
off,
|
||||||
end,
|
end,
|
||||||
|
@ -86,12 +86,12 @@ impl<'a> Headline<'a> {
|
||||||
let (title, tags) = if let Some(i) = memrchr(b' ', tail.as_bytes()) {
|
let (title, tags) = if let Some(i) = memrchr(b' ', tail.as_bytes()) {
|
||||||
let last = &tail[i + 1..];
|
let last = &tail[i + 1..];
|
||||||
if last.len() > 2 && last.starts_with(':') && last.ends_with(':') {
|
if last.len() > 2 && last.starts_with(':') && last.ends_with(':') {
|
||||||
(tail[..i].trim(), Some(last))
|
(tail[..i].trim(), last)
|
||||||
} else {
|
} else {
|
||||||
(tail, None)
|
(tail, "")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(tail, None)
|
(tail, "")
|
||||||
};
|
};
|
||||||
|
|
||||||
(
|
(
|
||||||
|
@ -100,7 +100,7 @@ impl<'a> Headline<'a> {
|
||||||
keyword,
|
keyword,
|
||||||
priority,
|
priority,
|
||||||
title,
|
title,
|
||||||
tags,
|
tags: tags.split(':').filter(|s| !s.is_empty()).collect(),
|
||||||
},
|
},
|
||||||
off,
|
off,
|
||||||
end,
|
end,
|
||||||
|
@ -138,99 +138,96 @@ impl<'a> Headline<'a> {
|
||||||
|
|
||||||
/// checks if this headline is "archived"
|
/// checks if this headline is "archived"
|
||||||
pub fn is_archived(&self) -> bool {
|
pub fn is_archived(&self) -> bool {
|
||||||
self.tags
|
self.tags.contains(&"ARCHIVE")
|
||||||
.map(|tags| tags[1..].split_terminator(':').any(|t| t == "ARCHIVE"))
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[test]
|
||||||
mod tests {
|
fn parse() {
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse() {
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Headline::parse("**** TODO [#A] COMMENT Title :tag:a2%:", DEFAULT_KEYWORDS).0,
|
Headline::parse("**** TODO [#A] COMMENT Title :tag:a2%:", &["TODO"]).0,
|
||||||
Headline {
|
Headline {
|
||||||
level: 4,
|
level: 4,
|
||||||
priority: Some('A'),
|
priority: Some('A'),
|
||||||
keyword: Some("TODO"),
|
keyword: Some("TODO"),
|
||||||
title: "COMMENT Title",
|
title: "COMMENT Title",
|
||||||
tags: Some(":tag:a2%:"),
|
tags: vec!["tag", "a2%"],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Headline::parse("**** ToDO [#A] COMMENT Title :tag:a2%:", DEFAULT_KEYWORDS).0,
|
Headline::parse("**** ToDO [#A] COMMENT Title :tag:a2%:", &["TODO"]).0,
|
||||||
Headline {
|
Headline {
|
||||||
level: 4,
|
level: 4,
|
||||||
priority: None,
|
priority: None,
|
||||||
tags: Some(":tag:a2%:"),
|
tags: vec!["tag", "a2%"],
|
||||||
title: "ToDO [#A] COMMENT Title",
|
title: "ToDO [#A] COMMENT Title",
|
||||||
keyword: None,
|
keyword: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Headline::parse("**** T0DO [#A] COMMENT Title :tag:a2%:", DEFAULT_KEYWORDS).0,
|
Headline::parse("**** T0DO [#A] COMMENT Title :tag:a2%:", &["TODO"]).0,
|
||||||
Headline {
|
Headline {
|
||||||
level: 4,
|
level: 4,
|
||||||
priority: None,
|
priority: None,
|
||||||
tags: Some(":tag:a2%:"),
|
tags: vec!["tag", "a2%"],
|
||||||
title: "T0DO [#A] COMMENT Title",
|
title: "T0DO [#A] COMMENT Title",
|
||||||
keyword: None,
|
keyword: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Headline::parse("**** TODO [#1] COMMENT Title :tag:a2%:", DEFAULT_KEYWORDS).0,
|
Headline::parse("**** TODO [#1] COMMENT Title :tag:a2%:", &["TODO"]).0,
|
||||||
Headline {
|
Headline {
|
||||||
level: 4,
|
level: 4,
|
||||||
priority: None,
|
priority: None,
|
||||||
tags: Some(":tag:a2%:"),
|
tags: vec!["tag", "a2%"],
|
||||||
title: "[#1] COMMENT Title",
|
title: "[#1] COMMENT Title",
|
||||||
keyword: Some("TODO")
|
keyword: Some("TODO")
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Headline::parse("**** TODO [#a] COMMENT Title :tag:a2%:", DEFAULT_KEYWORDS).0,
|
Headline::parse("**** TODO [#a] COMMENT Title :tag:a2%:", &["TODO"]).0,
|
||||||
Headline {
|
Headline {
|
||||||
level: 4,
|
level: 4,
|
||||||
priority: None,
|
priority: None,
|
||||||
tags: Some(":tag:a2%:"),
|
tags: vec!["tag", "a2%"],
|
||||||
title: "[#a] COMMENT Title",
|
title: "[#a] COMMENT Title",
|
||||||
keyword: Some("TODO")
|
keyword: Some("TODO")
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Headline::parse("**** TODO [#A] COMMENT Title :tag:a2%", DEFAULT_KEYWORDS).0,
|
Headline::parse("**** TODO [#A] COMMENT Title :tag:a2%", &["TODO"]).0,
|
||||||
Headline {
|
Headline {
|
||||||
level: 4,
|
level: 4,
|
||||||
priority: Some('A'),
|
priority: Some('A'),
|
||||||
tags: None,
|
tags: Vec::new(),
|
||||||
title: "COMMENT Title :tag:a2%",
|
title: "COMMENT Title :tag:a2%",
|
||||||
keyword: Some("TODO"),
|
keyword: Some("TODO"),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Headline::parse("**** TODO [#A] COMMENT Title tag:a2%:", DEFAULT_KEYWORDS).0,
|
Headline::parse("**** TODO [#A] COMMENT Title tag:a2%:", &["TODO"]).0,
|
||||||
Headline {
|
Headline {
|
||||||
level: 4,
|
level: 4,
|
||||||
priority: Some('A'),
|
priority: Some('A'),
|
||||||
tags: None,
|
tags: Vec::new(),
|
||||||
title: "COMMENT Title tag:a2%:",
|
title: "COMMENT Title tag:a2%:",
|
||||||
keyword: Some("TODO"),
|
keyword: Some("TODO"),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Headline::parse("**** COMMENT Title tag:a2%:", DEFAULT_KEYWORDS).0,
|
Headline::parse("**** COMMENT Title tag:a2%:", &["TODO"]).0,
|
||||||
Headline {
|
Headline {
|
||||||
level: 4,
|
level: 4,
|
||||||
priority: None,
|
priority: None,
|
||||||
tags: None,
|
tags: Vec::new(),
|
||||||
title: "COMMENT Title tag:a2%:",
|
title: "COMMENT Title tag:a2%:",
|
||||||
keyword: None,
|
keyword: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_todo_keywords() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Headline::parse("**** TODO [#A] COMMENT Title :tag:a2%:", &[]).0,
|
Headline::parse("**** TODO [#A] COMMENT Title :tag:a2%:", &[]).0,
|
||||||
Headline {
|
Headline {
|
||||||
|
@ -238,7 +235,7 @@ mod tests {
|
||||||
priority: None,
|
priority: None,
|
||||||
keyword: None,
|
keyword: None,
|
||||||
title: "TODO [#A] COMMENT Title",
|
title: "TODO [#A] COMMENT Title",
|
||||||
tags: Some(":tag:a2%:"),
|
tags: vec!["tag", "a2%"],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -248,63 +245,34 @@ mod tests {
|
||||||
priority: Some('A'),
|
priority: Some('A'),
|
||||||
keyword: Some("TASK"),
|
keyword: Some("TASK"),
|
||||||
title: "COMMENT Title",
|
title: "COMMENT Title",
|
||||||
tags: Some(":tag:a2%:"),
|
tags: vec!["tag", "a2%"],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_commented() {
|
fn is_commented() {
|
||||||
assert!(Headline::parse("* COMMENT Title", DEFAULT_KEYWORDS)
|
assert!(Headline::parse("* COMMENT Title", &[]).0.is_commented());
|
||||||
.0
|
assert!(!Headline::parse("* Title", &[]).0.is_commented());
|
||||||
.is_commented());
|
assert!(!Headline::parse("* C0MMENT Title", &[]).0.is_commented());
|
||||||
assert!(!Headline::parse("* Title", DEFAULT_KEYWORDS)
|
assert!(!Headline::parse("* comment Title", &[]).0.is_commented());
|
||||||
.0
|
}
|
||||||
.is_commented());
|
|
||||||
assert!(!Headline::parse("* C0MMENT Title", DEFAULT_KEYWORDS)
|
#[test]
|
||||||
.0
|
fn is_archived() {
|
||||||
.is_commented());
|
assert!(Headline::parse("* Title :ARCHIVE:", &[]).0.is_archived());
|
||||||
assert!(!Headline::parse("* comment Title", DEFAULT_KEYWORDS)
|
assert!(Headline::parse("* Title :t:ARCHIVE:", &[]).0.is_archived());
|
||||||
.0
|
assert!(Headline::parse("* Title :ARCHIVE:t:", &[]).0.is_archived());
|
||||||
.is_commented());
|
assert!(!Headline::parse("* Title", &[]).0.is_commented());
|
||||||
}
|
assert!(!Headline::parse("* Title :ARCHIVED:", &[]).0.is_archived());
|
||||||
|
assert!(!Headline::parse("* Title :ARCHIVES:", &[]).0.is_archived());
|
||||||
#[test]
|
assert!(!Headline::parse("* Title :archive:", &[]).0.is_archived());
|
||||||
fn is_archived() {
|
}
|
||||||
assert!(Headline::parse("* Title :ARCHIVE:", DEFAULT_KEYWORDS)
|
|
||||||
.0
|
#[test]
|
||||||
.is_archived());
|
fn find_level() {
|
||||||
assert!(Headline::parse("* Title :tag:ARCHIVE:", DEFAULT_KEYWORDS)
|
assert_eq!(
|
||||||
.0
|
Headline::find_level("\n** Title\n* Title\n** Title\n", 1),
|
||||||
.is_archived());
|
"\n** Title\n".len()
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,7 +144,7 @@ pub struct Parser<'a> {
|
||||||
off: usize,
|
off: usize,
|
||||||
ele_buf: Option<(Event<'a>, usize, usize, usize)>,
|
ele_buf: Option<(Event<'a>, usize, usize, usize)>,
|
||||||
obj_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> {
|
impl<'a> Parser<'a> {
|
||||||
|
@ -157,12 +157,12 @@ impl<'a> Parser<'a> {
|
||||||
off: 0,
|
off: 0,
|
||||||
ele_buf: None,
|
ele_buf: None,
|
||||||
obj_buf: None,
|
obj_buf: None,
|
||||||
keywords: DEFAULT_KEYWORDS,
|
todo_keywords: DEFAULT_TODO_KEYWORDS,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// creates a new parser from string, with the specified 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 {
|
Parser {
|
||||||
text,
|
text,
|
||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
|
@ -170,7 +170,7 @@ impl<'a> Parser<'a> {
|
||||||
off: 0,
|
off: 0,
|
||||||
ele_buf: None,
|
ele_buf: None,
|
||||||
obj_buf: None,
|
obj_buf: None,
|
||||||
keywords,
|
todo_keywords,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,9 +184,9 @@ impl<'a> Parser<'a> {
|
||||||
self.stack.len()
|
self.stack.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// set keywords
|
/// set todo keywords
|
||||||
pub fn set_keywords(&mut self, keywords: &'a [&'a str]) {
|
pub fn set_todo_keywords(&mut self, todo_keywords: &'a [&'a str]) {
|
||||||
self.keywords = keywords;
|
self.todo_keywords = todo_keywords;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// set text
|
/// set text
|
||||||
|
@ -210,7 +210,7 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_headline(&mut self, text: &'a str) -> Event<'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.push_stack(Container::Headline(self.off + off), end, end);
|
||||||
self.off += off;
|
self.off += off;
|
||||||
Event::HeadlineBeg(hdl)
|
Event::HeadlineBeg(hdl)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::elements::{fn_def, Keyword};
|
use crate::elements::{fn_def, Keyword};
|
||||||
use crate::headline::{Headline, DEFAULT_KEYWORDS};
|
use crate::headline::{Headline, DEFAULT_TODO_KEYWORDS};
|
||||||
use memchr::memchr;
|
use memchr::memchr;
|
||||||
|
|
||||||
type Headlines<'a> = Vec<Headline<'a>>;
|
type Headlines<'a> = Vec<Headline<'a>>;
|
||||||
|
@ -13,7 +13,7 @@ pub fn metadata(src: &str) -> (Headlines<'_>, Keywords<'_>, Footnotes<'_>) {
|
||||||
if line.starts_with('*') {
|
if line.starts_with('*') {
|
||||||
let level = memchr(b' ', line.as_bytes()).unwrap_or_else(|| line.len());
|
let level = memchr(b' ', line.as_bytes()).unwrap_or_else(|| line.len());
|
||||||
if line.as_bytes()[0..level].iter().all(|&c| c == b'*') {
|
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("#+") {
|
} else if line.starts_with("#+") {
|
||||||
if let Some((key, _, value, _)) = Keyword::parse(line) {
|
if let Some((key, _, value, _)) = Keyword::parse(line) {
|
||||||
|
|
Loading…
Reference in a new issue