/// Create iterators for ast nodes where it only has to iterate over children macro_rules! compare_properties { ($emacs:expr, $rust:expr, $($emacs_field:expr, $rust_value_getter:expr, $compare_fn: ident),+) => { { let mut this_status = DiffStatus::Good; let mut message: Option = None; let children = $emacs.as_list()?; let attributes_child = children .iter() .nth(1) .ok_or("Should have an attributes child.")?; let attributes_map = attributes_child.as_map()?; let mut emacs_keys: BTreeSet<&str> = attributes_map.keys().map(|s| *s).collect(); if emacs_keys.contains(":standard-properties") { emacs_keys.remove(":standard-properties"); } else { this_status = DiffStatus::Bad; message = Some(format!( "Emacs token lacks :standard-properties field.", )); } $( match $emacs_field { EmacsField::Required(name) if emacs_keys.contains(name) => { emacs_keys.remove(name); }, EmacsField::Optional(name) if emacs_keys.contains(name) => { emacs_keys.remove(name); }, EmacsField::Required(name) => { this_status = DiffStatus::Bad; message = Some(format!( "Emacs token lacks required field: {}", name )); }, EmacsField::Optional(_name) => {}, } ),+ if !emacs_keys.is_empty() { let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect(); let unexpected_keys = unexpected_keys.join(", "); this_status = DiffStatus::Bad; message = Some(format!( "Emacs token had extra field(s): {}", unexpected_keys )); } $( let emacs_name = match $emacs_field { EmacsField::Required(name) => { name }, EmacsField::Optional(name) => { name }, }; let result = $compare_fn($emacs, $rust, emacs_name, $rust_value_getter)?; match result { Some((DiffStatus::Good, _)) => unreachable!("No comparison functions should return Some() when DiffStatus is good."), Some((status, msg)) => { this_status = status; message = msg; }, _ => {} } ),+ match this_status { DiffStatus::Good => { let result: Result<_, Box> = Ok(None); result }, _ => { Ok(Some((this_status, message))) } } } }; // Default case for when there are no expected properties except for :standard-properties ($emacs:expr) => { { let mut this_status = DiffStatus::Good; let mut message: Option = None; let children = $emacs.as_list()?; let attributes_child = children .iter() .nth(1) .ok_or("Should have an attributes child.")?; let attributes_map = attributes_child.as_map()?; let mut emacs_keys: BTreeSet<&str> = attributes_map.keys().map(|s| *s).collect(); if emacs_keys.contains(":standard-properties") { emacs_keys.remove(":standard-properties"); } else { this_status = DiffStatus::Bad; message = Some(format!( "Emacs token lacks :standard-properties field.", )); } if !emacs_keys.is_empty() { let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect(); let unexpected_keys = unexpected_keys.join(", "); this_status = DiffStatus::Bad; message = Some(format!( "Emacs token had extra field(s): {}", unexpected_keys )); } match this_status { DiffStatus::Good => { let result: Result<_, Box> = Ok(None); result }, _ => { Ok(Some((this_status, message))) } } } }; } pub(crate) use compare_properties;