use proc_macro::TokenStream as TokenStream1;
use proc_macro2::TokenStream;
use proc_macro_crate::{FoundCrate, crate_name};
use quote::quote;
use syn::{ImplItem, ItemImpl, parse_quote};
#[derive(Clone, Copy, PartialEq)]
pub enum Kind {
Page,
Layout,
}
fn adapter_path(package: &str, facade: &str) -> TokenStream {
let facade_module = syn::Ident::new(facade, proc_macro2::Span::call_site());
let named = |name: &str| syn::Ident::new(name, proc_macro2::Span::call_site());
let adapter = crate_name(package);
if let Ok(FoundCrate::Itself) = adapter {
return quote!(crate);
}
let guinea = crate_name("guinea");
if let Ok(FoundCrate::Name(name)) = &guinea {
let ident = named(name);
return quote!(::#ident::#facade_module);
}
if let Ok(FoundCrate::Name(name)) = adapter {
let ident = named(&name);
return quote!(::#ident);
}
match guinea {
Ok(FoundCrate::Itself) => quote!(crate::#facade_module),
_ => quote!(::guinea::#facade_module),
}
}
pub fn node_impl(item: TokenStream1, kind: Kind, package: &str, facade: &str) -> TokenStream1 {
let mut item = syn::parse_macro_input!(item as ItemImpl);
let adapter = adapter_path(package, facade);
let declared_type = |name: &str| {
item.items
.iter()
.any(|entry| matches!(entry, ImplItem::Type(ty) if ty.ident == name))
};
let declared_fn = |name: &str| {
item.items
.iter()
.any(|entry| matches!(entry, ImplItem::Fn(f) if f.sig.ident == name))
};
let has_params = declared_type("Params");
let has_installs = declared_type("Installs");
let has_message = declared_type("Message");
let has_update = declared_fn("update");
let has_install = declared_fn("install");
let gc = crate::handler::guinea_core_crate_path();
item.items.push(parse_quote! {
const DECLARED: ::core::option::Option<#gc::actor::shape::Declared> =
::core::option::Option::Some(#gc::actor::shape::Declared {
file: ::core::file!(),
line: ::core::line!(),
column: ::core::column!(),
crate_dir: ::core::env!("CARGO_MANIFEST_DIR"),
});
});
if kind == Kind::Page && !has_params {
item.items.push(parse_quote!(
type Params = ();
));
}
if !has_installs {
item.items.push(parse_quote!(
type Installs = ();
));
if !has_install {
item.items.push(parse_quote! {
fn install(
_ctx: &#adapter::FeatureInitContext,
_params: &Self::Params,
) -> #gc::__private::anyhow::Result<()> {
::std::result::Result::Ok(())
}
});
}
}
if !has_message {
item.items.push(parse_quote!(
type Message = ::core::convert::Infallible;
));
if !has_update {
item.items.push(parse_quote! {
fn update(
&mut self,
message: Self::Message,
_cx: &mut #adapter::UpdateCx<'_, Self>,
) {
match message {}
}
});
}
}
quote!(#item).into()
}