// Copyright 2024 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use proc_macro::TokenStream;
use quote::quote;
use syn::{ImplItem, Item, TraitItem};

/// Attribute to specify the async runtime parameter for the `uniffi`
/// export macros if there any `async fn`s in the input.
#[proc_macro_attribute]
pub fn export(attr: TokenStream, item: TokenStream) -> TokenStream {
    let has_async_fn = |item| {
        if let Item::Fn(fun) = &item {
            if fun.sig.asyncness.is_some() {
                return true;
            }
        } else if let Item::Impl(blk) = &item {
            for item in &blk.items {
                if let ImplItem::Fn(fun) = item {
                    if fun.sig.asyncness.is_some() {
                        return true;
                    }
                }
            }
        } else if let Item::Trait(blk) = &item {
            for item in &blk.items {
                if let TraitItem::Fn(fun) = item {
                    if fun.sig.asyncness.is_some() {
                        return true;
                    }
                }
            }
        }

        false
    };

    let attr2 = proc_macro2::TokenStream::from(attr);
    let item2 = proc_macro2::TokenStream::from(item.clone());

    let res = match syn::parse(item) {
        Ok(item) => match has_async_fn(item) {
            true => quote! { #[uniffi::export(async_runtime = "tokio", #attr2)] },
            false => quote! { #[uniffi::export(#attr2)] },
        },
        Err(e) => e.into_compile_error(),
    };

    quote! {
        #res
        #item2
    }
    .into()
}
