chore: make the fields of Planning struct public
This commit is contained in:
parent
c2c554a3d5
commit
9c82b268d6
|
@ -1,4 +1,4 @@
|
||||||
use crate::objects::timestamp::{self, Datetime, Delay, Repeater, Timestamp};
|
use crate::objects::timestamp::{Datetime, Delay, Repeater, Timestamp};
|
||||||
use memchr::memchr;
|
use memchr::memchr;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
|
@ -32,7 +32,7 @@ impl<'a> Clock<'a> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
match timestamp::parse_inactive(tail).map(|(t, off)| (t, tail[off..].trim_start())) {
|
match Timestamp::parse_inactive(tail).map(|(t, off)| (t, tail[off..].trim_start())) {
|
||||||
Some((
|
Some((
|
||||||
Timestamp::InactiveRange {
|
Timestamp::InactiveRange {
|
||||||
start,
|
start,
|
||||||
|
|
|
@ -1,37 +1,36 @@
|
||||||
use crate::objects::timestamp::{self, Timestamp};
|
use crate::objects::timestamp::Timestamp;
|
||||||
use memchr::memchr;
|
use memchr::memchr;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Planning<'a> {
|
pub struct Planning<'a> {
|
||||||
deadline: Option<Timestamp<'a>>,
|
pub deadline: Option<Timestamp<'a>>,
|
||||||
scheduled: Option<Timestamp<'a>>,
|
pub scheduled: Option<Timestamp<'a>>,
|
||||||
closed: Option<Timestamp<'a>>,
|
pub closed: Option<Timestamp<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Planning<'a> {
|
impl<'a> Planning<'a> {
|
||||||
pub(crate) fn parse(text: &'a str) -> Option<(Planning<'a>, usize)> {
|
pub(crate) fn parse(text: &'a str) -> Option<(Planning<'a>, usize)> {
|
||||||
let (text, off) = memchr(b'\n', text.as_bytes())
|
let (mut deadline, mut scheduled, mut closed) = (None, None, None);
|
||||||
|
let (mut tail, off) = memchr(b'\n', text.as_bytes())
|
||||||
.map(|i| (text[..i].trim(), i + 1))
|
.map(|i| (text[..i].trim(), i + 1))
|
||||||
.unwrap_or_else(|| (text.trim(), text.len()));
|
.unwrap_or_else(|| (text.trim(), text.len()));
|
||||||
|
|
||||||
let mut words = text.split_ascii_whitespace();
|
while let Some(i) = memchr(b' ', tail.as_bytes()) {
|
||||||
let (mut deadline, mut scheduled, mut closed) = (None, None, None);
|
let next = &tail[i + 1..].trim_start();
|
||||||
|
|
||||||
while let Some(word) = words.next() {
|
|
||||||
let next = words.next()?;
|
|
||||||
|
|
||||||
macro_rules! set_timestamp {
|
macro_rules! set_timestamp {
|
||||||
($timestamp:expr) => {
|
($timestamp:expr) => {
|
||||||
if $timestamp.is_none() {
|
if $timestamp.is_none() {
|
||||||
$timestamp = if next.starts_with('<') {
|
if next.starts_with('<') {
|
||||||
Some(
|
let (timestamp, off) = Timestamp::parse_active(next)
|
||||||
timestamp::parse_active(next)
|
.or_else(|| Timestamp::parse_diary(next))?;
|
||||||
.or_else(|| timestamp::parse_diary(next))?
|
$timestamp = Some(timestamp);
|
||||||
.0,
|
tail = &next[off..].trim_start();
|
||||||
)
|
} else if next.starts_with('<') {
|
||||||
} else if next.starts_with('[') {
|
let (timestamp, off) = Timestamp::parse_active(next)?;
|
||||||
Some(timestamp::parse_inactive(next)?.0)
|
$timestamp = Some(timestamp);
|
||||||
|
tail = &next[off..].trim_start();
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -41,11 +40,11 @@ impl<'a> Planning<'a> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match word {
|
match &tail[..i] {
|
||||||
"DEADLINE:" => set_timestamp!(deadline),
|
"DEADLINE:" => set_timestamp!(deadline),
|
||||||
"SCHEDULED:" => set_timestamp!(scheduled),
|
"SCHEDULED:" => set_timestamp!(scheduled),
|
||||||
"CLOSED:" => set_timestamp!(closed),
|
"CLOSED:" => set_timestamp!(closed),
|
||||||
_ => (),
|
_ => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,3 +62,31 @@ impl<'a> Planning<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn prase() {
|
||||||
|
use super::Planning;
|
||||||
|
use crate::objects::timestamp::{Datetime, Timestamp};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Planning::parse("SCHEDULED: <2019-04-08 Mon>\n"),
|
||||||
|
Some((
|
||||||
|
Planning {
|
||||||
|
scheduled: Some(Timestamp::Active {
|
||||||
|
start: Datetime {
|
||||||
|
date: (2019, 4, 8),
|
||||||
|
time: None
|
||||||
|
},
|
||||||
|
repeater: None,
|
||||||
|
delay: None
|
||||||
|
}),
|
||||||
|
closed: None,
|
||||||
|
deadline: None,
|
||||||
|
},
|
||||||
|
"SCHEDULED: <2019-04-08 Mon>\n".len()
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//! Headline
|
//! Headline
|
||||||
|
|
||||||
|
use jetscii::ByteSubstring;
|
||||||
use memchr::{memchr, memchr2, memrchr};
|
use memchr::{memchr, memchr2, memrchr};
|
||||||
|
|
||||||
pub(crate) const DEFAULT_KEYWORDS: &[&str] =
|
pub(crate) const DEFAULT_KEYWORDS: &[&str] =
|
||||||
|
@ -28,7 +29,16 @@ impl<'a> Headline<'a> {
|
||||||
debug_assert!(text.as_bytes()[0..level].iter().all(|&c| c == b'*'));
|
debug_assert!(text.as_bytes()[0..level].iter().all(|&c| c == b'*'));
|
||||||
|
|
||||||
let (off, end) = memchr(b'\n', text.as_bytes())
|
let (off, end) = memchr(b'\n', text.as_bytes())
|
||||||
.map(|i| (i + 1, Headline::find_level(&text[i + 1..], level) + i + 1))
|
.map(|i| {
|
||||||
|
(
|
||||||
|
i + 1,
|
||||||
|
if i + 1 == text.len() {
|
||||||
|
Headline::find_level(&text[i + 1..], level) + i + 1
|
||||||
|
} else {
|
||||||
|
i + 1
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
.unwrap_or_else(|| (text.len(), text.len()));
|
.unwrap_or_else(|| (text.len(), text.len()));
|
||||||
|
|
||||||
if level == off {
|
if level == off {
|
||||||
|
@ -98,8 +108,6 @@ impl<'a> Headline<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn find_level(text: &str, level: usize) -> usize {
|
pub(crate) fn find_level(text: &str, level: usize) -> usize {
|
||||||
use jetscii::ByteSubstring;
|
|
||||||
|
|
||||||
let bytes = text.as_bytes();
|
let bytes = text.as_bytes();
|
||||||
if bytes[0] == b'*' {
|
if bytes[0] == b'*' {
|
||||||
if let Some(stars) = memchr2(b'\n', b' ', bytes) {
|
if let Some(stars) = memchr2(b'\n', b' ', bytes) {
|
||||||
|
|
|
@ -76,18 +76,19 @@ pub enum Timestamp<'a> {
|
||||||
Diary(&'a str),
|
Diary(&'a str),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_active(text: &str) -> Option<(Timestamp<'_>, usize)> {
|
impl<'a> Timestamp<'a> {
|
||||||
|
pub(crate) fn parse_active(text: &str) -> Option<(Timestamp<'_>, 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) = parse_datetime(&bytes[1..off])?;
|
let (start, mut end) = Self::parse_datetime(&bytes[1..off])?;
|
||||||
if end.is_none()
|
if end.is_none()
|
||||||
&& off <= text.len() - 14 /* --<YYYY-MM-DD> */
|
&& 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, _)) = parse_datetime(&bytes[off + 4..off + 1 + new_off]) {
|
if let Some((start, _)) = Self::parse_datetime(&bytes[off + 4..off + 1 + new_off]) {
|
||||||
end = Some(start);
|
end = Some(start);
|
||||||
off += new_off + 1;
|
off += new_off + 1;
|
||||||
}
|
}
|
||||||
|
@ -113,18 +114,18 @@ pub fn parse_active(text: &str) -> Option<(Timestamp<'_>, usize)> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_inactive(text: &str) -> Option<(Timestamp<'_>, usize)> {
|
pub(crate) fn parse_inactive(text: &str) -> Option<(Timestamp<'_>, 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) = parse_datetime(&bytes[1..off])?;
|
let (start, mut end) = Self::parse_datetime(&bytes[1..off])?;
|
||||||
if end.is_none()
|
if end.is_none()
|
||||||
&& off <= text.len() - 14 /* --[YYYY-MM-DD] */
|
&& 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, _)) = parse_datetime(&bytes[off + 4..off + 1 + new_off]) {
|
if let Some((start, _)) = Self::parse_datetime(&bytes[off + 4..off + 1 + new_off]) {
|
||||||
end = Some(start);
|
end = Some(start);
|
||||||
off += new_off + 1;
|
off += new_off + 1;
|
||||||
}
|
}
|
||||||
|
@ -186,7 +187,12 @@ fn parse_datetime(bytes: &[u8]) -> Option<(Datetime, Option<Datetime>)> {
|
||||||
|
|
||||||
let _dayname = words.next().filter(|word| {
|
let _dayname = words.next().filter(|word| {
|
||||||
word.iter().all(|&c| {
|
word.iter().all(|&c| {
|
||||||
!(c == b'+' || c == b'-' || c == b']' || c == b'>' || c.is_ascii_digit() || c == b'\n')
|
!(c == b'+'
|
||||||
|
|| c == b'-'
|
||||||
|
|| c == b']'
|
||||||
|
|| c == b'>'
|
||||||
|
|| c.is_ascii_digit()
|
||||||
|
|| c == b'\n')
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -276,7 +282,7 @@ fn parse_datetime(bytes: &[u8]) -> Option<(Datetime, Option<Datetime>)> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_diary(text: &str) -> Option<(Timestamp<'_>, usize)> {
|
pub(crate) fn parse_diary(text: &str) -> Option<(Timestamp<'_>, usize)> {
|
||||||
debug_assert!(text.starts_with('<'));
|
debug_assert!(text.starts_with('<'));
|
||||||
|
|
||||||
if text.len() <= 6 /* <%%()> */ || &text[1..4] != "%%(" {
|
if text.len() <= 6 /* <%%()> */ || &text[1..4] != "%%(" {
|
||||||
|
@ -289,6 +295,7 @@ pub fn parse_diary(text: &str) -> Option<(Timestamp<'_>, usize)> {
|
||||||
.filter(|i| bytes[i - 1] == b')' && bytes[4..i - 1].iter().all(|&c| c != b'\n'))
|
.filter(|i| bytes[i - 1] == b')' && bytes[4..i - 1].iter().all(|&c| c != b'\n'))
|
||||||
.map(|i| (Timestamp::Diary(&text[4..i - 1]), i))
|
.map(|i| (Timestamp::Diary(&text[4..i - 1]), i))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -297,7 +304,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_inactive("[2003-09-16 Tue]"),
|
Timestamp::parse_inactive("[2003-09-16 Tue]"),
|
||||||
Some((
|
Some((
|
||||||
Timestamp::Inactive {
|
Timestamp::Inactive {
|
||||||
start: Datetime {
|
start: Datetime {
|
||||||
|
@ -311,7 +318,7 @@ mod tests {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_inactive("[2003-09-16 Tue 09:39]--[2003-09-16 Tue 10:39]"),
|
Timestamp::parse_inactive("[2003-09-16 Tue 09:39]--[2003-09-16 Tue 10:39]"),
|
||||||
Some((
|
Some((
|
||||||
Timestamp::InactiveRange {
|
Timestamp::InactiveRange {
|
||||||
start: Datetime {
|
start: Datetime {
|
||||||
|
@ -329,7 +336,7 @@ mod tests {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_active("<2003-09-16 Tue 09:39-10:39>"),
|
Timestamp::parse_active("<2003-09-16 Tue 09:39-10:39>"),
|
||||||
Some((
|
Some((
|
||||||
Timestamp::ActiveRange {
|
Timestamp::ActiveRange {
|
||||||
start: Datetime {
|
start: Datetime {
|
||||||
|
@ -353,7 +360,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_datetime(b"2003-09-16 Tue"),
|
Timestamp::parse_datetime(b"2003-09-16 Tue"),
|
||||||
Some((
|
Some((
|
||||||
Datetime {
|
Datetime {
|
||||||
date: (2003, 9, 16),
|
date: (2003, 9, 16),
|
||||||
|
@ -363,7 +370,7 @@ mod tests {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_datetime(b"2003-09-16 Tue 9:39"),
|
Timestamp::parse_datetime(b"2003-09-16 Tue 9:39"),
|
||||||
Some((
|
Some((
|
||||||
Datetime {
|
Datetime {
|
||||||
date: (2003, 9, 16),
|
date: (2003, 9, 16),
|
||||||
|
@ -373,7 +380,7 @@ mod tests {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_datetime(b"2003-09-16 Tue 09:39"),
|
Timestamp::parse_datetime(b"2003-09-16 Tue 09:39"),
|
||||||
Some((
|
Some((
|
||||||
Datetime {
|
Datetime {
|
||||||
date: (2003, 9, 16),
|
date: (2003, 9, 16),
|
||||||
|
@ -383,7 +390,7 @@ mod tests {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_datetime(b"2003-09-16 Tue 9:39-10:39"),
|
Timestamp::parse_datetime(b"2003-09-16 Tue 9:39-10:39"),
|
||||||
Some((
|
Some((
|
||||||
Datetime {
|
Datetime {
|
||||||
date: (2003, 9, 16),
|
date: (2003, 9, 16),
|
||||||
|
@ -396,9 +403,9 @@ mod tests {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(parse_datetime(b"2003-9-16 Tue"), None);
|
assert_eq!(Timestamp::parse_datetime(b"2003-9-16 Tue"), None);
|
||||||
assert_eq!(parse_datetime(b"2003-09-16"), None);
|
assert_eq!(Timestamp::parse_datetime(b"2003-09-16"), None);
|
||||||
assert_eq!(parse_datetime(b"2003-09-16 09:39"), None);
|
assert_eq!(Timestamp::parse_datetime(b"2003-09-16 09:39"), None);
|
||||||
assert_eq!(parse_datetime(b"2003-09-16 Tue 0939"), None);
|
assert_eq!(Timestamp::parse_datetime(b"2003-09-16 Tue 0939"), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -485,10 +485,10 @@ impl<'a> Parser<'a> {
|
||||||
target::parse(text).map(|(target, off)| (Event::Target { target }, off, 0, 0))
|
target::parse(text).map(|(target, off)| (Event::Target { target }, off, 0, 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b'<' => timestamp::parse_active(text)
|
b'<' => Timestamp::parse_active(text)
|
||||||
.map(|(timestamp, off)| (Event::Timestamp(timestamp), off, 0, 0))
|
.map(|(timestamp, off)| (Event::Timestamp(timestamp), off, 0, 0))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
timestamp::parse_diary(text)
|
Timestamp::parse_diary(text)
|
||||||
.map(|(timestamp, off)| (Event::Timestamp(timestamp), off, 0, 0))
|
.map(|(timestamp, off)| (Event::Timestamp(timestamp), off, 0, 0))
|
||||||
}),
|
}),
|
||||||
b'[' => {
|
b'[' => {
|
||||||
|
@ -502,7 +502,7 @@ impl<'a> Parser<'a> {
|
||||||
cookie::parse(text)
|
cookie::parse(text)
|
||||||
.map(|(cookie, off)| (Event::Cookie(cookie), off, 0, 0))
|
.map(|(cookie, off)| (Event::Cookie(cookie), off, 0, 0))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
timestamp::parse_inactive(text)
|
Timestamp::parse_inactive(text)
|
||||||
.map(|(timestamp, off)| (Event::Timestamp(timestamp), off, 0, 0))
|
.map(|(timestamp, off)| (Event::Timestamp(timestamp), off, 0, 0))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -601,7 +601,6 @@ impl<'a> Iterator for Parser<'a> {
|
||||||
|
|
||||||
Some(match container {
|
Some(match container {
|
||||||
Container::Headline(beg) => {
|
Container::Headline(beg) => {
|
||||||
debug_assert!(self.off >= beg);
|
|
||||||
if self.off >= limit {
|
if self.off >= limit {
|
||||||
self.off = end;
|
self.off = end;
|
||||||
self.stack.pop();
|
self.stack.pop();
|
||||||
|
@ -645,7 +644,7 @@ impl<'a> Iterator for Parser<'a> {
|
||||||
Container::List(ident, ordered) => {
|
Container::List(ident, ordered) => {
|
||||||
if let Some(bullet) = self.next_item.pop().unwrap() {
|
if let Some(bullet) = self.next_item.pop().unwrap() {
|
||||||
self.off += bullet.len() + ident;
|
self.off += bullet.len() + ident;
|
||||||
let (limit, end, next) = list::parse(&self.text[self.off..limit], ident);
|
let (limit, end, next) = list::parse(tail, ident);
|
||||||
self.push_stack(Container::ListItem, limit, end);
|
self.push_stack(Container::ListItem, limit, end);
|
||||||
self.next_item.push(next);
|
self.next_item.push(next);
|
||||||
Event::ListItemBeg { bullet }
|
Event::ListItemBeg { bullet }
|
||||||
|
|
Loading…
Reference in a new issue