use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{
Ident, Result, Token,
parse::{Parse, ParseStream},
};
struct ComponentLocationArgs {
component_name: Ident,
role: Option<Ident>,
}
impl Parse for ComponentLocationArgs {
fn parse(input: ParseStream) -> Result<Self> {
let component_name = input.parse()?;
let mut role = None;
if input.peek(Token![,]) {
let _ = input.parse::<Token![,]>()?;
if input.peek(Ident) {
let key: Ident = input.parse()?;
if key == "role" {
let _ = input.parse::<Token![=]>()?;
role = Some(input.parse()?);
if input.peek(Token![,]) {
let _ = input.parse::<Token![,]>()?;
}
}
}
}
Ok(ComponentLocationArgs {
component_name,
role,
})
}
}
pub fn expand(input: TokenStream) -> TokenStream {
let args = match syn::parse::<ComponentLocationArgs>(input) {
Ok(args) => args,
Err(err) => {
let err_msg = err.to_string();
return TokenStream::from(quote! {
compile_error!(#err_msg);
});
}
};
let component_name = args.component_name;
let component_str = component_name.to_string();
let component_str_lower = component_str.to_lowercase();
let role_enum = if let Some(role_ident) = args.role {
let role_str = role_ident.to_string().to_lowercase();
match role_str.as_str() {
"public" => quote! { Some(::waddling_errors::Role::Public) },
"developer" => quote! { Some(::waddling_errors::Role::Developer) },
"internal" => quote! { Some(::waddling_errors::Role::Internal) },
_ => {
let err_msg = format!(
"Invalid role '{}'. Expected: public, developer, or internal",
role_str
);
return TokenStream::from(quote! {
compile_error!(#err_msg);
});
}
}
} else {
quote! { Some(::waddling_errors::Role::Internal) }
};
let marker_mod_name = Ident::new(
&format!("__component_loc_{}", component_str_lower),
Span::call_site(),
);
let register_fn_name = Ident::new(
&format!("__register_component_loc_{}", component_str_lower),
Span::call_site(),
);
let output = quote! {
#[doc(hidden)]
#[allow(non_snake_case, dead_code)]
pub mod #marker_mod_name {
pub const COMPONENT: &str = #component_str;
pub const FILE: &str = file!();
pub const MODULE_PATH: &str = module_path!();
pub const ROLE: Option<::waddling_errors::Role> = #role_enum;
#[cfg(feature = "metadata")]
pub fn register(registry: &mut ::waddling_errors::doc_generator::DocRegistry) {
registry.register_component_location_with_role(COMPONENT, FILE, ROLE);
}
#[cfg(all(feature = "auto-register", feature = "metadata"))]
#[::ctor::ctor]
fn #register_fn_name() {
::waddling_errors::registry::register_component_location(
COMPONENT,
FILE,
ROLE,
);
}
}
};
TokenStream::from(output)
}