refator(elements): rewrite Timestamp struct

This commit is contained in:
PoiScript 2019-08-04 14:12:26 +08:00
parent 9023837eb4
commit 8d18fb04c1
7 changed files with 251 additions and 290 deletions

View file

@ -1,4 +1,4 @@
use crate::elements::{Date, Element, Time, Timestamp};
use crate::elements::{Datetime, Element, Timestamp};
use memchr::memchr;
/// clock elements
@ -10,18 +10,15 @@ use memchr::memchr;
pub enum Clock<'a> {
/// closed Clock
Closed {
start_date: Date<'a>,
start_time: Option<Time>,
end_date: Date<'a>,
end_time: Option<Time>,
start: Datetime<'a>,
end: Datetime<'a>,
repeater: Option<&'a str>,
delay: Option<&'a str>,
duration: &'a str,
},
/// running Clock
Running {
start_date: Date<'a>,
start_time: Option<Time>,
start: Datetime<'a>,
repeater: Option<&'a str>,
delay: Option<&'a str>,
},
@ -49,50 +46,54 @@ impl Clock<'_> {
match timestamp {
Timestamp::InactiveRange {
start_date,
start_time,
end_date,
end_time,
start,
end,
repeater,
delay,
} if tail.starts_with("=>") => {
let duration = &tail[3..].trim();
let colon = memchr(b':', duration.as_bytes())?;
if duration.as_bytes()[0..colon].iter().all(u8::is_ascii_digit)
&& colon == duration.len() - 3
&& duration.as_bytes()[colon + 1].is_ascii_digit()
&& duration.as_bytes()[colon + 2].is_ascii_digit()
{
} => {
if tail.starts_with("=>") {
let duration = &tail[3..].trim();
let colon = memchr(b':', duration.as_bytes())?;
if duration.as_bytes()[0..colon].iter().all(u8::is_ascii_digit)
&& colon == duration.len() - 3
&& duration.as_bytes()[colon + 1].is_ascii_digit()
&& duration.as_bytes()[colon + 2].is_ascii_digit()
{
Some((
&text[eol..],
Element::Clock(Clock::Closed {
start,
end,
repeater,
delay,
duration,
}),
))
} else {
None
}
} else {
None
}
}
Timestamp::Inactive {
start,
repeater,
delay,
} => {
if tail.is_empty() {
Some((
&text[eol..],
Element::Clock(Clock::Closed {
start_date,
start_time,
end_date,
end_time,
Element::Clock(Clock::Running {
start,
repeater,
delay,
duration,
}),
))
} else {
None
}
}
Timestamp::Inactive {
start_date,
start_time,
repeater,
delay,
} if tail.is_empty() => Some((
&text[eol..],
Element::Clock(Clock::Running {
start_date,
start_time,
repeater,
delay,
}),
)),
_ => None,
}
}
@ -123,33 +124,27 @@ impl Clock<'_> {
/// constructs a new timestamp object from the clock
pub fn value(&self) -> Timestamp<'_> {
match *self {
match &*self {
Clock::Closed {
start_date,
start_time,
end_date,
end_time,
start,
end,
repeater,
delay,
..
} => Timestamp::InactiveRange {
start_date,
start_time,
end_date,
end_time,
repeater,
delay,
start: start.clone(),
end: end.clone(),
repeater: repeater.clone(),
delay: delay.clone(),
},
Clock::Running {
start_date,
start_time,
start,
repeater,
delay,
} => Timestamp::Inactive {
start_date,
start_time,
repeater,
delay,
start: start.clone(),
repeater: repeater.clone(),
delay: delay.clone(),
},
}
}
@ -162,16 +157,14 @@ fn parse() {
Some((
"",
Element::Clock(Clock::Running {
start_date: Date {
start: Datetime {
year: 2003,
month: 9,
day: 16,
dayname: "Tue"
dayname: "Tue",
hour: Some(9),
minute: Some(39)
},
start_time: Some(Time {
hour: 9,
minute: 39
}),
repeater: None,
delay: None,
})
@ -182,26 +175,22 @@ fn parse() {
Some((
"",
Element::Clock(Clock::Closed {
start_date: Date {
start: Datetime {
year: 2003,
month: 9,
day: 16,
dayname: "Tue"
dayname: "Tue",
hour: Some(9),
minute: Some(39)
},
start_time: Some(Time {
hour: 9,
minute: 39
}),
end_date: Date {
end: Datetime {
year: 2003,
month: 9,
day: 16,
dayname: "Tue"
dayname: "Tue",
hour: Some(10),
minute: Some(39)
},
end_time: Some(Time {
hour: 10,
minute: 39
}),
repeater: None,
delay: None,
duration: "1:00",

View file

@ -22,8 +22,8 @@ mod target;
mod timestamp;
mod title;
pub(crate) use emphasis::parse as parse_emphasis;
pub(crate) use block::Block;
pub(crate) use emphasis::parse as parse_emphasis;
pub use self::{
block::{
@ -47,7 +47,7 @@ pub use self::{
rule::Rule,
snippet::Snippet,
target::Target,
timestamp::{Date, Time, Timestamp},
timestamp::{Datetime, Timestamp},
title::Title,
};

View file

@ -68,7 +68,7 @@ impl Planning<'_> {
#[test]
fn prase() {
use crate::elements::Date;
use crate::elements::Datetime;
assert_eq!(
Planning::parse("SCHEDULED: <2019-04-08 Mon>\n"),
@ -76,13 +76,14 @@ fn prase() {
"",
Planning {
scheduled: Some(Box::new(Timestamp::Active {
start_date: Date {
start: Datetime {
year: 2019,
month: 4,
day: 8,
dayname: "Mon"
dayname: "Mon",
hour: None,
minute: None
},
start_time: None,
repeater: None,
delay: None
})),

View file

@ -1,9 +1,9 @@
use nom::{
branch::alt,
bytes::complete::{tag, take, take_while_m_n},
bytes::complete::{tag, take_while_m_n},
character::complete::space0,
combinator::{map, not},
IResult,
error::ErrorKind,
Err, IResult,
};
use std::usize;
@ -17,11 +17,19 @@ impl Rule {
let (input, _) = space0(input)?;
let (input, _) = take_while_m_n(5, usize::MAX, |c| c == '-')(input)?;
let (input, _) = space0(input)?;
let (input, _) = alt((tag("\n"), map(not(take(1usize)), |_| "")))(input)?;
let (input, _) = alt((tag("\n"), eof))(input)?;
Ok((input, Element::Rule))
}
}
fn eof(input: &str) -> IResult<&str, &str> {
if input.is_empty() {
Ok(("", ""))
} else {
Err(Err::Error(("", ErrorKind::Tag)))
}
}
#[test]
fn parse() {
assert_eq!(Rule::parse("-----"), Ok(("", Element::Rule)));

View file

@ -1,13 +1,11 @@
#[cfg(feature = "chrono")]
use chrono::*;
use nom::{
bytes::complete::{tag, take, take_till, take_while, take_while_m_n},
character::complete::{space0, space1},
combinator::{map_res, opt},
combinator::{map, map_res, opt},
IResult,
};
/// Date
/// Datetime
///
/// # Syntax
///
@ -17,117 +15,145 @@ use nom::{
///
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Debug, Clone, Copy)]
pub struct Date<'a> {
#[derive(Debug, Clone)]
pub struct Datetime<'a> {
pub year: u16,
pub month: u8,
pub day: u8,
pub dayname: &'a str,
pub hour: Option<u8>,
pub minute: Option<u8>,
}
impl Date<'_> {
fn parse(input: &str) -> IResult<&str, Date<'_>> {
let (input, year) = map_res(take(4usize), |num| u16::from_str_radix(num, 10))(input)?;
let (input, _) = tag("-")(input)?;
let (input, month) = map_res(take(2usize), |num| u8::from_str_radix(num, 10))(input)?;
let (input, _) = tag("-")(input)?;
let (input, day) = map_res(take(2usize), |num| u8::from_str_radix(num, 10))(input)?;
let (input, _) = space1(input)?;
let (input, dayname) = take_while(|c: char| {
!c.is_ascii_whitespace()
&& !c.is_ascii_digit()
&& c != '+'
&& c != '-'
&& c != ']'
&& c != '>'
})(input)?;
fn parse_time(input: &str) -> IResult<&str, (u8, u8)> {
let (input, hour) = map_res(take_while_m_n(1, 2, |c: char| c.is_ascii_digit()), |num| {
u8::from_str_radix(num, 10)
})(input)?;
let (input, _) = tag(":")(input)?;
let (input, minute) = map_res(take(2usize), |num| u8::from_str_radix(num, 10))(input)?;
Ok((input, (hour, minute)))
}
Ok((
input,
Date {
year,
month,
day,
dayname,
},
))
}
}
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Debug, Clone, Copy)]
pub struct Time {
pub hour: u8,
pub minute: u8,
}
impl Time {
fn parse(input: &str) -> IResult<&str, Time> {
let (input, hour) = map_res(take_while_m_n(1, 2, |c: char| c.is_ascii_digit()), |num| {
u8::from_str_radix(num, 10)
})(input)?;
let (input, _) = tag(":")(input)?;
let (input, minute) = map_res(take(2usize), |num| u8::from_str_radix(num, 10))(input)?;
Ok((input, Time { hour, minute }))
fn parse_datetime(input: &str) -> IResult<&str, Datetime<'_>> {
let parse_u8 = |num| u8::from_str_radix(num, 10);
let (input, year) = map_res(take(4usize), |num| u16::from_str_radix(num, 10))(input)?;
let (input, _) = tag("-")(input)?;
let (input, month) = map_res(take(2usize), parse_u8)(input)?;
let (input, _) = tag("-")(input)?;
let (input, day) = map_res(take(2usize), parse_u8)(input)?;
let (input, _) = space1(input)?;
let (input, dayname) = take_while(|c: char| {
!c.is_ascii_whitespace()
&& !c.is_ascii_digit()
&& c != '+'
&& c != '-'
&& c != ']'
&& c != '>'
})(input)?;
let (input, (hour, minute)) = map(
opt(|input| {
let (input, _) = space1(input)?;
parse_time(input)
}),
|time| (time.map(|t| t.0), time.map(|t| t.1)),
)(input)?;
Ok((
input,
Datetime {
year,
month,
day,
dayname,
hour,
minute,
},
))
}
#[cfg(feature = "chrono")]
mod chrono {
use super::Datetime;
use chrono::*;
impl Into<NaiveDate> for Datetime<'_> {
fn into(self) -> NaiveDate {
NaiveDate::from_ymd(self.year.into(), self.month.into(), self.day.into())
}
}
impl Into<NaiveTime> for Datetime<'_> {
fn into(self) -> NaiveTime {
NaiveTime::from_hms(
self.hour.unwrap_or_default().into(),
self.minute.unwrap_or_default().into(),
0,
)
}
}
impl Into<NaiveDateTime> for Datetime<'_> {
fn into(self) -> NaiveDateTime {
NaiveDate::from_ymd(self.year.into(), self.month.into(), self.day.into()).and_hms(
self.hour.unwrap_or_default().into(),
self.minute.unwrap_or_default().into(),
0,
)
}
}
}
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename_all = "snake_case"))]
#[derive(Debug)]
pub enum Timestamp<'a> {
Active {
start_date: Date<'a>,
start_time: Option<Time>,
start: Datetime<'a>,
repeater: Option<&'a str>,
delay: Option<&'a str>,
},
Inactive {
start_date: Date<'a>,
start_time: Option<Time>,
start: Datetime<'a>,
repeater: Option<&'a str>,
delay: Option<&'a str>,
},
ActiveRange {
start_date: Date<'a>,
start_time: Option<Time>,
end_date: Date<'a>,
end_time: Option<Time>,
start: Datetime<'a>,
end: Datetime<'a>,
repeater: Option<&'a str>,
delay: Option<&'a str>,
},
InactiveRange {
start_date: Date<'a>,
start_time: Option<Time>,
end_date: Date<'a>,
end_time: Option<Time>,
start: Datetime<'a>,
end: Datetime<'a>,
repeater: Option<&'a str>,
delay: Option<&'a str>,
},
Diary(&'a str),
Diary {
value: &'a str,
},
}
impl Timestamp<'_> {
pub(crate) fn parse_active(input: &str) -> IResult<&str, Timestamp<'_>> {
let (input, _) = tag("<")(input)?;
let (input, start_date) = Date::parse(input)?;
let (input, _) = space0(input)?;
let (input, start_time) = opt(Time::parse)(input)?;
let (input, start) = parse_datetime(input)?;
if input.starts_with('-') {
let (input, end_time) = opt(Time::parse)(&input[1..])?;
let (input, (hour, minute)) = parse_time(&input[1..])?;
let (input, _) = space0(input)?;
// TODO: delay-or-repeater
let (input, _) = tag(">")(input)?;
let mut end = start.clone();
end.hour = Some(hour);
end.minute = Some(minute);
return Ok((
input,
Timestamp::ActiveRange {
start_date,
start_time,
end_date: start_date,
end_time,
start,
end,
repeater: None,
delay: None,
},
@ -139,19 +165,15 @@ impl Timestamp<'_> {
let (input, _) = tag(">")(input)?;
if input.starts_with("--<") {
let (input, end_date) = Date::parse(&input["--<".len()..])?;
let (input, _) = space0(input)?;
let (input, end_time) = opt(Time::parse)(input)?;
let (input, end) = parse_datetime(&input["--<".len()..])?;
let (input, _) = space0(input)?;
// TODO: delay-or-repeater
let (input, _) = tag(">")(input)?;
Ok((
input,
Timestamp::ActiveRange {
start_date,
start_time,
end_date,
end_time,
start,
end,
repeater: None,
delay: None,
},
@ -160,8 +182,7 @@ impl Timestamp<'_> {
Ok((
input,
Timestamp::Active {
start_date,
start_time,
start,
repeater: None,
delay: None,
},
@ -171,22 +192,21 @@ impl Timestamp<'_> {
pub(crate) fn parse_inactive(input: &str) -> IResult<&str, Timestamp<'_>> {
let (input, _) = tag("[")(input)?;
let (input, start_date) = Date::parse(input)?;
let (input, _) = space0(input)?;
let (input, start_time) = opt(Time::parse)(input)?;
let (input, start) = parse_datetime(input)?;
if input.starts_with('-') {
let (input, end_time) = opt(Time::parse)(&input[1..])?;
let (input, (hour, minute)) = parse_time(&input[1..])?;
let (input, _) = space0(input)?;
// TODO: delay-or-repeater
let (input, _) = tag("]")(input)?;
let mut end = start.clone();
end.hour = Some(hour);
end.minute = Some(minute);
return Ok((
input,
Timestamp::InactiveRange {
start_date,
start_time,
end_date: start_date,
end_time,
start,
end,
repeater: None,
delay: None,
},
@ -198,19 +218,15 @@ impl Timestamp<'_> {
let (input, _) = tag("]")(input)?;
if input.starts_with("--[") {
let (input, end_date) = Date::parse(&input["--[".len()..])?;
let (input, _) = space0(input)?;
let (input, end_time) = opt(Time::parse)(input)?;
let (input, end) = parse_datetime(&input["--[".len()..])?;
let (input, _) = space0(input)?;
// TODO: delay-or-repeater
let (input, _) = tag("]")(input)?;
Ok((
input,
Timestamp::InactiveRange {
start_date,
start_time,
end_date,
end_time,
start,
end,
repeater: None,
delay: None,
},
@ -219,8 +235,7 @@ impl Timestamp<'_> {
Ok((
input,
Timestamp::Inactive {
start_date,
start_time,
start,
repeater: None,
delay: None,
},
@ -230,10 +245,10 @@ impl Timestamp<'_> {
pub(crate) fn parse_diary(input: &str) -> IResult<&str, Timestamp<'_>> {
let (input, _) = tag("<%%(")(input)?;
let (input, sexp) = take_till(|c| c == ')' || c == '>' || c == '\n')(input)?;
let (input, value) = take_till(|c| c == ')' || c == '>' || c == '\n')(input)?;
let (input, _) = tag(")>")(input)?;
Ok((input, Timestamp::Diary(sexp)))
Ok((input, Timestamp::Diary { value }))
}
}
@ -291,13 +306,14 @@ fn parse() {
Ok((
"",
Timestamp::Inactive {
start_date: Date {
start: Datetime {
year: 2003,
month: 9,
day: 16,
dayname: "Tue"
dayname: "Tue",
hour: None,
minute: None
},
start_time: None,
repeater: None,
delay: None,
},
@ -308,26 +324,22 @@ fn parse() {
Ok((
"",
Timestamp::InactiveRange {
start_date: Date {
start: Datetime {
year: 2003,
month: 9,
day: 16,
dayname: "Tue"
dayname: "Tue",
hour: Some(9),
minute: Some(39)
},
start_time: Some(Time {
hour: 9,
minute: 39
}),
end_date: Date {
end: Datetime {
year: 2003,
month: 9,
day: 16,
dayname: "Tue"
dayname: "Tue",
hour: Some(10),
minute: Some(39),
},
end_time: Some(Time {
hour: 10,
minute: 39
}),
repeater: None,
delay: None
},
@ -338,26 +350,22 @@ fn parse() {
Ok((
"",
Timestamp::ActiveRange {
start_date: Date {
start: Datetime {
year: 2003,
month: 9,
day: 16,
dayname: "Tue"
dayname: "Tue",
hour: Some(9),
minute: Some(39),
},
start_time: Some(Time {
hour: 9,
minute: 39
}),
end_date: Date {
end: Datetime {
year: 2003,
month: 9,
day: 16,
dayname: "Tue"
dayname: "Tue",
hour: Some(10),
minute: Some(39),
},
end_time: Some(Time {
hour: 10,
minute: 39
}),
repeater: None,
delay: None
},

View file

@ -1,4 +1,4 @@
use crate::elements::Element;
use crate::elements::{Datetime, Element};
use jetscii::bytes;
use std::fmt;
use std::io::{Error, Write};
@ -104,7 +104,7 @@ pub trait HtmlHandler<E: From<Error>> {
Target(_target) => (),
Text { value } => write!(w, "{}", Escape(value))?,
Timestamp(timestamp) => {
use crate::elements::{Date, Time, Timestamp::*};
use crate::elements::Timestamp;
write!(
&mut w,
@ -114,61 +114,37 @@ pub trait HtmlHandler<E: From<Error>> {
fn write_datetime<W: Write>(
mut w: W,
start: &str,
date: &Date,
time: &Option<Time>,
datetime: &Datetime,
end: &str,
) -> Result<(), Error> {
write!(w, "{}", start)?;
write!(
w,
"{}-{}-{} {}",
date.year,
date.month,
date.day,
Escape(date.dayname)
datetime.year, datetime.month, datetime.day, datetime.dayname
)?;
if let Some(time) = time {
write!(w, " {}:{}", time.hour, time.minute)?;
if let (Some(hour), Some(minute)) = (datetime.hour, datetime.minute) {
write!(w, " {}:{}", hour, minute)?;
}
write!(w, "{}", end)
}
match timestamp {
Active {
start_date,
start_time,
..
} => {
write_datetime(&mut w, "&lt;", start_date, start_time, "&gt;")?;
Timestamp::Active { start, .. } => {
write_datetime(&mut w, "&lt;", start, "&gt;")?;
}
Inactive {
start_date,
start_time,
..
} => {
write_datetime(&mut w, "[", start_date, start_time, "]")?;
Timestamp::Inactive { start, .. } => {
write_datetime(&mut w, "[", start, "]")?;
}
ActiveRange {
start_date,
start_time,
end_date,
end_time,
..
} => {
write_datetime(&mut w, "&lt;", start_date, start_time, "&gt;&#x2013;")?;
write_datetime(&mut w, "&lt;", end_date, end_time, "&gt;")?;
Timestamp::ActiveRange { start, end, .. } => {
write_datetime(&mut w, "&lt;", start, "&gt;&#x2013;")?;
write_datetime(&mut w, "&lt;", end, "&gt;")?;
}
InactiveRange {
start_date,
start_time,
end_date,
end_time,
..
} => {
write_datetime(&mut w, "[", start_date, start_time, "]&#x2013;")?;
write_datetime(&mut w, "[", end_date, end_time, "]")?;
Timestamp::InactiveRange { start, end, .. } => {
write_datetime(&mut w, "[", start, "]&#x2013;")?;
write_datetime(&mut w, "[", end, "]")?;
}
Diary(value) => write!(&mut w, "&lt;%%({})&gt;", Escape(value))?,
Timestamp::Diary { value } => write!(&mut w, "&lt;%%({})&gt;", Escape(value))?,
}
write!(&mut w, "</span></span>")?;

View file

@ -1,4 +1,4 @@
use crate::elements::Element;
use crate::elements::{Datetime, Element};
use std::io::{Error, Write};
pub trait OrgHandler<E: From<Error>> {
@ -89,63 +89,42 @@ pub trait OrgHandler<E: From<Error>> {
Target(_target) => (),
Text { value } => write!(w, "{}", value)?,
Timestamp(timestamp) => {
use crate::elements::{Date, Time, Timestamp::*};
use crate::elements::Timestamp;
fn write_datetime<W: Write>(
mut w: W,
start: &str,
date: &Date,
time: &Option<Time>,
datetime: &Datetime,
end: &str,
) -> Result<(), Error> {
write!(w, "{}", start)?;
write!(
w,
"{}-{}-{} {}",
date.year, date.month, date.day, date.dayname
datetime.year, datetime.month, datetime.day, datetime.dayname
)?;
if let Some(time) = time {
write!(w, " {}:{}", time.hour, time.minute,)?;
if let (Some(hour), Some(minute)) = (datetime.hour, datetime.minute) {
write!(w, " {}:{}", hour, minute)?;
}
write!(w, "{}", end)
}
match timestamp {
Active {
start_date,
start_time,
..
} => {
write_datetime(&mut w, "<", start_date, start_time, ">")?;
Timestamp::Active { start, .. } => {
write_datetime(&mut w, "<", start, ">")?;
}
Inactive {
start_date,
start_time,
..
} => {
write_datetime(&mut w, "[", start_date, start_time, "]")?;
Timestamp::Inactive { start, .. } => {
write_datetime(&mut w, "[", start, "]")?;
}
ActiveRange {
start_date,
start_time,
end_date,
end_time,
..
} => {
write_datetime(&mut w, "<", start_date, start_time, ">--")?;
write_datetime(&mut w, "<", end_date, end_time, ">")?;
Timestamp::ActiveRange { start, end, .. } => {
write_datetime(&mut w, "<", start, ">--")?;
write_datetime(&mut w, "<", end, ">")?;
}
InactiveRange {
start_date,
start_time,
end_date,
end_time,
..
} => {
write_datetime(&mut w, "[", start_date, start_time, "]--")?;
write_datetime(&mut w, "[", end_date, end_time, "]")?;
Timestamp::InactiveRange { start, end, .. } => {
write_datetime(&mut w, "[", start, "]--")?;
write_datetime(&mut w, "[", end, "]")?;
}
Diary(value) => write!(w, "<%%({})>", value)?,
Timestamp::Diary { value } => write!(w, "<%%({})>", value)?,
}
}
Verbatim { value } => write!(w, "={}=", value)?,