#![warn(missing_docs)]
#![warn(clippy::missing_docs_in_private_items)]
use proc_macro::{TokenStream, TokenTree};
use quote::{quote, ToTokens};
use syn::{parse::ParseStream, parse_macro_input, spanned::Spanned, DeriveInput};
#[proc_macro_derive(AuxStorage, attributes(UpdateCycle, UpdateMechanics))]
pub fn aux_storage(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let struct_name = ast.ident;
let struct_generics = ast.generics.clone();
let mut result = TokenStream::new();
let data: syn::DataStruct = match ast.data {
syn::Data::Struct(data) => data,
_ => panic!("Usage of #[UpdateCycle] on a non-struct type"),
};
for field in data.fields.iter() {
if field.attrs.iter().any(|x| match &x.meta {
syn::Meta::Path(path) => path.is_ident("UpdateCycle"),
_ => false,
}) {
let name = &field.ident;
let res2 = quote! {
impl #struct_generics UpdateCycle for #struct_name #struct_generics {
fn set_cycle_events(&mut self, events: Vec<CycleEvent>) {
self.#name.set_cycle_events(events)
}
fn get_cycle_events(&self) -> Vec<CycleEvent> {
self.#name.get_cycle_events()
}
fn add_cycle_event(&mut self, event: CycleEvent) {
self.#name.add_cycle_event(event)
}
}
};
result.extend(TokenStream::from(res2));
}
else if field.attrs.iter().any(|x| match &x.meta {
syn::Meta::Path(path) => path.is_ident("UpdateMechanics"),
_ => false,
}) {
let name = &field.ident;
let generic_args = match &field.ty {
syn::Type::Path(path) => {
path.path
.segments
.first()
.and_then(|segment| match &segment.arguments {
syn::PathArguments::AngleBracketed(arg) => {
Some(arg.args.clone().into_iter().collect::<Vec<_>>())
}
_ => None,
})
}
_ => None,
}
.or(Some(Vec::new()))
.unwrap();
let position_generic = generic_args[0].clone();
let velocity_generic = generic_args[1].clone();
let force_generic = generic_args[2].clone();
let res2 = quote! {
impl #struct_generics UpdateMechanics <#(#generic_args),*> for #struct_name #struct_generics
where
F: Clone + core::ops::AddAssign<F> + num::Zero,
{
fn set_last_position(&mut self, pos: #position_generic) {
self.#name.set_last_position(pos)
}
fn previous_positions(&self) -> std::collections::vec_deque::Iter<#position_generic> {
self.#name.previous_positions()
}
fn set_last_velocity(&mut self, vel: #velocity_generic) {
self.#name.set_last_velocity(vel)
}
fn previous_velocities(&self) -> std::collections::vec_deque::Iter<#velocity_generic> {
self.#name.previous_velocities()
}
fn add_force(&mut self, force: #force_generic) {
self.#name.add_force(force);
}
fn get_current_force(&self) -> #force_generic {
self.#name.get_current_force()
}
fn clear_forces(&mut self) {
self.#name.clear_forces()
}
}
};
result.extend(TokenStream::from(res2));
}
}
TokenStream::from(result)
}
fn parse_non_delimiter_tokens(tokenstream: TokenStream) -> Vec<TokenStream> {
use itertools::Itertools;
tokenstream
.into_iter()
.group_by(|token| match token {
TokenTree::Punct(p) => p.as_char() == char::from(','),
_ => false,
})
.into_iter()
.filter_map(|(is_comma, group)| if !is_comma { Some(group) } else { None })
.map(|group| TokenStream::from_iter(group.into_iter()))
.collect()
}
#[proc_macro]
pub fn identical(tokenstream: TokenStream) -> TokenStream {
let tokens = parse_non_delimiter_tokens(tokenstream);
let tokens_length = tokens.len();
if tokens_length != 3 {
panic!("Macro requires two identifiers to compare against each other and one expression to insert");
} else {
let m1 = tokens[0].clone();
let m2 = tokens[1].clone();
let expr = TokenStream::from_iter(tokens.into_iter().skip(2).into_iter());
if m1.to_string() == m2.to_string() {
expr
} else {
TokenStream::from(quote!())
}
}
}