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::{Ident, TokenStream};
use quote::{ToTokens, quote_spanned};
use syn::{
  AngleBracketedGenericArguments, Expr, Member, Result, Token, parenthesized,
  parse::{Parse, ParseStream},
  punctuated::Punctuated,
  token::Paren,
};

use crate::{
  symbol_process::{DollarRef, DollarRefsCtx, DollarUsedInfo},
  variable_names::{BUILTIN_INFOS, BuiltinMemberType},
};

pub fn gen_part_wrier(input: TokenStream, refs_ctx: &mut DollarRefsCtx) -> TokenStream {
  match syn::parse2::<PartState>(input) {
    Ok(part) => {
      let info = part.state_info(refs_ctx);
      let host = part.host_tokens(&info, refs_ctx);
      let PartState { and_token, mutability, state, dot, part_expr, tail_dot, tail_expr } = part;
      let tokens = quote_spanned! { state.span() =>
        #host #dot map_writer(
          |w| PartMut::new(#and_token #mutability w #dot #part_expr #tail_dot #tail_expr)
        )
      };
      refs_ctx.add_dollar_ref(info);
      tokens
    }
    Err(err) => err.to_compile_error(),
  }
}

pub fn gen_split_wrier(input: TokenStream, refs_ctx: &mut DollarRefsCtx) -> TokenStream {
  match syn::parse2::<PartState>(input) {
    Ok(part) => {
      let info = part.state_info(refs_ctx);
      let host = part.host_tokens(&info, refs_ctx);
      let PartState { and_token, mutability, state, dot, part_expr, tail_dot, tail_expr } = part;
      let tokens = quote_spanned! { state.span() =>
        #host #dot split_writer(
          |w| PartMut::new(#and_token #mutability w #dot #part_expr #tail_dot #tail_expr)
        )
      };
      refs_ctx.add_dollar_ref(info);
      tokens
    }
    Err(err) => err.to_compile_error(),
  }
}

pub fn gen_part_reader(input: TokenStream, refs_ctx: &mut DollarRefsCtx) -> TokenStream {
  match syn::parse2::<PartState>(input) {
    Ok(part) => {
      let info = part.state_info(refs_ctx);
      let host = part.host_tokens(&info, refs_ctx);
      let PartState { and_token, mutability, state, dot, part_expr, tail_dot, tail_expr } = part;
      let tokens = quote_spanned! { state.span() =>
        #host #dot map_reader(
          |r| PartRef::new(#and_token #mutability r #dot #part_expr #tail_dot #tail_expr)
        )
      };
      refs_ctx.add_dollar_ref(info);
      tokens
    }
    Err(err) => err.to_compile_error(),
  }
}

pub fn gen_part_watcher(input: TokenStream, refs_ctx: &mut DollarRefsCtx) -> TokenStream {
  match syn::parse2::<PartState>(input) {
    Ok(part) => {
      let info = part.state_info(refs_ctx);
      let host = part.host_tokens(&info, refs_ctx);
      let PartState { and_token, mutability, state, dot, part_expr, tail_dot, tail_expr } = part;
      let tokens = quote_spanned! { state.span() =>
        #host #dot map_watcher(
          |r| PartRef::new(#and_token #mutability r #dot #part_expr #tail_dot #tail_expr)
        )
      };
      refs_ctx.add_dollar_ref(info);
      tokens
    }
    Err(err) => err.to_compile_error(),
  }
}

struct PartState {
  and_token: Option<Token![&]>,
  mutability: Option<Token![mut]>,
  state: Ident,
  dot: Token![.],
  part_expr: PartExpr,
  tail_dot: Option<Token![.]>,
  tail_expr: Option<Expr>,
}

enum PartExpr {
  Member(Member),
  Method {
    method: Ident,
    turbofish: Option<AngleBracketedGenericArguments>,
    paren_token: Paren,
    args: Punctuated<Expr, Token![,]>,
  },
}

impl Parse for PartState {
  fn parse(input: ParseStream) -> Result<Self> {
    let and_token = input.parse()?;
    let mutability = input.parse()?;
    let state = if input.peek(Token![self]) {
      let this = input.parse::<Token![self]>()?;
      Ident::from(this)
    } else {
      input.parse()?
    };
    let dot = input.parse()?;

    let part_expr = if input.peek2(Token![::]) || input.peek2(Paren) {
      let method = input.parse()?;
      let turbofish = if input.peek(Token![::]) {
        Some(AngleBracketedGenericArguments::parse_turbofish(input)?)
      } else {
        None
      };
      let content;
      PartExpr::Method {
        method,
        turbofish,
        paren_token: parenthesized!(content in input),
        args: content.parse_terminated(Expr::parse, Token![,])?,
      }
    } else {
      PartExpr::Member(input.parse()?)
    };

    let tail_dot = input.parse()?;
    let tail_expr = if input.is_empty() { None } else { Some(input.parse()?) };
    Ok(Self { and_token, mutability, state, dot, part_expr, tail_dot, tail_expr })
  }
}

impl PartState {
  fn state_info(&self, refs_ctx: &DollarRefsCtx) -> DollarRef {
    let builtin_info = match &self.part_expr {
      PartExpr::Member(Member::Named(member)) => BUILTIN_INFOS
        .get(&member.to_string())
        .filter(|info| info.mem_ty == BuiltinMemberType::Field),
      PartExpr::Method { method, .. } => BUILTIN_INFOS
        .get(&method.to_string())
        .filter(|info| info.mem_ty == BuiltinMemberType::Method),
      _ => None,
    };
    if let Some(info) = builtin_info {
      refs_ctx.builtin_dollar_ref(self.state.clone(), info, DollarUsedInfo::Writer)
    } else {
      DollarRef { name: self.state.clone(), builtin: None, used: DollarUsedInfo::Writer }
    }
  }

  fn host_tokens(&self, writer_info: &DollarRef, refs_ctx: &DollarRefsCtx) -> TokenStream {
    if writer_info.builtin.is_some() {
      refs_ctx.builtin_host_tokens(writer_info)
    } else {
      self.state.to_token_stream()
    }
  }
}

impl ToTokens for PartExpr {
  fn to_tokens(&self, tokens: &mut TokenStream) {
    match self {
      PartExpr::Member(member) => member.to_tokens(tokens),
      PartExpr::Method { method, turbofish, paren_token, args } => {
        method.to_tokens(tokens);
        turbofish.to_tokens(tokens);
        paren_token.surround(tokens, |tokens| args.to_tokens(tokens));
      }
    }
  }
}