From 7727b5ef47e3846e62c4b930aabc5c8ed56a3110 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 29 Sep 2023 18:45:38 -0400 Subject: [PATCH] Compare plain list item counter. --- src/compare/diff.rs | 32 ++++++++++++++++++++++++++++++-- src/parser/plain_list.rs | 29 +++++++++++++++++++++++------ src/types/greater_element.rs | 3 +++ src/types/mod.rs | 1 + 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index da30bef4..86afe8e4 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -52,6 +52,7 @@ use crate::types::Paragraph; use crate::types::PlainLink; use crate::types::PlainList; use crate::types::PlainListItem; +use crate::types::PlainListItemCounter; use crate::types::PlainListType; use crate::types::PlainText; use crate::types::Planning; @@ -754,6 +755,7 @@ fn compare_plain_list<'s>( let mut this_status = DiffStatus::Good; let mut message = None; + // Compare type // :type is an unquoted atom of either descriptive, ordered, or unordered let list_type = get_property_unquoted_atom(emacs, ":type")?; match (list_type, &rust.list_type) { @@ -831,17 +833,41 @@ fn compare_plain_list_item<'s>( .collect::, _>>()?; child_status.push(artificial_diff_scope("contents", contents_status)?); - // TODO: Compare :counter :pre-blank + // Compare bullet let bullet = get_property_quoted_string(emacs, ":bullet")? .ok_or("Plain list items must have a :bullet.")?; if bullet != rust.bullet { this_status = DiffStatus::Bad; message = Some(format!( - "Plain list item bullet mismatch (emacs != rust) {:?} != {:?}", + "Bullet mismatch (emacs != rust) {:?} != {:?}", bullet, rust.bullet )); } + // Compare counter + let counter = get_property_unquoted_atom(emacs, ":counter")?; + let counter: Option = counter + .map(|val| val.parse()) + .map_or(Ok(None), |r| r.map(Some))?; + match (counter, rust.counter) { + (None, None) => {} + (None, Some(_)) | (Some(_), None) => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Counter mismatch (emacs != rust) {:?} != {:?}", + counter, rust.counter + )); + } + (Some(e), Some(r)) if e != r => { + this_status = DiffStatus::Bad; + message = Some(format!( + "Counter mismatch (emacs != rust) {:?} != {:?}", + counter, rust.counter + )); + } + (Some(_), Some(_)) => {} + }; + // Compare checkbox let checkbox = get_property(emacs, ":checkbox")? .map(Token::as_atom) @@ -861,6 +887,8 @@ fn compare_plain_list_item<'s>( } }; + // TODO: Compare :pre-blank + Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index 564db263..76a16e11 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -44,6 +44,7 @@ use crate::types::IndentationLevel; use crate::types::Object; use crate::types::PlainList; use crate::types::PlainListItem; +use crate::types::PlainListItemCounter; use crate::types::PlainListType; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] @@ -166,8 +167,9 @@ fn plain_list_item<'b, 'g, 'r, 's>( |(_bullet_type, bull)| !Into::<&str>::into(bull).starts_with("*") || indent_level > 0, )(remaining)?; - let (remaining, _maybe_counter_set) = + let (remaining, maybe_counter_set) = opt(tuple((space1, tag("[@"), counter_set_value, tag("]"))))(remaining)?; + let maybe_counter_set = maybe_counter_set.map(|(_, _, val, _)| val); let (remaining, maybe_checkbox) = opt(tuple((space1, item_checkbox)))(remaining)?; @@ -213,6 +215,7 @@ fn plain_list_item<'b, 'g, 'r, 's>( source: source.into(), indentation: indent_level, bullet: bull.into(), + counter: maybe_counter_set, checkbox: None, tag: maybe_tag .map(|(_ws, item_tag)| item_tag) @@ -256,6 +259,7 @@ fn plain_list_item<'b, 'g, 'r, 's>( source: source.into(), indentation: indent_level, bullet: bull.into(), + counter: maybe_counter_set, checkbox: maybe_checkbox.map(|(_, (checkbox_type, source))| { (checkbox_type, Into::<&str>::into(source)) }), @@ -316,12 +320,25 @@ fn counter<'b, 'g, 'r, 's>( } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn counter_set_value<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { +fn counter_set_value<'s>(input: OrgSource<'s>) -> Res, PlainListItemCounter> { alt(( - recognize(one_of( - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", - )), - digit1, + map( + one_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), + |letter| { + let num = match letter { + 'a'..='z' => (letter as u32) - ('a' as u32) + 1, + 'A'..='Z' => (letter as u32) - ('A' as u32) + 1, + _ => unreachable!(), + }; + PlainListItemCounter::try_from(num) + .expect("Counter set value should be between 1 and 26 inclusive.") + }, + ), + map(digit1, |num: OrgSource<'_>| { + Into::<&str>::into(num) + .parse() + .expect("digit1 must parse to a number.") + }), ))(input) } diff --git a/src/types/greater_element.rs b/src/types/greater_element.rs index bfe469a9..c5977cf3 100644 --- a/src/types/greater_element.rs +++ b/src/types/greater_element.rs @@ -26,11 +26,14 @@ pub struct PlainListItem<'s> { pub source: &'s str, pub indentation: IndentationLevel, pub bullet: &'s str, + pub counter: Option, pub checkbox: Option<(CheckboxType, &'s str)>, pub tag: Vec>, pub children: Vec>, } +pub type PlainListItemCounter = u16; + #[derive(Debug)] pub enum CheckboxType { On, diff --git a/src/types/mod.rs b/src/types/mod.rs index ca31e249..0a17d710 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -24,6 +24,7 @@ pub use greater_element::IndentationLevel; pub use greater_element::NodeProperty; pub use greater_element::PlainList; pub use greater_element::PlainListItem; +pub use greater_element::PlainListItemCounter; pub use greater_element::PlainListType; pub use greater_element::PropertyDrawer; pub use greater_element::Table;