floop 0.1.2

A more convenient and less error prone replacement for loop `{ select! { .. }}`
Documentation
use std::fmt::Display;

use unsynn::*;

use crate::parser::{Block, KBefore, KBiased, KIn, KUnbiased};

unsynn! {
    enum Fragment {
        After {
            _before: KBefore,
            _arrow: FatArrow,
            block: Block,
        },
        Before {
            _before: KBefore,
            _arrow: FatArrow,
            block: Block,
            
        },
        Block {
            pattern: LazyVec<TokenTree, KIn>,
            expr: LazyVec<TokenTree, Either<FatArrow, EndOfStream>>,
            block: Block,
        },
        Expr {
            pattern: LazyVec<TokenTree, KIn>,
            expr: LazyVec<TokenTree, Either<FatArrow, EndOfStream>>,
        },
        Pattern {
            _except: Except<KIn>,
            pattern: LazyVec<TokenTree, KIn>,
        }
    }

    enum Ast {
        Unfinished {
            _biasedness: Either<KBiased, KUnbiased>,
            fragments: Vec<Fragment>,
        },
        MissingBiasedness {
            _missing: Except<Either<KBiased, KUnbiased>>,
            tokens: LazyVecUntil<TokenTree, EndOfStream>,
        }
    }
}

// any code this generates never runs (a `compile_error!(...)` is added to the end),
// so it doesn't need to generate functional or hygenic code, it just needs to generate code that tricks rust-analyzer into giving good suggestions.
pub(crate) fn friendly_error<E: Display>(tokens: TokenStream, err: E) -> TokenStream {
    let mut output = TokenStream::new();
    let ast = Ast::parse(&mut tokens.to_token_iter()).expect("this parser should accept anything");
    let mut before = None;
    let mut after = None;
    let mut suggest_before_after = false;

    match ast {
        Ast::Unfinished { _biasedness, fragments } => {
            for fragment in fragments {
                match fragment {
                    Fragment::Before { _before, _arrow, block } => before = Some(block),
                    Fragment::After { _before, _arrow, block } => after = Some(block),
                    Fragment::Block { pattern, expr, block } => {
                        let pattern = pattern.vec;
                        let expr = expr.vec;
                        output.extend(quote! {
                            if let #pattern = { #expr }.await {
                                #block
                            }
                        });
                    },
                    Fragment::Expr { pattern, expr } => {
                        let pattern = pattern.vec;
                        let expr = expr.vec;
                        output.extend(quote! {
                            if let #pattern = { #expr }.await {
                                
                            }
                        });
                    },
                    Fragment::Pattern { _except, pattern } => {
                        suggest_before_after = true;
                        let pattern = pattern.vec;
                        output.extend(quote! {
                            if let #pattern = ::core::panic!() {
                                
                            }
                        });
                    }
                }
            }
        },
        Ast::MissingBiasedness { _missing, tokens } => {
            output.extend(quote! {
                struct FloopHelper;

                let biased = FloopHelper;
                let unbiased = FloopHelper;

                let biasedness: FloopHelper = #tokens;
            });
        }
    }

    let before_after_suggestion = suggest_before_after.then_some(quote! {
        enum FloopHelper {
            before,
            after
        }

        // this makes rust-analyzer suggest "before" and "after".
        use FloopHelper::*;
    });
    let err = format_literal_string!("{}", err);

    quote! {
        {
            #before_after_suggestion

            async {
                loop {
                    #before

                    #output

                    #after
                }
            };
            

            ::core::compile_error!(#err)
        }
    }
}