#![doc = include_str!("../README.md")]
use {
proc_macro::TokenStream,
quote::quote,
syn::{parse_macro_input, spanned::Spanned, Token},
};
#[proc_macro]
pub fn ff(tokens: TokenStream) -> TokenStream {
let FlatfishBody { source_type, chain } = parse_macro_input!(tokens as FlatfishBody);
let mut out = quote! { #source_type };
for step in chain {
let FlatfishItem { trait_, item } = step;
out = quote!(< #out as #trait_ >:: #item)
}
TokenStream::from(out)
}
struct FlatfishBody {
source_type: syn::TypePath,
chain: Vec<FlatfishItem>,
}
impl syn::parse::Parse for FlatfishBody {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let source_type = input.parse()?;
let mut chain = vec![];
loop {
let lookahead = input.lookahead1();
if !lookahead.peek(Token![|]) {
break;
}
chain.push(input.parse()?);
}
Ok(Self { source_type, chain })
}
}
struct FlatfishItem {
trait_: syn::TypePath,
item: syn::PathSegment,
}
impl syn::parse::Parse for FlatfishItem {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let _: Token![|] = input.parse()?;
let mut trait_: syn::TypePath = input.parse()?;
let error_msg = "Syntax is `path::to::Trait::Item`";
let Some(item) = trait_.path.segments.pop() else {
return Err(syn::Error::new(trait_.span(), error_msg));
};
let item = item.into_value();
if trait_.path.segments.is_empty() {
return Err(syn::Error::new(trait_.span(), error_msg));
}
let _ = trait_.path.segments.pop_punct();
Ok(Self { trait_, item })
}
}