lucia-macros 0.2.0

Procedural macros for lucia
Documentation
mod attrs;

use crate::{misc::create_ident, transport_group::TransportGroup};
use alloc::borrow::Cow;
use proc_macro2::{Ident, Span};
use quote::ToTokens;
use syn::{
  parse_macro_input, punctuated::Punctuated, spanned::Spanned, AttributeArgs, Item, Path,
  PathArguments, PathSegment,
};

pub(crate) fn api_types(
  attrs: proc_macro::TokenStream,
  item: proc_macro::TokenStream,
) -> crate::Result<proc_macro::TokenStream> {
  let attr_args = parse_macro_input::parse::<AttributeArgs>(attrs)?;
  let mut item: Item = syn::parse_macro_input::parse(item)?;

  let attrs::Attrs { pkgs_aux, transports } = attrs::Attrs::try_from(&*attr_args)?;

  let pkgs_aux_path = pkgs_aux.map_or_else(
    || {
      let mut segments = Punctuated::new();
      segments.push(PathSegment {
        ident: Ident::new("PkgsAux", Span::mixed_site()),
        arguments: PathArguments::None,
      });
      Cow::Owned(Path { leading_colon: None, segments })
    },
    Cow::Borrowed,
  );

  let api_ident = match item {
    Item::Enum(ref container) => &container.ident,
    Item::Struct(ref container) => &container.ident,
    Item::Type(ref mut container) => &container.ident,
    Item::Const(_)
    | Item::ExternCrate(_)
    | Item::Fn(_)
    | Item::ForeignMod(_)
    | Item::Impl(_)
    | Item::Macro(_)
    | Item::Macro2(_)
    | Item::Mod(_)
    | Item::Static(_)
    | Item::Trait(_)
    | Item::TraitAlias(_)
    | Item::Union(_)
    | Item::Use(_)
    | Item::Verbatim(_)
    | _ => return Err(crate::Error::NoEnumStructOrType(item.span())),
  };

  let mut buffer = String::new();
  buffer.push_str(&api_ident.to_string());

  let generic_pair_ident = create_ident(&mut buffer, ["Pair"]);
  let generic_pair_tt = quote::quote_spanned!(api_ident.span() =>
    #[allow(unused_qualifications)]
    #[doc = concat!("[lucia::misc::Pair] with [", stringify!(#api_ident), "] as the API.")]
    pub type #generic_pair_ident<DRSR, T> = lucia::misc::Pair<
      #pkgs_aux_path<#api_ident, DRSR, <T as lucia::network::transport::Transport<DRSR>>::Params>,
      T
    >;
  );

  let generic_pkgs_aux_ident = create_ident(&mut buffer, ["PkgsAux"]);
  let generic_pkgs_aux_tt = quote::quote_spanned!(api_ident.span() =>
    #[doc = concat!("[", stringify!(#pkgs_aux_path), "] with [", stringify!(#api_ident), "] as the API.")]
    pub type #generic_pkgs_aux_ident<DRSR, TP> = #pkgs_aux_path<#api_ident, DRSR, TP>;
  );

  let mut tys = Vec::new();
  let mut custom_placeholder;

  for transport in transports {
    let [camel_abbr, params] = match transport {
      TransportGroup::Custom(tt) => {
        custom_placeholder = tt.to_string();
        [custom_placeholder.as_str(), custom_placeholder.as_str()]
      }
      TransportGroup::Http => ["Http", "HttpParams"],
      TransportGroup::Stub => ["Stub", "()"],
      TransportGroup::Tcp => ["Tcp", "TcpParams"],
      TransportGroup::Udp => ["Udp", "UdpParams"],
      TransportGroup::WebSocket => ["Ws", "WsParams"],
    };
    let local_tp_ident = Ident::new(params, Span::mixed_site());
    let local_ty_ident = create_ident(&mut buffer, [camel_abbr, "PkgsAux"]);
    tys.push(quote::quote!(
      #[allow(unused_qualifications)]
      #[doc = concat!(
        "[", stringify!(#pkgs_aux_path), "] with [",
        stringify!(#api_ident),
        "] as the API and [lucia::network::",
        stringify!(#local_tp_ident),
        "] as the transport parameters."
      )]
      pub type #local_ty_ident<DRSR> = #pkgs_aux_path<#api_ident, DRSR, lucia::network::#local_tp_ident>;
    ));
  }

  Ok(
    quote::quote!(
      #item
      #generic_pair_tt
      #generic_pkgs_aux_tt
      #(#tys)*
    )
    .to_token_stream()
    .into(),
  )
}