3437 lines
108 KiB
Rust
3437 lines
108 KiB
Rust
use std::borrow::Cow;
|
|
// TODO: Add a check for unexpected keys in the properties
|
|
use std::collections::BTreeSet;
|
|
use std::collections::HashSet;
|
|
|
|
use super::compare_field::compare_identity;
|
|
use super::compare_field::compare_property_quoted_string;
|
|
use super::compare_field::impl_compare_noop;
|
|
use super::elisp_fact::ElispFact;
|
|
use super::elisp_fact::GetElispFact;
|
|
use super::sexp::unquote;
|
|
use super::sexp::Token;
|
|
use super::util::compare_standard_properties;
|
|
use super::util::get_property;
|
|
use super::util::get_property_boolean;
|
|
use super::util::get_property_numeric;
|
|
use super::util::get_property_quoted_string;
|
|
use super::util::get_property_unquoted_atom;
|
|
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::CharOffsetInLine;
|
|
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::DayOfMonthInner;
|
|
use crate::types::DiarySexp;
|
|
use crate::types::Document;
|
|
use crate::types::DocumentElement;
|
|
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::GetStandardProperties;
|
|
use crate::types::Heading;
|
|
use crate::types::HorizontalRule;
|
|
use crate::types::Hour;
|
|
use crate::types::HourInner;
|
|
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::LineNumber;
|
|
use crate::types::Minute;
|
|
use crate::types::MinuteInner;
|
|
use crate::types::Month;
|
|
use crate::types::MonthInner;
|
|
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::PlainListItemCounter;
|
|
use crate::types::PlainListItemPreBlank;
|
|
use crate::types::PlainListType;
|
|
use crate::types::PlainText;
|
|
use crate::types::Planning;
|
|
use crate::types::PriorityCookie;
|
|
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::RepeaterWarningDelayValueType;
|
|
use crate::types::RetainLabels;
|
|
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::SwitchNumberLines;
|
|
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::types::YearInner;
|
|
|
|
#[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<String>,
|
|
children: Vec<DiffEntry<'b, 's>>,
|
|
rust_source: &'s str,
|
|
#[allow(dead_code)]
|
|
emacs_token: &'b Token<'s>,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
pub(crate) enum DiffStatus {
|
|
Good,
|
|
Bad,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct DiffLayer<'b, 's> {
|
|
name: Cow<'s, str>,
|
|
children: Vec<DiffEntry<'b, 's>>,
|
|
}
|
|
|
|
impl<'b, 's> From<DiffResult<'b, 's>> for DiffEntry<'b, 's> {
|
|
fn from(value: DiffResult<'b, 's>) -> Self {
|
|
DiffEntry::DiffResult(value)
|
|
}
|
|
}
|
|
|
|
impl<'b, 's> From<DiffLayer<'b, 's>> 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) => 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<dyn std::error::Error>> {
|
|
self.print_indented(0, original_document)
|
|
}
|
|
|
|
fn print_indented(
|
|
&self,
|
|
indentation: usize,
|
|
original_document: &str,
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
let status_text = {
|
|
match self.status {
|
|
DiffStatus::Good => {
|
|
if self.has_bad_children() {
|
|
format!(
|
|
"{color}BADCHILD{reset}",
|
|
color = DiffResult::foreground_color(255, 255, 0),
|
|
reset = DiffResult::reset_color(),
|
|
)
|
|
} else {
|
|
format!(
|
|
"{color}GOOD{reset}",
|
|
color = DiffResult::foreground_color(0, 255, 0),
|
|
reset = DiffResult::reset_color(),
|
|
)
|
|
}
|
|
}
|
|
DiffStatus::Bad => format!(
|
|
"{color}BAD{reset}",
|
|
color = DiffResult::foreground_color(255, 0, 0),
|
|
reset = DiffResult::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_ref().map(|m| m.as_str()).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())
|
|
}
|
|
|
|
pub(crate) fn foreground_color(red: u8, green: u8, blue: u8) -> String {
|
|
if DiffResult::should_use_color() {
|
|
format!(
|
|
"\x1b[38;2;{red};{green};{blue}m",
|
|
red = red,
|
|
green = green,
|
|
blue = blue
|
|
)
|
|
} else {
|
|
String::new()
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub(crate) fn background_color(red: u8, green: u8, blue: u8) -> String {
|
|
if DiffResult::should_use_color() {
|
|
format!(
|
|
"\x1b[48;2;{red};{green};{blue}m",
|
|
red = red,
|
|
green = green,
|
|
blue = blue
|
|
)
|
|
} else {
|
|
String::new()
|
|
}
|
|
}
|
|
|
|
pub(crate) fn reset_color() -> &'static str {
|
|
if DiffResult::should_use_color() {
|
|
"\x1b[0m"
|
|
} else {
|
|
""
|
|
}
|
|
}
|
|
|
|
fn should_use_color() -> bool {
|
|
!std::env::var("NO_COLOR").is_ok_and(|val| !val.is_empty())
|
|
}
|
|
}
|
|
|
|
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<dyn std::error::Error>> {
|
|
let status_text = if self.has_bad_children() {
|
|
format!(
|
|
"{color}BADCHILD{reset}",
|
|
color = DiffResult::foreground_color(255, 255, 0),
|
|
reset = DiffResult::reset_color(),
|
|
)
|
|
} else {
|
|
format!(
|
|
"{color}GOOD{reset}",
|
|
color = DiffResult::foreground_color(0, 255, 0),
|
|
reset = DiffResult::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(())
|
|
}
|
|
}
|
|
|
|
fn artificial_diff_scope<'b, 's>(
|
|
name: &'static str,
|
|
children: Vec<DiffEntry<'b, 's>>,
|
|
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
Ok(DiffLayer {
|
|
name: name.into(),
|
|
children,
|
|
}
|
|
.into())
|
|
}
|
|
|
|
fn compare_ast_node<'b, 's>(
|
|
source: &'s str,
|
|
emacs: &'b Token<'s>,
|
|
rust: AstNode<'b, 's>,
|
|
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let compare_result: Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> = 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_standard_properties().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 {
|
|
match compare_standard_properties(source, emacs, &rust) {
|
|
Err(err) => {
|
|
compare_result.status = DiffStatus::Bad;
|
|
compare_result.message = Some(err.to_string())
|
|
}
|
|
Ok(_) => {}
|
|
}
|
|
}
|
|
|
|
Ok(compare_result.into())
|
|
}
|
|
|
|
pub fn compare_document<'b, 's>(
|
|
emacs: &'b Token<'s>,
|
|
rust: &'b Document<'s>,
|
|
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// Compare :path
|
|
// :path is a quoted string to the absolute path of the document.
|
|
let document_path = get_property_quoted_string(emacs, ":path")?;
|
|
let rust_document_path = rust.path.as_ref().map(|p| p.to_str()).flatten();
|
|
match (
|
|
document_path.as_ref().map(|s| s.as_str()),
|
|
rust_document_path,
|
|
) {
|
|
(None, None) => {}
|
|
(None, Some(_)) | (Some(_), None) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Path mismatch (emacs != rust) {:?} != {:?}",
|
|
document_path, rust_document_path
|
|
));
|
|
}
|
|
(Some(e), Some(r)) if e != r => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Path mismatch (emacs != rust) {:?} != {:?}",
|
|
document_path, rust_document_path
|
|
));
|
|
}
|
|
(Some(_), Some(_)) => {}
|
|
};
|
|
|
|
// Compare category
|
|
// :CATEGORY is specified either from "#+CATEGORY:" or it is the file name without the ".org" extension.
|
|
let category = get_property_quoted_string(emacs, ":CATEGORY")?;
|
|
match (category.as_ref(), rust.category.as_ref()) {
|
|
(None, None) => {}
|
|
(None, Some(_)) | (Some(_), None) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Category mismatch (emacs != rust) {:?} != {:?}",
|
|
category, rust.category
|
|
));
|
|
}
|
|
(Some(e), Some(r)) if e != r => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Category mismatch (emacs != rust) {:?} != {:?}",
|
|
category, rust.category
|
|
));
|
|
}
|
|
(Some(_), Some(_)) => {}
|
|
};
|
|
|
|
// Skipping "org-data" and its properties
|
|
for (i, token) in children.iter().skip(2).enumerate() {
|
|
let section_or_headline = token.as_list()?;
|
|
let first_cell = section_or_headline
|
|
.first()
|
|
.ok_or("Should have at least one child.")?
|
|
.as_atom()?;
|
|
if first_cell == "section" {
|
|
if i != 0 {
|
|
return Err("Section cannot be after the first child of document.".into());
|
|
}
|
|
child_status.push(compare_ast_node(
|
|
rust.source,
|
|
token,
|
|
rust.zeroth_section
|
|
.as_ref()
|
|
.ok_or("No corresponding zeroth-section")?
|
|
.into(),
|
|
)?);
|
|
} else if first_cell == "headline" {
|
|
let corresponding_heading = rust
|
|
.children
|
|
.iter()
|
|
.nth(i - rust.zeroth_section.as_ref().map(|_| 1).unwrap_or(0))
|
|
.ok_or("Should have a corresponding heading.")?;
|
|
child_status.push(compare_ast_node(
|
|
rust.source,
|
|
token,
|
|
corresponding_heading.into(),
|
|
)?);
|
|
} else {
|
|
return Err(format!(
|
|
"Document should only contain sections and headlines, found: {}",
|
|
first_cell
|
|
)
|
|
.into());
|
|
}
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let this_status = DiffStatus::Good;
|
|
let mut child_status = Vec::new();
|
|
let message = None;
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
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_heading<'b, 's>(
|
|
source: &'s str,
|
|
emacs: &'b Token<'s>,
|
|
rust: &'b Heading<'s>,
|
|
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// Compare level
|
|
let level = get_property(emacs, ":level")?
|
|
.ok_or("Level should not be nil")?
|
|
.as_atom()?;
|
|
if rust.level.to_string() != level {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Headline level do not match (emacs != rust): {} != {}",
|
|
level, rust.level
|
|
))
|
|
}
|
|
|
|
// Compare tags
|
|
let emacs_tags = get_tags_from_heading(emacs)?;
|
|
let emacs_tags: HashSet<_> = emacs_tags.iter().map(|val| val.as_str()).collect();
|
|
let rust_tags: HashSet<&str> = rust.tags.iter().map(|val| *val).collect();
|
|
let difference: Vec<&str> = emacs_tags
|
|
.symmetric_difference(&rust_tags)
|
|
.map(|val| *val)
|
|
.collect();
|
|
if !difference.is_empty() {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!("Mismatched tags: {}", difference.join(", ")));
|
|
}
|
|
|
|
// Compare todo-keyword
|
|
let todo_keyword = get_property(emacs, ":todo-keyword")?
|
|
.map(Token::as_atom)
|
|
.map_or(Ok(None), |r| r.map(Some))?
|
|
.unwrap_or("nil");
|
|
match (todo_keyword, &rust.todo_keyword, unquote(todo_keyword)) {
|
|
("nil", None, _) => {}
|
|
(_, Some((_rust_todo_type, rust_todo)), Ok(emacs_todo)) if emacs_todo == *rust_todo => {}
|
|
(emacs_todo, rust_todo, _) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"(emacs != rust) {:?} != {:?}",
|
|
emacs_todo, rust_todo
|
|
));
|
|
}
|
|
};
|
|
// Compare todo-type
|
|
let todo_type = get_property(emacs, ":todo-type")?
|
|
.map(Token::as_atom)
|
|
.map_or(Ok(None), |r| r.map(Some))?
|
|
.unwrap_or("nil");
|
|
// todo-type is an unquoted string either todo, done, or nil
|
|
match (todo_type, &rust.todo_keyword) {
|
|
("nil", None) => {}
|
|
("todo", Some((TodoKeywordType::Todo, _))) => {}
|
|
("done", Some((TodoKeywordType::Done, _))) => {}
|
|
(emacs_todo, rust_todo) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"(emacs != rust) {:?} != {:?}",
|
|
emacs_todo, rust_todo
|
|
));
|
|
}
|
|
};
|
|
|
|
// Compare title
|
|
let title = get_property(emacs, ":title")?;
|
|
match (title, rust.title.len()) {
|
|
(None, 0) => {}
|
|
(None, _) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Titles do not match (emacs != rust): {:?} != {:?}",
|
|
title, rust.title
|
|
))
|
|
}
|
|
(Some(title), _) => {
|
|
let title_status = title
|
|
.as_list()?
|
|
.iter()
|
|
.zip(rust.title.iter())
|
|
.map(|(emacs_child, rust_child)| {
|
|
compare_ast_node(source, emacs_child, rust_child.into())
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
child_status.push(artificial_diff_scope("title", title_status)?);
|
|
}
|
|
};
|
|
|
|
// Compare priority
|
|
let priority = get_property(emacs, ":priority")?;
|
|
match (priority, rust.priority_cookie) {
|
|
(None, None) => {}
|
|
(None, Some(_)) | (Some(_), None) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Priority cookie mismatch (emacs != rust) {:?} != {:?}",
|
|
priority, rust.priority_cookie
|
|
));
|
|
}
|
|
(Some(emacs_priority_cookie), Some(rust_priority_cookie)) => {
|
|
let emacs_priority_cookie =
|
|
emacs_priority_cookie.as_atom()?.parse::<PriorityCookie>()?;
|
|
if emacs_priority_cookie != rust_priority_cookie {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Priority cookie mismatch (emacs != rust) {:?} != {:?}",
|
|
emacs_priority_cookie, rust_priority_cookie
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compare archived
|
|
let archived = get_property(emacs, ":archivedp")?;
|
|
match (archived, rust.is_archived) {
|
|
(None, true) | (Some(_), false) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"archived mismatch (emacs != rust) {:?} != {:?}",
|
|
archived, rust.is_archived
|
|
));
|
|
}
|
|
(None, false) | (Some(_), true) => {}
|
|
}
|
|
|
|
// Compare commented
|
|
let commented = get_property(emacs, ":commentedp")?;
|
|
match (commented, rust.is_comment) {
|
|
(None, true) | (Some(_), false) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"commented mismatch (emacs != rust) {:?} != {:?}",
|
|
commented, rust.is_comment
|
|
));
|
|
}
|
|
(None, false) | (Some(_), true) => {}
|
|
}
|
|
|
|
// Compare raw-value
|
|
let raw_value = get_property_quoted_string(emacs, ":raw-value")?
|
|
.ok_or("Headlines should have :raw-value.")?;
|
|
let rust_raw_value = rust.get_raw_value();
|
|
if raw_value != rust_raw_value {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"raw-value mismatch (emacs != rust) {:?} != {:?}",
|
|
raw_value, rust_raw_value
|
|
));
|
|
}
|
|
|
|
// Compare footnote-section-p
|
|
let footnote_section = get_property_boolean(emacs, ":footnote-section-p")?;
|
|
if footnote_section != rust.is_footnote_section {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"footnote section mismatch (emacs != rust) {:?} != {:?}",
|
|
footnote_section, rust.is_footnote_section
|
|
));
|
|
}
|
|
|
|
// Compare scheduled
|
|
let scheduled = get_property(emacs, ":scheduled")?;
|
|
match (scheduled, &rust.scheduled) {
|
|
(None, None) => {}
|
|
(None, Some(_)) | (Some(_), None) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Scheduled mismatch (emacs != rust) {:?} != {:?}",
|
|
scheduled, rust.scheduled
|
|
));
|
|
}
|
|
(Some(emacs_child), Some(rust_child)) => {
|
|
let result = compare_ast_node(source, emacs_child, rust_child.into())?;
|
|
child_status.push(artificial_diff_scope("scheduled", vec![result])?);
|
|
}
|
|
}
|
|
|
|
// Compare deadline
|
|
let deadline = get_property(emacs, ":deadline")?;
|
|
match (deadline, &rust.deadline) {
|
|
(None, None) => {}
|
|
(None, Some(_)) | (Some(_), None) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Deadline mismatch (emacs != rust) {:?} != {:?}",
|
|
deadline, rust.deadline
|
|
));
|
|
}
|
|
(Some(emacs_child), Some(rust_child)) => {
|
|
let result = compare_ast_node(source, emacs_child, rust_child.into())?;
|
|
child_status.push(artificial_diff_scope("deadline", vec![result])?);
|
|
}
|
|
}
|
|
|
|
// Compare closed
|
|
let closed = get_property(emacs, ":closed")?;
|
|
match (closed, &rust.closed) {
|
|
(None, None) => {}
|
|
(None, Some(_)) | (Some(_), None) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Closed mismatch (emacs != rust) {:?} != {:?}",
|
|
closed, rust.closed
|
|
));
|
|
}
|
|
(Some(emacs_child), Some(rust_child)) => {
|
|
let result = compare_ast_node(source, emacs_child, rust_child.into())?;
|
|
child_status.push(artificial_diff_scope("closed", vec![result])?);
|
|
}
|
|
}
|
|
|
|
// TODO: Compare :pre-blank
|
|
|
|
// Compare section
|
|
let section_status = children
|
|
.iter()
|
|
.skip(2)
|
|
.zip(rust.children.iter())
|
|
.map(|(emacs_child, rust_child)| match rust_child {
|
|
DocumentElement::Heading(rust_heading) => {
|
|
compare_ast_node(source, emacs_child, rust_heading.into())
|
|
}
|
|
DocumentElement::Section(rust_section) => {
|
|
compare_ast_node(source, emacs_child, rust_section.into())
|
|
}
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
child_status.push(artificial_diff_scope("section", section_status)?);
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: child_status,
|
|
rust_source: rust.get_source(),
|
|
emacs_token: emacs,
|
|
}
|
|
.into())
|
|
}
|
|
|
|
fn get_tags_from_heading<'b, 's>(
|
|
emacs: &'b Token<'s>,
|
|
) -> Result<HashSet<String>, Box<dyn std::error::Error>> {
|
|
let tags = match get_property(emacs, ":tags")? {
|
|
Some(prop) => prop,
|
|
None => return Ok(HashSet::new()),
|
|
};
|
|
|
|
match tags.as_atom() {
|
|
Ok(val) => panic!("Unexpected value for tags: {:?}", val),
|
|
Err(_) => {}
|
|
};
|
|
let tags = {
|
|
let tags = tags.as_list()?;
|
|
let strings = tags
|
|
.iter()
|
|
.map(Token::as_atom)
|
|
.collect::<Result<Vec<&str>, _>>()?;
|
|
strings
|
|
.into_iter()
|
|
.map(unquote)
|
|
.collect::<Result<HashSet<String>, _>>()?
|
|
};
|
|
Ok(tags)
|
|
}
|
|
|
|
fn compare_paragraph<'b, 's>(
|
|
source: &'s str,
|
|
emacs: &'b Token<'s>,
|
|
rust: &'b Paragraph<'s>,
|
|
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
// 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) {
|
|
(None, _) => panic!("Emacs returned a list with no type."),
|
|
(Some("unordered"), PlainListType::Unordered) => {}
|
|
(Some("ordered"), PlainListType::Ordered) => {}
|
|
(Some("descriptive"), PlainListType::Descriptive) => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"List type mismatch (emacs != rust) {:?} != {:?}",
|
|
list_type, rust.list_type
|
|
));
|
|
}
|
|
}
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// Compare tag
|
|
let tag = get_property(emacs, ":tag")?;
|
|
match (tag, rust.tag.is_empty()) {
|
|
(None, true) => {}
|
|
(None, false) | (Some(_), true) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some("Mismatched tags".to_owned());
|
|
}
|
|
(Some(tag), false) => {
|
|
let tag_status = tag
|
|
.as_list()?
|
|
.iter()
|
|
.zip(rust.tag.iter())
|
|
.map(|(emacs_child, rust_child)| {
|
|
compare_ast_node(source, emacs_child, rust_child.into())
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
child_status.push(artificial_diff_scope("tag", tag_status)?);
|
|
}
|
|
};
|
|
|
|
// Compare contents
|
|
let contents_status = children
|
|
.iter()
|
|
.skip(2)
|
|
.zip(rust.children.iter())
|
|
.map(|(emacs_child, rust_child)| compare_ast_node(source, emacs_child, rust_child.into()))
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
child_status.push(artificial_diff_scope("contents", contents_status)?);
|
|
|
|
// 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!(
|
|
"Bullet mismatch (emacs != rust) {:?} != {:?}",
|
|
bullet, rust.bullet
|
|
));
|
|
}
|
|
|
|
// Compare counter
|
|
let counter = get_property_unquoted_atom(emacs, ":counter")?;
|
|
let counter: Option<PlainListItemCounter> = 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)
|
|
.map_or(Ok(None), |r| r.map(Some))?
|
|
.unwrap_or("nil");
|
|
match (checkbox, &rust.checkbox) {
|
|
("nil", None) => {}
|
|
("off", Some((CheckboxType::Off, _))) => {}
|
|
("trans", Some((CheckboxType::Trans, _))) => {}
|
|
("on", Some((CheckboxType::On, _))) => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Checkbox mismatch (emacs != rust) {:?} != {:?}",
|
|
checkbox, rust.checkbox
|
|
));
|
|
}
|
|
};
|
|
|
|
// Compare pre-blank
|
|
// :pre-blank appears to count the line breaks between "::" and the contents in a descriptive list. Oddly enough it does not count the spaces so I'm not quite sure what the value is.
|
|
let pre_blank = get_property_unquoted_atom(emacs, ":pre-blank")?;
|
|
let pre_blank: Option<PlainListItemPreBlank> = pre_blank
|
|
.map(|val| val.parse())
|
|
.map_or(Ok(None), |r| r.map(Some))?;
|
|
if pre_blank.unwrap_or(0) != rust.pre_blank {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Pre-blank mismatch (emacs != rust) {:?} != {:?}",
|
|
pre_blank, rust.pre_blank
|
|
));
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
// Compare type
|
|
let special_block_type =
|
|
get_property_quoted_string(emacs, ":type")?.ok_or("Special blocks should have a name.")?;
|
|
if special_block_type != rust.block_type {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
special_block_type, rust.block_type
|
|
));
|
|
}
|
|
|
|
// Compare parameters
|
|
let parameters = get_property_quoted_string(emacs, ":parameters")?;
|
|
match (parameters.as_ref(), rust.parameters) {
|
|
(None, None) => {}
|
|
(Some(emacs_parameters), Some(rust_parameters)) if emacs_parameters == rust_parameters => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Parameters mismatch (emacs != rust) {:?} != {:?}",
|
|
parameters, rust.parameters
|
|
));
|
|
}
|
|
}
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
// Compare block-name
|
|
let block_name = get_property_quoted_string(emacs, ":block-name")?
|
|
.ok_or("Dynamic blocks should have a name.")?;
|
|
if block_name != rust.block_name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
block_name, rust.block_name
|
|
));
|
|
}
|
|
|
|
// Compare arguments
|
|
let parameters = get_property_quoted_string(emacs, ":arguments")?;
|
|
match (parameters.as_ref(), rust.parameters) {
|
|
(None, None) => {}
|
|
(Some(emacs_parameters), Some(rust_parameters)) if emacs_parameters == rust_parameters => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Parameters mismatch (emacs != rust) {:?} != {:?}",
|
|
parameters, rust.parameters
|
|
));
|
|
}
|
|
}
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
// TODO: Compare :pre-blank
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
// Compare label
|
|
let label = get_property_quoted_string(emacs, ":label")?
|
|
.ok_or("Footnote definitions should have a name.")?;
|
|
if label != rust.label {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Label mismatch (emacs != rust) {:?} != {:?}",
|
|
label, rust.label
|
|
));
|
|
}
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// Compare value
|
|
let value =
|
|
get_property_quoted_string(emacs, ":value")?.ok_or("Comments should have a value.")?;
|
|
let rust_value = rust.get_value();
|
|
if value != rust_value {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Value mismatch (emacs != rust) {:?} != {:?}",
|
|
value, rust_value
|
|
));
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
// Compare drawer-name
|
|
let drawer_name =
|
|
get_property_quoted_string(emacs, ":drawer-name")?.ok_or("Drawers should have a name.")?;
|
|
if drawer_name != rust.drawer_name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Drawer name mismatch (emacs != rust) {:?} != {:?}",
|
|
drawer_name, rust.drawer_name
|
|
));
|
|
}
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut child_status = Vec::new();
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// Compare key
|
|
let key =
|
|
get_property_quoted_string(emacs, ":key")?.ok_or("Node properties should have a key.")?;
|
|
if key != rust.property_name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Key mismatch (emacs != rust) {:?} != {:?}",
|
|
key, rust.property_name
|
|
));
|
|
}
|
|
|
|
// Compare value
|
|
let value = get_property_quoted_string(emacs, ":value")?;
|
|
match (value.as_ref(), rust.value) {
|
|
(None, None) => {}
|
|
(Some(emacs_value), Some(rust_value)) if emacs_value == rust_value => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Value mismatch (emacs != rust) {:?} != {:?}",
|
|
value, rust.value
|
|
));
|
|
}
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
// Compare formulas
|
|
//
|
|
// :tblfm is either nil or a list () filled with quoted strings containing the value for any tblfm keywords at the end of the table.
|
|
let emacs_formulas = get_property(emacs, ":tblfm")?;
|
|
if let Some(emacs_formulas) = emacs_formulas {
|
|
let emacs_formulas = emacs_formulas.as_list()?;
|
|
if emacs_formulas.len() != rust.formulas.len() {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Formulas do not match (emacs != rust): {:?} != {:?}",
|
|
emacs_formulas, rust.formulas
|
|
))
|
|
} else {
|
|
let atoms = emacs_formulas
|
|
.into_iter()
|
|
.map(Token::as_atom)
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
let unquoted = atoms
|
|
.into_iter()
|
|
.map(unquote)
|
|
.collect::<Result<BTreeSet<_>, _>>()?;
|
|
for kw in &rust.formulas {
|
|
if !unquoted.contains(kw.value) {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!("Could not find formula in emacs: {}", kw.value))
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if !rust.formulas.is_empty() {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Formulas do not match (emacs != rust): {:?} != {:?}",
|
|
emacs_formulas, rust.formulas
|
|
))
|
|
}
|
|
}
|
|
|
|
// Compare type
|
|
let table_type = get_property_unquoted_atom(emacs, ":type")?.expect("Table should have a type");
|
|
if table_type != "org" {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Table type mismatch (emacs != rust) {:?} != {:?}",
|
|
table_type, "org"
|
|
));
|
|
}
|
|
|
|
// Compare value
|
|
let value = get_property(emacs, ":value")?;
|
|
if value.is_some() {
|
|
// I don't know what :value is for, but it seems to always be nil. This is here to alert me to value being non-nil so I can investigate.
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!("Non-nil value {:?}", value))
|
|
}
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// Compare type
|
|
let row_type = get_property_unquoted_atom(emacs, ":type")?;
|
|
let rust_row_type = rust.get_type();
|
|
match (row_type, &rust_row_type) {
|
|
(Some("standard"), TableRowType::Standard) => {}
|
|
(Some("rule"), TableRowType::Rule) => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Type mismatch (emacs != rust) {:?} != {:?}",
|
|
row_type, rust_row_type
|
|
));
|
|
}
|
|
}
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let child_status = Vec::new();
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
for (_emacs_child, _rust_child) in children.iter().skip(2).zip(rust.children.iter()) {}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
for (_emacs_child, _rust_child) in children.iter().skip(2).zip(rust.children.iter()) {}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
// Compare value
|
|
let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new());
|
|
if contents != rust.contents {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Value mismatch (emacs != rust) {:?} != {:?}",
|
|
contents, rust.contents
|
|
));
|
|
}
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error + 's>> {
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
// Compare value
|
|
let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new());
|
|
if contents != rust.contents {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Value mismatch (emacs != rust) {:?} != {:?}",
|
|
contents, rust.contents
|
|
));
|
|
}
|
|
|
|
// Compare switches
|
|
let switches = get_property_quoted_string(emacs, ":switches")?;
|
|
match (switches.as_ref().map(String::as_str), rust.switches) {
|
|
(None, None) => {}
|
|
(Some(""), None) => {}
|
|
(None, Some("")) => {
|
|
unreachable!("The organic parser would return a None instead of an empty string.");
|
|
}
|
|
(Some(e), Some(r)) if e == r => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Switches mismatch (emacs != rust) {:?} != {:?}",
|
|
switches, rust.switches
|
|
));
|
|
}
|
|
}
|
|
|
|
// Compare number-lines
|
|
let number_lines = get_property(emacs, ":number-lines")?;
|
|
match (number_lines, &rust.number_lines) {
|
|
(None, None) => {}
|
|
(Some(number_lines), Some(rust_number_lines)) => {
|
|
let token_list = number_lines.as_list()?;
|
|
let number_type = token_list
|
|
.get(0)
|
|
.map(Token::as_atom)
|
|
.map_or(Ok(None), |r| r.map(Some))?
|
|
.ok_or(":number-lines should have a type.")?;
|
|
let number_value = token_list
|
|
.get(2)
|
|
.map(Token::as_atom)
|
|
.map_or(Ok(None), |r| r.map(Some))?
|
|
.map(|val| val.parse::<LineNumber>())
|
|
.map_or(Ok(None), |r| r.map(Some))?
|
|
.ok_or(":number-lines should have a value.")?;
|
|
match (number_type, number_value, rust_number_lines) {
|
|
("new", emacs_val, SwitchNumberLines::New(rust_val)) if emacs_val == *rust_val => {}
|
|
("continued", emacs_val, SwitchNumberLines::Continued(rust_val))
|
|
if emacs_val == *rust_val => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Number lines mismatch (emacs != rust) {:?} != {:?}",
|
|
number_lines, rust.number_lines
|
|
));
|
|
}
|
|
}
|
|
}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Number lines mismatch (emacs != rust) {:?} != {:?}",
|
|
number_lines, rust.number_lines
|
|
));
|
|
}
|
|
};
|
|
|
|
// Compare preserve-indent
|
|
let preserve_indent: Option<CharOffsetInLine> =
|
|
get_property_numeric(emacs, ":preserve-indent")?;
|
|
if preserve_indent != rust.preserve_indent {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Prserve indent mismatch (emacs != rust) {:?} != {:?}",
|
|
preserve_indent, rust.preserve_indent
|
|
));
|
|
}
|
|
|
|
// Compare retain-labels
|
|
// retain-labels is t by default, nil if -r is set, or a number if -k and -r is set.
|
|
let retain_labels = get_property_unquoted_atom(emacs, ":retain-labels")?;
|
|
if let Some(retain_labels) = retain_labels {
|
|
if retain_labels == "t" {
|
|
match rust.retain_labels {
|
|
RetainLabels::Yes => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Retain labels mismatch (emacs != rust) {:?} != {:?}",
|
|
retain_labels, rust.retain_labels
|
|
));
|
|
}
|
|
}
|
|
} else {
|
|
let retain_labels: CharOffsetInLine = get_property_numeric(emacs, ":retain-labels")?.expect("Cannot be None or else the earlier get_property_unquoted_atom would have been None.");
|
|
match (retain_labels, &rust.retain_labels) {
|
|
(e, RetainLabels::Keep(r)) if e == *r => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Retain labels mismatch (emacs != rust) {:?} != {:?}",
|
|
retain_labels, rust.retain_labels
|
|
));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
match rust.retain_labels {
|
|
RetainLabels::No => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Retain labels mismatch (emacs != rust) {:?} != {:?}",
|
|
retain_labels, rust.retain_labels
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compare use-labels
|
|
let use_labels = get_property_boolean(emacs, ":use-labels")?;
|
|
if use_labels != rust.use_labels {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Use labels mismatch (emacs != rust) {:?} != {:?}",
|
|
use_labels, rust.use_labels
|
|
));
|
|
}
|
|
|
|
// Compare label-fmt
|
|
let label_format = get_property_quoted_string(emacs, ":label-fmt")?;
|
|
match (label_format.as_ref(), rust.label_format) {
|
|
(None, None) => {}
|
|
(Some(emacs_label_format), Some(rust_label_format))
|
|
if emacs_label_format == rust_label_format => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Label format mismatch (emacs != rust) {:?} != {:?}",
|
|
label_format, rust.label_format
|
|
));
|
|
}
|
|
}
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
|
|
// Compare type
|
|
let export_type = get_property_quoted_string(emacs, ":type")?;
|
|
let rust_export_type = rust.get_export_type();
|
|
if export_type != rust_export_type {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Export type mismatch (emacs != rust) {:?} != {:?}",
|
|
export_type, rust.export_type
|
|
));
|
|
}
|
|
|
|
// Compare value
|
|
let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new());
|
|
if contents != rust.contents {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Value mismatch (emacs != rust) {:?} != {:?}",
|
|
contents, rust.contents
|
|
));
|
|
}
|
|
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error + 's>> {
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
// Compare language
|
|
let language = get_property_quoted_string(emacs, ":language")?;
|
|
if language.as_ref().map(String::as_str) != rust.language {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Language mismatch (emacs != rust) {:?} != {:?}",
|
|
language, rust.language
|
|
));
|
|
}
|
|
|
|
// Compare value
|
|
let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new());
|
|
if contents != rust.contents {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Value mismatch (emacs != rust) {:?} != {:?}",
|
|
contents, rust.contents
|
|
));
|
|
}
|
|
|
|
// Compare switches
|
|
let switches = get_property_quoted_string(emacs, ":switches")?;
|
|
match (switches.as_ref().map(String::as_str), rust.switches) {
|
|
(None, None) => {}
|
|
(Some(""), None) => {}
|
|
(None, Some("")) => {
|
|
unreachable!("The organic parser would return a None instead of an empty string.");
|
|
}
|
|
(Some(e), Some(r)) if e == r => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Switches mismatch (emacs != rust) {:?} != {:?}",
|
|
switches, rust.switches
|
|
));
|
|
}
|
|
}
|
|
|
|
// Compare parameters
|
|
let parameters = get_property_quoted_string(emacs, ":parameters")?;
|
|
match (parameters.as_ref().map(String::as_str), rust.parameters) {
|
|
(None, None) => {}
|
|
(Some(""), None) => {}
|
|
(None, Some("")) => {
|
|
unreachable!("The organic parser would return a None instead of an empty string.");
|
|
}
|
|
(Some(e), Some(r)) if e == r => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Parameters mismatch (emacs != rust) {:?} != {:?}",
|
|
parameters, rust.parameters
|
|
));
|
|
}
|
|
}
|
|
|
|
// Compare number-lines
|
|
let number_lines = get_property(emacs, ":number-lines")?;
|
|
match (number_lines, &rust.number_lines) {
|
|
(None, None) => {}
|
|
(Some(number_lines), Some(rust_number_lines)) => {
|
|
let token_list = number_lines.as_list()?;
|
|
let number_type = token_list
|
|
.get(0)
|
|
.map(Token::as_atom)
|
|
.map_or(Ok(None), |r| r.map(Some))?
|
|
.ok_or(":number-lines should have a type.")?;
|
|
let number_value = token_list
|
|
.get(2)
|
|
.map(Token::as_atom)
|
|
.map_or(Ok(None), |r| r.map(Some))?
|
|
.map(|val| val.parse::<LineNumber>())
|
|
.map_or(Ok(None), |r| r.map(Some))?
|
|
.ok_or(":number-lines should have a value.")?;
|
|
match (number_type, number_value, rust_number_lines) {
|
|
("new", emacs_val, SwitchNumberLines::New(rust_val)) if emacs_val == *rust_val => {}
|
|
("continued", emacs_val, SwitchNumberLines::Continued(rust_val))
|
|
if emacs_val == *rust_val => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Number lines mismatch (emacs != rust) {:?} != {:?}",
|
|
number_lines, rust.number_lines
|
|
));
|
|
}
|
|
}
|
|
}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Number lines mismatch (emacs != rust) {:?} != {:?}",
|
|
number_lines, rust.number_lines
|
|
));
|
|
}
|
|
};
|
|
|
|
// Compare preserve-indent
|
|
let preserve_indent: Option<CharOffsetInLine> =
|
|
get_property_numeric(emacs, ":preserve-indent")?;
|
|
if preserve_indent != rust.preserve_indent {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Prserve indent mismatch (emacs != rust) {:?} != {:?}",
|
|
preserve_indent, rust.preserve_indent
|
|
));
|
|
}
|
|
|
|
// Compare retain-labels
|
|
// retain-labels is t by default, nil if -r is set, or a number if -k and -r is set.
|
|
let retain_labels = get_property_unquoted_atom(emacs, ":retain-labels")?;
|
|
if let Some(retain_labels) = retain_labels {
|
|
if retain_labels == "t" {
|
|
match rust.retain_labels {
|
|
RetainLabels::Yes => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Retain labels mismatch (emacs != rust) {:?} != {:?}",
|
|
retain_labels, rust.retain_labels
|
|
));
|
|
}
|
|
}
|
|
} else {
|
|
let retain_labels: CharOffsetInLine = get_property_numeric(emacs, ":retain-labels")?.expect("Cannot be None or else the earlier get_property_unquoted_atom would have been None.");
|
|
match (retain_labels, &rust.retain_labels) {
|
|
(e, RetainLabels::Keep(r)) if e == *r => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Retain labels mismatch (emacs != rust) {:?} != {:?}",
|
|
retain_labels, rust.retain_labels
|
|
));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
match rust.retain_labels {
|
|
RetainLabels::No => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Retain labels mismatch (emacs != rust) {:?} != {:?}",
|
|
retain_labels, rust.retain_labels
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compare use-labels
|
|
let use_labels = get_property_boolean(emacs, ":use-labels")?;
|
|
if use_labels != rust.use_labels {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Use labels mismatch (emacs != rust) {:?} != {:?}",
|
|
use_labels, rust.use_labels
|
|
));
|
|
}
|
|
|
|
// Compare label-fmt
|
|
let label_format = get_property_quoted_string(emacs, ":label-fmt")?;
|
|
match (label_format.as_ref(), rust.label_format) {
|
|
(None, None) => {}
|
|
(Some(emacs_label_format), Some(rust_label_format))
|
|
if emacs_label_format == rust_label_format => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Label format mismatch (emacs != rust) {:?} != {:?}",
|
|
label_format, rust.label_format
|
|
));
|
|
}
|
|
}
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// Compare value
|
|
let value = get_property(emacs, ":value")?;
|
|
match value {
|
|
Some(e) => {
|
|
let result = compare_ast_node(_source, e, (&rust.timestamp).into())?;
|
|
child_status.push(artificial_diff_scope("value", vec![result])?);
|
|
}
|
|
None => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Value mismatch (emacs != rust) {:?} != {:?}",
|
|
value, rust.timestamp
|
|
));
|
|
}
|
|
}
|
|
|
|
// Compare duration
|
|
let duration = get_property_quoted_string(emacs, ":duration")?;
|
|
if duration.as_ref().map(String::as_str) != rust.duration {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Duration mismatch (emacs != rust) {:?} != {:?}",
|
|
duration, rust.duration
|
|
));
|
|
}
|
|
|
|
// Compare status
|
|
let status = get_property_unquoted_atom(emacs, ":status")?;
|
|
match (status, &rust.status) {
|
|
(Some("running"), ClockStatus::Running) => {}
|
|
(Some("closed"), ClockStatus::Closed) => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Status mismatch (emacs != rust) {:?} != {:?}",
|
|
status, rust.status
|
|
));
|
|
}
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
// Compare value
|
|
let value = get_property_quoted_string(emacs, ":value")?;
|
|
if value.as_ref().map(String::as_str) != Some(rust.value) {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Value mismatch (emacs != rust) {:?} != {:?}",
|
|
value, rust.value
|
|
));
|
|
}
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let mut child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// Compare scheduled
|
|
let scheduled = get_property(emacs, ":scheduled")?;
|
|
match (scheduled, &rust.scheduled) {
|
|
(None, None) => {}
|
|
(None, Some(_)) | (Some(_), None) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Scheduled mismatch (emacs != rust) {:?} != {:?}",
|
|
scheduled, rust.scheduled
|
|
));
|
|
}
|
|
(Some(emacs_child), Some(rust_child)) => {
|
|
let result = compare_ast_node(source, emacs_child, rust_child.into())?;
|
|
child_status.push(artificial_diff_scope("scheduled", vec![result])?);
|
|
}
|
|
}
|
|
|
|
// Compare deadline
|
|
let deadline = get_property(emacs, ":deadline")?;
|
|
match (deadline, &rust.deadline) {
|
|
(None, None) => {}
|
|
(None, Some(_)) | (Some(_), None) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Deadline mismatch (emacs != rust) {:?} != {:?}",
|
|
deadline, rust.deadline
|
|
));
|
|
}
|
|
(Some(emacs_child), Some(rust_child)) => {
|
|
let result = compare_ast_node(source, emacs_child, rust_child.into())?;
|
|
child_status.push(artificial_diff_scope("deadline", vec![result])?);
|
|
}
|
|
}
|
|
|
|
// Compare closed
|
|
let closed = get_property(emacs, ":closed")?;
|
|
match (closed, &rust.closed) {
|
|
(None, None) => {}
|
|
(None, Some(_)) | (Some(_), None) => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Closed mismatch (emacs != rust) {:?} != {:?}",
|
|
closed, rust.closed
|
|
));
|
|
}
|
|
(Some(emacs_child), Some(rust_child)) => {
|
|
let result = compare_ast_node(source, emacs_child, rust_child.into())?;
|
|
child_status.push(artificial_diff_scope("closed", vec![result])?);
|
|
}
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
// Compare value
|
|
let value = get_property_quoted_string(emacs, ":value")?
|
|
.ok_or("Fixed width area should have a value.")?;
|
|
let rust_value = rust.get_value();
|
|
if value != rust_value {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Value mismatch (emacs != rust) {:?} != {:?}",
|
|
value, rust_value
|
|
));
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
let key = unquote(
|
|
get_property(emacs, ":key")?
|
|
.ok_or("Emacs keywords should have a :key")?
|
|
.as_atom()?,
|
|
)?;
|
|
if key != rust.key.to_uppercase() {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Mismatchs keyword keys (emacs != rust) {:?} != {:?}",
|
|
key, rust.key
|
|
))
|
|
}
|
|
let value = unquote(
|
|
get_property(emacs, ":value")?
|
|
.ok_or("Emacs keywords should have a :value")?
|
|
.as_atom()?,
|
|
)?;
|
|
if value != rust.value {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Mismatchs keyword values (emacs != rust) {:?} != {:?}",
|
|
value, rust.value
|
|
))
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
// Compare value
|
|
let value = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new());
|
|
if value != rust.value {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Value mismatch (emacs != rust) {:?} != {:?}",
|
|
value, rust.value
|
|
))
|
|
}
|
|
|
|
// Compare call
|
|
let call = get_property_quoted_string(emacs, ":call")?;
|
|
if call.as_ref().map(String::as_str) != rust.call {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Call mismatch (emacs != rust) {:?} != {:?}",
|
|
call, rust.call
|
|
))
|
|
}
|
|
|
|
// Compare arguments
|
|
let arguments = get_property_quoted_string(emacs, ":arguments")?;
|
|
if arguments.as_ref().map(String::as_str) != rust.arguments {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Arguments mismatch (emacs != rust) {:?} != {:?}",
|
|
arguments, rust.arguments
|
|
))
|
|
}
|
|
|
|
// Compare inside header
|
|
let inside_header = get_property_quoted_string(emacs, ":inside-header")?;
|
|
if inside_header.as_ref().map(String::as_str) != rust.inside_header {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Inside header mismatch (emacs != rust) {:?} != {:?}",
|
|
inside_header, rust.inside_header
|
|
))
|
|
}
|
|
|
|
// Compare end header
|
|
let end_header = get_property_quoted_string(emacs, ":end-header")?;
|
|
if end_header.as_ref().map(String::as_str) != rust.end_header {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"End header mismatch (emacs != rust) {:?} != {:?}",
|
|
end_header, rust.end_header
|
|
))
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let child_status = Vec::new();
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// TODO: Compare :caption
|
|
// Compare name
|
|
let name = get_property_quoted_string(emacs, ":name")?;
|
|
if name.as_ref().map(String::as_str) != rust.name {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Name mismatch (emacs != rust) {:?} != {:?}",
|
|
name, rust.name
|
|
));
|
|
}
|
|
|
|
// Compare value
|
|
let value = get_property_quoted_string(emacs, ":value")?;
|
|
if value.as_ref().map(String::as_str) != Some(rust.value) {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Value mismatch (emacs != rust) {:?} != {:?}",
|
|
value, rust.value
|
|
));
|
|
}
|
|
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
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
|
|
.get(0)
|
|
.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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut child_status = Vec::new();
|
|
let mut message = None;
|
|
|
|
if let Some((new_status, new_message)) = compare_properties!(emacs)? {
|
|
this_status = new_status;
|
|
message = new_message;
|
|
}
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let this_status = DiffStatus::Good;
|
|
let mut child_status = Vec::new();
|
|
let message = None;
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let this_status = DiffStatus::Good;
|
|
let mut child_status = Vec::new();
|
|
let message = None;
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
if let Some((new_status, new_message)) = compare_properties!(
|
|
emacs,
|
|
rust,
|
|
(
|
|
EmacsField::Required(":value"),
|
|
|r| Some(r.contents),
|
|
compare_property_quoted_string
|
|
),
|
|
(
|
|
EmacsField::Required(":value"),
|
|
|r| Some(r.contents),
|
|
compare_property_quoted_string
|
|
),
|
|
(
|
|
EmacsField::Required(":foo"),
|
|
compare_identity,
|
|
impl_compare_noop
|
|
)
|
|
)? {
|
|
this_status = new_status;
|
|
message = new_message;
|
|
}
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
if let Some((new_status, new_message)) = compare_properties!(
|
|
emacs,
|
|
rust,
|
|
(
|
|
EmacsField::Required(":value"),
|
|
|r| Some(r.contents),
|
|
compare_property_quoted_string
|
|
)
|
|
)? {
|
|
this_status = new_status;
|
|
message = new_message;
|
|
}
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let children = emacs.as_list()?;
|
|
let this_status = DiffStatus::Good;
|
|
let mut child_status = Vec::new();
|
|
let message = None;
|
|
|
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
|
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
|
}
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :type :path :format :raw-link :application :search-option
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :type :path :format :raw-link :application :search-option
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :value
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :type :path :format :raw-link :application :search-option
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :type :path :format :raw-link :application :search-option
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :key :value :args
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :name :latex :latex-math-p :html :ascii :latin1 :utf-8 :use-brackets-p
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :value
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :back-end :value
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :label :type
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :style :prefix :suffix
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :key :prefix :suffix
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :call :inside-header :arguments :end-header :value
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :language :value :parameters
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :value
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :value
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :use-brackets-p
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
|
let this_status = DiffStatus::Good;
|
|
let message = None;
|
|
|
|
// TODO: Compare :use-brackets-p
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
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<DiffEntry<'b, 's>, Box<dyn std::error::Error + 's>> {
|
|
let mut this_status = DiffStatus::Good;
|
|
let mut message = None;
|
|
|
|
// Compare type
|
|
let timestamp_type = get_property_unquoted_atom(emacs, ":type")?;
|
|
match (timestamp_type, &rust.timestamp_type) {
|
|
(Some("diary"), TimestampType::Diary) => {}
|
|
(Some("active"), TimestampType::Active) => {}
|
|
(Some("inactive"), TimestampType::Inactive) => {}
|
|
(Some("active-range"), TimestampType::ActiveRange) => {}
|
|
(Some("inactive-range"), TimestampType::InactiveRange) => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Timestamp type mismatch (emacs != rust) {:?} != {:?}",
|
|
timestamp_type, rust.timestamp_type
|
|
));
|
|
}
|
|
}
|
|
|
|
// Compare range-type
|
|
let range_type = get_property_unquoted_atom(emacs, ":range-type")?;
|
|
match (range_type, &rust.range_type) {
|
|
(Some("daterange"), TimestampRangeType::DateRange) => {}
|
|
(Some("timerange"), TimestampRangeType::TimeRange) => {}
|
|
(None, TimestampRangeType::None) => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Range type mismatch (emacs != rust) {:?} != {:?}",
|
|
range_type, rust.range_type
|
|
));
|
|
}
|
|
}
|
|
|
|
// Compare raw-value
|
|
let raw_value = get_property_quoted_string(emacs, ":raw-value")?
|
|
.ok_or("Timestamps should have a :raw-value.")?;
|
|
if raw_value != rust.get_raw_value() {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Raw value mismatch (emacs != rust) {:?} != {:?}",
|
|
raw_value,
|
|
rust.get_raw_value()
|
|
));
|
|
}
|
|
|
|
// Compare start
|
|
let year_start: Option<YearInner> = get_property_numeric(emacs, ":year-start")?;
|
|
let month_start: Option<MonthInner> = get_property_numeric(emacs, ":month-start")?;
|
|
let day_of_month_start: Option<DayOfMonthInner> = get_property_numeric(emacs, ":day-start")?;
|
|
let rust_year_start = rust.start.as_ref().map(Date::get_year).map(Year::get_value);
|
|
let rust_month_start = rust
|
|
.start
|
|
.as_ref()
|
|
.map(Date::get_month)
|
|
.map(Month::get_value);
|
|
let rust_day_of_month_start = rust
|
|
.start
|
|
.as_ref()
|
|
.map(Date::get_day_of_month)
|
|
.map(DayOfMonth::get_value);
|
|
if year_start != rust_year_start {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"year start mismatch (emacs != rust) {:?} != {:?}",
|
|
year_start, rust_year_start
|
|
));
|
|
}
|
|
if month_start != rust_month_start {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"month start mismatch (emacs != rust) {:?} != {:?}",
|
|
month_start, rust_month_start
|
|
));
|
|
}
|
|
if day_of_month_start != rust_day_of_month_start {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"day of month start mismatch (emacs != rust) {:?} != {:?}",
|
|
day_of_month_start, rust_day_of_month_start
|
|
));
|
|
}
|
|
|
|
// Compare end
|
|
let year_end: Option<YearInner> = get_property_numeric(emacs, ":year-end")?;
|
|
let month_end: Option<MonthInner> = get_property_numeric(emacs, ":month-end")?;
|
|
let day_of_month_end: Option<DayOfMonthInner> = get_property_numeric(emacs, ":day-end")?;
|
|
let rust_year_end = rust.end.as_ref().map(Date::get_year).map(Year::get_value);
|
|
let rust_month_end = rust.end.as_ref().map(Date::get_month).map(Month::get_value);
|
|
let rust_day_of_month_end = rust
|
|
.end
|
|
.as_ref()
|
|
.map(Date::get_day_of_month)
|
|
.map(DayOfMonth::get_value);
|
|
if year_end != rust_year_end {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"year end mismatch (emacs != rust) {:?} != {:?}",
|
|
year_end, rust_year_end
|
|
));
|
|
}
|
|
if month_end != rust_month_end {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"month end mismatch (emacs != rust) {:?} != {:?}",
|
|
month_end, rust_month_end
|
|
));
|
|
}
|
|
if day_of_month_end != rust_day_of_month_end {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"day of month end mismatch (emacs != rust) {:?} != {:?}",
|
|
day_of_month_end, rust_day_of_month_end
|
|
));
|
|
}
|
|
|
|
// Compare time start
|
|
let hour_start: Option<HourInner> = get_property_numeric(emacs, ":hour-start")?;
|
|
let minute_start: Option<MinuteInner> = get_property_numeric(emacs, ":minute-start")?;
|
|
let rust_hour_start = rust
|
|
.start_time
|
|
.as_ref()
|
|
.map(Time::get_hour)
|
|
.map(Hour::get_value);
|
|
let rust_minute_start = rust
|
|
.start_time
|
|
.as_ref()
|
|
.map(Time::get_minute)
|
|
.map(Minute::get_value);
|
|
if hour_start != rust_hour_start {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"hour start mismatch (emacs != rust) {:?} != {:?}",
|
|
hour_start, rust_hour_start
|
|
));
|
|
}
|
|
if minute_start != rust_minute_start {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"minute start mismatch (emacs != rust) {:?} != {:?}",
|
|
minute_start, rust_minute_start
|
|
));
|
|
}
|
|
|
|
// Compare time end
|
|
let hour_end: Option<HourInner> = get_property_numeric(emacs, ":hour-end")?;
|
|
let minute_end: Option<MinuteInner> = get_property_numeric(emacs, ":minute-end")?;
|
|
let rust_hour_end = rust
|
|
.end_time
|
|
.as_ref()
|
|
.map(Time::get_hour)
|
|
.map(Hour::get_value);
|
|
let rust_minute_end = rust
|
|
.end_time
|
|
.as_ref()
|
|
.map(Time::get_minute)
|
|
.map(Minute::get_value);
|
|
if hour_end != rust_hour_end {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"hour end mismatch (emacs != rust) {:?} != {:?}",
|
|
hour_end, rust_hour_end
|
|
));
|
|
}
|
|
if minute_end != rust_minute_end {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"minute end mismatch (emacs != rust) {:?} != {:?}",
|
|
minute_end, rust_minute_end
|
|
));
|
|
}
|
|
|
|
// Compare repeater
|
|
let repeater_type = get_property_unquoted_atom(emacs, ":repeater-type")?;
|
|
let repeater_value: Option<RepeaterWarningDelayValueType> =
|
|
get_property_numeric(emacs, ":repeater-value")?;
|
|
let repeater_unit = get_property_unquoted_atom(emacs, ":repeater-unit")?;
|
|
let rust_repeater_type = rust
|
|
.repeater
|
|
.as_ref()
|
|
.map(|repeater| &repeater.repeater_type);
|
|
let rust_repeater_value = rust.repeater.as_ref().map(|repeater| repeater.value);
|
|
let rust_repeater_unit = rust.repeater.as_ref().map(|repeater| &repeater.unit);
|
|
match (repeater_type, rust_repeater_type) {
|
|
(Some("cumulate"), Some(RepeaterType::Cumulative)) => {}
|
|
(Some("catch-up"), Some(RepeaterType::CatchUp)) => {}
|
|
(Some("restart"), Some(RepeaterType::Restart)) => {}
|
|
(None, None) => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Repeater type mismatch (emacs != rust) {:?} != {:?}",
|
|
repeater_type, rust_repeater_type
|
|
));
|
|
}
|
|
}
|
|
if repeater_value != rust_repeater_value {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Repeater value mismatch (emacs != rust) {:?} != {:?}",
|
|
repeater_value, rust_repeater_value
|
|
));
|
|
}
|
|
match (repeater_unit, rust_repeater_unit) {
|
|
(Some("hour"), Some(TimeUnit::Hour)) => {}
|
|
(Some("day"), Some(TimeUnit::Day)) => {}
|
|
(Some("week"), Some(TimeUnit::Week)) => {}
|
|
(Some("month"), Some(TimeUnit::Month)) => {}
|
|
(Some("year"), Some(TimeUnit::Year)) => {}
|
|
(None, None) => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Repeater unit mismatch (emacs != rust) {:?} != {:?}",
|
|
repeater_unit, rust_repeater_unit
|
|
));
|
|
}
|
|
}
|
|
|
|
// Compare warning_delay
|
|
let warning_delay_type = get_property_unquoted_atom(emacs, ":warning-type")?;
|
|
let warning_delay_value: Option<RepeaterWarningDelayValueType> =
|
|
get_property_numeric(emacs, ":warning-value")?;
|
|
let warning_delay_unit = get_property_unquoted_atom(emacs, ":warning-unit")?;
|
|
let rust_warning_delay_type = rust
|
|
.warning_delay
|
|
.as_ref()
|
|
.map(|warning_delay| &warning_delay.warning_delay_type);
|
|
let rust_warning_delay_value = rust
|
|
.warning_delay
|
|
.as_ref()
|
|
.map(|warning_delay| warning_delay.value);
|
|
let rust_warning_delay_unit = rust
|
|
.warning_delay
|
|
.as_ref()
|
|
.map(|warning_delay| &warning_delay.unit);
|
|
match (warning_delay_type, rust_warning_delay_type) {
|
|
(Some("all"), Some(WarningDelayType::All)) => {}
|
|
(Some("first"), Some(WarningDelayType::First)) => {}
|
|
(None, None) => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Warning delay type mismatch (emacs != rust) {:?} != {:?}",
|
|
warning_delay_type, rust_warning_delay_type
|
|
));
|
|
}
|
|
}
|
|
if warning_delay_value != rust_warning_delay_value {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Warning delay value mismatch (emacs != rust) {:?} != {:?}",
|
|
warning_delay_value, rust_warning_delay_value
|
|
));
|
|
}
|
|
match (warning_delay_unit, rust_warning_delay_unit) {
|
|
(Some("hour"), Some(TimeUnit::Hour)) => {}
|
|
(Some("day"), Some(TimeUnit::Day)) => {}
|
|
(Some("week"), Some(TimeUnit::Week)) => {}
|
|
(Some("month"), Some(TimeUnit::Month)) => {}
|
|
(Some("year"), Some(TimeUnit::Year)) => {}
|
|
(None, None) => {}
|
|
_ => {
|
|
this_status = DiffStatus::Bad;
|
|
message = Some(format!(
|
|
"Warning delay unit mismatch (emacs != rust) {:?} != {:?}",
|
|
warning_delay_unit, rust_warning_delay_unit
|
|
));
|
|
}
|
|
}
|
|
|
|
Ok(DiffResult {
|
|
status: this_status,
|
|
name: rust.get_elisp_name(),
|
|
message,
|
|
children: Vec::new(),
|
|
rust_source: rust.get_source(),
|
|
emacs_token: emacs,
|
|
}
|
|
.into())
|
|
}
|