use std::collections::HashSet; use std::path::PathBuf; use itertools::Itertools; use proc_macro2::{Delimiter, Group, Ident}; use quote::{format_ident, quote, ToTokens}; const CNT: usize = 4; fn main() { let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); let impls = (0..CNT).flat_map(create_input_filter_impls).join("\n"); std::fs::write(out_dir.join("system_input_impls.rs"), impls).unwrap(); } fn create_input_filter_impls(cnt: usize) -> Vec { let mut present = HashSet::new(); let elements = (0..cnt) .map(|_| ElementKind::Element) .chain(vec![ElementKind::Excluded; cnt]) .permutations(cnt) .filter(|combination| { if present.contains(combination) { return false; } present.insert(combination.clone()); true }) .map(|elements| { elements .into_iter() .enumerate() .map(|(index, element)| match element { ElementKind::Element => { IdentOrTuple::Ident(format_ident!("Elem{index}")) } ElementKind::Excluded => IdentOrTuple::Tuple, }) .collect::>() }) .collect::>(); elements .into_iter() .map(create_single_input_filter_impl) .collect() } fn create_single_input_filter_impl( elements: Vec, ) -> proc_macro2::TokenStream { let ident_elements = elements .iter() .filter(|element| matches!(element, IdentOrTuple::Ident(_))) .collect::>(); quote! { impl<#(#ident_elements: Input,)*> InputFilter for (#(#elements,)*) { type Filtered = (#(#ident_elements,)*); } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] enum ElementKind { Element, Excluded, } #[derive(Debug)] enum IdentOrTuple { Ident(Ident), Tuple, } impl ToTokens for IdentOrTuple { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { match self { Self::Ident(ident) => ident.to_tokens(tokens), Self::Tuple => { Group::new(Delimiter::Parenthesis, proc_macro2::TokenStream::new()) .to_tokens(tokens) } } } }