review-macro 0.4.1

A React-inspired framework for making client-side single-page apps
Documentation
use crate::body::BodyRewriter;
use proc_macro2::{Span, TokenStream};
use proc_macro_error::emit_error;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::visit_mut;
use syn::{Ident, ItemFn, Signature};

extern crate self as review;

#[derive(Clone)]
pub struct HookFn {
    inner: ItemFn,
}

impl Parse for HookFn {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let func: ItemFn = input.parse()?;

        let sig = func.sig.clone();

        if sig.asyncness.is_some() {
            emit_error!(sig.asyncness, "async functions can't be hooks");
        }

        if sig.constness.is_some() {
            emit_error!(sig.constness, "const functions can't be hooks");
        }

        if sig.abi.is_some() {
            emit_error!(sig.abi, "extern functions can't be hooks");
        }

        if sig.unsafety.is_some() {
            emit_error!(sig.unsafety, "unsafe functions can't be hooks");
        }

        if !sig.ident.to_string().starts_with("use_") {
            emit_error!(sig.ident, "hooks must have a name starting with `use_`");
        }

        Ok(Self { inner: func })
    }
}

pub fn hook_impl(component: HookFn) -> syn::Result<TokenStream> {
    let HookFn { inner: original_fn } = component;

    let ItemFn {
        vis,
        sig,
        mut block,
        ..
    } = original_fn;

    let Signature {
        ref fn_token,
        ref ident,
        ref inputs,
        output: ref hook_return_type,
        ref generics,
        ..
    } = sig;

    let (_, _, where_clause) = generics.split_for_impl();

    let ctx_ident = Ident::new("context", Span::mixed_site());

    let mut body_rewriter = BodyRewriter::default();
    visit_mut::visit_block_mut(&mut body_rewriter, &mut *block);

    let prev_inputs = if !inputs.is_empty() {
        quote!( #inputs, )
    } else {
        quote!()
    };

    let output = quote! {
        #vis #fn_token #ident #generics ( #prev_inputs #ctx_ident: &mut (review::FiberId, &mut review::HookContext)) #hook_return_type #where_clause #block
    };

    Ok(output)
}