Implement comparison for object tree.
This commit is contained in:
parent
33800c4a88
commit
0aa746fb1e
@ -16,7 +16,6 @@ use super::util::get_property_unquoted_atom;
|
||||
use crate::types::AstNode;
|
||||
use crate::types::CharOffsetInLine;
|
||||
use crate::types::LineNumber;
|
||||
use crate::types::Object;
|
||||
use crate::types::RetainLabels;
|
||||
use crate::types::SwitchNumberLines;
|
||||
|
||||
@ -513,118 +512,133 @@ where
|
||||
Ok(ComparePropertiesResult::NoChange)
|
||||
}
|
||||
|
||||
/// Special compare used for affiliate keywords that are parsed as objects.
|
||||
///
|
||||
/// Org-mode seems to store these as a 3-deep list:
|
||||
/// - Outer list with 1 element per #+caption keyword (or other parsed keyword).
|
||||
/// - Middle list which has:
|
||||
/// - first element is a list of objects representing the value after the colon.
|
||||
/// - every additional element is a list of objects from inside the square brackets (the optional value).
|
||||
pub(crate) fn compare_property_list_of_list_of_list_of_ast_nodes<
|
||||
pub(crate) fn compare_property_object_tree<
|
||||
'b,
|
||||
's,
|
||||
'x,
|
||||
R,
|
||||
RG: Fn(R) -> Option<&'b Vec<(Option<Vec<Object<'s>>>, Vec<Object<'s>>)>>,
|
||||
RV: std::fmt::Debug + 'b,
|
||||
ROV: std::fmt::Debug + 'b,
|
||||
RI: Iterator<Item = &'b (Option<Vec<ROV>>, Vec<RV>)> + ExactSizeIterator + std::fmt::Debug,
|
||||
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>> {
|
||||
// TODO: Replace Object<'s> with generics. I hard-coded Object in to make lifetimes easier.
|
||||
let rust_value = rust_value_getter(rust_node);
|
||||
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>>
|
||||
where
|
||||
AstNode<'b, 's>: From<&'b RV>,
|
||||
AstNode<'b, 's>: From<&'b ROV>,
|
||||
{
|
||||
let value = get_property(emacs, emacs_field)?
|
||||
.map(Token::as_list)
|
||||
.map_or(Ok(None), |r| r.map(Some))?;
|
||||
let (value, rust_value) = match (value, rust_value) {
|
||||
let rust_value = rust_value_getter(rust_node);
|
||||
let (outer_emacs_list, outer_rust_list) = match (value, rust_value) {
|
||||
(None, None) => {
|
||||
return Ok(ComparePropertiesResult::NoChange);
|
||||
}
|
||||
(None, Some(_)) | (Some(_), None) => {
|
||||
(None, rv @ Some(_)) | (Some(_), rv @ None) => {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||
emacs_field, value, rust_value
|
||||
emacs_field, value, rv
|
||||
));
|
||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||
}
|
||||
(Some(value), Some(rust_value)) if value.len() != rust_value.len() => {
|
||||
(Some(el), Some(rl)) if el.len() != rl.len() => {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||
emacs_field, value, rust_value
|
||||
emacs_field, el, rl
|
||||
));
|
||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||
}
|
||||
(Some(value), Some(rust_value)) => (value, rust_value),
|
||||
(Some(el), Some(rl)) => (el, rl),
|
||||
};
|
||||
let mut full_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(outer_rust_list.len());
|
||||
|
||||
let mut full_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len());
|
||||
|
||||
// Iterate the outer lists
|
||||
for (value, (rust_optional, rust_value)) in value.iter().zip(rust_value.iter()) {
|
||||
let mut middle_value = value.as_list()?.iter();
|
||||
// First element of middle list is the mandatory value (the value past the colon).
|
||||
let mandatory_value = middle_value.next();
|
||||
let mandatory_value = match mandatory_value {
|
||||
Some(mandatory_value) => mandatory_value,
|
||||
None => {
|
||||
for (kw_e, kw_r) in outer_emacs_list.into_iter().zip(outer_rust_list) {
|
||||
let kw_e = kw_e.as_list()?;
|
||||
let child_status_length = kw_r.1.len() + kw_r.0.as_ref().map(|opt| opt.len()).unwrap_or(0);
|
||||
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(child_status_length);
|
||||
if let Some(or) = &kw_r.0 {
|
||||
// if optional value
|
||||
let mut kw_e = kw_e.into_iter();
|
||||
// First element is a list representing the mandatory value.
|
||||
if let Some(val_e) = kw_e.next() {
|
||||
let el = val_e.as_list()?;
|
||||
if el.len() != kw_r.1.len() {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||
emacs_field, kw_e, kw_r
|
||||
));
|
||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||
}
|
||||
for (e, r) in el.into_iter().zip(kw_r.1.iter()) {
|
||||
child_status.push(compare_ast_node(source, e, r.into())?);
|
||||
}
|
||||
} else {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||
emacs_field, value, rust_value
|
||||
emacs_field, kw_e, kw_r
|
||||
));
|
||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||
}
|
||||
};
|
||||
|
||||
// Compare optional value
|
||||
if let Some(rust_optional) = rust_optional {
|
||||
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len());
|
||||
if rust_optional.len() != middle_value.len() {
|
||||
// Remaining elements are the optional value.
|
||||
if kw_e.len() != or.len() {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
"{} optional value length mismatch (emacs != rust) {} != {} | {:?}",
|
||||
emacs_field,
|
||||
middle_value.len(),
|
||||
rust_optional.len(),
|
||||
rust_optional
|
||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||
emacs_field, kw_e, kw_r
|
||||
));
|
||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||
}
|
||||
for (e, r) in middle_value.zip(rust_optional) {
|
||||
for (e, r) in kw_e.zip(or.iter()) {
|
||||
child_status.push(compare_ast_node(source, e, r.into())?);
|
||||
}
|
||||
if !child_status.is_empty() {
|
||||
let diff_scope = artificial_diff_scope("optional value", child_status)?;
|
||||
full_status.push(diff_scope);
|
||||
} else {
|
||||
// if no optional value
|
||||
if !kw_e.len() == 1 {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||
emacs_field, kw_e, kw_r
|
||||
));
|
||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||
}
|
||||
}
|
||||
|
||||
// Compare mandatory value
|
||||
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len());
|
||||
let mandatory_value = mandatory_value.as_list()?;
|
||||
if rust_value.len() != mandatory_value.len() {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
"{} mandatory value length mismatch (emacs != rust) {} != {} | {:?}",
|
||||
emacs_field,
|
||||
mandatory_value.len(),
|
||||
rust_value.len(),
|
||||
rust_value
|
||||
));
|
||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||
}
|
||||
for (e, r) in mandatory_value.iter().zip(rust_value) {
|
||||
child_status.push(compare_ast_node(source, e, r.into())?);
|
||||
let e = kw_e
|
||||
.first()
|
||||
.map(Token::as_list)
|
||||
.map_or(Ok(None), |r| r.map(Some))?
|
||||
.expect("The above if-statement proves this will be Some.")
|
||||
.iter();
|
||||
let r = kw_r.1.iter();
|
||||
|
||||
if e.len() != r.len() {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||
emacs_field, kw_e, kw_r
|
||||
));
|
||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||
}
|
||||
|
||||
for (e, r) in e.zip(r) {
|
||||
child_status.push(compare_ast_node(source, e, r.into())?);
|
||||
}
|
||||
}
|
||||
if !child_status.is_empty() {
|
||||
let diff_scope = artificial_diff_scope("mandatory value", child_status)?;
|
||||
full_status.push(diff_scope);
|
||||
}
|
||||
}
|
||||
|
||||
if full_status.is_empty() {
|
||||
Ok(ComparePropertiesResult::NoChange)
|
||||
} else {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::compare_field::compare_property_list_of_list_of_list_of_ast_nodes;
|
||||
use super::compare_field::compare_property_list_of_quoted_string;
|
||||
use super::compare_field::compare_property_object_tree;
|
||||
use super::compare_field::compare_property_optional_pair;
|
||||
use super::compare_field::compare_property_quoted_string;
|
||||
use super::compare_field::ComparePropertiesResult;
|
||||
@ -386,20 +386,17 @@ where
|
||||
|_| Some((*optval, *val)),
|
||||
)?;
|
||||
ret.push(diff);
|
||||
// todo
|
||||
}
|
||||
AffiliatedKeywordValue::ObjectTree(_) => {
|
||||
// todo
|
||||
} // AffiliatedKeywordValue::ListOfListsOfObjects(rust_value) => {
|
||||
// let diff = compare_property_list_of_list_of_list_of_ast_nodes(
|
||||
// source,
|
||||
// emacs,
|
||||
// rust,
|
||||
// emacs_property_name.as_str(),
|
||||
// |_| Some(rust_value),
|
||||
// )?;
|
||||
// ret.push(diff);
|
||||
// }
|
||||
AffiliatedKeywordValue::ObjectTree(rust_value) => {
|
||||
let diff = compare_property_object_tree(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
emacs_property_name.as_str(),
|
||||
|_| Some(rust_value.iter()),
|
||||
)?;
|
||||
ret.push(diff);
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(ret)
|
||||
|
@ -171,41 +171,3 @@ fn identify_keyword_type<'g, 's>(
|
||||
(false, false, false) => AffiliatedKeywordType::SingleString,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_single_string_keyword<'g, 's>(
|
||||
_global_settings: &'g GlobalSettings<'g, 's>,
|
||||
name: &'s str,
|
||||
) -> bool {
|
||||
// TODO: Is this defined by an elisp variable?
|
||||
for single_string_name in ["plot", "name"] {
|
||||
if name.eq_ignore_ascii_case(single_string_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_list_of_single_string_keyword<'g, 's>(
|
||||
_global_settings: &'g GlobalSettings<'g, 's>,
|
||||
name: &'s str,
|
||||
) -> bool {
|
||||
// TODO: Is this defined by an elisp variable?
|
||||
for single_string_name in ["results"] {
|
||||
if name.eq_ignore_ascii_case(single_string_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_list_of_objects_keyword<'g, 's>(
|
||||
global_settings: &'g GlobalSettings<'g, 's>,
|
||||
name: &'s str,
|
||||
) -> bool {
|
||||
for parsed_keyword in global_settings.element_parsed_keywords {
|
||||
if name.eq_ignore_ascii_case(parsed_keyword) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user