use proc_macro::{TokenStream, TokenTree}; use quote::quote; /// Subtracts two numbers and calls a given callback macro with the result. /// /// # Input /// `number - number, callback` /// /// # Examples /// ``` /// # use std::any::TypeId; /// use util_macros::sub; /// /// macro_rules! sub_cb { /// ($num: literal) => { /// $num /// }; /// } /// /// type Foo = [u8; sub!(5 - 2, sub_cb)]; /// /// assert_eq!(TypeId::of::(), TypeId::of::<[u8; 3]>()); /// ``` ///
/// /// The callback is called with the identifier `overflow` if a overflow occurs. /// ``` /// # use std::any::TypeId; /// use util_macros::sub; /// /// macro_rules! sub_cb { /// ($num: literal) => { /// $num /// }; /// /// (overflow) => { /// 128 /// }; /// } /// /// type Foo = [u8; sub!(3 - 10, sub_cb)]; /// /// assert_eq!(TypeId::of::(), TypeId::of::<[u8; 128]>()); /// ``` #[proc_macro] pub fn sub(input: TokenStream) -> TokenStream { let mut input_tt_iter = input.into_iter(); let num_a = match input_tt_iter.next().unwrap() { TokenTree::Literal(lit) => lit.to_string().parse::().unwrap(), _ => { panic!("Expected a number literal"); } }; match input_tt_iter.next().unwrap() { TokenTree::Punct(punct) if punct.as_char() == '-' => {} _ => { panic!("Expected a '-' token"); } }; let num_b = match input_tt_iter.next().unwrap() { TokenTree::Literal(lit) => lit.to_string().parse::().unwrap(), _ => { panic!("Expected a number literal"); } }; match input_tt_iter.next().unwrap() { TokenTree::Punct(punct) if punct.as_char() == ',' => {} _ => { panic!("Expected a ',' token"); } }; let cb_ident = match input_tt_iter.next().unwrap() { TokenTree::Ident(cb_ident) => { proc_macro2::Ident::new(&cb_ident.to_string(), cb_ident.span().into()) } _ => { panic!("Expected a identifier"); } }; let Some(subtracted) = num_a.checked_sub(num_b) else { return quote! { #cb_ident!(overflow) } .into(); }; let subtracted_lit = proc_macro2::Literal::u32_unsuffixed(subtracted); quote! { #cb_ident!(#subtracted_lit) } .into() }