organic/src/compare/macros.rs

83 lines
2.8 KiB
Rust

#[derive(Debug)]
pub(crate) enum EmacsField<'s> {
Required(&'s str),
Optional(&'s str),
}
/// Create iterators for ast nodes where it only has to iterate over children
macro_rules! compare_properties {
($emacs:expr, $($emacs_field:expr, $rust_value:expr, $compare_fn: ident),+) => {
{
let mut this_status = DiffStatus::Good;
let mut message: Option<String> = 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();
$(
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, emacs_name, $rust_value)?;
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<dyn std::error::Error>> = Ok(None);
result
},
_ => {
Ok(Some((this_status, message)))
}
}
}
};
}
pub(crate) use compare_properties;