diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 275a957..7138fa1 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -72,6 +72,8 @@ use crate::types::PropertyDrawer; use crate::types::RadioLink; use crate::types::RadioTarget; use crate::types::RegularLink; +use crate::types::RepeaterType; +use crate::types::RepeaterWarningDelayValueType; use crate::types::Section; use crate::types::SrcBlock; use crate::types::StandardProperties; @@ -84,6 +86,7 @@ use crate::types::TableCell; use crate::types::TableRow; use crate::types::Target; use crate::types::Time; +use crate::types::TimeUnit; use crate::types::Timestamp; use crate::types::TimestampRangeType; use crate::types::TimestampType; @@ -91,6 +94,7 @@ use crate::types::TodoKeywordType; use crate::types::Underline; use crate::types::Verbatim; use crate::types::VerseBlock; +use crate::types::WarningDelayType; use crate::types::Year; use crate::types::YearInner; @@ -2304,18 +2308,104 @@ fn compare_timestamp<'b, 's>( )); } - // TODO: Compare :repeater-type :repeater-value :repeater-unit :warning-type :warning-value :warning-unit - // - // :type unquoted atom either diary, active, inactive, active-range, or inactive-range. - // :range-type unquoted atom either nil, daterange - // :raw-value quoted string of the source - // :*-start :*-end unquoted integers - // :repeater-type optional unquoted atom with value cumulate - // :repeater-value unquoted integer - // :repeater-unit unquoted atom with value week - // :warning-type optional unquoted atom with value all - // :warning-value unquoted integer - // :warning-unit unquoted atom with value day + // Compare repeater + let repeater_type = get_property_unquoted_atom(emacs, ":repeater-type")?; + let repeater_value: Option = + 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 = + 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 { status: this_status, diff --git a/src/parser/timestamp.rs b/src/parser/timestamp.rs index 68681fe..f50b4b1 100644 --- a/src/parser/timestamp.rs +++ b/src/parser/timestamp.rs @@ -1,10 +1,10 @@ use nom::branch::alt; use nom::bytes::complete::tag; use nom::character::complete::anychar; -use nom::character::complete::digit0; use nom::character::complete::digit1; use nom::character::complete::one_of; use nom::character::complete::space1; +use nom::combinator::map; use nom::combinator::opt; use nom::combinator::recognize; use nom::combinator::verify; @@ -26,10 +26,15 @@ 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::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"))] @@ -70,8 +75,10 @@ fn diary_timestamp<'b, 'g, 'r, 's>( range_type: TimestampRangeType::None, start: None, end: None, - start_time: None, // TODO - end_time: None, // TODO + start_time: None, + end_time: None, + repeater: None, + warning_delay: None, }, )) } @@ -122,9 +129,9 @@ fn active_timestamp<'b, 'g, 'r, 's>( space1, parser_with_context!(time(true))(&time_context), )))(remaining)?; - let (remaining, _repeater) = + let (remaining, repeater) = opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?; - let (remaining, _warning_delay) = opt(tuple(( + let (remaining, warning_delay) = opt(tuple(( space1, parser_with_context!(warning_delay)(context), )))(remaining)?; @@ -144,6 +151,8 @@ fn active_timestamp<'b, 'g, 'r, 's>( 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,9 +173,9 @@ fn inactive_timestamp<'b, 'g, 'r, 's>( space1, parser_with_context!(time(true))(&time_context), )))(remaining)?; - let (remaining, _repeater) = + let (remaining, repeater) = opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?; - let (remaining, _warning_delay) = opt(tuple(( + let (remaining, warning_delay) = opt(tuple(( space1, parser_with_context!(warning_delay)(context), )))(remaining)?; @@ -186,6 +195,8 @@ fn inactive_timestamp<'b, 'g, 'r, 's>( 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), }, )) } @@ -214,6 +225,10 @@ fn active_date_range_timestamp<'b, 'g, 'r, 's>( 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), }, )) } @@ -241,9 +256,9 @@ fn active_time_range_timestamp<'b, 'g, 'r, 's>( ))(remaining)?; let (remaining, _) = tag("-")(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)?; - let (remaining, _warning_delay) = opt(tuple(( + let (remaining, warning_delay) = opt(tuple(( space1, parser_with_context!(warning_delay)(context), )))(remaining)?; @@ -263,6 +278,8 @@ fn active_time_range_timestamp<'b, 'g, 'r, 's>( 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), }, )) } @@ -292,6 +309,10 @@ fn inactive_date_range_timestamp<'b, 'g, 'r, 's>( 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), }, )) } @@ -319,9 +340,9 @@ fn inactive_time_range_timestamp<'b, 'g, 'r, 's>( ))(remaining)?; let (remaining, _) = tag("-")(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)?; - let (remaining, _warning_delay) = opt(tuple(( + let (remaining, warning_delay) = opt(tuple(( space1, parser_with_context!(warning_delay)(context), )))(remaining)?; @@ -341,6 +362,8 @@ fn inactive_time_range_timestamp<'b, 'g, 'r, 's>( 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), }, )) } @@ -460,32 +483,18 @@ fn time_rest<'b, 'g, 'r, 's>( #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn active_time_rest_end<'b, 'g, 'r, 's>( - context: RefContext<'b, 'g, 'r, 's>, + _context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { - alt(( - recognize(verify(anychar, |c| ">\n".contains(*c))), - recognize(tuple((space1, parser_with_context!(repeater)(context)))), - recognize(tuple(( - space1, - parser_with_context!(warning_delay)(context), - ))), - ))(input) + recognize(verify(anychar, |c| ">\n".contains(*c)))(input) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn inactive_time_rest_end<'b, 'g, 'r, 's>( - context: RefContext<'b, 'g, 'r, 's>, + _context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { - alt(( - recognize(verify(anychar, |c| "]\n".contains(*c))), - recognize(tuple((space1, parser_with_context!(repeater)(context)))), - recognize(tuple(( - space1, - parser_with_context!(warning_delay)(context), - ))), - ))(input) + recognize(verify(anychar, |c| "]\n".contains(*c)))(input) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] @@ -506,29 +515,66 @@ fn time_range_rest_end<'b, 'g, 'r, 's>( fn repeater<'b, 'g, 'r, 's>( _context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, OrgSource<'s>> { +) -> Res, Repeater> { // + for cumulative type // ++ for catch-up type // .+ for restart type - let (remaining, _mark) = alt((tag("++"), tag("+"), tag(".+")))(input)?; - let (remaining, _value) = digit0(remaining)?; + let (remaining, repeater_type) = alt(( + map(tag("++"), |_| RepeaterType::Cumulative), + map(tag("+"), |_| RepeaterType::CatchUp), + 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 - let (remaining, _unit) = recognize(one_of("hdwmy"))(remaining)?; - let source = get_consumed(input, remaining); - Ok((remaining, source)) + let (remaining, unit) = alt(( + map(tag("h"), |_| TimeUnit::Hour), + 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"))] fn warning_delay<'b, 'g, 'r, 's>( _context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, OrgSource<'s>> { +) -> Res, WarningDelay> { // - for all type // -- for first type - let (remaining, _mark) = alt((tag("--"), tag("-")))(input)?; - let (remaining, _value) = digit0(remaining)?; + let (remaining, warning_delay_type) = alt(( + 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 - let (remaining, _unit) = recognize(one_of("hdwmy"))(remaining)?; - let source = get_consumed(input, remaining); - Ok((remaining, source)) + let (remaining, unit) = alt(( + map(tag("h"), |_| TimeUnit::Hour), + 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, + }, + )) } diff --git a/src/types/mod.rs b/src/types/mod.rs index 8c7bd4b..89d5015 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -78,17 +78,23 @@ pub use object::PlainText; pub use object::RadioLink; pub use object::RadioTarget; pub use object::RegularLink; +pub use object::Repeater; +pub use object::RepeaterType; +pub use object::RepeaterWarningDelayValueType; pub use object::StatisticsCookie; pub use object::StrikeThrough; pub use object::Subscript; pub use object::Superscript; pub use object::Target; pub use object::Time; +pub use object::TimeUnit; pub use object::Timestamp; pub use object::TimestampRangeType; pub use object::TimestampType; pub use object::Underline; 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; diff --git a/src/types/object.rs b/src/types/object.rs index a898694..3440997 100644 --- a/src/types/object.rs +++ b/src/types/object.rs @@ -191,6 +191,8 @@ pub struct Timestamp<'s> { pub end: Option>, pub start_time: Option>, pub end_time: Option>, + pub repeater: Option, + pub warning_delay: Option, } #[derive(Debug, PartialEq)] @@ -394,6 +396,44 @@ impl<'s> Time<'s> { } } +#[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> { fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> { match self {