Merge branch 'timestamp_properties'
This commit is contained in:
commit
be483110ef
@ -0,0 +1,7 @@
|
|||||||
|
# All the marks for repeater and warning delay
|
||||||
|
[1970-01-01 Thu 8:15-13:15foo +1h -2h]
|
||||||
|
[1970-01-01 Thu 8:15-13:15foo ++1d -2d]
|
||||||
|
[1970-01-01 Thu 8:15-13:15foo .+1w -2w]
|
||||||
|
[1970-01-01 Thu 8:15-13:15foo +1m --2m]
|
||||||
|
[1970-01-01 Thu 8:15-13:15foo ++1y --2y]
|
||||||
|
[1970-01-01 Thu 8:15-13:15foo .+1d --2h]
|
@ -2,13 +2,17 @@
|
|||||||
<%%(foo bar baz)>
|
<%%(foo bar baz)>
|
||||||
# active
|
# active
|
||||||
<1970-01-01 Thu 8:15rest +1w -1d>
|
<1970-01-01 Thu 8:15rest +1w -1d>
|
||||||
|
# Any value for "REST" in the first timestamp makes this a regular timestamp rather than a time range.
|
||||||
|
<1970-01-01 Thu 8:15rest-13:15otherrest +1w -1d>
|
||||||
# inactive
|
# inactive
|
||||||
[1970-01-01 Thu 8:15rest +1w -1d]
|
[1970-01-01 Thu 8:15rest +1w -1d]
|
||||||
|
# Any value for "REST" in the first timestamp makes this a regular timestamp rather than a time range.
|
||||||
|
[1970-01-01 Thu 8:15rest-13:15otherrest +1w -1d]
|
||||||
# active date range
|
# active date range
|
||||||
<1970-01-01 Thu 8:15rest +1w -1d>--<1970-01-01 Thu 8:15rest +1w -1d>
|
<1970-01-01 Thu 8:15rest +1w -1d>--<1970-01-01 Thu 8:15rest +1w -1d>
|
||||||
# active time range
|
# active time range
|
||||||
<1970-01-01 Thu 8:15rest-13:15otherrest +1w -1d>
|
<1970-01-01 Thu 8:15-13:15otherrest +1w -1d>
|
||||||
# inactive date range
|
# inactive date range
|
||||||
[1970-01-01 Thu 8:15rest +1w -1d]--[1970-01-01 Thu 8:15rest +1w -1d]
|
[1970-01-01 Thu 8:15rest +1w -1d]--[1970-01-01 Thu 8:15rest +1w -1d]
|
||||||
# inactive time range
|
# inactive time range
|
||||||
[1970-01-01 Thu 8:15rest-13:15otherrest +1w -1d]
|
[1970-01-01 Thu 8:15-13:15otherrest +1w -1d]
|
||||||
|
@ -10,10 +10,11 @@ use super::sexp::Token;
|
|||||||
use super::util::compare_standard_properties;
|
use super::util::compare_standard_properties;
|
||||||
use super::util::get_property;
|
use super::util::get_property;
|
||||||
use super::util::get_property_boolean;
|
use super::util::get_property_boolean;
|
||||||
|
use super::util::get_property_numeric;
|
||||||
use super::util::get_property_quoted_string;
|
use super::util::get_property_quoted_string;
|
||||||
use super::util::get_property_unquoted_atom;
|
use super::util::get_property_unquoted_atom;
|
||||||
use crate::types::AstNode;
|
|
||||||
use crate::types::AngleLink;
|
use crate::types::AngleLink;
|
||||||
|
use crate::types::AstNode;
|
||||||
use crate::types::BabelCall;
|
use crate::types::BabelCall;
|
||||||
use crate::types::Bold;
|
use crate::types::Bold;
|
||||||
use crate::types::CheckboxType;
|
use crate::types::CheckboxType;
|
||||||
@ -23,6 +24,9 @@ use crate::types::Clock;
|
|||||||
use crate::types::Code;
|
use crate::types::Code;
|
||||||
use crate::types::Comment;
|
use crate::types::Comment;
|
||||||
use crate::types::CommentBlock;
|
use crate::types::CommentBlock;
|
||||||
|
use crate::types::Date;
|
||||||
|
use crate::types::DayOfMonth;
|
||||||
|
use crate::types::DayOfMonthInner;
|
||||||
use crate::types::DiarySexp;
|
use crate::types::DiarySexp;
|
||||||
use crate::types::Document;
|
use crate::types::Document;
|
||||||
use crate::types::DocumentElement;
|
use crate::types::DocumentElement;
|
||||||
@ -39,6 +43,8 @@ use crate::types::GetStandardProperties;
|
|||||||
use crate::types::GreaterBlock;
|
use crate::types::GreaterBlock;
|
||||||
use crate::types::Heading;
|
use crate::types::Heading;
|
||||||
use crate::types::HorizontalRule;
|
use crate::types::HorizontalRule;
|
||||||
|
use crate::types::Hour;
|
||||||
|
use crate::types::HourInner;
|
||||||
use crate::types::InlineBabelCall;
|
use crate::types::InlineBabelCall;
|
||||||
use crate::types::InlineSourceBlock;
|
use crate::types::InlineSourceBlock;
|
||||||
use crate::types::Italic;
|
use crate::types::Italic;
|
||||||
@ -46,6 +52,10 @@ use crate::types::Keyword;
|
|||||||
use crate::types::LatexEnvironment;
|
use crate::types::LatexEnvironment;
|
||||||
use crate::types::LatexFragment;
|
use crate::types::LatexFragment;
|
||||||
use crate::types::LineBreak;
|
use crate::types::LineBreak;
|
||||||
|
use crate::types::Minute;
|
||||||
|
use crate::types::MinuteInner;
|
||||||
|
use crate::types::Month;
|
||||||
|
use crate::types::MonthInner;
|
||||||
use crate::types::NodeProperty;
|
use crate::types::NodeProperty;
|
||||||
use crate::types::OrgMacro;
|
use crate::types::OrgMacro;
|
||||||
use crate::types::Paragraph;
|
use crate::types::Paragraph;
|
||||||
@ -62,6 +72,8 @@ use crate::types::PropertyDrawer;
|
|||||||
use crate::types::RadioLink;
|
use crate::types::RadioLink;
|
||||||
use crate::types::RadioTarget;
|
use crate::types::RadioTarget;
|
||||||
use crate::types::RegularLink;
|
use crate::types::RegularLink;
|
||||||
|
use crate::types::RepeaterType;
|
||||||
|
use crate::types::RepeaterWarningDelayValueType;
|
||||||
use crate::types::Section;
|
use crate::types::Section;
|
||||||
use crate::types::SrcBlock;
|
use crate::types::SrcBlock;
|
||||||
use crate::types::StandardProperties;
|
use crate::types::StandardProperties;
|
||||||
@ -73,11 +85,18 @@ use crate::types::Table;
|
|||||||
use crate::types::TableCell;
|
use crate::types::TableCell;
|
||||||
use crate::types::TableRow;
|
use crate::types::TableRow;
|
||||||
use crate::types::Target;
|
use crate::types::Target;
|
||||||
|
use crate::types::Time;
|
||||||
|
use crate::types::TimeUnit;
|
||||||
use crate::types::Timestamp;
|
use crate::types::Timestamp;
|
||||||
|
use crate::types::TimestampRangeType;
|
||||||
|
use crate::types::TimestampType;
|
||||||
use crate::types::TodoKeywordType;
|
use crate::types::TodoKeywordType;
|
||||||
use crate::types::Underline;
|
use crate::types::Underline;
|
||||||
use crate::types::Verbatim;
|
use crate::types::Verbatim;
|
||||||
use crate::types::VerseBlock;
|
use crate::types::VerseBlock;
|
||||||
|
use crate::types::WarningDelayType;
|
||||||
|
use crate::types::Year;
|
||||||
|
use crate::types::YearInner;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DiffEntry<'b, 's> {
|
pub enum DiffEntry<'b, 's> {
|
||||||
@ -2115,11 +2134,278 @@ fn compare_timestamp<'b, 's>(
|
|||||||
_source: &'s str,
|
_source: &'s str,
|
||||||
emacs: &'b Token<'s>,
|
emacs: &'b Token<'s>,
|
||||||
rust: &'b Timestamp<'s>,
|
rust: &'b Timestamp<'s>,
|
||||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error + 's>> {
|
||||||
let this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
let message = None;
|
let mut message = None;
|
||||||
|
|
||||||
// TODO: Compare :type :range-type :raw-value :year-start :month-start :day-start :hour-start :minute-start :year-end :month-end :day-end :hour-end :minute-end
|
// Compare type
|
||||||
|
let timestamp_type = get_property_unquoted_atom(emacs, ":type")?;
|
||||||
|
match (timestamp_type, &rust.timestamp_type) {
|
||||||
|
(Some("diary"), TimestampType::Diary) => {}
|
||||||
|
(Some("active"), TimestampType::Active) => {}
|
||||||
|
(Some("inactive"), TimestampType::Inactive) => {}
|
||||||
|
(Some("active-range"), TimestampType::ActiveRange) => {}
|
||||||
|
(Some("inactive-range"), TimestampType::InactiveRange) => {}
|
||||||
|
_ => {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"Timestamp type mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
timestamp_type, rust.timestamp_type
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare range-type
|
||||||
|
let range_type = get_property_unquoted_atom(emacs, ":range-type")?;
|
||||||
|
match (range_type, &rust.range_type) {
|
||||||
|
(Some("daterange"), TimestampRangeType::DateRange) => {}
|
||||||
|
(Some("timerange"), TimestampRangeType::TimeRange) => {}
|
||||||
|
(None, TimestampRangeType::None) => {}
|
||||||
|
_ => {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"Range type mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
range_type, rust.range_type
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare raw-value
|
||||||
|
let raw_value = get_property_quoted_string(emacs, ":raw-value")?
|
||||||
|
.ok_or("Timestamps should have a :raw-value.")?;
|
||||||
|
if raw_value != rust.get_raw_value() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"Raw value mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
raw_value,
|
||||||
|
rust.get_raw_value()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare start
|
||||||
|
let year_start: Option<YearInner> = get_property_numeric(emacs, ":year-start")?;
|
||||||
|
let month_start: Option<MonthInner> = get_property_numeric(emacs, ":month-start")?;
|
||||||
|
let day_of_month_start: Option<DayOfMonthInner> = get_property_numeric(emacs, ":day-start")?;
|
||||||
|
let rust_year_start = rust.start.as_ref().map(Date::get_year).map(Year::get_value);
|
||||||
|
let rust_month_start = rust
|
||||||
|
.start
|
||||||
|
.as_ref()
|
||||||
|
.map(Date::get_month)
|
||||||
|
.map(Month::get_value);
|
||||||
|
let rust_day_of_month_start = rust
|
||||||
|
.start
|
||||||
|
.as_ref()
|
||||||
|
.map(Date::get_day_of_month)
|
||||||
|
.map(DayOfMonth::get_value);
|
||||||
|
if year_start != rust_year_start {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"year start mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
year_start, rust_year_start
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if month_start != rust_month_start {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"month start mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
month_start, rust_month_start
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if day_of_month_start != rust_day_of_month_start {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"day of month start mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
day_of_month_start, rust_day_of_month_start
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare end
|
||||||
|
let year_end: Option<YearInner> = get_property_numeric(emacs, ":year-end")?;
|
||||||
|
let month_end: Option<MonthInner> = get_property_numeric(emacs, ":month-end")?;
|
||||||
|
let day_of_month_end: Option<DayOfMonthInner> = get_property_numeric(emacs, ":day-end")?;
|
||||||
|
let rust_year_end = rust.end.as_ref().map(Date::get_year).map(Year::get_value);
|
||||||
|
let rust_month_end = rust.end.as_ref().map(Date::get_month).map(Month::get_value);
|
||||||
|
let rust_day_of_month_end = rust
|
||||||
|
.end
|
||||||
|
.as_ref()
|
||||||
|
.map(Date::get_day_of_month)
|
||||||
|
.map(DayOfMonth::get_value);
|
||||||
|
if year_end != rust_year_end {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"year end mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
year_end, rust_year_end
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if month_end != rust_month_end {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"month end mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
month_end, rust_month_end
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if day_of_month_end != rust_day_of_month_end {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"day of month end mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
day_of_month_end, rust_day_of_month_end
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare time start
|
||||||
|
let hour_start: Option<HourInner> = get_property_numeric(emacs, ":hour-start")?;
|
||||||
|
let minute_start: Option<MinuteInner> = get_property_numeric(emacs, ":minute-start")?;
|
||||||
|
let rust_hour_start = rust
|
||||||
|
.start_time
|
||||||
|
.as_ref()
|
||||||
|
.map(Time::get_hour)
|
||||||
|
.map(Hour::get_value);
|
||||||
|
let rust_minute_start = rust
|
||||||
|
.start_time
|
||||||
|
.as_ref()
|
||||||
|
.map(Time::get_minute)
|
||||||
|
.map(Minute::get_value);
|
||||||
|
if hour_start != rust_hour_start {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"hour start mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
hour_start, rust_hour_start
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if minute_start != rust_minute_start {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"minute start mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
minute_start, rust_minute_start
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare time end
|
||||||
|
let hour_end: Option<HourInner> = get_property_numeric(emacs, ":hour-end")?;
|
||||||
|
let minute_end: Option<MinuteInner> = get_property_numeric(emacs, ":minute-end")?;
|
||||||
|
let rust_hour_end = rust
|
||||||
|
.end_time
|
||||||
|
.as_ref()
|
||||||
|
.map(Time::get_hour)
|
||||||
|
.map(Hour::get_value);
|
||||||
|
let rust_minute_end = rust
|
||||||
|
.end_time
|
||||||
|
.as_ref()
|
||||||
|
.map(Time::get_minute)
|
||||||
|
.map(Minute::get_value);
|
||||||
|
if hour_end != rust_hour_end {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"hour end mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
hour_end, rust_hour_end
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if minute_end != rust_minute_end {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"minute end mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
minute_end, rust_minute_end
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare repeater
|
||||||
|
let repeater_type = get_property_unquoted_atom(emacs, ":repeater-type")?;
|
||||||
|
let repeater_value: Option<RepeaterWarningDelayValueType> =
|
||||||
|
get_property_numeric(emacs, ":repeater-value")?;
|
||||||
|
let repeater_unit = get_property_unquoted_atom(emacs, ":repeater-unit")?;
|
||||||
|
let rust_repeater_type = rust
|
||||||
|
.repeater
|
||||||
|
.as_ref()
|
||||||
|
.map(|repeater| &repeater.repeater_type);
|
||||||
|
let rust_repeater_value = rust.repeater.as_ref().map(|repeater| repeater.value);
|
||||||
|
let rust_repeater_unit = rust.repeater.as_ref().map(|repeater| &repeater.unit);
|
||||||
|
match (repeater_type, rust_repeater_type) {
|
||||||
|
(Some("cumulate"), Some(RepeaterType::Cumulative)) => {}
|
||||||
|
(Some("catch-up"), Some(RepeaterType::CatchUp)) => {}
|
||||||
|
(Some("restart"), Some(RepeaterType::Restart)) => {}
|
||||||
|
(None, None) => {}
|
||||||
|
_ => {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"Repeater type mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
repeater_type, rust_repeater_type
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if repeater_value != rust_repeater_value {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"Repeater value mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
repeater_value, rust_repeater_value
|
||||||
|
));
|
||||||
|
}
|
||||||
|
match (repeater_unit, rust_repeater_unit) {
|
||||||
|
(Some("hour"), Some(TimeUnit::Hour)) => {}
|
||||||
|
(Some("day"), Some(TimeUnit::Day)) => {}
|
||||||
|
(Some("week"), Some(TimeUnit::Week)) => {}
|
||||||
|
(Some("month"), Some(TimeUnit::Month)) => {}
|
||||||
|
(Some("year"), Some(TimeUnit::Year)) => {}
|
||||||
|
(None, None) => {}
|
||||||
|
_ => {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"Repeater unit mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
repeater_unit, rust_repeater_unit
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare warning_delay
|
||||||
|
let warning_delay_type = get_property_unquoted_atom(emacs, ":warning-type")?;
|
||||||
|
let warning_delay_value: Option<RepeaterWarningDelayValueType> =
|
||||||
|
get_property_numeric(emacs, ":warning-value")?;
|
||||||
|
let warning_delay_unit = get_property_unquoted_atom(emacs, ":warning-unit")?;
|
||||||
|
let rust_warning_delay_type = rust
|
||||||
|
.warning_delay
|
||||||
|
.as_ref()
|
||||||
|
.map(|warning_delay| &warning_delay.warning_delay_type);
|
||||||
|
let rust_warning_delay_value = rust
|
||||||
|
.warning_delay
|
||||||
|
.as_ref()
|
||||||
|
.map(|warning_delay| warning_delay.value);
|
||||||
|
let rust_warning_delay_unit = rust
|
||||||
|
.warning_delay
|
||||||
|
.as_ref()
|
||||||
|
.map(|warning_delay| &warning_delay.unit);
|
||||||
|
match (warning_delay_type, rust_warning_delay_type) {
|
||||||
|
(Some("all"), Some(WarningDelayType::All)) => {}
|
||||||
|
(Some("first"), Some(WarningDelayType::First)) => {}
|
||||||
|
(None, None) => {}
|
||||||
|
_ => {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"Warning delay type mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
warning_delay_type, rust_warning_delay_type
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if warning_delay_value != rust_warning_delay_value {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"Warning delay value mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
warning_delay_value, rust_warning_delay_value
|
||||||
|
));
|
||||||
|
}
|
||||||
|
match (warning_delay_unit, rust_warning_delay_unit) {
|
||||||
|
(Some("hour"), Some(TimeUnit::Hour)) => {}
|
||||||
|
(Some("day"), Some(TimeUnit::Day)) => {}
|
||||||
|
(Some("week"), Some(TimeUnit::Week)) => {}
|
||||||
|
(Some("month"), Some(TimeUnit::Month)) => {}
|
||||||
|
(Some("year"), Some(TimeUnit::Year)) => {}
|
||||||
|
(None, None) => {}
|
||||||
|
_ => {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"Warning delay unit mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
warning_delay_unit, rust_warning_delay_unit
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::types::AstNode;
|
|
||||||
use crate::types::AngleLink;
|
use crate::types::AngleLink;
|
||||||
|
use crate::types::AstNode;
|
||||||
use crate::types::BabelCall;
|
use crate::types::BabelCall;
|
||||||
use crate::types::Bold;
|
use crate::types::Bold;
|
||||||
use crate::types::Citation;
|
use crate::types::Citation;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use super::elisp_fact::GetElispFact;
|
use super::elisp_fact::GetElispFact;
|
||||||
use super::sexp::Token;
|
use super::sexp::Token;
|
||||||
use crate::compare::sexp::unquote;
|
use crate::compare::sexp::unquote;
|
||||||
@ -170,9 +172,7 @@ fn maybe_token_to_usize(
|
|||||||
|
|
||||||
/// Get a named property from the emacs token.
|
/// Get a named property from the emacs token.
|
||||||
///
|
///
|
||||||
/// Returns Ok(None) if value is nil.
|
/// Returns Ok(None) if value is nil or absent.
|
||||||
///
|
|
||||||
/// Returns error if the attribute is not specified on the token at all.
|
|
||||||
pub(crate) fn get_property<'b, 's, 'x>(
|
pub(crate) fn get_property<'b, 's, 'x>(
|
||||||
emacs: &'b Token<'s>,
|
emacs: &'b Token<'s>,
|
||||||
key: &'x str,
|
key: &'x str,
|
||||||
@ -183,14 +183,12 @@ pub(crate) fn get_property<'b, 's, 'x>(
|
|||||||
.nth(1)
|
.nth(1)
|
||||||
.ok_or("Should have an attributes child.")?;
|
.ok_or("Should have an attributes child.")?;
|
||||||
let attributes_map = attributes_child.as_map()?;
|
let attributes_map = attributes_child.as_map()?;
|
||||||
let prop = attributes_map
|
let prop = attributes_map.get(key).map(|token| *token);
|
||||||
.get(key)
|
match prop.map(|token| token.as_atom()) {
|
||||||
.ok_or(format!("Missing {} attribute.", key))?;
|
Some(Ok("nil")) => return Ok(None),
|
||||||
match prop.as_atom() {
|
|
||||||
Ok("nil") => return Ok(None),
|
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
Ok(Some(*prop))
|
Ok(prop)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a named property containing an unquoted atom from the emacs token.
|
/// Get a named property containing an unquoted atom from the emacs token.
|
||||||
@ -234,3 +232,23 @@ pub(crate) fn get_property_boolean<'b, 's, 'x>(
|
|||||||
.unwrap_or("nil")
|
.unwrap_or("nil")
|
||||||
!= "nil")
|
!= "nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a named property containing an unquoted numeric value.
|
||||||
|
///
|
||||||
|
/// Returns None if key is not found.
|
||||||
|
pub(crate) fn get_property_numeric<'b, 's, 'x, N: FromStr>(
|
||||||
|
emacs: &'b Token<'s>,
|
||||||
|
key: &'x str,
|
||||||
|
) -> Result<Option<N>, Box<dyn std::error::Error + 's>>
|
||||||
|
where
|
||||||
|
<N as FromStr>::Err: std::error::Error,
|
||||||
|
<N as FromStr>::Err: 's,
|
||||||
|
{
|
||||||
|
let unparsed_string = get_property(emacs, key)?
|
||||||
|
.map(Token::as_atom)
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?;
|
||||||
|
let parsed_number = unparsed_string
|
||||||
|
.map(|val| val.parse::<N>())
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?;
|
||||||
|
Ok(parsed_number)
|
||||||
|
}
|
||||||
|
@ -9,6 +9,7 @@ pub enum CustomError<I> {
|
|||||||
MyError(MyError<&'static str>),
|
MyError(MyError<&'static str>),
|
||||||
Nom(I, ErrorKind),
|
Nom(I, ErrorKind),
|
||||||
IO(std::io::Error),
|
IO(std::io::Error),
|
||||||
|
BoxedError(Box<dyn std::error::Error>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -36,3 +37,9 @@ impl<I> From<&'static str> for CustomError<I> {
|
|||||||
CustomError::MyError(MyError(value))
|
CustomError::MyError(MyError(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<I> From<Box<dyn std::error::Error>> for CustomError<I> {
|
||||||
|
fn from(value: Box<dyn std::error::Error>) -> Self {
|
||||||
|
CustomError::BoxedError(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -20,9 +20,9 @@ use crate::context::RefContext;
|
|||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::MyError;
|
use crate::error::MyError;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::types::AstNode;
|
|
||||||
use crate::parser::org_source::convert_error;
|
use crate::parser::org_source::convert_error;
|
||||||
use crate::parser::util::blank_line;
|
use crate::parser::util::blank_line;
|
||||||
|
use crate::types::AstNode;
|
||||||
use crate::types::Document;
|
use crate::types::Document;
|
||||||
use crate::types::Object;
|
use crate::types::Object;
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ use super::OrgSource;
|
|||||||
use crate::context::HeadlineLevelFilter;
|
use crate::context::HeadlineLevelFilter;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::types::AstNode;
|
|
||||||
use crate::settings::GlobalSettings;
|
use crate::settings::GlobalSettings;
|
||||||
|
use crate::types::AstNode;
|
||||||
use crate::types::Document;
|
use crate::types::Document;
|
||||||
use crate::types::Keyword;
|
use crate::types::Keyword;
|
||||||
|
|
||||||
|
@ -388,6 +388,7 @@ impl<'s> From<CustomError<OrgSource<'s>>> for CustomError<&'s str> {
|
|||||||
CustomError::MyError(err) => CustomError::MyError(err.into()),
|
CustomError::MyError(err) => CustomError::MyError(err.into()),
|
||||||
CustomError::Nom(input, error_kind) => CustomError::Nom(input.into(), error_kind),
|
CustomError::Nom(input, error_kind) => CustomError::Nom(input.into(), error_kind),
|
||||||
CustomError::IO(err) => CustomError::IO(err),
|
CustomError::IO(err) => CustomError::IO(err),
|
||||||
|
CustomError::BoxedError(err) => CustomError::BoxedError(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::character::complete::anychar;
|
use nom::character::complete::anychar;
|
||||||
use nom::character::complete::digit0;
|
|
||||||
use nom::character::complete::digit1;
|
use nom::character::complete::digit1;
|
||||||
use nom::character::complete::one_of;
|
use nom::character::complete::one_of;
|
||||||
use nom::character::complete::space1;
|
use nom::character::complete::space1;
|
||||||
|
use nom::combinator::map;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
@ -21,7 +21,21 @@ use crate::context::ExitMatcherNode;
|
|||||||
use crate::context::RefContext;
|
use crate::context::RefContext;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
|
use crate::types::Date;
|
||||||
|
use crate::types::DayOfMonth;
|
||||||
|
use crate::types::Hour;
|
||||||
|
use crate::types::Minute;
|
||||||
|
use crate::types::Month;
|
||||||
|
use crate::types::Repeater;
|
||||||
|
use crate::types::RepeaterType;
|
||||||
|
use crate::types::Time;
|
||||||
|
use crate::types::TimeUnit;
|
||||||
use crate::types::Timestamp;
|
use crate::types::Timestamp;
|
||||||
|
use crate::types::TimestampRangeType;
|
||||||
|
use crate::types::TimestampType;
|
||||||
|
use crate::types::WarningDelay;
|
||||||
|
use crate::types::WarningDelayType;
|
||||||
|
use crate::types::Year;
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn timestamp<'b, 'g, 'r, 's>(
|
pub(crate) fn timestamp<'b, 'g, 'r, 's>(
|
||||||
@ -57,6 +71,14 @@ fn diary_timestamp<'b, 'g, 'r, 's>(
|
|||||||
remaining,
|
remaining,
|
||||||
Timestamp {
|
Timestamp {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
timestamp_type: TimestampType::Diary,
|
||||||
|
range_type: TimestampRangeType::None,
|
||||||
|
start: None,
|
||||||
|
end: None,
|
||||||
|
start_time: None,
|
||||||
|
end_time: None,
|
||||||
|
repeater: None,
|
||||||
|
warning_delay: None,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -97,17 +119,26 @@ fn active_timestamp<'b, 'g, 'r, 's>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
||||||
let (remaining, _) = tag("<")(input)?;
|
let (remaining, _) = tag("<")(input)?;
|
||||||
let (remaining, _date) = date(context, remaining)?;
|
let (remaining, start) = date(context, remaining)?;
|
||||||
let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Gamma,
|
class: ExitClass::Gamma,
|
||||||
exit_matcher: &active_time_rest_end,
|
exit_matcher: &active_time_rest_end,
|
||||||
});
|
});
|
||||||
let time_context = context.with_additional_node(&time_context);
|
let time_context = context.with_additional_node(&time_context);
|
||||||
let (remaining, _time) =
|
let (remaining, time) = opt(tuple((
|
||||||
opt(tuple((space1, parser_with_context!(time)(&time_context))))(remaining)?;
|
space1,
|
||||||
let (remaining, _repeater) =
|
parser_with_context!(time(true))(&time_context),
|
||||||
|
)))(remaining)?;
|
||||||
|
let remaining = if time.is_none() {
|
||||||
|
// Upstream org-mode accepts malformed timestamps. For example '<2016-02-14 Sun ++y>'.
|
||||||
|
let (remain, _) = opt(parser_with_context!(time_rest)(&time_context))(remaining)?;
|
||||||
|
remain
|
||||||
|
} else {
|
||||||
|
remaining
|
||||||
|
};
|
||||||
|
let (remaining, repeater) =
|
||||||
opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?;
|
opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?;
|
||||||
let (remaining, _warning_delay) = opt(tuple((
|
let (remaining, warning_delay) = opt(tuple((
|
||||||
space1,
|
space1,
|
||||||
parser_with_context!(warning_delay)(context),
|
parser_with_context!(warning_delay)(context),
|
||||||
)))(remaining)?;
|
)))(remaining)?;
|
||||||
@ -121,6 +152,14 @@ fn active_timestamp<'b, 'g, 'r, 's>(
|
|||||||
remaining,
|
remaining,
|
||||||
Timestamp {
|
Timestamp {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
timestamp_type: TimestampType::Active,
|
||||||
|
range_type: TimestampRangeType::None,
|
||||||
|
start: Some(start.clone()),
|
||||||
|
end: Some(start),
|
||||||
|
start_time: time.as_ref().map(|(_, time)| time.clone()),
|
||||||
|
end_time: time.map(|(_, time)| time),
|
||||||
|
repeater: repeater.map(|(_, repeater)| repeater),
|
||||||
|
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -131,17 +170,26 @@ fn inactive_timestamp<'b, 'g, 'r, 's>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
||||||
let (remaining, _) = tag("[")(input)?;
|
let (remaining, _) = tag("[")(input)?;
|
||||||
let (remaining, _date) = date(context, remaining)?;
|
let (remaining, start) = date(context, remaining)?;
|
||||||
let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Gamma,
|
class: ExitClass::Gamma,
|
||||||
exit_matcher: &inactive_time_rest_end,
|
exit_matcher: &inactive_time_rest_end,
|
||||||
});
|
});
|
||||||
let time_context = context.with_additional_node(&time_context);
|
let time_context = context.with_additional_node(&time_context);
|
||||||
let (remaining, _time) =
|
let (remaining, time) = opt(tuple((
|
||||||
opt(tuple((space1, parser_with_context!(time)(&time_context))))(remaining)?;
|
space1,
|
||||||
let (remaining, _repeater) =
|
parser_with_context!(time(true))(&time_context),
|
||||||
|
)))(remaining)?;
|
||||||
|
let remaining = if time.is_none() {
|
||||||
|
// Upstream org-mode accepts malformed timestamps. For example '<2016-02-14 Sun ++y>'.
|
||||||
|
let (remain, _) = opt(parser_with_context!(time_rest)(&time_context))(remaining)?;
|
||||||
|
remain
|
||||||
|
} else {
|
||||||
|
remaining
|
||||||
|
};
|
||||||
|
let (remaining, repeater) =
|
||||||
opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?;
|
opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?;
|
||||||
let (remaining, _warning_delay) = opt(tuple((
|
let (remaining, warning_delay) = opt(tuple((
|
||||||
space1,
|
space1,
|
||||||
parser_with_context!(warning_delay)(context),
|
parser_with_context!(warning_delay)(context),
|
||||||
)))(remaining)?;
|
)))(remaining)?;
|
||||||
@ -155,6 +203,14 @@ fn inactive_timestamp<'b, 'g, 'r, 's>(
|
|||||||
remaining,
|
remaining,
|
||||||
Timestamp {
|
Timestamp {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
timestamp_type: TimestampType::Inactive,
|
||||||
|
range_type: TimestampRangeType::None,
|
||||||
|
start: Some(start.clone()),
|
||||||
|
end: Some(start),
|
||||||
|
start_time: time.as_ref().map(|(_, time)| time.clone()),
|
||||||
|
end_time: time.map(|(_, time)| time),
|
||||||
|
repeater: repeater.map(|(_, repeater)| repeater),
|
||||||
|
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -164,10 +220,10 @@ fn active_date_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
||||||
let (remaining, _first_timestamp) = active_timestamp(context, input)?;
|
let (remaining, first_timestamp) = active_timestamp(context, input)?;
|
||||||
// TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace
|
// TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace
|
||||||
let (remaining, _separator) = tag("--")(remaining)?;
|
let (remaining, _separator) = tag("--")(remaining)?;
|
||||||
let (remaining, _second_timestamp) = active_timestamp(context, remaining)?;
|
let (remaining, second_timestamp) = active_timestamp(context, remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, _trailing_whitespace) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
@ -177,6 +233,16 @@ fn active_date_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
remaining,
|
remaining,
|
||||||
Timestamp {
|
Timestamp {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
timestamp_type: TimestampType::ActiveRange,
|
||||||
|
range_type: TimestampRangeType::DateRange,
|
||||||
|
start: first_timestamp.start,
|
||||||
|
end: second_timestamp.end,
|
||||||
|
start_time: first_timestamp.start_time,
|
||||||
|
end_time: second_timestamp.end_time,
|
||||||
|
repeater: first_timestamp.repeater.or(second_timestamp.repeater),
|
||||||
|
warning_delay: first_timestamp
|
||||||
|
.warning_delay
|
||||||
|
.or(second_timestamp.warning_delay),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -187,7 +253,7 @@ fn active_time_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
||||||
let (remaining, _) = tag("<")(input)?;
|
let (remaining, _) = tag("<")(input)?;
|
||||||
let (remaining, _date) = date(context, remaining)?;
|
let (remaining, start_date) = date(context, remaining)?;
|
||||||
let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Gamma,
|
class: ExitClass::Gamma,
|
||||||
exit_matcher: &active_time_rest_end,
|
exit_matcher: &active_time_rest_end,
|
||||||
@ -198,13 +264,15 @@ fn active_time_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
exit_matcher: &time_range_rest_end,
|
exit_matcher: &time_range_rest_end,
|
||||||
});
|
});
|
||||||
let first_time_context = time_context.with_additional_node(&first_time_context);
|
let first_time_context = time_context.with_additional_node(&first_time_context);
|
||||||
let (remaining, _first_time) =
|
let (remaining, (_, first_time)) = tuple((
|
||||||
tuple((space1, parser_with_context!(time)(&first_time_context)))(remaining)?;
|
space1,
|
||||||
|
parser_with_context!(time(false))(&first_time_context),
|
||||||
|
))(remaining)?;
|
||||||
let (remaining, _) = tag("-")(remaining)?;
|
let (remaining, _) = tag("-")(remaining)?;
|
||||||
let (remaining, _second_time) = parser_with_context!(time)(&time_context)(remaining)?;
|
let (remaining, second_time) = parser_with_context!(time(true))(&time_context)(remaining)?;
|
||||||
let (remaining, _repeater) =
|
let (remaining, repeater) =
|
||||||
opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?;
|
opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?;
|
||||||
let (remaining, _warning_delay) = opt(tuple((
|
let (remaining, warning_delay) = opt(tuple((
|
||||||
space1,
|
space1,
|
||||||
parser_with_context!(warning_delay)(context),
|
parser_with_context!(warning_delay)(context),
|
||||||
)))(remaining)?;
|
)))(remaining)?;
|
||||||
@ -218,6 +286,14 @@ fn active_time_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
remaining,
|
remaining,
|
||||||
Timestamp {
|
Timestamp {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
timestamp_type: TimestampType::ActiveRange,
|
||||||
|
range_type: TimestampRangeType::TimeRange,
|
||||||
|
start: Some(start_date.clone()),
|
||||||
|
end: Some(start_date),
|
||||||
|
start_time: Some(first_time),
|
||||||
|
end_time: Some(second_time),
|
||||||
|
repeater: repeater.map(|(_, repeater)| repeater),
|
||||||
|
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -227,10 +303,10 @@ fn inactive_date_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
||||||
let (remaining, _first_timestamp) = inactive_timestamp(context, input)?;
|
let (remaining, first_timestamp) = inactive_timestamp(context, input)?;
|
||||||
// TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace
|
// TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace
|
||||||
let (remaining, _separator) = tag("--")(remaining)?;
|
let (remaining, _separator) = tag("--")(remaining)?;
|
||||||
let (remaining, _second_timestamp) = inactive_timestamp(context, remaining)?;
|
let (remaining, second_timestamp) = inactive_timestamp(context, remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, _trailing_whitespace) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
@ -240,6 +316,17 @@ fn inactive_date_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
remaining,
|
remaining,
|
||||||
Timestamp {
|
Timestamp {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
|
||||||
|
timestamp_type: TimestampType::InactiveRange,
|
||||||
|
range_type: TimestampRangeType::DateRange,
|
||||||
|
start: first_timestamp.start,
|
||||||
|
end: second_timestamp.end,
|
||||||
|
start_time: first_timestamp.start_time,
|
||||||
|
end_time: second_timestamp.end_time,
|
||||||
|
repeater: first_timestamp.repeater.or(second_timestamp.repeater),
|
||||||
|
warning_delay: first_timestamp
|
||||||
|
.warning_delay
|
||||||
|
.or(second_timestamp.warning_delay),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -250,7 +337,7 @@ fn inactive_time_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
||||||
let (remaining, _) = tag("[")(input)?;
|
let (remaining, _) = tag("[")(input)?;
|
||||||
let (remaining, _date) = date(context, remaining)?;
|
let (remaining, start_date) = date(context, remaining)?;
|
||||||
let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Gamma,
|
class: ExitClass::Gamma,
|
||||||
exit_matcher: &inactive_time_rest_end,
|
exit_matcher: &inactive_time_rest_end,
|
||||||
@ -261,13 +348,15 @@ fn inactive_time_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
exit_matcher: &time_range_rest_end,
|
exit_matcher: &time_range_rest_end,
|
||||||
});
|
});
|
||||||
let first_time_context = time_context.with_additional_node(&first_time_context);
|
let first_time_context = time_context.with_additional_node(&first_time_context);
|
||||||
let (remaining, _first_time) =
|
let (remaining, (_, first_time)) = tuple((
|
||||||
tuple((space1, parser_with_context!(time)(&first_time_context)))(remaining)?;
|
space1,
|
||||||
|
parser_with_context!(time(false))(&first_time_context),
|
||||||
|
))(remaining)?;
|
||||||
let (remaining, _) = tag("-")(remaining)?;
|
let (remaining, _) = tag("-")(remaining)?;
|
||||||
let (remaining, _second_time) = parser_with_context!(time)(&time_context)(remaining)?;
|
let (remaining, second_time) = parser_with_context!(time(true))(&time_context)(remaining)?;
|
||||||
let (remaining, _repeater) =
|
let (remaining, repeater) =
|
||||||
opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?;
|
opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?;
|
||||||
let (remaining, _warning_delay) = opt(tuple((
|
let (remaining, warning_delay) = opt(tuple((
|
||||||
space1,
|
space1,
|
||||||
parser_with_context!(warning_delay)(context),
|
parser_with_context!(warning_delay)(context),
|
||||||
)))(remaining)?;
|
)))(remaining)?;
|
||||||
@ -281,6 +370,14 @@ fn inactive_time_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
remaining,
|
remaining,
|
||||||
Timestamp {
|
Timestamp {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
timestamp_type: TimestampType::InactiveRange,
|
||||||
|
range_type: TimestampRangeType::TimeRange,
|
||||||
|
start: Some(start_date.clone()),
|
||||||
|
end: Some(start_date),
|
||||||
|
start_time: Some(first_time),
|
||||||
|
end_time: Some(second_time),
|
||||||
|
repeater: repeater.map(|(_, repeater)| repeater),
|
||||||
|
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -289,18 +386,33 @@ fn inactive_time_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
fn date<'b, 'g, 'r, 's>(
|
fn date<'b, 'g, 'r, 's>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, Date<'s>> {
|
||||||
let (remaining, _year) = verify(digit1, |year: &OrgSource<'_>| year.len() == 4)(input)?;
|
let (remaining, year) = verify(digit1, |year: &OrgSource<'_>| year.len() == 4)(input)?;
|
||||||
let (remaining, _) = tag("-")(remaining)?;
|
let (remaining, _) = tag("-")(remaining)?;
|
||||||
let (remaining, _month) = verify(digit1, |month: &OrgSource<'_>| month.len() == 2)(remaining)?;
|
let (remaining, month) = verify(digit1, |month: &OrgSource<'_>| month.len() == 2)(remaining)?;
|
||||||
let (remaining, _) = tag("-")(remaining)?;
|
let (remaining, _) = tag("-")(remaining)?;
|
||||||
let (remaining, _day_of_month) = verify(digit1, |day_of_month: &OrgSource<'_>| {
|
let (remaining, day_of_month) = verify(digit1, |day_of_month: &OrgSource<'_>| {
|
||||||
day_of_month.len() == 2
|
day_of_month.len() == 2
|
||||||
})(remaining)?;
|
})(remaining)?;
|
||||||
let (remaining, _dayname) =
|
let (remaining, day_name) =
|
||||||
opt(tuple((space1, parser_with_context!(dayname)(context))))(remaining)?;
|
opt(tuple((space1, parser_with_context!(dayname)(context))))(remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
|
||||||
Ok((remaining, source))
|
let year = Year::new(Into::<&str>::into(year))
|
||||||
|
.expect("TODO: I should be able to return CustomError from nom parsers.");
|
||||||
|
let month = Month::new(Into::<&str>::into(month))
|
||||||
|
.expect("TODO: I should be able to return CustomError from nom parsers.");
|
||||||
|
let day_of_month = DayOfMonth::new(Into::<&str>::into(day_of_month))
|
||||||
|
.expect("TODO: I should be able to return CustomError from nom parsers.");
|
||||||
|
|
||||||
|
let date = Date::new(
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day_of_month,
|
||||||
|
day_name.map(|(_, day_name)| Into::<&str>::into(day_name)),
|
||||||
|
)
|
||||||
|
.expect("TODO: I should be able to return CustomError from nom parsers.");
|
||||||
|
|
||||||
|
Ok((remaining, date))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
@ -335,20 +447,39 @@ fn dayname_end<'b, 'g, 'r, 's>(
|
|||||||
}))(input)
|
}))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn time<'c>(
|
||||||
|
allow_rest: bool,
|
||||||
|
) -> impl for<'b, 'g, 'r, 's> Fn(RefContext<'b, 'g, 'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, Time<'s>>
|
||||||
|
{
|
||||||
|
move |context, input| _time(context, input, allow_rest)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn time<'b, 'g, 'r, 's>(
|
fn _time<'b, 'g, 'r, 's>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
allow_rest: bool,
|
||||||
let (remaining, _hour) = verify(digit1, |hour: &OrgSource<'_>| {
|
) -> Res<OrgSource<'s>, Time<'s>> {
|
||||||
|
let (remaining, hour) = verify(digit1, |hour: &OrgSource<'_>| {
|
||||||
hour.len() >= 1 && hour.len() <= 2
|
hour.len() >= 1 && hour.len() <= 2
|
||||||
})(input)?;
|
})(input)?;
|
||||||
let (remaining, _) = tag(":")(remaining)?;
|
let (remaining, _) = tag(":")(remaining)?;
|
||||||
let (remaining, _minute) =
|
let (remaining, minute) =
|
||||||
verify(digit1, |minute: &OrgSource<'_>| minute.len() == 2)(remaining)?;
|
verify(digit1, |minute: &OrgSource<'_>| minute.len() == 2)(remaining)?;
|
||||||
let (remaining, _time_rest) = opt(parser_with_context!(time_rest)(context))(remaining)?;
|
let (remaining, time_rest) = if allow_rest {
|
||||||
let source = get_consumed(input, remaining);
|
opt(parser_with_context!(time_rest)(context))(remaining)?
|
||||||
Ok((remaining, source))
|
} else {
|
||||||
|
(remaining, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
let hour = Hour::new(Into::<&str>::into(hour))
|
||||||
|
.expect("TODO: I should be able to return CustomError from nom parsers.");
|
||||||
|
let minute = Minute::new(Into::<&str>::into(minute))
|
||||||
|
.expect("TODO: I should be able to return CustomError from nom parsers.");
|
||||||
|
let time = Time::new(hour, minute, time_rest.map(Into::<&str>::into))
|
||||||
|
.expect("TODO: I should be able to return CustomError from nom parsers.");
|
||||||
|
|
||||||
|
Ok((remaining, time))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
@ -401,8 +532,10 @@ fn time_range_rest_end<'b, 'g, 'r, 's>(
|
|||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
// We pop off the most recent context element to get a context tree with just the active/inactive_time_rest_end exit matcher (removing this function from the exit matcher chain) because the 2nd time in the range does not end when a "-TIME" pattern is found.
|
// We pop off the most recent context element to get a context tree with just the active/inactive_time_rest_end exit matcher (removing this function from the exit matcher chain) because the 2nd time in the range does not end when a "-TIME" pattern is found.
|
||||||
let parent_node = context.get_parent().expect("Two context elements are added to the tree when adding this exit matcher, so it should be impossible for this to return None.");
|
let parent_node = context.get_parent().expect("Two context elements are added to the tree when adding this exit matcher, so it should be impossible for this to return None.");
|
||||||
let exit_contents =
|
let exit_contents = recognize(tuple((
|
||||||
recognize(tuple((tag("-"), parser_with_context!(time)(&parent_node))))(input);
|
tag("-"),
|
||||||
|
parser_with_context!(time(true))(&parent_node),
|
||||||
|
)))(input);
|
||||||
exit_contents
|
exit_contents
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,29 +543,66 @@ fn time_range_rest_end<'b, 'g, 'r, 's>(
|
|||||||
fn repeater<'b, 'g, 'r, 's>(
|
fn repeater<'b, 'g, 'r, 's>(
|
||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, Repeater> {
|
||||||
// + for cumulative type
|
// + for cumulative type
|
||||||
// ++ for catch-up type
|
// ++ for catch-up type
|
||||||
// .+ for restart type
|
// .+ for restart type
|
||||||
let (remaining, _mark) = alt((tag("++"), tag("+"), tag(".+")))(input)?;
|
let (remaining, repeater_type) = alt((
|
||||||
let (remaining, _value) = digit0(remaining)?;
|
map(tag("++"), |_| RepeaterType::CatchUp),
|
||||||
|
map(tag("+"), |_| RepeaterType::Cumulative),
|
||||||
|
map(tag(".+"), |_| RepeaterType::Restart),
|
||||||
|
))(input)?;
|
||||||
|
let (remaining, value) = digit1(remaining)?;
|
||||||
|
let value = Into::<&str>::into(value)
|
||||||
|
.parse()
|
||||||
|
.expect("digit1 ensures this will parse as a number.");
|
||||||
// h = hour, d = day, w = week, m = month, y = year
|
// h = hour, d = day, w = week, m = month, y = year
|
||||||
let (remaining, _unit) = recognize(one_of("hdwmy"))(remaining)?;
|
let (remaining, unit) = alt((
|
||||||
let source = get_consumed(input, remaining);
|
map(tag("h"), |_| TimeUnit::Hour),
|
||||||
Ok((remaining, source))
|
map(tag("d"), |_| TimeUnit::Day),
|
||||||
|
map(tag("w"), |_| TimeUnit::Week),
|
||||||
|
map(tag("m"), |_| TimeUnit::Month),
|
||||||
|
map(tag("y"), |_| TimeUnit::Year),
|
||||||
|
))(remaining)?;
|
||||||
|
Ok((
|
||||||
|
remaining,
|
||||||
|
Repeater {
|
||||||
|
repeater_type,
|
||||||
|
value,
|
||||||
|
unit,
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn warning_delay<'b, 'g, 'r, 's>(
|
fn warning_delay<'b, 'g, 'r, 's>(
|
||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, WarningDelay> {
|
||||||
// - for all type
|
// - for all type
|
||||||
// -- for first type
|
// -- for first type
|
||||||
let (remaining, _mark) = alt((tag("--"), tag("-")))(input)?;
|
let (remaining, warning_delay_type) = alt((
|
||||||
let (remaining, _value) = digit0(remaining)?;
|
map(tag("--"), |_| WarningDelayType::First),
|
||||||
|
map(tag("-"), |_| WarningDelayType::All),
|
||||||
|
))(input)?;
|
||||||
|
let (remaining, value) = digit1(remaining)?;
|
||||||
|
let value = Into::<&str>::into(value)
|
||||||
|
.parse()
|
||||||
|
.expect("digit1 ensures this will parse as a number.");
|
||||||
// h = hour, d = day, w = week, m = month, y = year
|
// h = hour, d = day, w = week, m = month, y = year
|
||||||
let (remaining, _unit) = recognize(one_of("hdwmy"))(remaining)?;
|
let (remaining, unit) = alt((
|
||||||
let source = get_consumed(input, remaining);
|
map(tag("h"), |_| TimeUnit::Hour),
|
||||||
Ok((remaining, source))
|
map(tag("d"), |_| TimeUnit::Day),
|
||||||
|
map(tag("w"), |_| TimeUnit::Week),
|
||||||
|
map(tag("m"), |_| TimeUnit::Month),
|
||||||
|
map(tag("y"), |_| TimeUnit::Year),
|
||||||
|
))(remaining)?;
|
||||||
|
Ok((
|
||||||
|
remaining,
|
||||||
|
WarningDelay {
|
||||||
|
warning_delay_type,
|
||||||
|
value,
|
||||||
|
unit,
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
@ -54,14 +54,23 @@ pub use object::Bold;
|
|||||||
pub use object::Citation;
|
pub use object::Citation;
|
||||||
pub use object::CitationReference;
|
pub use object::CitationReference;
|
||||||
pub use object::Code;
|
pub use object::Code;
|
||||||
|
pub use object::Date;
|
||||||
|
pub use object::DayOfMonth;
|
||||||
|
pub use object::DayOfMonthInner;
|
||||||
pub use object::Entity;
|
pub use object::Entity;
|
||||||
pub use object::ExportSnippet;
|
pub use object::ExportSnippet;
|
||||||
pub use object::FootnoteReference;
|
pub use object::FootnoteReference;
|
||||||
|
pub use object::Hour;
|
||||||
|
pub use object::HourInner;
|
||||||
pub use object::InlineBabelCall;
|
pub use object::InlineBabelCall;
|
||||||
pub use object::InlineSourceBlock;
|
pub use object::InlineSourceBlock;
|
||||||
pub use object::Italic;
|
pub use object::Italic;
|
||||||
pub use object::LatexFragment;
|
pub use object::LatexFragment;
|
||||||
pub use object::LineBreak;
|
pub use object::LineBreak;
|
||||||
|
pub use object::Minute;
|
||||||
|
pub use object::MinuteInner;
|
||||||
|
pub use object::Month;
|
||||||
|
pub use object::MonthInner;
|
||||||
pub use object::Object;
|
pub use object::Object;
|
||||||
pub use object::OrgMacro;
|
pub use object::OrgMacro;
|
||||||
pub use object::PlainLink;
|
pub use object::PlainLink;
|
||||||
@ -69,13 +78,24 @@ pub use object::PlainText;
|
|||||||
pub use object::RadioLink;
|
pub use object::RadioLink;
|
||||||
pub use object::RadioTarget;
|
pub use object::RadioTarget;
|
||||||
pub use object::RegularLink;
|
pub use object::RegularLink;
|
||||||
|
pub use object::Repeater;
|
||||||
|
pub use object::RepeaterType;
|
||||||
|
pub use object::RepeaterWarningDelayValueType;
|
||||||
pub use object::StatisticsCookie;
|
pub use object::StatisticsCookie;
|
||||||
pub use object::StrikeThrough;
|
pub use object::StrikeThrough;
|
||||||
pub use object::Subscript;
|
pub use object::Subscript;
|
||||||
pub use object::Superscript;
|
pub use object::Superscript;
|
||||||
pub use object::Target;
|
pub use object::Target;
|
||||||
|
pub use object::Time;
|
||||||
|
pub use object::TimeUnit;
|
||||||
pub use object::Timestamp;
|
pub use object::Timestamp;
|
||||||
|
pub use object::TimestampRangeType;
|
||||||
|
pub use object::TimestampType;
|
||||||
pub use object::Underline;
|
pub use object::Underline;
|
||||||
pub use object::Verbatim;
|
pub use object::Verbatim;
|
||||||
|
pub use object::WarningDelay;
|
||||||
|
pub use object::WarningDelayType;
|
||||||
|
pub use object::Year;
|
||||||
|
pub use object::YearInner;
|
||||||
pub(crate) use source::SetSource;
|
pub(crate) use source::SetSource;
|
||||||
pub use standard_properties::StandardProperties;
|
pub use standard_properties::StandardProperties;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use super::GetStandardProperties;
|
use super::GetStandardProperties;
|
||||||
use super::StandardProperties;
|
use super::StandardProperties;
|
||||||
|
|
||||||
|
// TODO: Why did we make Object implement PartialEq again? Was it just for tests?
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Object<'s> {
|
pub enum Object<'s> {
|
||||||
Bold(Bold<'s>),
|
Bold(Bold<'s>),
|
||||||
@ -184,6 +185,253 @@ pub struct Superscript<'s> {
|
|||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Timestamp<'s> {
|
pub struct Timestamp<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
|
pub timestamp_type: TimestampType,
|
||||||
|
pub range_type: TimestampRangeType,
|
||||||
|
pub start: Option<Date<'s>>,
|
||||||
|
pub end: Option<Date<'s>>,
|
||||||
|
pub start_time: Option<Time<'s>>,
|
||||||
|
pub end_time: Option<Time<'s>>,
|
||||||
|
pub repeater: Option<Repeater>,
|
||||||
|
pub warning_delay: Option<WarningDelay>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum TimestampType {
|
||||||
|
Diary,
|
||||||
|
Active,
|
||||||
|
Inactive,
|
||||||
|
ActiveRange,
|
||||||
|
InactiveRange,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum TimestampRangeType {
|
||||||
|
None,
|
||||||
|
DateRange,
|
||||||
|
TimeRange,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type YearInner = u16;
|
||||||
|
pub type MonthInner = u8;
|
||||||
|
pub type DayOfMonthInner = u8;
|
||||||
|
pub type HourInner = u8;
|
||||||
|
pub type MinuteInner = u8;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct Year(YearInner);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct Month(MonthInner);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct DayOfMonth(DayOfMonthInner);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct Hour(HourInner);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct Minute(MinuteInner);
|
||||||
|
|
||||||
|
impl Year {
|
||||||
|
// TODO: Make a real error type instead of a boxed any error.
|
||||||
|
pub fn new<'s>(source: &'s str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
let year = source.parse::<YearInner>()?;
|
||||||
|
Ok(Year(year))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self) -> YearInner {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Month {
|
||||||
|
// TODO: Make a real error type instead of a boxed any error.
|
||||||
|
pub fn new<'s>(source: &'s str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
let month = source.parse::<MonthInner>()?;
|
||||||
|
if month < 1 || month > 12 {
|
||||||
|
Err("Month exceeds possible range.")?;
|
||||||
|
}
|
||||||
|
Ok(Month(month))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self) -> MonthInner {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DayOfMonth {
|
||||||
|
// TODO: Make a real error type instead of a boxed any error.
|
||||||
|
pub fn new<'s>(source: &'s str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
let day_of_month = source.parse::<DayOfMonthInner>()?;
|
||||||
|
if day_of_month < 1 || day_of_month > 31 {
|
||||||
|
Err("Day of month exceeds possible range.")?;
|
||||||
|
}
|
||||||
|
Ok(DayOfMonth(day_of_month))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self) -> DayOfMonthInner {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hour {
|
||||||
|
// TODO: Make a real error type instead of a boxed any error.
|
||||||
|
pub fn new<'s>(source: &'s str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
let hour = source.parse::<HourInner>()?;
|
||||||
|
if hour > 23 {
|
||||||
|
Err("Hour exceeds possible range.")?;
|
||||||
|
}
|
||||||
|
Ok(Hour(hour))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self) -> HourInner {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Minute {
|
||||||
|
// TODO: Make a real error type instead of a boxed any error.
|
||||||
|
pub fn new<'s>(source: &'s str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
let minute = source.parse::<MinuteInner>()?;
|
||||||
|
if minute > 59 {
|
||||||
|
Err("Minute exceeds possible range.")?;
|
||||||
|
}
|
||||||
|
Ok(Minute(minute))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self) -> MinuteInner {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct Date<'s> {
|
||||||
|
year: Year,
|
||||||
|
month: Month,
|
||||||
|
day_of_month: DayOfMonth,
|
||||||
|
day_name: Option<&'s str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> Date<'s> {
|
||||||
|
// TODO: Make a real error type instead of a boxed any error.
|
||||||
|
pub fn new(
|
||||||
|
year: Year,
|
||||||
|
month: Month,
|
||||||
|
day_of_month: DayOfMonth,
|
||||||
|
day_name: Option<&'s str>,
|
||||||
|
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
// TODO: Does org-mode support non-gregorian calendars?
|
||||||
|
// TODO: Do I want to validate leap year?
|
||||||
|
match (month.get_value(), day_of_month.get_value()) {
|
||||||
|
(1, 1..=31) => {}
|
||||||
|
(2, 1..=29) => {}
|
||||||
|
(3, 1..=31) => {}
|
||||||
|
(4, 1..=30) => {}
|
||||||
|
(5, 1..=31) => {}
|
||||||
|
(6, 1..=30) => {}
|
||||||
|
(7, 1..=31) => {}
|
||||||
|
(8, 1..=31) => {}
|
||||||
|
(9, 1..=30) => {}
|
||||||
|
(10, 1..=31) => {}
|
||||||
|
(11, 1..=30) => {}
|
||||||
|
(12, 1..=31) => {}
|
||||||
|
_ => Err("Invalid day of month for the month.")?,
|
||||||
|
};
|
||||||
|
Ok(Date {
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day_of_month,
|
||||||
|
day_name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_year(&self) -> &Year {
|
||||||
|
&self.year
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_month(&self) -> &Month {
|
||||||
|
&self.month
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_day_of_month(&self) -> &DayOfMonth {
|
||||||
|
&self.day_of_month
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_day_name(&self) -> Option<&'s str> {
|
||||||
|
self.day_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct Time<'s> {
|
||||||
|
hour: Hour,
|
||||||
|
minute: Minute,
|
||||||
|
postfix: Option<&'s str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> Time<'s> {
|
||||||
|
// TODO: Make a real error type instead of a boxed any error.
|
||||||
|
pub fn new(
|
||||||
|
hour: Hour,
|
||||||
|
minute: Minute,
|
||||||
|
postfix: Option<&'s str>,
|
||||||
|
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
Ok(Time {
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
postfix,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_hour(&self) -> &Hour {
|
||||||
|
&self.hour
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_minute(&self) -> &Minute {
|
||||||
|
&self.minute
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_postfix(&self) -> Option<&'s str> {
|
||||||
|
self.postfix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum RepeaterType {
|
||||||
|
Cumulative,
|
||||||
|
CatchUp,
|
||||||
|
Restart,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum WarningDelayType {
|
||||||
|
All,
|
||||||
|
First,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum TimeUnit {
|
||||||
|
Hour,
|
||||||
|
Day,
|
||||||
|
Week,
|
||||||
|
Month,
|
||||||
|
Year,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type RepeaterWarningDelayValueType = u16;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct Repeater {
|
||||||
|
pub repeater_type: RepeaterType,
|
||||||
|
pub value: RepeaterWarningDelayValueType,
|
||||||
|
pub unit: TimeUnit,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct WarningDelay {
|
||||||
|
pub warning_delay_type: WarningDelayType,
|
||||||
|
pub value: RepeaterWarningDelayValueType,
|
||||||
|
pub unit: TimeUnit,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> GetStandardProperties<'s> for Object<'s> {
|
impl<'s> GetStandardProperties<'s> for Object<'s> {
|
||||||
@ -381,3 +629,9 @@ impl<'s> StandardProperties<'s> for PlainText<'s> {
|
|||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'s> Timestamp<'s> {
|
||||||
|
pub fn get_raw_value(&self) -> &'s str {
|
||||||
|
self.source.trim_end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user