ribir_macros 0.4.0-alpha.26

A non-intrusive declarative GUI framework, to build modern native/wasm cross-platform applications.
Documentation
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::{
  Stmt,
  fold::Fold,
  parse::{Parse, ParseStream},
  spanned::Spanned,
};

use crate::{
  error::{Error, Result, result_to_token_stream},
  symbol_process::{DollarRefsCtx, symbol_to_macro},
};

pub fn gen_code(input: TokenStream, refs_ctx: Option<&mut DollarRefsCtx>) -> TokenStream {
  let span = input.span();
  let res = process_watch_body(input, refs_ctx)
    .map(|(upstream, map_handler)| quote_spanned! { span => #upstream.map(#map_handler) });
  result_to_token_stream(res)
}

pub fn process_watch_body(
  input: TokenStream, refs_ctx: Option<&mut DollarRefsCtx>,
) -> Result<(TokenStream, TokenStream)> {
  let span = input.span();
  let input = symbol_to_macro(input)?;
  let mut body = syn::parse2::<BodyExpr>(input)?;

  let refs = if let Some(refs_ctx) = refs_ctx {
    refs_ctx.new_dollar_scope(None);
    body = body.fold(refs_ctx);
    refs_ctx.pop_dollar_scope(true)
  } else {
    let mut refs_ctx = DollarRefsCtx::top_level();
    body = body.fold(&mut refs_ctx);
    refs_ctx.pop_dollar_scope(true)
  };

  let expr = body.0;
  let map_handler = quote! { move |_: ModifyScope| { #(#expr)* } };
  if refs.is_empty() {
    Err(Error::WatchNothing(span))
  } else {
    let upstream = refs.upstream_tokens();
    Ok((upstream, quote! {{ #refs  #map_handler }}))
  }
}

pub(crate) struct BodyExpr(pub(crate) Vec<Stmt>);

impl Parse for BodyExpr {
  fn parse(input: ParseStream) -> syn::Result<Self> { Ok(Self(syn::Block::parse_within(input)?)) }
}

impl BodyExpr {
  pub fn fold(self, refs_ctx: &mut DollarRefsCtx) -> Self {
    Self(
      self
        .0
        .into_iter()
        .map(|s| refs_ctx.fold_stmt(s))
        .collect::<Vec<_>>(),
    )
  }
}