feat(parser): planning parsing
This commit is contained in:
parent
8bb7ae41d3
commit
406fd22aee
|
@ -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
65
src/elements/planning.rs
Normal 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,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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)?,
|
||||||
|
|
|
@ -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])
|
||||||
|
|
Loading…
Reference in a new issue