bind2 0.1.0

Generate some let statements, similar to bind, but very lightweight
Documentation
#![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()
}