docs: some doc comments

This commit is contained in:
PoiScript 2019-04-12 22:19:07 +08:00
parent 2467cb3db3
commit 0101612029
9 changed files with 182 additions and 120 deletions

View file

@ -1,18 +1,23 @@
use crate::objects::timestamp::{Datetime, Delay, Repeater, Timestamp}; use crate::objects::timestamp::{Datetime, Delay, Repeater, Timestamp};
use memchr::memchr; use memchr::memchr;
/// clock elements
///
/// there are two types of clock: *closed* clock and *running* clock.
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[derive(Debug)] #[derive(Debug)]
pub enum Clock<'a> { pub enum Clock<'a> {
/// closed Clock
Closed { Closed {
start: Datetime, start: Datetime<'a>,
end: Datetime, end: Datetime<'a>,
repeater: Option<Repeater>, repeater: Option<Repeater>,
delay: Option<Delay>, delay: Option<Delay>,
duration: &'a str, duration: &'a str,
}, },
/// running Clock
Running { Running {
start: Datetime, start: Datetime<'a>,
repeater: Option<Repeater>, repeater: Option<Repeater>,
delay: Option<Delay>, delay: Option<Delay>,
}, },
@ -88,6 +93,7 @@ impl<'a> Clock<'a> {
None None
} }
/// returns `true` if the clock is running
pub fn is_running(&self) -> bool { pub fn is_running(&self) -> bool {
match self { match self {
Clock::Closed { .. } => false, Clock::Closed { .. } => false,
@ -95,6 +101,7 @@ impl<'a> Clock<'a> {
} }
} }
/// returns `true` if the clock is closed
pub fn is_closed(&self) -> bool { pub fn is_closed(&self) -> bool {
match self { match self {
Clock::Closed { .. } => true, Clock::Closed { .. } => true,
@ -102,6 +109,7 @@ impl<'a> Clock<'a> {
} }
} }
/// returns `Some` if the clock is closed, `None` if running
pub fn duration(&self) -> Option<&'a str> { pub fn duration(&self) -> Option<&'a str> {
match self { match self {
Clock::Closed { duration, .. } => Some(duration), Clock::Closed { duration, .. } => Some(duration),
@ -109,6 +117,7 @@ impl<'a> Clock<'a> {
} }
} }
/// constructs a new timestamp object from the clock
pub fn value(&self) -> Timestamp<'_> { pub fn value(&self) -> Timestamp<'_> {
match *self { match *self {
Clock::Closed { Clock::Closed {
@ -150,7 +159,8 @@ mod tests {
Clock::Running { Clock::Running {
start: Datetime { start: Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
time: Some((9, 39)) time: Some((9, 39)),
dayname: "Tue"
}, },
repeater: None, repeater: None,
delay: None, delay: None,
@ -164,11 +174,13 @@ mod tests {
Clock::Closed { Clock::Closed {
start: Datetime { start: Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
time: Some((9, 39)) time: Some((9, 39)),
dayname: "Tue"
}, },
end: Datetime { end: Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
time: Some((10, 39)) time: Some((10, 39)),
dayname: "Tue"
}, },
repeater: None, repeater: None,
delay: None, delay: None,

View file

@ -1,3 +1,6 @@
/// elements
///
/// elements means some syntactical parts that have the same level with paragraph.
pub(crate) mod block; pub(crate) mod block;
pub(crate) mod clock; pub(crate) mod clock;
pub(crate) mod drawer; pub(crate) mod drawer;

View file

@ -1,11 +1,15 @@
use crate::objects::timestamp::Timestamp; use crate::objects::timestamp::Timestamp;
use memchr::memchr; use memchr::memchr;
/// palnning elements
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[derive(Debug)] #[derive(Debug)]
pub struct Planning<'a> { pub struct Planning<'a> {
/// the date when the task should be done
pub deadline: Option<Timestamp<'a>>, pub deadline: Option<Timestamp<'a>>,
/// the date when you should start working on the task
pub scheduled: Option<Timestamp<'a>>, pub scheduled: Option<Timestamp<'a>>,
/// the date when the task is closed
pub closed: Option<Timestamp<'a>>, pub closed: Option<Timestamp<'a>>,
} }
@ -22,18 +26,9 @@ impl<'a> Planning<'a> {
macro_rules! set_timestamp { macro_rules! set_timestamp {
($timestamp:expr) => { ($timestamp:expr) => {
if $timestamp.is_none() { if $timestamp.is_none() {
if next.starts_with('<') { let (timestamp, off) = Timestamp::parse(next)?;
let (timestamp, off) = Timestamp::parse_active(next) $timestamp = Some(timestamp);
.or_else(|| Timestamp::parse_diary(next))?; tail = &next[off..].trim_start();
$timestamp = Some(timestamp);
tail = &next[off..].trim_start();
} else if next.starts_with('<') {
let (timestamp, off) = Timestamp::parse_active(next)?;
$timestamp = Some(timestamp);
tail = &next[off..].trim_start();
} else {
return None;
}
} else { } else {
return None; return None;
} }
@ -77,7 +72,8 @@ mod tests {
scheduled: Some(Timestamp::Active { scheduled: Some(Timestamp::Active {
start: Datetime { start: Datetime {
date: (2019, 4, 8), date: (2019, 4, 8),
time: None time: None,
dayname: "Mon"
}, },
repeater: None, repeater: None,
delay: None delay: None

View file

@ -90,6 +90,7 @@
//! let result = String::from_utf8(cursor.into_inner()).expect("invalid utf-8"); //! let result = String::from_utf8(cursor.into_inner()).expect("invalid utf-8");
//! ``` //! ```
#[warn(missing_docs)]
pub mod elements; pub mod elements;
pub mod export; pub mod export;
pub mod headline; pub mod headline;

View file

@ -1,3 +1,6 @@
/// objects
///
/// objects is something that included in an element.
pub(crate) mod cookie; pub(crate) mod cookie;
pub(crate) mod emphasis; pub(crate) mod emphasis;
pub(crate) mod fn_ref; pub(crate) mod fn_ref;

View file

@ -6,7 +6,7 @@ pub fn parse(text: &str) -> Option<(&str, usize)> {
let bytes = text.as_bytes(); let bytes = text.as_bytes();
let (target, off) = Substring::new(">>") Substring::new(">>")
.find(text) .find(text)
.filter(|&i| { .filter(|&i| {
bytes[2] != b' ' bytes[2] != b' '
@ -15,9 +15,7 @@ pub fn parse(text: &str) -> Option<(&str, usize)> {
.iter() .iter()
.all(|&c| c != b'<' && c != b'\n' && c != b'>') .all(|&c| c != b'<' && c != b'\n' && c != b'>')
}) })
.map(|i| (&text[2..i], i + 2 /* >> */))?; .map(|i| (&text[2..i], i + 2 /* >> */))
Some((target, off))
} }
#[cfg(test)] #[cfg(test)]

View file

@ -2,9 +2,10 @@ use memchr::memchr;
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Datetime { pub struct Datetime<'a> {
pub date: (u16, u8, u8), pub date: (u16, u8, u8),
pub time: Option<(u8, u8)>, pub time: Option<(u8, u8)>,
pub dayname: &'a str,
} }
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
@ -48,28 +49,29 @@ pub struct Delay {
pub unit: TimeUnit, pub unit: TimeUnit,
} }
/// timestamp obejcts
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[derive(Debug)] #[derive(Debug)]
pub enum Timestamp<'a> { pub enum Timestamp<'a> {
Active { Active {
start: Datetime, start: Datetime<'a>,
repeater: Option<Repeater>, repeater: Option<Repeater>,
delay: Option<Delay>, delay: Option<Delay>,
}, },
Inactive { Inactive {
start: Datetime, start: Datetime<'a>,
repeater: Option<Repeater>, repeater: Option<Repeater>,
delay: Option<Delay>, delay: Option<Delay>,
}, },
ActiveRange { ActiveRange {
start: Datetime, start: Datetime<'a>,
end: Datetime, end: Datetime<'a>,
repeater: Option<Repeater>, repeater: Option<Repeater>,
delay: Option<Delay>, delay: Option<Delay>,
}, },
InactiveRange { InactiveRange {
start: Datetime, start: Datetime<'a>,
end: Datetime, end: Datetime<'a>,
repeater: Option<Repeater>, repeater: Option<Repeater>,
delay: Option<Delay>, delay: Option<Delay>,
}, },
@ -77,18 +79,27 @@ pub enum Timestamp<'a> {
} }
impl<'a> Timestamp<'a> { impl<'a> Timestamp<'a> {
pub(crate) fn parse_active(text: &str) -> Option<(Timestamp<'_>, usize)> { pub(crate) fn parse(text: &'a str) -> Option<(Timestamp<'a>, usize)> {
if text.starts_with('<') {
Timestamp::parse_active(text).or_else(|| Timestamp::parse_diary(text))
} else if text.starts_with('[') {
Timestamp::parse_inactive(text)
} else {
None
}
}
pub(crate) fn parse_active(text: &'a str) -> Option<(Timestamp<'a>, usize)> {
debug_assert!(text.starts_with('<')); debug_assert!(text.starts_with('<'));
let bytes = text.as_bytes(); let bytes = text.as_bytes();
let mut off = memchr(b'>', bytes)?; let mut off = memchr(b'>', bytes)?;
let (start, mut end) = Self::parse_datetime(&bytes[1..off])?; let (start, mut end) = Self::parse_datetime(&text[1..off])?;
if end.is_none()
&& off <= text.len() - 14 /* --<YYYY-MM-DD> */ if end.is_none() && off <= text.len() - 14 /* --<YYYY-MM-DD> */ && text[off + 1..].starts_with("--<")
&& text[off + 1..].starts_with("--<")
{ {
if let Some(new_off) = memchr(b'>', &bytes[off + 1..]) { if let Some(new_off) = memchr(b'>', &bytes[off + 1..]) {
if let Some((start, _)) = Self::parse_datetime(&bytes[off + 4..off + 1 + new_off]) { if let Some((start, _)) = Self::parse_datetime(&text[off + 4..off + 1 + new_off]) {
end = Some(start); end = Some(start);
off += new_off + 1; off += new_off + 1;
} }
@ -114,18 +125,16 @@ impl<'a> Timestamp<'a> {
)) ))
} }
pub(crate) fn parse_inactive(text: &str) -> Option<(Timestamp<'_>, usize)> { pub(crate) fn parse_inactive(text: &'a str) -> Option<(Timestamp<'a>, usize)> {
debug_assert!(text.starts_with('[')); debug_assert!(text.starts_with('['));
let bytes = text.as_bytes(); let bytes = text.as_bytes();
let mut off = memchr(b']', bytes)?; let mut off = memchr(b']', bytes)?;
let (start, mut end) = Self::parse_datetime(&bytes[1..off])?; let (start, mut end) = Self::parse_datetime(&text[1..off])?;
if end.is_none() if end.is_none() && off <= text.len() - 14 /* --[YYYY-MM-DD] */ && text[off + 1..].starts_with("--[")
&& off <= text.len() - 14 /* --[YYYY-MM-DD] */
&& text[off + 1..].starts_with("--[")
{ {
if let Some(new_off) = memchr(b']', &bytes[off + 1..]) { if let Some(new_off) = memchr(b']', &bytes[off + 1..]) {
if let Some((start, _)) = Self::parse_datetime(&bytes[off + 4..off + 1 + new_off]) { if let Some((start, _)) = Self::parse_datetime(&text[off + 4..off + 1 + new_off]) {
end = Some(start); end = Some(start);
off += new_off + 1; off += new_off + 1;
} }
@ -151,30 +160,30 @@ impl<'a> Timestamp<'a> {
)) ))
} }
fn parse_datetime(bytes: &[u8]) -> Option<(Datetime, Option<Datetime>)> { fn parse_datetime(text: &'a str) -> Option<(Datetime<'a>, Option<Datetime<'a>>)> {
if bytes.is_empty() if text.is_empty()
|| !bytes[0].is_ascii_digit() || !text.starts_with(|c: char| c.is_ascii_digit())
|| !bytes[bytes.len() - 1].is_ascii_alphanumeric() || !text.ends_with(|c: char| c.is_ascii_alphanumeric())
{ {
return None; return None;
} }
// similar to str::split_ascii_whitespace, but for &[u8] let mut words = text.split_ascii_whitespace();
let mut words = bytes
.split(u8::is_ascii_whitespace)
.filter(|s| !s.is_empty());
let date = words let date = words
.next() .next()
.filter(|word| { .filter(|word| {
word.len() == 10 /* YYYY-MM-DD */ let word = word.as_bytes();
&& word[0..4].iter().all(u8::is_ascii_digit) // YYYY-MM-DD
&& word[4] == b'-' word.len() == 10
&& word[5..7].iter().all(u8::is_ascii_digit) && word[0..4].iter().all(u8::is_ascii_digit)
&& word[7] == b'-' && word[4] == b'-'
&& word[8..10].iter().all(u8::is_ascii_digit) && word[5..7].iter().all(u8::is_ascii_digit)
&& word[7] == b'-'
&& word[8..10].iter().all(u8::is_ascii_digit)
}) })
.map(|word| { .map(|word| {
let word = word.as_bytes();
( (
(u16::from(word[0]) - u16::from(b'0')) * 1000 (u16::from(word[0]) - u16::from(b'0')) * 1000
+ (u16::from(word[1]) - u16::from(b'0')) * 100 + (u16::from(word[1]) - u16::from(b'0')) * 100
@ -185,8 +194,8 @@ impl<'a> Timestamp<'a> {
) )
})?; })?;
let _dayname = words.next().filter(|word| { let dayname = words.next().filter(|word| {
word.iter().all(|&c| { word.as_bytes().iter().all(|&c| {
!(c == b'+' !(c == b'+'
|| c == b'-' || c == b'-'
|| c == b']' || c == b']'
@ -197,16 +206,20 @@ impl<'a> Timestamp<'a> {
})?; })?;
let (start, end) = if let Some(word) = words.next() { let (start, end) = if let Some(word) = words.next() {
let word = word.as_bytes();
macro_rules! datetime { macro_rules! datetime {
($a:expr, $b:expr, $c:expr) => { ($a:expr, $b:expr, $c:expr) => {
Datetime { Datetime {
date, date,
dayname,
time: Some((word[$a] - b'0', (word[$b] - b'0') * 10 + (word[$c] - b'0'))), time: Some((word[$a] - b'0', (word[$b] - b'0') * 10 + (word[$c] - b'0'))),
} }
}; };
($a:expr, $b:expr, $c:expr, $d:expr) => { ($a:expr, $b:expr, $c:expr, $d:expr) => {
Datetime { Datetime {
date, date,
dayname,
time: Some(( time: Some((
(word[$a] - b'0') * 10 + (word[$b] - b'0'), (word[$a] - b'0') * 10 + (word[$b] - b'0'),
(word[$c] - b'0') * 10 + (word[$d] - b'0'), (word[$c] - b'0') * 10 + (word[$d] - b'0'),
@ -215,63 +228,76 @@ impl<'a> Timestamp<'a> {
}; };
} }
if word.len() == 4 // H:MM if word.len() == 4
&& word[0].is_ascii_digit() && word[0].is_ascii_digit()
&& word[1] == b':' && word[1] == b':'
&& word[2..4].iter().all(u8::is_ascii_digit) && word[2..4].iter().all(u8::is_ascii_digit)
{ {
// H:MM
(datetime!(0, 2, 3), None) (datetime!(0, 2, 3), None)
} else if word.len() == 5 // HH:MM } else if word.len() == 5
&& word[0..2].iter().all(u8::is_ascii_digit) && word[0..2].iter().all(u8::is_ascii_digit)
&& word[2] == b':' && word[2] == b':'
&& word[3..5].iter().all(u8::is_ascii_digit) && word[3..5].iter().all(u8::is_ascii_digit)
{ {
// HH:MM
(datetime!(0, 1, 3, 4), None) (datetime!(0, 1, 3, 4), None)
} else if word.len() == 9 // H:MM-H:MM } else if word.len() == 9
&& word[0].is_ascii_digit() && word[0].is_ascii_digit()
&& word[1] == b':' && word[1] == b':'
&& word[2..4].iter().all(u8::is_ascii_digit) && word[2..4].iter().all(u8::is_ascii_digit)
&& word[4] == b'-' && word[4] == b'-'
&& word[5].is_ascii_digit() && word[5].is_ascii_digit()
&& word[6] == b':' && word[6] == b':'
&& word[7..9].iter().all(u8::is_ascii_digit) && word[7..9].iter().all(u8::is_ascii_digit)
{ {
// H:MM-H:MM
(datetime!(0, 2, 3), Some(datetime!(5, 7, 8))) (datetime!(0, 2, 3), Some(datetime!(5, 7, 8)))
} else if word.len() == 10 // H:MM-HH:MM } else if word.len() == 10
&& word[0].is_ascii_digit() && word[0].is_ascii_digit()
&& word[1] == b':' && word[1] == b':'
&& word[2..4].iter().all(u8::is_ascii_digit) && word[2..4].iter().all(u8::is_ascii_digit)
&& word[4] == b'-' && word[4] == b'-'
&& word[5..7].iter().all(u8::is_ascii_digit) && word[5..7].iter().all(u8::is_ascii_digit)
&& word[7] == b':' && word[7] == b':'
&& word[8..10].iter().all(u8::is_ascii_digit) && word[8..10].iter().all(u8::is_ascii_digit)
{ {
// H:MM-HH:MM
(datetime!(0, 2, 3), Some(datetime!(5, 6, 8, 9))) (datetime!(0, 2, 3), Some(datetime!(5, 6, 8, 9)))
} else if word.len() == 10 // HH:MM-H:MM } else if word.len() == 10
&& word[0..2].iter().all(u8::is_ascii_digit) && word[0..2].iter().all(u8::is_ascii_digit)
&& word[2] == b':' && word[2] == b':'
&& word[3..5].iter().all(u8::is_ascii_digit) && word[3..5].iter().all(u8::is_ascii_digit)
&& word[5] == b'-' && word[5] == b'-'
&& word[6].is_ascii_digit() && word[6].is_ascii_digit()
&& word[7] == b':' && word[7] == b':'
&& word[8..10].iter().all(u8::is_ascii_digit) && word[8..10].iter().all(u8::is_ascii_digit)
{ {
// HH:MM-H:MM
(datetime!(0, 1, 3, 4), Some(datetime!(6, 8, 9))) (datetime!(0, 1, 3, 4), Some(datetime!(6, 8, 9)))
} else if word.len() == 11 // HH:MM-HH:MM } else if word.len() == 11
&& word[0..2].iter().all(u8::is_ascii_digit) && word[0..2].iter().all(u8::is_ascii_digit)
&& word[2] == b':' && word[2] == b':'
&& word[3..5].iter().all(u8::is_ascii_digit) && word[3..5].iter().all(u8::is_ascii_digit)
&& word[5] == b'-' && word[5] == b'-'
&& word[6..8].iter().all(u8::is_ascii_digit) && word[6..8].iter().all(u8::is_ascii_digit)
&& word[8] == b':' && word[8] == b':'
&& word[9..11].iter().all(u8::is_ascii_digit) && word[9..11].iter().all(u8::is_ascii_digit)
{ {
// HH:MM-HH:MM
(datetime!(0, 1, 3, 4), Some(datetime!(6, 7, 9, 10))) (datetime!(0, 1, 3, 4), Some(datetime!(6, 7, 9, 10)))
} else { } else {
return None; return None;
} }
} else { } else {
(Datetime { date, time: None }, None) (
Datetime {
date,
dayname,
time: None,
},
None,
)
}; };
// TODO: repeater and delay // TODO: repeater and delay
@ -309,7 +335,8 @@ mod tests {
Timestamp::Inactive { Timestamp::Inactive {
start: Datetime { start: Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
time: None time: None,
dayname: "Tue"
}, },
repeater: None, repeater: None,
delay: None, delay: None,
@ -323,11 +350,13 @@ mod tests {
Timestamp::InactiveRange { Timestamp::InactiveRange {
start: Datetime { start: Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
time: Some((9, 39)) time: Some((9, 39)),
dayname: "Tue"
}, },
end: Datetime { end: Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
time: Some((10, 39)) time: Some((10, 39)),
dayname: "Tue"
}, },
repeater: None, repeater: None,
delay: None delay: None
@ -341,11 +370,13 @@ mod tests {
Timestamp::ActiveRange { Timestamp::ActiveRange {
start: Datetime { start: Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
time: Some((9, 39)) time: Some((9, 39)),
dayname: "Tue"
}, },
end: Datetime { end: Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
time: Some((10, 39)) time: Some((10, 39)),
dayname: "Tue"
}, },
repeater: None, repeater: None,
delay: None delay: None
@ -360,52 +391,57 @@ mod tests {
use super::*; use super::*;
assert_eq!( assert_eq!(
Timestamp::parse_datetime(b"2003-09-16 Tue"), Timestamp::parse_datetime("2003-09-16 Tue"),
Some(( Some((
Datetime { Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
time: None time: None,
dayname: "Tue"
}, },
None None
)) ))
); );
assert_eq!( assert_eq!(
Timestamp::parse_datetime(b"2003-09-16 Tue 9:39"), Timestamp::parse_datetime("2003-09-16 Tue 9:39"),
Some(( Some((
Datetime { Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
time: Some((9, 39)) time: Some((9, 39)),
dayname: "Tue"
}, },
None None
)) ))
); );
assert_eq!( assert_eq!(
Timestamp::parse_datetime(b"2003-09-16 Tue 09:39"), Timestamp::parse_datetime("2003-09-16 Tue 09:39"),
Some(( Some((
Datetime { Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
time: Some((9, 39)) time: Some((9, 39)),
dayname: "Tue"
}, },
None None
)) ))
); );
assert_eq!( assert_eq!(
Timestamp::parse_datetime(b"2003-09-16 Tue 9:39-10:39"), Timestamp::parse_datetime("2003-09-16 Tue 9:39-10:39"),
Some(( Some((
Datetime { Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
time: Some((9, 39)) time: Some((9, 39)),
dayname: "Tue"
}, },
Some(Datetime { Some(Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
time: Some((10, 39)) time: Some((10, 39)),
dayname: "Tue"
}), }),
)) ))
); );
assert_eq!(Timestamp::parse_datetime(b"2003-9-16 Tue"), None); assert_eq!(Timestamp::parse_datetime("2003-9-16 Tue"), None);
assert_eq!(Timestamp::parse_datetime(b"2003-09-16"), None); assert_eq!(Timestamp::parse_datetime("2003-09-16"), None);
assert_eq!(Timestamp::parse_datetime(b"2003-09-16 09:39"), None); assert_eq!(Timestamp::parse_datetime("2003-09-16 09:39"), None);
assert_eq!(Timestamp::parse_datetime(b"2003-09-16 Tue 0939"), None); assert_eq!(Timestamp::parse_datetime("2003-09-16 Tue 0939"), None);
} }
} }

View file

@ -185,6 +185,19 @@ impl<'a> Parser<'a> {
} }
} }
/// creates a new parser from string, with the specified keywords
pub fn with_keywrods(text: &'a str, keywords: &'a [&'a str]) -> Parser<'a> {
Parser {
text,
stack: Vec::new(),
next_item: Vec::new(),
off: 0,
ele_buf: None,
obj_buf: None,
keywords,
}
}
/// returns current offset /// returns current offset
pub fn offset(&self) -> usize { pub fn offset(&self) -> usize {
self.off self.off
@ -195,10 +208,12 @@ impl<'a> Parser<'a> {
self.stack.len() self.stack.len()
} }
/// set keywords
pub fn set_keywords(&mut self, keywords: &'a [&'a str]) { pub fn set_keywords(&mut self, keywords: &'a [&'a str]) {
self.keywords = keywords; self.keywords = keywords;
} }
/// set text
pub fn set_text(&mut self, text: &'a str) { pub fn set_text(&mut self, text: &'a str) {
self.off = 0; self.off = 0;
self.stack.clear(); self.stack.clear();

View file

@ -7,9 +7,7 @@ type Keywords<'a> = Vec<(Key<'a>, &'a str)>;
type Footnotes<'a> = Vec<&'a str>; type Footnotes<'a> = Vec<&'a str>;
pub fn metadata(src: &str) -> (Headlines<'_>, Keywords<'_>, Footnotes<'_>) { pub fn metadata(src: &str) -> (Headlines<'_>, Keywords<'_>, Footnotes<'_>) {
let mut headlines = Vec::new(); let (mut headlines, mut keywords, mut footnotes) = (Vec::new(), Vec::new(), Vec::new());
let mut keywords = Vec::new();
let mut footnotes = Vec::new();
for line in src.lines().filter(|l| !l.is_empty()) { for line in src.lines().filter(|l| !l.is_empty()) {
if line.starts_with('*') { if line.starts_with('*') {