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
	 Tom Alexander
						Tom Alexander