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 keyword;
|
||||
pub(crate) mod list;
|
||||
pub(crate) mod planning;
|
||||
pub(crate) mod rule;
|
||||
|
||||
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)]
|
||||
|
||||
use crate::elements::Key;
|
||||
use crate::headline::Headline;
|
||||
use crate::objects::{Cookie, Timestamp};
|
||||
use crate::parser::Parser;
|
||||
use crate::{
|
||||
elements::{Key, Planning},
|
||||
headline::Headline,
|
||||
objects::{Cookie, Timestamp},
|
||||
parser::Parser,
|
||||
};
|
||||
use jetscii::ascii_chars;
|
||||
use std::convert::From;
|
||||
use std::fmt;
|
||||
use std::io::{Error, Write};
|
||||
use std::marker::PhantomData;
|
||||
use std::{
|
||||
convert::From,
|
||||
fmt,
|
||||
io::{Error, Write},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
pub trait HtmlHandler<W: Write, E: From<Error>> {
|
||||
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> {
|
||||
Ok(write!(w, "{}", Escape(cont))?)
|
||||
}
|
||||
fn planning(&mut self, w: &mut W, planning: Planning) -> Result<(), E> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DefaultHtmlHandler;
|
||||
|
|
|
@ -28,6 +28,7 @@ macro_rules! handle_event {
|
|||
ListItemBeg { bullet } => $handler.list_beg_item($writer, bullet)?,
|
||||
ListItemEnd => $handler.list_end_item($writer)?,
|
||||
Call { value } => $handler.call($writer, value)?,
|
||||
Planning(p) => $handler.planning($writer, p)?,
|
||||
Clock => $handler.clock($writer)?,
|
||||
Timestamp(t) => $handler.timestamp($writer, t)?,
|
||||
Comment(c) => $handler.comment($writer, c)?,
|
||||
|
|
|
@ -8,7 +8,7 @@ use memchr::memchr_iter;
|
|||
#[derive(Copy, Clone, Debug)]
|
||||
enum Container {
|
||||
Headline(usize),
|
||||
Section,
|
||||
Section(usize),
|
||||
Paragraph,
|
||||
CtrBlock,
|
||||
QteBlock,
|
||||
|
@ -90,6 +90,8 @@ pub enum Event<'a> {
|
|||
Comment(&'a str),
|
||||
FixedWidth(&'a str),
|
||||
|
||||
Planning(Planning<'a>),
|
||||
|
||||
TableStart,
|
||||
TableEnd,
|
||||
TableCell,
|
||||
|
@ -197,7 +199,7 @@ impl<'a> Parser<'a> {
|
|||
let end = Headline::find_level(&self.text[self.off..], std::usize::MAX);
|
||||
debug_assert!(end <= self.text[self.off..].len());
|
||||
if end != 0 {
|
||||
self.push_stack(Container::Section, end, end);
|
||||
self.push_stack(Container::Section(self.off), end, end);
|
||||
Event::SectionBeg
|
||||
} else {
|
||||
self.next_headline()
|
||||
|
@ -207,8 +209,8 @@ impl<'a> Parser<'a> {
|
|||
fn next_headline(&mut self) -> Event<'a> {
|
||||
let (hdl, off, end) = Headline::parse(&self.text[self.off..], self.keywords);
|
||||
debug_assert!(end <= self.text[self.off..].len());
|
||||
self.push_stack(Container::Headline(self.off + off), end, end);
|
||||
self.off += off;
|
||||
self.push_stack(Container::Headline(self.off), end, end);
|
||||
Event::HeadlineBeg(hdl)
|
||||
}
|
||||
|
||||
|
@ -520,7 +522,7 @@ impl<'a> Parser<'a> {
|
|||
Container::ListItem => Event::ListItemEnd,
|
||||
Container::Paragraph => Event::ParagraphEnd,
|
||||
Container::QteBlock => Event::QteBlockEnd,
|
||||
Container::Section => Event::SectionEnd,
|
||||
Container::Section(_) => Event::SectionEnd,
|
||||
Container::SplBlock => Event::SplBlockEnd,
|
||||
Container::Strike => Event::StrikeEnd,
|
||||
Container::Underline => Event::UnderlineEnd,
|
||||
|
@ -551,8 +553,20 @@ impl<'a> Iterator for Parser<'a> {
|
|||
| Container::CtrBlock
|
||||
| Container::QteBlock
|
||||
| Container::SplBlock
|
||||
| Container::ListItem
|
||||
| Container::Section => self.next_ele(&self.text[self.off..limit]),
|
||||
| Container::ListItem => 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, _) => {
|
||||
if self.list_more_item {
|
||||
self.next_list_item(ident, &self.text[self.off..limit])
|
||||
|
|
Loading…
Reference in a new issue