use proc_macro2::Span;
use proc_macro_error::emit_error;
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::visit_mut::{self, VisitMut};
use syn::{parse_quote, Error, Lit, Member, Result, Token};
#[allow(non_camel_case_types)]
mod kw {
syn::custom_keyword!(error_emitted);
}
pub struct BaseInput;
impl Parse for BaseInput {
fn parse(input: ParseStream) -> Result<Self> {
if input.peek(Lit) {
let _ = input.parse::<Lit>()?;
if input.is_empty() {
return Ok(Self);
}
} else if input.peek(kw::error_emitted) {
let _ = input.parse::<kw::error_emitted>()?;
if input.is_empty() {
return Ok(Self);
}
}
let msg = "usage of `widget_index!` invalid outside of `impl_scope!` macro with `#[widget]` attribute";
Err(Error::new(Span::call_site(), msg))
}
}
pub struct WidgetInput {
pub ident: Member,
}
impl Parse for WidgetInput {
fn parse(input: ParseStream) -> Result<Self> {
let _ = input.parse::<Token![self]>()?;
let _ = input.parse::<Token![.]>()?;
let ident = input.parse()?;
Ok(WidgetInput { ident })
}
}
struct Visitor<'a, I: Clone + Iterator<Item = &'a Member>> {
children: I,
}
impl<'a, I: Clone + Iterator<Item = &'a Member>> VisitMut for Visitor<'a, I> {
fn visit_macro_mut(&mut self, node: &mut syn::Macro) {
if node.path == parse_quote! { widget_index } {
let args = match syn::parse2::<WidgetInput>(node.tokens.clone()) {
Ok(args) => args,
Err(err) => {
emit_error!(node.tokens.span(), "{}", err);
node.tokens = parse_quote! { error_emitted };
return;
}
};
for (i, child) in self.children.clone().enumerate() {
if args.ident == *child {
node.tokens = parse_quote! { #i };
return;
}
}
emit_error!(args.ident.span(), "does not match any child widget");
node.tokens = parse_quote! { error_emitted };
return;
}
visit_mut::visit_macro_mut(self, node);
}
}
pub fn visit_impls<'a, I: Clone + Iterator<Item = &'a Member>>(
children: I,
impls: &mut [syn::ItemImpl],
) {
let mut obj = Visitor { children };
for impl_ in impls {
obj.visit_item_impl_mut(impl_);
}
}