ttgraph_macros 0.5.0

Proc-macros for TTGraph
Documentation
use std::collections::BTreeMap;

use proc_macro2::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{self, braced, Ident, Token};

use super::*;

pub(crate) struct NamedGroup {
  pub name: Ident,
  pub idents: Vec<Ident>,
}

impl Parse for NamedGroup {
  fn parse(input: ParseStream) -> syn::Result<Self> {
    let name: Ident = input.parse()?;
    let content;
    let _ = braced!(content in input);
    let inside = content.parse_terminated(Ident::parse, Token![,])?;
    Ok(NamedGroup {
      name,
      idents: inside.into_iter().collect(),
    })
  }
}

pub(crate) struct NamedGroupVec {
  pub groups: Vec<NamedGroup>,
}

impl Parse for NamedGroupVec {
  fn parse(input: ParseStream) -> syn::Result<Self> {
    let inside = input.parse_terminated(NamedGroup::parse, Token![,])?;
    Ok(NamedGroupVec { groups: inside.into_iter().collect() })
  }
}

pub(crate) fn make_get_links_by_group(links: &[LinkType], groups: &[Vec<Ident>]) -> TokenStream {
  let mut group_map: BTreeMap<Ident, Vec<LinkType>> = BTreeMap::new();
  for (link, group) in links.iter().zip(groups.iter()) {
    for g in group {
      group_map.entry(g.clone()).or_default().push(link.clone());
    }
  }

  let mut arms = Vec::new();
  for (group_name, links) in group_map {
    let mut extends = Vec::new();
    for link in links {
      extends.push(match link {
        LinkType::Direct(ident, _) => {
          quote! {if !self.#ident.is_empty() {result.push(self.#ident);}}
        },
        LinkType::Set(ident, _) => quote! {result.extend(self.#ident.clone());},
        LinkType::Vec(ident, _) => quote! {result.extend(self.#ident.clone());},
        LinkType::Empty => quote! {},
      });
    }
    arms.push(quote! {
      std::stringify!(#group_name) => {
        let mut result = Vec::new();
        #(#extends)*
        result
      },
    });
  }
  quote! {
    fn get_links_by_group(&self, name: &'static str) -> Vec<ttgraph::NodeIndex> {
      match name{
        #(#arms)*
        _ => vec![],
      }
    }
  }
}

pub(crate) fn make_get_link_or_group(links: &[LinkType], group_map: &BTreeMap<Ident, Vec<Ident>>) -> TokenStream {
  let mut arms = Vec::new();
  for l in links {
    match l {
      LinkType::Direct(ident, camel) => {
        arms.push(quote! {std::stringify!(#ident) => Some(Self::LoGMirror::#camel),});
      },
      LinkType::Set(ident, camel) => {
        arms.push(quote! {std::stringify!(#ident) => Some(Self::LoGMirror::#camel),});
      },
      LinkType::Vec(ident, camel) => {
        arms.push(quote! {std::stringify!(#ident) => Some(Self::LoGMirror::#camel),});
      },
      LinkType::Empty => {},
    }
  }
  for (g, _) in group_map {
    let c = upper_camel(g);
    arms.push(quote! {std::stringify!(#g) => Some(Self::LoGMirror::#c),});
  }
  quote! {
    fn get_link_or_group_by_name(name: &'static str) -> Option<Self::LoGMirror> {
      match name {
        #(#arms)*
        _ => None
      }
    }
  }
}

pub(crate) fn make_in_group(groups: &[NamedGroup]) -> TokenStream {
  let mut name_arms = Vec::new();
  for NamedGroup { name, idents } in groups {
    let mut matched_vars = Vec::new();
    for i in idents {
      matched_vars.push(quote! {Self::#i(_)});
    }
    name_arms.push(quote! {
      std::stringify!(#name) => match self {
        #(#matched_vars)|* => true,
        _ => false,
      },
    });
  }
  quote! {
    fn in_group(&self, name: &'static str) -> bool {
      match name {
        #(#name_arms)*
        _ => false,
      }
    }
  }
}