#![deny(warnings)]

use proc_macro2::{Span, TokenStream};
use syn::{
    ItemFn,
    parse::{self},
};

/// Marks the firmware entry point.
pub fn main(args: TokenStream, f: ItemFn) -> TokenStream {
    if f.sig.asyncness.is_some() {
        return parse::Error::new(
            Span::call_site(),
            "If you want to use `async` please use `esp-rtos`'s `#[esp_rtos::main]` macro instead.",
        )
        .to_compile_error();
    }

    if !args.is_empty() {
        return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
            .to_compile_error();
    }

    let root = match proc_macro_crate::crate_name("esp-hal") {
        Ok(proc_macro_crate::FoundCrate::Name(ref name)) => quote::format_ident!("{name}"),
        _ => quote::format_ident!("esp_hal"),
    };

    quote::quote!(
        #[doc = "The main entry point of the firmware, generated by the `#[main]` macro."]
        #[#root::__macro_implementation::__entry]
        #f
    )
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_basic() {
        let result = main(
            quote::quote! {}.into(),
            syn::parse2(quote::quote! {
                fn main() {}
            })
            .unwrap(),
        );

        assert_eq!(
            result.to_string(),
            quote::quote! {
                #[doc = "The main entry point of the firmware, generated by the `#[main]` macro."]
                #[esp_hal::__macro_implementation::__entry]
                fn main () { }
            }
            .to_string()
        );
    }

    #[test]
    fn test_try_async() {
        let result = main(
            quote::quote! {}.into(),
            syn::parse2(quote::quote! {
                async fn main() {}
            })
            .unwrap(),
        );

        assert_eq!(
            result.to_string(),
            quote::quote! {
                ::core::compile_error!{ "If you want to use `async` please use `esp-rtos`'s `#[esp_rtos::main]` macro instead." }
            }
            .to_string()
        );
    }

    #[test]
    fn test_try_non_emptyargs() {
        let result = main(
            quote::quote! {non_empty}.into(),
            syn::parse2(quote::quote! {
                fn main() {}
            })
            .unwrap(),
        );

        assert_eq!(
            result.to_string(),
            quote::quote! {
                ::core::compile_error!{ "This attribute accepts no arguments" }
            }
            .to_string()
        );
    }
}
