feat(parser): planning parsing

This commit is contained in:
PoiScript 2019-04-06 14:49:47 +08:00
parent 8bb7ae41d3
commit 406fd22aee
5 changed files with 103 additions and 14 deletions

View file

@ -3,6 +3,8 @@ pub(crate) mod dyn_block;
pub(crate) mod fn_def; pub(crate) mod fn_def;
pub(crate) mod keyword; pub(crate) mod keyword;
pub(crate) mod list; pub(crate) mod list;
pub(crate) mod planning;
pub(crate) mod rule; pub(crate) mod rule;
pub use self::keyword::Key; pub use self::keyword::Key;
pub use self::planning::Planning;

65
src/elements/planning.rs Normal file
View file

@ -0,0 +1,65 @@
use crate::objects::timestamp::{self, Timestamp};
use memchr::memchr;
#[cfg_attr(test, derive(PartialEq))]
#[derive(Debug)]
pub struct Planning<'a> {
deadline: Option<Timestamp<'a>>,
scheduled: Option<Timestamp<'a>>,
closed: Option<Timestamp<'a>>,
}
impl<'a> Planning<'a> {
pub(crate) fn parse(text: &'a str) -> Option<(Planning<'a>, usize)> {
let (text, off) = memchr(b'\n', text.as_bytes())
.map(|i| (text[..i].trim(), i + 1))
.unwrap_or_else(|| (text.trim(), text.len()));
let mut words = text.split_ascii_whitespace();
let (mut deadline, mut scheduled, mut closed) = (None, None, None);
while let Some(word) = words.next() {
let next = words.next()?;
macro_rules! set_timestamp {
($timestamp:expr) => {
if $timestamp.is_none() {
$timestamp = if next.starts_with('<') {
Some(
timestamp::parse_active(next)
.or_else(|| timestamp::parse_diary(next))?
.0,
)
} else if next.starts_with('[') {
Some(timestamp::parse_inactive(next)?.0)
} else {
return None;
}
} else {
return None;
}
};
}
match word {
"DEADLINE:" => set_timestamp!(deadline),
"SCHEDULED:" => set_timestamp!(scheduled),
"CLOSED:" => set_timestamp!(closed),
_ => (),
}
}
if deadline.is_none() && scheduled.is_none() && closed.is_none() {
None
} else {
Some((
Planning {
deadline,
scheduled,
closed,
},
off,
))
}
}
}

View file

@ -1,14 +1,18 @@
#![allow(unused_variables)] #![allow(unused_variables)]
use crate::elements::Key; use crate::{
use crate::headline::Headline; elements::{Key, Planning},
use crate::objects::{Cookie, Timestamp}; headline::Headline,
use crate::parser::Parser; objects::{Cookie, Timestamp},
parser::Parser,
};
use jetscii::ascii_chars; use jetscii::ascii_chars;
use std::convert::From; use std::{
use std::fmt; convert::From,
use std::io::{Error, Write}; fmt,
use std::marker::PhantomData; io::{Error, Write},
marker::PhantomData,
};
pub trait HtmlHandler<W: Write, E: From<Error>> { pub trait HtmlHandler<W: Write, E: From<Error>> {
fn headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result<(), E> { fn headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result<(), E> {
@ -216,6 +220,9 @@ pub trait HtmlHandler<W: Write, E: From<Error>> {
fn text(&mut self, w: &mut W, cont: &str) -> Result<(), E> { fn text(&mut self, w: &mut W, cont: &str) -> Result<(), E> {
Ok(write!(w, "{}", Escape(cont))?) Ok(write!(w, "{}", Escape(cont))?)
} }
fn planning(&mut self, w: &mut W, planning: Planning) -> Result<(), E> {
Ok(())
}
} }
pub struct DefaultHtmlHandler; pub struct DefaultHtmlHandler;

View file

@ -28,6 +28,7 @@ macro_rules! handle_event {
ListItemBeg { bullet } => $handler.list_beg_item($writer, bullet)?, ListItemBeg { bullet } => $handler.list_beg_item($writer, bullet)?,
ListItemEnd => $handler.list_end_item($writer)?, ListItemEnd => $handler.list_end_item($writer)?,
Call { value } => $handler.call($writer, value)?, Call { value } => $handler.call($writer, value)?,
Planning(p) => $handler.planning($writer, p)?,
Clock => $handler.clock($writer)?, Clock => $handler.clock($writer)?,
Timestamp(t) => $handler.timestamp($writer, t)?, Timestamp(t) => $handler.timestamp($writer, t)?,
Comment(c) => $handler.comment($writer, c)?, Comment(c) => $handler.comment($writer, c)?,

View file

@ -8,7 +8,7 @@ use memchr::memchr_iter;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
enum Container { enum Container {
Headline(usize), Headline(usize),
Section, Section(usize),
Paragraph, Paragraph,
CtrBlock, CtrBlock,
QteBlock, QteBlock,
@ -90,6 +90,8 @@ pub enum Event<'a> {
Comment(&'a str), Comment(&'a str),
FixedWidth(&'a str), FixedWidth(&'a str),
Planning(Planning<'a>),
TableStart, TableStart,
TableEnd, TableEnd,
TableCell, TableCell,
@ -197,7 +199,7 @@ impl<'a> Parser<'a> {
let end = Headline::find_level(&self.text[self.off..], std::usize::MAX); let end = Headline::find_level(&self.text[self.off..], std::usize::MAX);
debug_assert!(end <= self.text[self.off..].len()); debug_assert!(end <= self.text[self.off..].len());
if end != 0 { if end != 0 {
self.push_stack(Container::Section, end, end); self.push_stack(Container::Section(self.off), end, end);
Event::SectionBeg Event::SectionBeg
} else { } else {
self.next_headline() self.next_headline()
@ -207,8 +209,8 @@ impl<'a> Parser<'a> {
fn next_headline(&mut self) -> Event<'a> { fn next_headline(&mut self) -> Event<'a> {
let (hdl, off, end) = Headline::parse(&self.text[self.off..], self.keywords); let (hdl, off, end) = Headline::parse(&self.text[self.off..], self.keywords);
debug_assert!(end <= self.text[self.off..].len()); debug_assert!(end <= self.text[self.off..].len());
self.push_stack(Container::Headline(self.off + off), end, end);
self.off += off; self.off += off;
self.push_stack(Container::Headline(self.off), end, end);
Event::HeadlineBeg(hdl) Event::HeadlineBeg(hdl)
} }
@ -520,7 +522,7 @@ impl<'a> Parser<'a> {
Container::ListItem => Event::ListItemEnd, Container::ListItem => Event::ListItemEnd,
Container::Paragraph => Event::ParagraphEnd, Container::Paragraph => Event::ParagraphEnd,
Container::QteBlock => Event::QteBlockEnd, Container::QteBlock => Event::QteBlockEnd,
Container::Section => Event::SectionEnd, Container::Section(_) => Event::SectionEnd,
Container::SplBlock => Event::SplBlockEnd, Container::SplBlock => Event::SplBlockEnd,
Container::Strike => Event::StrikeEnd, Container::Strike => Event::StrikeEnd,
Container::Underline => Event::UnderlineEnd, Container::Underline => Event::UnderlineEnd,
@ -551,8 +553,20 @@ impl<'a> Iterator for Parser<'a> {
| Container::CtrBlock | Container::CtrBlock
| Container::QteBlock | Container::QteBlock
| Container::SplBlock | Container::SplBlock
| Container::ListItem | Container::ListItem => self.next_ele(&self.text[self.off..limit]),
| Container::Section => self.next_ele(&self.text[self.off..limit]), Container::Section(beg) => {
let tail = &self.text[self.off..limit];
if self.off == beg {
if let Some((planning, off)) = Planning::parse(tail) {
self.off += off;
Event::Planning(planning)
} else {
self.next_ele(tail)
}
} else {
self.next_ele(tail)
}
}
Container::List(ident, _) => { Container::List(ident, _) => {
if self.list_more_item { if self.list_more_item {
self.next_list_item(ident, &self.text[self.off..limit]) self.next_list_item(ident, &self.text[self.off..limit])