use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InterruptLine {
pub number: u32,
pub variant: Option<String>,
}
impl InterruptLine {
pub fn new(number: u32) -> Self {
Self {
number,
variant: None,
}
}
pub fn tokens(&self, hal: &TokenStream) -> Option<(TokenStream, String)> {
let number = u16::try_from(self.number).ok()?;
Some(match &self.variant {
Some(variant) => {
let variant = Ident::new(variant, Span::call_site());
(quote! { #hal::Interrupt::#variant }, variant.to_string())
}
None => (quote! { #hal::hal::nvic::Irq(#number) }, number.to_string()),
})
}
pub fn guard(&self, hal: &TokenStream) -> Option<TokenStream> {
let number = u16::try_from(self.number).ok()?;
let variant = Ident::new(self.variant.as_ref()?, Span::call_site());
let message = format!(
"{variant} is interrupt {number} in this core's Consortium chip map but not in \
the chip PAC's interrupt table; the generated vector and the NVIC enable would \
refer to different lines",
);
Some(quote! {
const _: () = ::core::assert!(#hal::Interrupt::#variant as u16 == #number, #message);
})
}
}
#[cfg(test)]
mod tests {
use super::*;
fn hal() -> TokenStream {
quote! { ::consortium_hal_stm32mp2 }
}
#[test]
fn names_the_pac_variant_when_one_applies() {
let line = InterruptLine {
number: 171,
variant: Some("IPCC1_RX".to_string()),
};
let (tokens, label) = line.tokens(&hal()).unwrap();
assert_eq!(
tokens.to_string(),
":: consortium_hal_stm32mp2 :: Interrupt :: IPCC1_RX"
);
assert_eq!(label, "IPCC1_RX");
}
#[test]
fn falls_back_to_the_raw_number_carrier() {
let (tokens, label) = InterruptLine::new(13).tokens(&hal()).unwrap();
assert_eq!(
tokens.to_string(),
":: consortium_hal_stm32mp2 :: hal :: nvic :: Irq (13u16)"
);
assert_eq!(label, "13");
}
#[test]
fn rejects_a_number_wider_than_an_interrupt_line() {
assert!(InterruptLine::new(0x1_0000).tokens(&hal()).is_none());
}
}