use crate::crate_paths::get_reinhardt_crate;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Attribute, ItemStruct, Result};
pub(crate) fn app_config_attribute_impl(
args: TokenStream,
mut input: ItemStruct,
) -> Result<TokenStream> {
let reinhardt = get_reinhardt_crate();
let has_derive_app_config = input.attrs.iter().any(|attr| {
if attr.path().is_ident("derive")
&& let syn::Meta::List(meta_list) = &attr.meta
{
return meta_list.tokens.to_string().contains("AppConfig");
}
false
});
if has_derive_app_config {
return Err(syn::Error::new_spanned(
&input.ident,
"#[derive(AppConfig)] must not be used with #[app_config(...)]. \
The #[app_config(...)] attribute automatically derives AppConfig.",
));
}
let config_attr: Attribute = if args.is_empty() {
syn::parse_quote! { #[app_config_internal] }
} else {
syn::parse_quote! { #[app_config_internal(#args)] }
};
let app_config_path = quote!(#reinhardt::macros::AppConfig);
let existing_derive_idx = input.attrs.iter().position(|attr| {
attr.path().is_ident("derive") && matches!(&attr.meta, syn::Meta::List(_))
});
if let Some(idx) = existing_derive_idx {
if let syn::Meta::List(ref meta_list) = input.attrs[idx].meta {
let existing_tokens = &meta_list.tokens;
let new_derive_attr: Attribute =
syn::parse_quote! { #[derive(#app_config_path, #existing_tokens)] };
input.attrs[idx] = new_derive_attr;
}
} else {
let derive_attr: Attribute = syn::parse_quote! { #[derive(#app_config_path)] };
input.attrs.insert(0, derive_attr);
}
let config_insert_pos = if let Some(idx) = existing_derive_idx {
idx + 1
} else {
1
};
input.attrs.insert(config_insert_pos, config_attr);
Ok(quote! { #input })
}