use std::borrow::Cow; // TODO: Update all to use the macro to assert there are no unexpected keys. // TODO: Check all compare funtions for whether they correctly iterate children. use std::collections::BTreeSet; use super::compare_field::compare_identity; use super::compare_field::compare_noop; use super::compare_field::compare_property_always_nil; use super::compare_field::compare_property_boolean; use super::compare_field::compare_property_list_of_ast_nodes; use super::compare_field::compare_property_list_of_quoted_string; use super::compare_field::compare_property_number_lines; use super::compare_field::compare_property_numeric; use super::compare_field::compare_property_quoted_string; use super::compare_field::compare_property_retain_labels; use super::compare_field::compare_property_set_of_quoted_string; use super::compare_field::compare_property_single_ast_node; use super::compare_field::compare_property_unquoted_atom; use super::util::affiliated_keywords_names; use super::util::assert_no_children; use super::util::compare_additional_properties; use super::util::compare_affiliated_keywords; use super::util::compare_children; use super::util::compare_children_iter; use super::util::compare_standard_properties; use crate::compare::compare_field::ComparePropertiesResult; use crate::compare::compare_field::EmacsField; use crate::compare::macros::compare_properties; use crate::types::AngleLink; use crate::types::AstNode; use crate::types::BabelCall; use crate::types::Bold; use crate::types::CenterBlock; use crate::types::CheckboxType; use crate::types::Citation; use crate::types::CitationReference; use crate::types::Clock; use crate::types::ClockStatus; use crate::types::Code; use crate::types::Comment; use crate::types::CommentBlock; use crate::types::Date; use crate::types::DayOfMonth; use crate::types::DiarySexp; use crate::types::Document; use crate::types::Drawer; use crate::types::DynamicBlock; use crate::types::Entity; use crate::types::ExampleBlock; use crate::types::ExportBlock; use crate::types::ExportSnippet; use crate::types::FixedWidthArea; use crate::types::FootnoteDefinition; use crate::types::FootnoteReference; use crate::types::FootnoteReferenceType; use crate::types::Heading; use crate::types::HorizontalRule; use crate::types::Hour; use crate::types::InlineBabelCall; use crate::types::InlineSourceBlock; use crate::types::Italic; use crate::types::Keyword; use crate::types::LatexEnvironment; use crate::types::LatexFragment; use crate::types::LineBreak; use crate::types::LinkType; use crate::types::Minute; use crate::types::Month; use crate::types::NodeProperty; use crate::types::OrgMacro; use crate::types::Paragraph; use crate::types::PlainLink; use crate::types::PlainList; use crate::types::PlainListItem; use crate::types::PlainListType; use crate::types::PlainText; use crate::types::Planning; use crate::types::PropertyDrawer; use crate::types::QuoteBlock; use crate::types::RadioLink; use crate::types::RadioTarget; use crate::types::RegularLink; use crate::types::RepeaterType; use crate::types::Section; use crate::types::SpecialBlock; use crate::types::SrcBlock; use crate::types::StandardProperties; use crate::types::StatisticsCookie; use crate::types::StrikeThrough; use crate::types::Subscript; use crate::types::Superscript; use crate::types::Table; use crate::types::TableCell; use crate::types::TableRow; use crate::types::TableRowType; use crate::types::Target; use crate::types::Time; use crate::types::TimeUnit; use crate::types::Timestamp; use crate::types::TimestampRangeType; use crate::types::TimestampType; 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::util::elisp::unquote; use crate::util::elisp::Token; use crate::util::elisp_fact::ElispFact; use crate::util::elisp_fact::GetElispFact; use crate::util::terminal::foreground_color; use crate::util::terminal::reset_color; #[derive(Debug)] pub enum DiffEntry<'b, 's> { DiffResult(DiffResult<'b, 's>), DiffLayer(DiffLayer<'b, 's>), } #[derive(Debug)] pub struct DiffResult<'b, 's> { status: DiffStatus, name: Cow<'s, str>, message: Option, children: Vec>, rust_source: &'s str, #[allow(dead_code)] emacs_token: &'b Token<'s>, } #[derive(Debug)] pub(crate) enum DiffStatus { Good, Bad, } #[derive(Debug)] pub struct DiffLayer<'b, 's> { name: Cow<'s, str>, children: Vec>, } impl<'b, 's> From> for DiffEntry<'b, 's> { fn from(value: DiffResult<'b, 's>) -> Self { DiffEntry::DiffResult(value) } } impl<'b, 's> From> for DiffEntry<'b, 's> { fn from(value: DiffLayer<'b, 's>) -> Self { DiffEntry::DiffLayer(value) } } impl<'b, 's> DiffEntry<'b, 's> { fn has_bad_children(&self) -> bool { match self { DiffEntry::DiffResult(diff) => &diff.children, DiffEntry::DiffLayer(diff) => &diff.children, } .iter() .any(|child| child.is_immediately_bad() || child.has_bad_children()) } fn is_immediately_bad(&self) -> bool { match self { DiffEntry::DiffResult(diff) => matches!(diff.status, DiffStatus::Bad), DiffEntry::DiffLayer(_) => false, } } pub fn is_bad(&self) -> bool { self.is_immediately_bad() || self.has_bad_children() } pub fn print(&self, original_document: &str) -> Result<(), Box> { self.print_indented(0, original_document) } fn print_indented( &self, indentation: usize, original_document: &str, ) -> Result<(), Box> { match self { DiffEntry::DiffResult(diff) => diff.print_indented(indentation, original_document), DiffEntry::DiffLayer(diff) => diff.print_indented(indentation, original_document), } } } impl<'b, 's> DiffResult<'b, 's> { fn print_indented( &self, indentation: usize, original_document: &str, ) -> Result<(), Box> { let status_text = { match self.status { DiffStatus::Good => { if self.has_bad_children() { format!( "{color}BADCHILD{reset}", color = foreground_color(255, 255, 0), reset = reset_color(), ) } else { format!( "{color}GOOD{reset}", color = foreground_color(0, 255, 0), reset = reset_color(), ) } } DiffStatus::Bad => format!( "{color}BAD{reset}", color = foreground_color(255, 0, 0), reset = reset_color(), ), } }; let rust_offset = self.rust_source.as_ptr() as usize - original_document.as_ptr() as usize; let preceding_text = &original_document[..rust_offset]; println!( "{indentation}{status_text} {name} char({char_offset}) {message}", indentation = " ".repeat(indentation), status_text = status_text, name = self.name, char_offset = preceding_text.chars().count() + 1, message = self.message.as_deref().unwrap_or("") ); for child in self.children.iter() { child.print_indented(indentation + 1, original_document)?; } Ok(()) } fn has_bad_children(&self) -> bool { self.children .iter() .any(|child| child.is_immediately_bad() || child.has_bad_children()) } } impl<'b, 's> DiffLayer<'b, 's> { fn has_bad_children(&self) -> bool { self.children .iter() .any(|child| child.is_immediately_bad() || child.has_bad_children()) } fn print_indented( &self, indentation: usize, original_document: &str, ) -> Result<(), Box> { let status_text = if self.has_bad_children() { format!( "{color}BADCHILD{reset}", color = foreground_color(255, 255, 0), reset = reset_color(), ) } else { format!( "{color}GOOD{reset}", color = foreground_color(0, 255, 0), reset = reset_color(), ) }; println!( "{indentation}{status_text} {name}", indentation = " ".repeat(indentation), status_text = status_text, name = self.name, ); for child in self.children.iter() { child.print_indented(indentation + 1, original_document)?; } Ok(()) } } pub(crate) fn artificial_diff_scope<'b, 's>( name: &'s str, children: Vec>, ) -> Result, Box> { Ok(DiffLayer { name: name.into(), children, } .into()) } pub(crate) fn artificial_owned_diff_scope<'b, 's>( name: &str, children: Vec>, ) -> Result, Box> { Ok(DiffLayer { name: name.to_string().into(), children, } .into()) } pub(crate) fn compare_ast_node<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: AstNode<'b, 's>, ) -> Result, Box> { let compare_result: Result, Box> = match rust { AstNode::Document(node) => _compare_document(source, emacs, node), AstNode::Heading(node) => compare_heading(source, emacs, node), AstNode::Section(node) => compare_section(source, emacs, node), AstNode::Paragraph(node) => compare_paragraph(source, emacs, node), AstNode::PlainList(node) => compare_plain_list(source, emacs, node), AstNode::PlainListItem(node) => compare_plain_list_item(source, emacs, node), AstNode::CenterBlock(node) => compare_center_block(source, emacs, node), AstNode::QuoteBlock(node) => compare_quote_block(source, emacs, node), AstNode::SpecialBlock(node) => compare_special_block(source, emacs, node), AstNode::DynamicBlock(node) => compare_dynamic_block(source, emacs, node), AstNode::FootnoteDefinition(node) => compare_footnote_definition(source, emacs, node), AstNode::Comment(node) => compare_comment(source, emacs, node), AstNode::Drawer(node) => compare_drawer(source, emacs, node), AstNode::PropertyDrawer(node) => compare_property_drawer(source, emacs, node), AstNode::NodeProperty(node) => compare_node_property(source, emacs, node), AstNode::Table(node) => compare_table(source, emacs, node), AstNode::TableRow(node) => compare_table_row(source, emacs, node), AstNode::VerseBlock(node) => compare_verse_block(source, emacs, node), AstNode::CommentBlock(node) => compare_comment_block(source, emacs, node), AstNode::ExampleBlock(node) => compare_example_block(source, emacs, node), AstNode::ExportBlock(node) => compare_export_block(source, emacs, node), AstNode::SrcBlock(node) => compare_src_block(source, emacs, node), AstNode::Clock(node) => compare_clock(source, emacs, node), AstNode::DiarySexp(node) => compare_diary_sexp(source, emacs, node), AstNode::Planning(node) => compare_planning(source, emacs, node), AstNode::FixedWidthArea(node) => compare_fixed_width_area(source, emacs, node), AstNode::HorizontalRule(node) => compare_horizontal_rule(source, emacs, node), AstNode::Keyword(node) => compare_keyword(source, emacs, node), AstNode::BabelCall(node) => compare_babel_call(source, emacs, node), AstNode::LatexEnvironment(node) => compare_latex_environment(source, emacs, node), AstNode::Bold(node) => compare_bold(source, emacs, node), AstNode::Italic(node) => compare_italic(source, emacs, node), AstNode::Underline(node) => compare_underline(source, emacs, node), AstNode::StrikeThrough(node) => compare_strike_through(source, emacs, node), AstNode::Code(node) => compare_code(source, emacs, node), AstNode::Verbatim(node) => compare_verbatim(source, emacs, node), AstNode::PlainText(node) => compare_plain_text(source, emacs, node), AstNode::RegularLink(node) => compare_regular_link(source, emacs, node), AstNode::RadioLink(node) => compare_radio_link(source, emacs, node), AstNode::RadioTarget(node) => compare_radio_target(source, emacs, node), AstNode::PlainLink(node) => compare_plain_link(source, emacs, node), AstNode::AngleLink(node) => compare_angle_link(source, emacs, node), AstNode::OrgMacro(node) => compare_org_macro(source, emacs, node), AstNode::Entity(node) => compare_entity(source, emacs, node), AstNode::LatexFragment(node) => compare_latex_fragment(source, emacs, node), AstNode::ExportSnippet(node) => compare_export_snippet(source, emacs, node), AstNode::FootnoteReference(node) => compare_footnote_reference(source, emacs, node), AstNode::Citation(node) => compare_citation(source, emacs, node), AstNode::CitationReference(node) => compare_citation_reference(source, emacs, node), AstNode::InlineBabelCall(node) => compare_inline_babel_call(source, emacs, node), AstNode::InlineSourceBlock(node) => compare_inline_source_block(source, emacs, node), AstNode::LineBreak(node) => compare_line_break(source, emacs, node), AstNode::Target(node) => compare_target(source, emacs, node), AstNode::StatisticsCookie(node) => compare_statistics_cookie(source, emacs, node), AstNode::Subscript(node) => compare_subscript(source, emacs, node), AstNode::Superscript(node) => compare_superscript(source, emacs, node), AstNode::TableCell(node) => compare_table_cell(source, emacs, node), AstNode::Timestamp(node) => compare_timestamp(source, emacs, node), }; let mut compare_result = match compare_result.unwrap_or_else(|e| { DiffResult { status: DiffStatus::Bad, name: rust.get_elisp_fact().get_elisp_name(), message: Some(e.to_string()), children: Vec::new(), rust_source: rust.get_source(), emacs_token: emacs, } .into() }) { DiffEntry::DiffResult(inner) => inner, DiffEntry::DiffLayer(_) => { unreachable!("Layers are only interior to DiffResults of AST nodes.") } }; // PlainText is a special case because upstream Org-Mode uses relative values for the bounds in plaintext rather than absolute so the below checks do not account for that. if let AstNode::PlainText(_) = rust { } else if let Err(err) = compare_standard_properties(source, emacs, &rust) { compare_result.status = DiffStatus::Bad; compare_result.message = Some(err.to_string()) } Ok(compare_result.into()) } pub fn compare_document<'b, 's>( emacs: &'b Token<'s>, rust: &'b Document<'s>, ) -> Result, Box> { compare_ast_node(rust.source, emacs, rust.into()) } fn _compare_document<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Document<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; let additional_property_names: Vec = rust .get_additional_properties() .map(|node_property| format!(":{}", node_property.property_name.to_uppercase())) .collect(); let additional_properties: Vec<(String, &str)> = rust .get_additional_properties() .map(|node_property| { ( format!(":{}", node_property.property_name.to_uppercase()), node_property.value.unwrap_or(""), ) }) .collect(); compare_additional_properties(emacs, additional_properties.into_iter())?.apply( &mut child_status, &mut this_status, &mut message, ); compare_children_iter( source, emacs, rust.into_iter(), &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, additional_property_names .iter() .map(String::as_str) .map(EmacsField::Required), ( EmacsField::Required(":path"), |r| r.path.as_ref().and_then(|p| p.to_str()), compare_property_quoted_string ), ( EmacsField::Required(":CATEGORY"), |r| r.category.as_ref(), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_section<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Section<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!(emacs) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } #[allow(dead_code)] fn compare_heading<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Heading<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; let additional_property_names: Vec = rust .get_additional_properties() .map(|node_property| format!(":{}", node_property.property_name.to_uppercase())) .collect(); let additional_properties: Vec<(String, &str)> = rust .get_additional_properties() .map(|node_property| { ( format!(":{}", node_property.property_name.to_uppercase()), node_property.value.unwrap_or(""), ) }) .collect(); compare_additional_properties(emacs, additional_properties.into_iter())?.apply( &mut child_status, &mut this_status, &mut message, ); compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, additional_property_names .iter() .map(String::as_str) .map(EmacsField::Required), ( EmacsField::Required(":level"), |r| Some(r.level), compare_property_numeric ), ( EmacsField::Required(":tags"), |r| if r.tags.is_empty() { None } else { Some(r.tags.iter()) }, compare_property_list_of_quoted_string ), ( EmacsField::Required(":todo-keyword"), |r| r.todo_keyword.as_ref().map(|(_, text)| text), compare_property_quoted_string ), ( EmacsField::Required(":todo-type"), |r| r .todo_keyword .as_ref() .map(|(todo_type, _)| todo_type) .map(|todo_type| match todo_type { TodoKeywordType::Todo => "todo", TodoKeywordType::Done => "done", }), compare_property_unquoted_atom ), ( EmacsField::Required(":title"), |r| if r.title.is_empty() { None } else { Some(r.title.iter()) }, compare_property_list_of_ast_nodes ), ( EmacsField::Required(":priority"), |r| r.priority_cookie, compare_property_numeric ), ( EmacsField::Required(":archivedp"), |r| r.is_archived, compare_property_boolean ), ( EmacsField::Required(":commentedp"), |r| r.is_comment, compare_property_boolean ), ( EmacsField::Required(":raw-value"), |r| Some(r.get_raw_value()), compare_property_quoted_string ), ( EmacsField::Required(":footnote-section-p"), |r| r.is_footnote_section, compare_property_boolean ), ( EmacsField::Optional(":scheduled"), |r| r.scheduled.as_ref(), compare_property_single_ast_node ), ( EmacsField::Optional(":deadline"), |r| r.deadline.as_ref(), compare_property_single_ast_node ), ( EmacsField::Optional(":closed"), |r| r.closed.as_ref(), compare_property_single_ast_node ), ( EmacsField::Required(":pre-blank"), compare_identity, compare_noop ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_paragraph<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Paragraph<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!(source, emacs, rust, [],) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_plain_list<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b PlainList<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":type"), |r| Some(match r.list_type { PlainListType::Unordered => "unordered", PlainListType::Ordered => "ordered", PlainListType::Descriptive => "descriptive", }), compare_property_unquoted_atom ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_plain_list_item<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b PlainListItem<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":tag"), |r| if r.tag.is_empty() { None } else { Some(r.tag.iter()) }, compare_property_list_of_ast_nodes ), ( EmacsField::Required(":bullet"), |r| Some(r.bullet), compare_property_quoted_string ), ( EmacsField::Required(":counter"), |r| r.counter, compare_property_numeric ), ( EmacsField::Required(":checkbox"), |r| r .checkbox .as_ref() .map(|(checkbox_type, _)| checkbox_type) .map(|checkbox_type| match checkbox_type { CheckboxType::On => "on", CheckboxType::Trans => "trans", CheckboxType::Off => "off", }), compare_property_unquoted_atom ), ( EmacsField::Required(":pre-blank"), |r| Some(r.pre_blank), compare_property_numeric ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_center_block<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b CenterBlock<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!(source, emacs, rust, [],) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_quote_block<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b QuoteBlock<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!(source, emacs, rust, [],) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_special_block<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b SpecialBlock<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":type"), |r| Some(r.block_type), compare_property_quoted_string ), ( EmacsField::Required(":parameters"), |r| r.parameters, compare_property_quoted_string ), ( EmacsField::Optional(":results"), compare_identity, compare_noop ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_dynamic_block<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b DynamicBlock<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":block-name"), |r| Some(r.block_name), compare_property_quoted_string ), ( EmacsField::Required(":arguments"), |r| r.parameters, compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_footnote_definition<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b FootnoteDefinition<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":label"), |r| Some(r.label), compare_property_quoted_string ), ( EmacsField::Required(":pre-blank"), compare_identity, compare_noop ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_comment<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Comment<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":value"), |r| Some(r.get_value()), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_drawer<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Drawer<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":drawer-name"), |r| Some(r.drawer_name), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_property_drawer<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b PropertyDrawer<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!(emacs) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_node_property<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b NodeProperty<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":key"), |r| Some(r.property_name), compare_property_quoted_string ), ( EmacsField::Required(":value"), |r| r.value, compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_table<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Table<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":tblfm"), |r| if r.formulas.is_empty() { None } else { Some(r.formulas.iter().map(|kw| kw.value)) }, compare_property_set_of_quoted_string ), ( EmacsField::Required(":type"), |_| Some("org"), compare_property_unquoted_atom ), ( EmacsField::Required(":value"), compare_identity, compare_property_always_nil ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_table_row<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b TableRow<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":type"), |r| Some(match r.get_type() { TableRowType::Standard => "standard", TableRowType::Rule => "rule", }), compare_property_unquoted_atom ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_table_cell<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b TableCell<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!(emacs) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_verse_block<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b VerseBlock<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!(source, emacs, rust, [],) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_comment_block<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b CommentBlock<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":value"), |r| Some(r.contents), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_example_block<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b ExampleBlock<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":value"), |r| Some(r.get_value()), compare_property_quoted_string ), ( EmacsField::Required(":switches"), |r| r.switches, compare_property_quoted_string ), ( EmacsField::Required(":number-lines"), |r| r.number_lines.as_ref(), compare_property_number_lines ), ( EmacsField::Required(":preserve-indent"), |r| r.preserve_indent, compare_property_numeric ), ( EmacsField::Required(":retain-labels"), |r| &r.retain_labels, compare_property_retain_labels ), ( EmacsField::Required(":use-labels"), |r| r.use_labels, compare_property_boolean ), ( EmacsField::Required(":label-fmt"), |r| r.label_format, compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_export_block<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b ExportBlock<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":type"), |r| r.get_export_type(), compare_property_quoted_string ), ( EmacsField::Required(":value"), |r| Some(r.get_value()), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_src_block<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b SrcBlock<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":language"), |r| r.language, compare_property_quoted_string ), ( EmacsField::Required(":value"), |r| Some(r.get_value()), compare_property_quoted_string ), ( EmacsField::Required(":switches"), |r| r.switches, compare_property_quoted_string ), ( EmacsField::Required(":parameters"), |r| r.parameters, compare_property_quoted_string ), ( EmacsField::Required(":number-lines"), |r| r.number_lines.as_ref(), compare_property_number_lines ), ( EmacsField::Required(":preserve-indent"), |r| r.preserve_indent, compare_property_numeric ), ( EmacsField::Required(":retain-labels"), |r| &r.retain_labels, compare_property_retain_labels ), ( EmacsField::Required(":use-labels"), |r| r.use_labels, compare_property_boolean ), ( EmacsField::Required(":label-fmt"), |r| r.label_format, compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_clock<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Clock<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":value"), |r| Some(&r.timestamp), compare_property_single_ast_node ), ( EmacsField::Required(":duration"), |r| r.duration, compare_property_quoted_string ), ( EmacsField::Required(":status"), |r| Some(match r.status { ClockStatus::Running => "running", ClockStatus::Closed => "closed", }), compare_property_unquoted_atom ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_diary_sexp<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b DiarySexp<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":value"), |r| Some(r.value), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_planning<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Planning<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":scheduled"), |r| r.scheduled.as_ref(), compare_property_single_ast_node ), ( EmacsField::Required(":deadline"), |r| r.deadline.as_ref(), compare_property_single_ast_node ), ( EmacsField::Required(":closed"), |r| r.closed.as_ref(), compare_property_single_ast_node ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_fixed_width_area<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b FixedWidthArea<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":value"), |r| Some(r.get_value()), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_horizontal_rule<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b HorizontalRule<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!(source, emacs, rust, [],) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_keyword<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Keyword<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":key"), |r| Some(r.key.to_uppercase()), compare_property_quoted_string ), ( EmacsField::Required(":value"), |r| Some(r.value), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_babel_call<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b BabelCall<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":value"), |r| Some(r.value), compare_property_quoted_string ), ( EmacsField::Required(":call"), |r| r.call, compare_property_quoted_string ), ( EmacsField::Required(":arguments"), |r| r.arguments, compare_property_quoted_string ), ( EmacsField::Required(":inside-header"), |r| r.inside_header, compare_property_quoted_string ), ( EmacsField::Required(":end-header"), |r| r.end_header, compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_latex_environment<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b LatexEnvironment<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, [], ( EmacsField::Required(":value"), |r| Some(r.value), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_plain_text<'b, 's>( _source: &'s str, emacs: &'b Token<'s>, rust: &'b PlainText<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut message = None; let rust_source = rust.get_source(); let text = emacs.as_text()?; let start_ind: usize = text .properties .first() .expect("Should have start index.") .as_atom()? .parse()?; let end_ind: usize = text .properties .get(1) .expect("Should have end index.") .as_atom()? .parse()?; let emacs_text_length = end_ind - start_ind; if rust_source.chars().count() != emacs_text_length { this_status = DiffStatus::Bad; message = Some(format!( "(emacs len != rust len) {:?} != {:?}", emacs_text_length, rust_source.len() )); } let unquoted_text = unquote(text.text)?; if unquoted_text != rust_source { this_status = DiffStatus::Bad; message = Some(format!( "(emacs != rust) {:?} != {:?}", unquoted_text, rust_source )); } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: Vec::new(), rust_source, emacs_token: emacs, } .into()) } fn compare_bold<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Bold<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!(emacs) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_italic<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Italic<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!(emacs) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_underline<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Underline<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!(emacs) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_verbatim<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Verbatim<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":value"), |r| Some(r.contents), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_code<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Code<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":value"), |r| Some(r.contents), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_strike_through<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b StrikeThrough<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!(emacs) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_regular_link<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b RegularLink<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":type"), |r| { match &r.link_type { LinkType::File => Some(Cow::Borrowed("file")), LinkType::Protocol(protocol) => Some(protocol.clone()), LinkType::Id => Some(Cow::Borrowed("id")), LinkType::CustomId => Some(Cow::Borrowed("custom-id")), LinkType::CodeRef => Some(Cow::Borrowed("coderef")), LinkType::Fuzzy => Some(Cow::Borrowed("fuzzy")), } }, compare_property_quoted_string ), ( EmacsField::Required(":path"), |r| Some(r.get_path()), compare_property_quoted_string ), ( EmacsField::Required(":format"), |_| Some("bracket"), compare_property_unquoted_atom ), ( EmacsField::Required(":raw-link"), |r| Some(r.get_raw_link()), compare_property_quoted_string ), ( EmacsField::Required(":application"), |r| r.application.as_ref(), compare_property_quoted_string ), ( EmacsField::Required(":search-option"), |r| r.get_search_option(), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_radio_link<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b RadioLink<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":type"), |_| { Some("radio") }, compare_property_quoted_string ), ( EmacsField::Required(":path"), |r| Some(&r.path), compare_property_quoted_string ), ( EmacsField::Required(":format"), |_| Some("plain"), compare_property_unquoted_atom ), ( EmacsField::Required(":raw-link"), |r| Some(r.get_raw_link()), compare_property_quoted_string ), ( EmacsField::Required(":application"), compare_identity, compare_property_always_nil ), ( EmacsField::Required(":search-option"), compare_identity, compare_property_always_nil ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_radio_target<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b RadioTarget<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":value"), |r| Some(r.value), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_plain_link<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b PlainLink<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":type"), |r| { match &r.link_type { LinkType::File => Some(Cow::Borrowed("file")), LinkType::Protocol(protocol) => Some(protocol.clone()), LinkType::Id => Some(Cow::Borrowed("id")), LinkType::CustomId => Some(Cow::Borrowed("custom-id")), LinkType::CodeRef => Some(Cow::Borrowed("coderef")), LinkType::Fuzzy => Some(Cow::Borrowed("fuzzy")), } }, compare_property_quoted_string ), ( EmacsField::Required(":path"), |r| Some(r.path), compare_property_quoted_string ), ( EmacsField::Required(":format"), |_| Some("plain"), compare_property_unquoted_atom ), ( EmacsField::Required(":raw-link"), |r| Some(r.raw_link), compare_property_quoted_string ), ( EmacsField::Required(":application"), |r| r.application, compare_property_quoted_string ), ( EmacsField::Required(":search-option"), |r| r.search_option, compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_angle_link<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b AngleLink<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":type"), |r| { match &r.link_type { LinkType::File => Some(Cow::Borrowed("file")), LinkType::Protocol(protocol) => Some(protocol.clone()), LinkType::Id => Some(Cow::Borrowed("id")), LinkType::CustomId => Some(Cow::Borrowed("custom-id")), LinkType::CodeRef => Some(Cow::Borrowed("coderef")), LinkType::Fuzzy => Some(Cow::Borrowed("fuzzy")), } }, compare_property_quoted_string ), ( EmacsField::Required(":path"), |r| Some(r.get_path()), compare_property_quoted_string ), ( EmacsField::Required(":format"), |_| Some("angle"), compare_property_unquoted_atom ), ( EmacsField::Required(":raw-link"), |r| Some(r.raw_link), compare_property_quoted_string ), ( EmacsField::Required(":application"), |r| r.application, compare_property_quoted_string ), ( EmacsField::Required(":search-option"), |r| r.get_search_option(), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_org_macro<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b OrgMacro<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":key"), |r| Some(r.get_key()), compare_property_quoted_string ), ( EmacsField::Required(":value"), |r| Some(r.value), compare_property_quoted_string ), ( EmacsField::Required(":args"), |r| if r.args.is_empty() { None } else { Some(r.get_args()) }, compare_property_list_of_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_entity<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Entity<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":name"), |r| Some(r.name), compare_property_quoted_string ), ( EmacsField::Required(":latex"), |r| Some(r.latex), compare_property_quoted_string ), ( EmacsField::Required(":latex-math-p"), |r| r.latex_math_mode, compare_property_boolean ), ( EmacsField::Required(":html"), |r| Some(r.html), compare_property_quoted_string ), ( EmacsField::Required(":ascii"), |r| Some(r.ascii), compare_property_quoted_string ), ( // latin1... like I give a shit. EmacsField::Required(":latin1"), compare_identity, compare_noop ), ( EmacsField::Required(":utf-8"), |r| Some(r.utf8), compare_property_quoted_string ), ( EmacsField::Required(":use-brackets-p"), |r| r.use_brackets, compare_property_boolean ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_latex_fragment<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b LatexFragment<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":value"), |r| Some(r.value), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_export_snippet<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b ExportSnippet<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":back-end"), |r| Some(r.backend), compare_property_quoted_string ), ( EmacsField::Required(":value"), |r| r.contents, compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_footnote_reference<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b FootnoteReference<'s>, ) -> Result, Box> { let mut child_status = Vec::new(); let mut this_status = DiffStatus::Good; let mut message = None; compare_children( source, emacs, &rust.definition, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":label"), |r| r.label, compare_property_quoted_string ), ( EmacsField::Required(":type"), |r| Some(match r.get_type() { FootnoteReferenceType::Standard => "standard", FootnoteReferenceType::Inline => "inline", }), compare_property_unquoted_atom ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_citation<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Citation<'s>, ) -> Result, Box> { let mut child_status = Vec::new(); let mut this_status = DiffStatus::Good; let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":style"), |r| r.style, compare_property_quoted_string ), ( EmacsField::Optional(":prefix"), |r| if r.prefix.is_empty() { None } else { Some(r.prefix.iter()) }, compare_property_list_of_ast_nodes ), ( EmacsField::Optional(":suffix"), |r| if r.suffix.is_empty() { None } else { Some(r.suffix.iter()) }, compare_property_list_of_ast_nodes ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_citation_reference<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b CitationReference<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":key"), |r| Some(r.key), compare_property_quoted_string ), ( EmacsField::Optional(":prefix"), |r| if r.prefix.is_empty() { None } else { Some(r.prefix.iter()) }, compare_property_list_of_ast_nodes ), ( EmacsField::Optional(":suffix"), |r| if r.suffix.is_empty() { None } else { Some(r.suffix.iter()) }, compare_property_list_of_ast_nodes ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_inline_babel_call<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b InlineBabelCall<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":call"), |r| Some(r.call), compare_property_quoted_string ), ( EmacsField::Required(":inside-header"), |r| r.inside_header, compare_property_quoted_string ), ( EmacsField::Required(":arguments"), |r| r.arguments, compare_property_quoted_string ), ( EmacsField::Required(":end-header"), |r| r.end_header, compare_property_quoted_string ), ( EmacsField::Required(":value"), |r| Some(r.value), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_inline_source_block<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b InlineSourceBlock<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":language"), |r| Some(r.language), compare_property_quoted_string ), ( EmacsField::Required(":value"), |r| Some(r.value), compare_property_quoted_string ), ( EmacsField::Required(":parameters"), |r| r.parameters, compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_line_break<'b, 's>( _source: &'s str, emacs: &'b Token<'s>, rust: &'b LineBreak<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!(emacs) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_target<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Target<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":value"), |r| Some(r.value), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_statistics_cookie<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b StatisticsCookie<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":value"), |r| Some(r.value), compare_property_quoted_string ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_subscript<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Subscript<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":use-brackets-p"), |r| r.use_brackets, compare_property_boolean ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_superscript<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Superscript<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; compare_children( source, emacs, &rust.children, &mut child_status, &mut this_status, &mut message, )?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":use-brackets-p"), |r| r.use_brackets, compare_property_boolean ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) } fn compare_timestamp<'b, 's>( source: &'s str, emacs: &'b Token<'s>, rust: &'b Timestamp<'s>, ) -> Result, Box> { let mut this_status = DiffStatus::Good; let mut child_status = Vec::new(); let mut message = None; assert_no_children(emacs, &mut this_status, &mut message)?; for diff in compare_properties!( source, emacs, rust, ( EmacsField::Required(":type"), |r| Some(match r.timestamp_type { TimestampType::Diary => "diary", TimestampType::Active => "active", TimestampType::Inactive => "inactive", TimestampType::ActiveRange => "active-range", TimestampType::InactiveRange => "inactive-range", }), compare_property_unquoted_atom ), ( EmacsField::Required(":range-type"), |r| match r.range_type { TimestampRangeType::DateRange => Some("daterange"), TimestampRangeType::TimeRange => Some("timerange"), TimestampRangeType::None => None, }, compare_property_unquoted_atom ), ( EmacsField::Required(":raw-value"), |r| Some(r.get_raw_value()), compare_property_quoted_string ), ( EmacsField::Required(":year-start"), |r| r.start.as_ref().map(Date::get_year).map(Year::get_value), compare_property_numeric ), ( EmacsField::Required(":month-start"), |r| r.start.as_ref().map(Date::get_month).map(Month::get_value), compare_property_numeric ), ( EmacsField::Required(":day-start"), |r| r .start .as_ref() .map(Date::get_day_of_month) .map(DayOfMonth::get_value), compare_property_numeric ), ( EmacsField::Required(":year-end"), |r| r.end.as_ref().map(Date::get_year).map(Year::get_value), compare_property_numeric ), ( EmacsField::Required(":month-end"), |r| r.end.as_ref().map(Date::get_month).map(Month::get_value), compare_property_numeric ), ( EmacsField::Required(":day-end"), |r| r .end .as_ref() .map(Date::get_day_of_month) .map(DayOfMonth::get_value), compare_property_numeric ), ( EmacsField::Required(":hour-start"), |r| r .start_time .as_ref() .map(Time::get_hour) .map(Hour::get_value), compare_property_numeric ), ( EmacsField::Required(":minute-start"), |r| r .start_time .as_ref() .map(Time::get_minute) .map(Minute::get_value), compare_property_numeric ), ( EmacsField::Required(":hour-end"), |r| r.end_time.as_ref().map(Time::get_hour).map(Hour::get_value), compare_property_numeric ), ( EmacsField::Required(":minute-end"), |r| r .end_time .as_ref() .map(Time::get_minute) .map(Minute::get_value), compare_property_numeric ), ( EmacsField::Optional(":repeater-type"), |r| match r.repeater.as_ref().map(|repeater| &repeater.repeater_type) { Some(RepeaterType::Cumulative) => Some("cumulate"), Some(RepeaterType::CatchUp) => Some("catch-up"), Some(RepeaterType::Restart) => Some("restart"), None => None, }, compare_property_unquoted_atom ), ( EmacsField::Optional(":repeater-unit"), |r| match r.repeater.as_ref().map(|repeater| &repeater.unit) { Some(TimeUnit::Hour) => Some("hour"), Some(TimeUnit::Day) => Some("day"), Some(TimeUnit::Week) => Some("week"), Some(TimeUnit::Month) => Some("month"), Some(TimeUnit::Year) => Some("year"), None => None, }, compare_property_unquoted_atom ), ( EmacsField::Optional(":repeater-value"), |r| r.repeater.as_ref().map(|repeater| repeater.value), compare_property_numeric ), ( EmacsField::Optional(":warning-type"), |r| match r .warning_delay .as_ref() .map(|repeater| &repeater.warning_delay_type) { Some(WarningDelayType::All) => Some("all"), Some(WarningDelayType::First) => Some("first"), None => None, }, compare_property_unquoted_atom ), ( EmacsField::Optional(":warning-unit"), |r| match r.warning_delay.as_ref().map(|repeater| &repeater.unit) { Some(TimeUnit::Hour) => Some("hour"), Some(TimeUnit::Day) => Some("day"), Some(TimeUnit::Week) => Some("week"), Some(TimeUnit::Month) => Some("month"), Some(TimeUnit::Year) => Some("year"), None => None, }, compare_property_unquoted_atom ), ( EmacsField::Optional(":warning-value"), |r| r.warning_delay.as_ref().map(|repeater| repeater.value), compare_property_numeric ) ) { match diff { ComparePropertiesResult::NoChange => {} ComparePropertiesResult::SelfChange(new_status, new_message) => { this_status = new_status; message = new_message } ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry), } } Ok(DiffResult { status: this_status, name: rust.get_elisp_name(), message, children: child_status, rust_source: rust.get_source(), emacs_token: emacs, } .into()) }