macro_rules! bail {
($msg:literal) => {
return Err(syn::Error::new(proc_macro2::Span::call_site(), format!($msg)))
};
($msg:ident) => {
return Err(syn::Error::new(proc_macro2::Span::call_site(), $msg))
};
($span:expr, $msg:literal) => {
return Err(syn::Error::new($span, format!($msg)))
};
($span:expr, $msg:ident) => {
return Err(syn::Error::new($span, $msg))
};
($fmt:literal, $($arg:tt)*) => {
return Err(syn::Error::new(proc_macro2::Span::call_site(), format!($fmt, $($arg)*)))
};
($span:expr, $fmt:literal, $($arg:tt)*) => {
return Err(syn::Error::new($span, format!($fmt, $($arg)*)))
};
}
pub(crate) use bail;
#[cfg_attr(test, mutants::skip)] pub(crate) fn generate_unique_field_name(existing_fields: &[&syn::Ident]) -> syn::Ident {
let mut candidate = "ohno_core".to_string();
let mut counter = 0;
while existing_fields.iter().any(|ident| ident == &AsRef::<str>::as_ref(&candidate)) {
counter += 1;
candidate = format!("ohno_core_{counter}");
}
syn::Ident::new(&candidate, proc_macro2::Span::call_site())
}
#[cfg(test)]
macro_rules! assert_formatted_snapshot {
($tokens:expr) => {{
let ast: syn::File = syn::parse2($tokens).expect("tokenstream is not valid Rust");
let formatted = prettyplease::unparse(&ast);
insta::assert_snapshot!(formatted);
}};
}
#[cfg(test)]
pub(crate) use assert_formatted_snapshot;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_inner_error_field_name() {
let ident1 = syn::Ident::new("msg", proc_macro2::Span::call_site());
let ident2 = syn::Ident::new("path", proc_macro2::Span::call_site());
let fields = vec![&ident1, &ident2];
let name = generate_unique_field_name(&fields);
assert_eq!(name.to_string(), "ohno_core");
}
#[test]
fn test_generate_inner_error_field_name_conflict() {
let ident1 = syn::Ident::new("ohno_core", proc_macro2::Span::call_site());
let ident2 = syn::Ident::new("path", proc_macro2::Span::call_site());
let fields = vec![&ident1, &ident2];
let name = generate_unique_field_name(&fields);
assert_eq!(name.to_string(), "ohno_core_1");
}
#[test]
fn test_generate_unique_field_name_equality_check() {
let ident1 = syn::Ident::new("ohno_core", proc_macro2::Span::call_site());
let ident2 = syn::Ident::new("other_field", proc_macro2::Span::call_site());
let fields = vec![&ident1, &ident2];
let name = generate_unique_field_name(&fields);
assert_eq!(name.to_string(), "ohno_core_1");
assert!(!fields.iter().any(|ident| name == ident.to_string()));
}
#[test]
fn test_generate_unique_field_name_multiple_collisions() {
let ident1 = syn::Ident::new("ohno_core", proc_macro2::Span::call_site());
let ident2 = syn::Ident::new("ohno_core_1", proc_macro2::Span::call_site());
let ident3 = syn::Ident::new("ohno_core_2", proc_macro2::Span::call_site());
let fields = vec![&ident1, &ident2, &ident3];
let name = generate_unique_field_name(&fields);
assert_eq!(name.to_string(), "ohno_core_3");
}
}