gentian 0.1.2

gentian: a proc macro that transforms generators to state machines
Documentation
use quote::ToTokens;
use syn::parse_quote;
use syn::Expr;
use syn::ItemFn;
use syn::Stmt;

pub(crate) fn nop_stmt() -> Stmt {
    let nop: ItemFn = parse_quote! {fn nop(){nop}};
    return nop.block.stmts[0].clone();
}

pub(crate) fn else_stmt() -> Stmt {
    let nop: ItemFn = parse_quote! {fn nop(){else_stmt}};
    return nop.block.stmts[0].clone();
}
pub(crate) fn final_stmt() -> Stmt {
    let nop: ItemFn = parse_quote! {fn nop(){final_stmt}};
    return nop.block.stmts[0].clone();
}
pub(crate) fn start_stmt() -> Stmt {
    let nop: ItemFn = parse_quote! {fn nop(){start_stmt}};
    return nop.block.stmts[0].clone();
}
pub(crate) fn start_node_stmt() -> Stmt {
    let nop: ItemFn = parse_quote! {fn nop(){start_node_stmt}};
    return nop.block.stmts[0].clone();
}
pub(crate) fn end_node_stmt() -> Stmt {
    let nop: ItemFn = parse_quote! {fn nop(){end_node_stmt}};
    return nop.block.stmts[0].clone();
}

pub(crate) fn semi_token() -> syn::token::Semi {
    return syn::token::Semi::default();
}

fn is_co_expr(path: &syn::ExprPath) -> bool {
    let name = get_expr_path_name(path);
    return name == "co_yield" || name == "co_return";
}

fn get_expr_path_name(path: &syn::ExprPath) -> String {
    let res = path.attrs.is_empty()
        && path.qself.is_none()
        && path.path.leading_colon.is_none()
        && path.path.leading_colon.is_none()
        && path.path.segments.len() == 1;
    if res {
        if let Some(name) = path.path.segments.last() {
            let name = name.ident.to_string();
            return name;
        }
    }
    String::new()
}

pub(crate) fn is_co_yield_or_co_return_expr(expr: &syn::Expr) -> bool {
    match expr {
        Expr::Path(expr) => {
            return is_co_expr(expr);
        }
        Expr::Call(e) => {
            let res = e.attrs.is_empty();
            if res {
                match e.func.as_ref() {
                    Expr::Path(expr) => {
                        return is_co_expr(expr);
                    }
                    _ => {}
                }
            }
        }
        _ => {}
    }
    false
}
pub(crate) fn is_yield_or_return(stmt: &syn::Stmt) -> bool {
    match stmt {
        Stmt::Semi(Expr::Path(expr), _) => {
            return is_co_expr(expr);
        }
        Stmt::Semi(Expr::Call(e), _) => {
            let res = e.attrs.is_empty();
            if res {
                match e.func.as_ref() {
                    Expr::Path(expr) => {
                        return is_co_expr(expr);
                    }
                    _ => {}
                }
            }
        }
        Stmt::Semi(Expr::Return(_), _) => {
            return true;
        }
        Stmt::Expr(Expr::Return(_)) => {
            return true;
        }
        _ => {}
    }
    false
}

pub(crate) fn transform_stmt_to_string(stmt: &syn::Stmt) -> (String, bool) {
    let mut is_yield_or_return = false;
    let stmt_str = match &stmt {
        Stmt::Semi(Expr::Path(expr), _) => {
            let name = get_expr_path_name(expr);
            if name == "co_return" || name == "co_yield" {
                return (String::from("return;"), true);
            }
            stmt.to_token_stream().to_string()
        }
        Stmt::Semi(Expr::Call(e), _) => {
            let res = e.attrs.is_empty();
            if res {
                match e.func.as_ref() {
                    Expr::Path(expr) => {
                        let name = get_expr_path_name(expr);
                        if name == "co_return" || name == "co_yield" {
                            return (
                                format!(
                                    "return {};",
                                    e.args.last().unwrap().to_token_stream().to_string()
                                ),
                                true,
                            );
                        }
                    }
                    _ => {}
                }
            }
            stmt.to_token_stream().to_string()
        }
        Stmt::Expr(Expr::Yield(e)) | Stmt::Semi(Expr::Yield(e), _) => {
            is_yield_or_return = true;
            if let Some(expr) = &e.expr {
                format!("{};", expr.to_token_stream().to_string())
            } else {
                format!(";")
            }
        }
        Stmt::Semi(Expr::Return(_), _) => {
            is_yield_or_return = true;
            stmt.to_token_stream().to_string()
        }
        _ => stmt.to_token_stream().to_string(),
    };
    return (stmt_str, is_yield_or_return);
}