Switch to returning ComparePropertiesResult.

This is to support returning lists of child results for properties that contain lists of ast nodes.
This commit is contained in:
Tom Alexander 2023-10-09 15:13:05 -04:00
parent c077d34933
commit f6155ecf93
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
3 changed files with 359 additions and 180 deletions

View File

@ -1,11 +1,15 @@
use std::fmt::Debug; use std::fmt::Debug;
use super::diff::artificial_diff_scope;
use super::diff::compare_ast_node;
use super::diff::DiffEntry;
use super::diff::DiffStatus; use super::diff::DiffStatus;
use super::sexp::unquote; use super::sexp::unquote;
use super::sexp::Token; use super::sexp::Token;
use super::util::get_property; use super::util::get_property;
use super::util::get_property_quoted_string; use super::util::get_property_quoted_string;
use super::util::get_property_unquoted_atom; use super::util::get_property_unquoted_atom;
use crate::types::AstNode;
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum EmacsField<'s> { pub(crate) enum EmacsField<'s> {
@ -14,23 +18,30 @@ pub(crate) enum EmacsField<'s> {
Optional(&'s str), Optional(&'s str),
} }
/// Do no comparison. #[derive(Debug)]
/// pub(crate) enum ComparePropertiesResult<'b, 's> {
/// This is for when you want to acknowledge that a field exists in the emacs token, but you do not have any validation for it when using the compare_properties!() macro. Ideally, this should be kept to a minimum since this represents untested values. NoChange,
#[allow(dead_code)] /// Return when you want the status for "this" node to change (as opposed to collecting child status).
pub(crate) fn compare_noop<'b, 's, 'x, R, RG>( SelfChange(DiffStatus, Option<String>),
_emacs: &'b Token<'s>, DiffEntry(DiffEntry<'b, 's>),
_rust_node: R, }
_emacs_field: &'x str,
_rust_value_getter: RG, /// Do no comparison.
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> { ///
Ok(None) /// This is for when you want to acknowledge that a field exists in the emacs token, but you do not have any validation for it when using the compare_properties!() macro. Ideally, this should be kept to a minimum since this represents untested values.
pub(crate) fn compare_noop<'b, 's, 'x, R, RG>(
_source: &'s str,
_emacs: &'b Token<'s>,
_rust_node: R,
_emacs_field: &'x str,
_rust_value_getter: RG,
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
Ok(ComparePropertiesResult::NoChange)
} }
/// Do no comparison. /// Do no comparison.
/// ///
/// This is for when you want to acknowledge that a field exists in the emacs token, but you do not have any validation for it when using the compare_properties!() macro. Ideally, this should be kept to a minimum since this represents untested values. /// This is for when you want to acknowledge that a field exists in the emacs token, but you do not have any validation for it when using the compare_properties!() macro. Ideally, this should be kept to a minimum since this represents untested values.
#[allow(dead_code)]
pub(crate) fn compare_identity() -> () { pub(crate) fn compare_identity() -> () {
() ()
} }
@ -39,11 +50,12 @@ pub(crate) fn compare_identity() -> () {
/// ///
/// This is usually used for fields which, in my testing, are always nil. Using this compare function instead of simply doing a compare_noop will enable us to be alerted when we finally come across an org-mode document that has a value other than nil for the property. /// This is usually used for fields which, in my testing, are always nil. Using this compare function instead of simply doing a compare_noop will enable us to be alerted when we finally come across an org-mode document that has a value other than nil for the property.
pub(crate) fn compare_property_always_nil<'b, 's, 'x, R, RG>( pub(crate) fn compare_property_always_nil<'b, 's, 'x, R, RG>(
_source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
_rust_node: R, _rust_node: R,
emacs_field: &'x str, emacs_field: &'x str,
_rust_value_getter: RG, _rust_value_getter: RG,
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> { ) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
let value = get_property(emacs, emacs_field)?; let value = get_property(emacs, emacs_field)?;
if value.is_some() { if value.is_some() {
let this_status = DiffStatus::Bad; let this_status = DiffStatus::Bad;
@ -51,9 +63,9 @@ pub(crate) fn compare_property_always_nil<'b, 's, 'x, R, RG>(
"{} was expected to always be nil: {:?}", "{} was expected to always be nil: {:?}",
emacs_field, value emacs_field, value
)); ));
Ok(Some((this_status, message))) Ok(ComparePropertiesResult::SelfChange(this_status, message))
} else { } else {
Ok(None) Ok(ComparePropertiesResult::NoChange)
} }
} }
@ -65,11 +77,12 @@ pub(crate) fn compare_property_quoted_string<
RV: AsRef<str> + std::fmt::Debug, RV: AsRef<str> + std::fmt::Debug,
RG: Fn(R) -> Option<RV>, RG: Fn(R) -> Option<RV>,
>( >(
_source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust_node: R, rust_node: R,
emacs_field: &'x str, emacs_field: &'x str,
rust_value_getter: RG, rust_value_getter: RG,
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> { ) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
let value = get_property_quoted_string(emacs, emacs_field)?; let value = get_property_quoted_string(emacs, emacs_field)?;
let rust_value = rust_value_getter(rust_node); let rust_value = rust_value_getter(rust_node);
if rust_value.as_ref().map(|s| s.as_ref()) != value.as_ref().map(String::as_str) { if rust_value.as_ref().map(|s| s.as_ref()) != value.as_ref().map(String::as_str) {
@ -78,18 +91,19 @@ pub(crate) fn compare_property_quoted_string<
"{} mismatch (emacs != rust) {:?} != {:?}", "{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value emacs_field, value, rust_value
)); ));
Ok(Some((this_status, message))) Ok(ComparePropertiesResult::SelfChange(this_status, message))
} else { } else {
Ok(None) Ok(ComparePropertiesResult::NoChange)
} }
} }
pub(crate) fn compare_property_unquoted_atom<'b, 's, 'x, R, RG: Fn(R) -> Option<&'s str>>( pub(crate) fn compare_property_unquoted_atom<'b, 's, 'x, R, RG: Fn(R) -> Option<&'s str>>(
_source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust_node: R, rust_node: R,
emacs_field: &'x str, emacs_field: &'x str,
rust_value_getter: RG, rust_value_getter: RG,
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> { ) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
let value = get_property_unquoted_atom(emacs, emacs_field)?; let value = get_property_unquoted_atom(emacs, emacs_field)?;
let rust_value = rust_value_getter(rust_node); let rust_value = rust_value_getter(rust_node);
if rust_value != value { if rust_value != value {
@ -98,9 +112,9 @@ pub(crate) fn compare_property_unquoted_atom<'b, 's, 'x, R, RG: Fn(R) -> Option<
"{} mismatch (emacs != rust) {:?} != {:?}", "{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value emacs_field, value, rust_value
)); ));
Ok(Some((this_status, message))) Ok(ComparePropertiesResult::SelfChange(this_status, message))
} else { } else {
Ok(None) Ok(ComparePropertiesResult::NoChange)
} }
} }
@ -113,11 +127,12 @@ pub(crate) fn compare_property_list_of_quoted_string<
RI: Iterator<Item = RV>, RI: Iterator<Item = RV>,
RG: Fn(R) -> Option<RI>, RG: Fn(R) -> Option<RI>,
>( >(
_source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust_node: R, rust_node: R,
emacs_field: &'x str, emacs_field: &'x str,
rust_value_getter: RG, rust_value_getter: RG,
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> { ) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
let value = get_property(emacs, emacs_field)? let value = get_property(emacs, emacs_field)?
.map(Token::as_list) .map(Token::as_list)
.map_or(Ok(None), |r| r.map(Some))?; .map_or(Ok(None), |r| r.map(Some))?;
@ -132,7 +147,7 @@ pub(crate) fn compare_property_list_of_quoted_string<
"{} mismatch (emacs != rust) {:?} != {:?}", "{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value emacs_field, value, rust_value
)); ));
return Ok(Some((this_status, message))); return Ok(ComparePropertiesResult::SelfChange(this_status, message));
} }
(Some(el), Some(rl)) if el.len() != rl.len() => { (Some(el), Some(rl)) if el.len() != rl.len() => {
let this_status = DiffStatus::Bad; let this_status = DiffStatus::Bad;
@ -140,7 +155,7 @@ pub(crate) fn compare_property_list_of_quoted_string<
"{} mismatch (emacs != rust) {:?} != {:?}", "{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value emacs_field, value, rust_value
)); ));
return Ok(Some((this_status, message))); return Ok(ComparePropertiesResult::SelfChange(this_status, message));
} }
(Some(el), Some(rl)) => { (Some(el), Some(rl)) => {
for (e, r) in el.iter().zip(rl) { for (e, r) in el.iter().zip(rl) {
@ -152,20 +167,21 @@ pub(crate) fn compare_property_list_of_quoted_string<
"{} mismatch (emacs != rust) {:?} != {:?}. Full list: {:?} != {:?}", "{} mismatch (emacs != rust) {:?} != {:?}. Full list: {:?} != {:?}",
emacs_field, e, r, value, rust_value emacs_field, e, r, value, rust_value
)); ));
return Ok(Some((this_status, message))); return Ok(ComparePropertiesResult::SelfChange(this_status, message));
} }
} }
} }
} }
Ok(None) Ok(ComparePropertiesResult::NoChange)
} }
pub(crate) fn compare_property_boolean<'b, 's, 'x, R, RG: Fn(R) -> bool>( pub(crate) fn compare_property_boolean<'b, 's, 'x, R, RG: Fn(R) -> bool>(
_source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust_node: R, rust_node: R,
emacs_field: &'x str, emacs_field: &'x str,
rust_value_getter: RG, rust_value_getter: RG,
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> { ) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
// get_property already converts nil to None. // get_property already converts nil to None.
let value = get_property(emacs, emacs_field)?.is_some(); let value = get_property(emacs, emacs_field)?.is_some();
let rust_value = rust_value_getter(rust_node); let rust_value = rust_value_getter(rust_node);
@ -175,8 +191,62 @@ pub(crate) fn compare_property_boolean<'b, 's, 'x, R, RG: Fn(R) -> bool>(
"{} mismatch (emacs != rust) {:?} != {:?}", "{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rust_value emacs_field, value, rust_value
)); ));
Ok(Some((this_status, message))) Ok(ComparePropertiesResult::SelfChange(this_status, message))
} else { } else {
Ok(None) Ok(ComparePropertiesResult::NoChange)
} }
} }
pub(crate) fn compare_property_list_of_ast_nodes<
'b,
's,
'x: 'b + 's,
R,
RV: std::fmt::Debug,
RI: Iterator<Item = RV>,
RG: Fn(R) -> Option<RI>,
>(
source: &'s str,
emacs: &'b Token<'s>,
rust_node: R,
emacs_field: &'x str,
rust_value_getter: RG,
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>>
where
AstNode<'b, 's>: From<RV>,
{
let value = get_property(emacs, emacs_field)?
.map(Token::as_list)
.map_or(Ok(None), |r| r.map(Some))?;
let rust_value = rust_value_getter(rust_node);
// TODO: Seems we are needlessly coverting to a vec here.
let rust_value: Option<Vec<RV>> = rust_value.map(|it| it.collect());
match (value, rust_value) {
(None, None) => {}
(None, rv @ Some(_)) | (Some(_), rv @ None) => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, value, rv
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
(Some(el), Some(rl)) if el.len() != rl.len() => {
let this_status = DiffStatus::Bad;
let message = Some(format!(
"{} mismatch (emacs != rust) {:?} != {:?}",
emacs_field, el, rl
));
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
}
(Some(el), Some(rl)) => {
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rl.len());
for (e, r) in el.iter().zip(rl) {
child_status.push(compare_ast_node(source, e, r.into())?);
}
let diff_scope = artificial_diff_scope(emacs_field, child_status)?;
return Ok(ComparePropertiesResult::DiffEntry(diff_scope));
}
}
Ok(ComparePropertiesResult::NoChange)
}

View File

@ -8,6 +8,7 @@ use super::compare_field::compare_identity;
use super::compare_field::compare_noop; use super::compare_field::compare_noop;
use super::compare_field::compare_property_always_nil; use super::compare_field::compare_property_always_nil;
use super::compare_field::compare_property_boolean; use super::compare_field::compare_property_boolean;
use super::compare_field::compare_property_list_of_ast_nodes;
use super::compare_field::compare_property_list_of_quoted_string; use super::compare_field::compare_property_list_of_quoted_string;
use super::compare_field::compare_property_quoted_string; use super::compare_field::compare_property_quoted_string;
use super::compare_field::compare_property_unquoted_atom; use super::compare_field::compare_property_unquoted_atom;
@ -23,6 +24,7 @@ use super::util::get_property_boolean;
use super::util::get_property_numeric; use super::util::get_property_numeric;
use super::util::get_property_quoted_string; use super::util::get_property_quoted_string;
use super::util::get_property_unquoted_atom; use super::util::get_property_unquoted_atom;
use crate::compare::compare_field::ComparePropertiesResult;
use crate::compare::compare_field::EmacsField; use crate::compare::compare_field::EmacsField;
use crate::compare::macros::compare_properties; use crate::compare::macros::compare_properties;
use crate::types::AngleLink; use crate::types::AngleLink;
@ -328,8 +330,8 @@ impl<'b, 's> DiffLayer<'b, 's> {
} }
} }
fn artificial_diff_scope<'b, 's>( pub(crate) fn artificial_diff_scope<'b, 's>(
name: &'static str, name: &'s str,
children: Vec<DiffEntry<'b, 's>>, children: Vec<DiffEntry<'b, 's>>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
Ok(DiffLayer { Ok(DiffLayer {
@ -2598,16 +2600,22 @@ fn compare_bold<'b, 's>(
&mut message, &mut message,
)?; )?;
if let Some((new_status, new_message)) = compare_properties!(emacs)? { for diff in compare_properties!(emacs) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -2632,16 +2640,22 @@ fn compare_italic<'b, 's>(
&mut message, &mut message,
)?; )?;
if let Some((new_status, new_message)) = compare_properties!(emacs)? { for diff in compare_properties!(emacs) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -2666,16 +2680,22 @@ fn compare_underline<'b, 's>(
&mut message, &mut message,
)?; )?;
if let Some((new_status, new_message)) = compare_properties!(emacs)? { for diff in compare_properties!(emacs) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -2683,16 +2703,18 @@ fn compare_underline<'b, 's>(
} }
fn compare_verbatim<'b, 's>( fn compare_verbatim<'b, 's>(
_source: &'s str, source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust: &'b Verbatim<'s>, rust: &'b Verbatim<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let mut this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let mut child_status = Vec::new();
let mut message = None; let mut message = None;
assert_no_children(emacs, &mut this_status, &mut message)?; assert_no_children(emacs, &mut this_status, &mut message)?;
if let Some((new_status, new_message)) = compare_properties!( for diff in compare_properties!(
source,
emacs, emacs,
rust, rust,
( (
@ -2700,16 +2722,22 @@ fn compare_verbatim<'b, 's>(
|r| Some(r.contents), |r| Some(r.contents),
compare_property_quoted_string compare_property_quoted_string
) )
)? { ) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -2717,16 +2745,18 @@ fn compare_verbatim<'b, 's>(
} }
fn compare_code<'b, 's>( fn compare_code<'b, 's>(
_source: &'s str, source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust: &'b Code<'s>, rust: &'b Code<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let mut this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let mut child_status = Vec::new();
let mut message = None; let mut message = None;
assert_no_children(emacs, &mut this_status, &mut message)?; assert_no_children(emacs, &mut this_status, &mut message)?;
if let Some((new_status, new_message)) = compare_properties!( for diff in compare_properties!(
source,
emacs, emacs,
rust, rust,
( (
@ -2734,16 +2764,22 @@ fn compare_code<'b, 's>(
|r| Some(r.contents), |r| Some(r.contents),
compare_property_quoted_string compare_property_quoted_string
) )
)? { ) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -2768,16 +2804,22 @@ fn compare_strike_through<'b, 's>(
&mut message, &mut message,
)?; )?;
if let Some((new_status, new_message)) = compare_properties!(emacs)? { for diff in compare_properties!(emacs) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -2802,7 +2844,8 @@ fn compare_regular_link<'b, 's>(
&mut message, &mut message,
)?; )?;
if let Some((new_status, new_message)) = compare_properties!( for diff in compare_properties!(
source,
emacs, emacs,
rust, rust,
( (
@ -2844,16 +2887,22 @@ fn compare_regular_link<'b, 's>(
|r| r.get_search_option(), |r| r.get_search_option(),
compare_property_quoted_string compare_property_quoted_string
) )
)? { ) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -2878,7 +2927,8 @@ fn compare_radio_link<'b, 's>(
&mut message, &mut message,
)?; )?;
if let Some((new_status, new_message)) = compare_properties!( for diff in compare_properties!(
source,
emacs, emacs,
rust, rust,
( (
@ -2911,16 +2961,22 @@ fn compare_radio_link<'b, 's>(
compare_identity, compare_identity,
compare_property_always_nil compare_property_always_nil
) )
)? { ) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -2945,7 +3001,8 @@ fn compare_radio_target<'b, 's>(
&mut message, &mut message,
)?; )?;
if let Some((new_status, new_message)) = compare_properties!( for diff in compare_properties!(
source,
emacs, emacs,
rust, rust,
( (
@ -2953,16 +3010,22 @@ fn compare_radio_target<'b, 's>(
|r| Some(r.value), |r| Some(r.value),
compare_property_quoted_string compare_property_quoted_string
) )
)? { ) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -2970,16 +3033,18 @@ fn compare_radio_target<'b, 's>(
} }
fn compare_plain_link<'b, 's>( fn compare_plain_link<'b, 's>(
_source: &'s str, source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust: &'b PlainLink<'s>, rust: &'b PlainLink<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let mut this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let mut child_status = Vec::new();
let mut message = None; let mut message = None;
assert_no_children(emacs, &mut this_status, &mut message)?; assert_no_children(emacs, &mut this_status, &mut message)?;
if let Some((new_status, new_message)) = compare_properties!( for diff in compare_properties!(
source,
emacs, emacs,
rust, rust,
( (
@ -3021,16 +3086,22 @@ fn compare_plain_link<'b, 's>(
|r| r.search_option, |r| r.search_option,
compare_property_quoted_string compare_property_quoted_string
) )
)? { ) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -3038,16 +3109,18 @@ fn compare_plain_link<'b, 's>(
} }
fn compare_angle_link<'b, 's>( fn compare_angle_link<'b, 's>(
_source: &'s str, source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust: &'b AngleLink<'s>, rust: &'b AngleLink<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let mut this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let mut child_status = Vec::new();
let mut message = None; let mut message = None;
assert_no_children(emacs, &mut this_status, &mut message)?; assert_no_children(emacs, &mut this_status, &mut message)?;
if let Some((new_status, new_message)) = compare_properties!( for diff in compare_properties!(
source,
emacs, emacs,
rust, rust,
( (
@ -3089,16 +3162,22 @@ fn compare_angle_link<'b, 's>(
|r| r.get_search_option(), |r| r.get_search_option(),
compare_property_quoted_string compare_property_quoted_string
) )
)? { ) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -3106,16 +3185,18 @@ fn compare_angle_link<'b, 's>(
} }
fn compare_org_macro<'b, 's>( fn compare_org_macro<'b, 's>(
_source: &'s str, source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust: &'b OrgMacro<'s>, rust: &'b OrgMacro<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let mut this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let mut child_status = Vec::new();
let mut message = None; let mut message = None;
assert_no_children(emacs, &mut this_status, &mut message)?; assert_no_children(emacs, &mut this_status, &mut message)?;
if let Some((new_status, new_message)) = compare_properties!( for diff in compare_properties!(
source,
emacs, emacs,
rust, rust,
( (
@ -3137,16 +3218,22 @@ fn compare_org_macro<'b, 's>(
}, },
compare_property_list_of_quoted_string compare_property_list_of_quoted_string
) )
)? { ) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -3154,16 +3241,18 @@ fn compare_org_macro<'b, 's>(
} }
fn compare_entity<'b, 's>( fn compare_entity<'b, 's>(
_source: &'s str, source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust: &'b Entity<'s>, rust: &'b Entity<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let mut this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let mut child_status = Vec::new();
let mut message = None; let mut message = None;
assert_no_children(emacs, &mut this_status, &mut message)?; assert_no_children(emacs, &mut this_status, &mut message)?;
if let Some((new_status, new_message)) = compare_properties!( for diff in compare_properties!(
source,
emacs, emacs,
rust, rust,
( (
@ -3207,16 +3296,22 @@ fn compare_entity<'b, 's>(
|r| r.use_brackets, |r| r.use_brackets,
compare_property_boolean compare_property_boolean
) )
)? { ) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -3224,16 +3319,18 @@ fn compare_entity<'b, 's>(
} }
fn compare_latex_fragment<'b, 's>( fn compare_latex_fragment<'b, 's>(
_source: &'s str, source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust: &'b LatexFragment<'s>, rust: &'b LatexFragment<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let mut this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let mut child_status = Vec::new();
let mut message = None; let mut message = None;
assert_no_children(emacs, &mut this_status, &mut message)?; assert_no_children(emacs, &mut this_status, &mut message)?;
if let Some((new_status, new_message)) = compare_properties!( for diff in compare_properties!(
source,
emacs, emacs,
rust, rust,
( (
@ -3241,16 +3338,22 @@ fn compare_latex_fragment<'b, 's>(
|r| Some(r.value), |r| Some(r.value),
compare_property_quoted_string compare_property_quoted_string
) )
)? { ) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -3258,16 +3361,18 @@ fn compare_latex_fragment<'b, 's>(
} }
fn compare_export_snippet<'b, 's>( fn compare_export_snippet<'b, 's>(
_source: &'s str, source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust: &'b ExportSnippet<'s>, rust: &'b ExportSnippet<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let mut this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let mut child_status = Vec::new();
let mut message = None; let mut message = None;
assert_no_children(emacs, &mut this_status, &mut message)?; assert_no_children(emacs, &mut this_status, &mut message)?;
if let Some((new_status, new_message)) = compare_properties!( for diff in compare_properties!(
source,
emacs, emacs,
rust, rust,
( (
@ -3280,16 +3385,22 @@ fn compare_export_snippet<'b, 's>(
|r| r.contents, |r| r.contents,
compare_property_quoted_string compare_property_quoted_string
) )
)? { ) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -3314,7 +3425,8 @@ fn compare_footnote_reference<'b, 's>(
&mut message, &mut message,
)?; )?;
if let Some((new_status, new_message)) = compare_properties!( for diff in compare_properties!(
source,
emacs, emacs,
rust, rust,
( (
@ -3330,9 +3442,15 @@ fn compare_footnote_reference<'b, 's>(
}), }),
compare_property_unquoted_atom compare_property_unquoted_atom
) )
)? { ) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
@ -3364,7 +3482,8 @@ fn compare_citation<'b, 's>(
&mut message, &mut message,
)?; )?;
if let Some((new_status, new_message)) = compare_properties!( for diff in compare_properties!(
source,
emacs, emacs,
rust, rust,
( (
@ -3374,24 +3493,30 @@ fn compare_citation<'b, 's>(
), ),
( (
EmacsField::Required(":prefix"), EmacsField::Required(":prefix"),
|r| r.prefix, |r| Some(r.prefix.iter()),
compare_property_unquoted_atom compare_property_list_of_ast_nodes
), ),
( (
EmacsField::Required(":suffix"), EmacsField::Required(":suffix"),
|r| r.suffix, |r| Some(r.suffix.iter()),
compare_property_unquoted_atom compare_property_list_of_ast_nodes
) )
)? { ) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }
@ -3399,16 +3524,18 @@ fn compare_citation<'b, 's>(
} }
fn compare_citation_reference<'b, 's>( fn compare_citation_reference<'b, 's>(
_source: &'s str, source: &'s str,
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust: &'b CitationReference<'s>, rust: &'b CitationReference<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let message = None; let mut child_status = Vec::new();
let mut message = None;
assert_no_children(emacs, &mut this_status, &mut message)?; assert_no_children(emacs, &mut this_status, &mut message)?;
if let Some((new_status, new_message)) = compare_properties!( for diff in compare_properties!(
source,
emacs, emacs,
rust, rust,
( (
@ -3418,24 +3545,30 @@ fn compare_citation_reference<'b, 's>(
), ),
( (
EmacsField::Required(":prefix"), EmacsField::Required(":prefix"),
|r| r.prefix, |r| Some(r.prefix.iter()),
compare_property_unquoted_atom compare_property_list_of_ast_nodes
), ),
( (
EmacsField::Required(":suffix"), EmacsField::Required(":suffix"),
|r| r.suffix, |r| Some(r.suffix.iter()),
compare_property_unquoted_atom compare_property_list_of_ast_nodes
) )
)? { ) {
this_status = new_status; match diff {
message = new_message; ComparePropertiesResult::NoChange => {}
ComparePropertiesResult::SelfChange(new_status, new_message) => {
this_status = new_status;
message = new_message
}
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
}
} }
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
name: rust.get_elisp_name(), name: rust.get_elisp_name(),
message, message,
children: Vec::new(), children: child_status,
rust_source: rust.get_source(), rust_source: rust.get_source(),
emacs_token: emacs, emacs_token: emacs,
} }

View File

@ -30,10 +30,9 @@
/// } /// }
/// ``` /// ```
macro_rules! compare_properties { macro_rules! compare_properties {
($emacs:expr, $rust:expr, $(($emacs_field:expr, $rust_value_getter:expr, $compare_fn: expr)),+) => { ($source:expr, $emacs:expr, $rust:expr, $(($emacs_field:expr, $rust_value_getter:expr, $compare_fn: expr)),+) => {
{ {
let mut this_status = DiffStatus::Good; let mut new_status = Vec::new();
let mut message: Option<String> = None;
let children = $emacs.as_list()?; let children = $emacs.as_list()?;
let attributes_child = children let attributes_child = children
.iter() .iter()
@ -44,10 +43,9 @@ macro_rules! compare_properties {
if emacs_keys.contains(":standard-properties") { if emacs_keys.contains(":standard-properties") {
emacs_keys.remove(":standard-properties"); emacs_keys.remove(":standard-properties");
} else { } else {
this_status = DiffStatus::Bad; new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
message = Some(format!(
"Emacs token lacks :standard-properties field.", "Emacs token lacks :standard-properties field.",
)); ))));
} }
$( $(
match $emacs_field { match $emacs_field {
@ -58,11 +56,10 @@ macro_rules! compare_properties {
emacs_keys.remove(name); emacs_keys.remove(name);
}, },
EmacsField::Required(name) => { EmacsField::Required(name) => {
this_status = DiffStatus::Bad; new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
message = Some(format!(
"Emacs token lacks required field: {}", "Emacs token lacks required field: {}",
name name
)); ))));
}, },
EmacsField::Optional(_name) => {}, EmacsField::Optional(_name) => {},
} }
@ -71,11 +68,10 @@ macro_rules! compare_properties {
if !emacs_keys.is_empty() { if !emacs_keys.is_empty() {
let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect(); let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect();
let unexpected_keys = unexpected_keys.join(", "); let unexpected_keys = unexpected_keys.join(", ");
this_status = DiffStatus::Bad; new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
message = Some(format!(
"Emacs token had extra field(s): {}", "Emacs token had extra field(s): {}",
unexpected_keys unexpected_keys
)); ))));
} }
$( $(
@ -87,33 +83,23 @@ macro_rules! compare_properties {
name name
}, },
}; };
let result = $compare_fn($emacs, $rust, emacs_name, $rust_value_getter)?; let result = $compare_fn($source, $emacs, $rust, emacs_name, $rust_value_getter)?;
match result { match result {
Some((DiffStatus::Good, _)) => unreachable!("No comparison functions should return Some() when DiffStatus is good."), ComparePropertiesResult::SelfChange(DiffStatus::Good, _) => unreachable!("No comparison functions should return SelfChange() when DiffStatus is good."),
Some((status, msg)) => { ComparePropertiesResult::NoChange => {},
this_status = status; result => {
message = msg; new_status.push(result);
}, }
_ => {}
} }
)+ )+
match this_status { new_status
DiffStatus::Good => {
let result: Result<_, Box<dyn std::error::Error>> = Ok(None);
result
},
_ => {
Ok(Some((this_status, message)))
}
}
} }
}; };
// Default case for when there are no expected properties except for :standard-properties // Default case for when there are no expected properties except for :standard-properties
($emacs:expr) => { ($emacs:expr) => {
{ {
let mut this_status = DiffStatus::Good; let mut new_status = Vec::new();
let mut message: Option<String> = None;
let children = $emacs.as_list()?; let children = $emacs.as_list()?;
let attributes_child = children let attributes_child = children
.iter() .iter()
@ -124,30 +110,20 @@ macro_rules! compare_properties {
if emacs_keys.contains(":standard-properties") { if emacs_keys.contains(":standard-properties") {
emacs_keys.remove(":standard-properties"); emacs_keys.remove(":standard-properties");
} else { } else {
this_status = DiffStatus::Bad; new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
message = Some(format!(
"Emacs token lacks :standard-properties field.", "Emacs token lacks :standard-properties field.",
)); ))));
} }
if !emacs_keys.is_empty() { if !emacs_keys.is_empty() {
let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect(); let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect();
let unexpected_keys = unexpected_keys.join(", "); let unexpected_keys = unexpected_keys.join(", ");
this_status = DiffStatus::Bad; new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
message = Some(format!(
"Emacs token had extra field(s): {}", "Emacs token had extra field(s): {}",
unexpected_keys unexpected_keys
)); ))));
}
match this_status {
DiffStatus::Good => {
let result: Result<_, Box<dyn std::error::Error>> = Ok(None);
result
},
_ => {
Ok(Some((this_status, message)))
}
} }
new_status
} }
}; };
} }