#![doc = include_str!("../README.md")]
use std::{convert::identity, iter::{from_fn, once}};
use proc_macro::*;
use proc_macro_tool::*;
#[doc = include_str!("../README.md")]
#[proc_macro]
pub fn bind(input: TokenStream) -> TokenStream {
let mut parse_iter = input.parse_iter();
from_fn(|| {
parse_iter.split_puncts_include(",")
.or_else(|| parse_iter.peek().is_some()
.then_some(parse_iter.by_ref().collect()))
})
.map(process_expr)
.collect::<Result<Vec<_>, _>>()
.map_or_else(identity, TokenStream::from_iter)
}
fn process_expr(expr: TokenStream) -> Result<TokenStream, TokenStream> {
let mut iter = expr.parse_iter();
let mut_tt = iter.next_if(|tt| tt.is_keyword("mut"));
let prefix = from_fn(|| iter.next_if(|tt| {
tt.is_punch('&') || tt.is_punch('*') || tt.is_keyword("mut")
})).collect::<Vec<_>>();
let (name, orig_name) = iter.next()
.ok_or_else(|| err!("unexpected end of input, expected name"))?
.into_ident()
.map(|name| (name.clone(), name.tt()))
.or_else(|tt| tt.into_group().and_then(|g| {
let mut stream = g.stream().into_iter();
match stream.next() {
Some(TokenTree::Ident(ident)) if stream.next().is_none() => {
Ok((ident, g.tt()))
},
_ => Err(g.tt()),
}
}))
.map_err(|e| err!("unexpected token, expected name", e))?;
let s = span_setter(name.span());
once("let".ident(name.span()).tt())
.chain(mut_tt)
.chain([name.tt(), s('='.punct(Spacing::Alone).tt())])
.chain(prefix)
.chain([orig_name])
.chain(iter.filter(|tt| !tt.is_punch(',')))
.chain([s(';'.punct(Spacing::Alone).tt())])
.map(Ok)
.collect()
}