Device macro to generate stateful and publish variants of device specific poll inputs.

This commit is contained in:
Zachary Sunforge
2023-06-19 19:24:47 -07:00
parent 9ea0e69d92
commit 0c7839491d
8 changed files with 283 additions and 156 deletions

View File

@ -1,45 +1,155 @@
use proc_macro::TokenStream;
use std::ops::Deref;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Data, Ident};
use std::ops::Deref;
use std::path::Path;
use syn::__private::Span;
use syn::punctuated::Punctuated;
use syn::token::{Colon, PathSep, Plus};
use syn::{parse_macro_input, Data, DeriveInput, GenericParam, Ident, PathSegment, Token, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, Meta, Expr, Lit};
// Struct name: Ads1256PollStateful, Ads1256PollPublish, Ads1256PollStatePub
#[proc_macro_derive(PollVariants)]
pub fn transducer_macro(input: TokenStream) -> TokenStream {
#[proc_macro_derive(PollVariants, attributes(value_type))]
pub fn poll_variant_macro(input: TokenStream) -> TokenStream {
// ----- Parse input ----------------------------------
let input = parse_macro_input!(input as DeriveInput);
let DeriveInput {
attrs,
vis,
ident,
generics,
data,
} = &input;
// Check that item the macro was used on is valid
let attrs = &input.attrs;
let vis = &input.vis;
let ident = &input.ident;
let data = &input.data;
let og_generics = &input.generics;
let (og_impl_generics, og_type_generics, og_where_clause) = &og_generics.split_for_impl();
// ----- Check that item the macro was used on is a struct ----------------------------------
match data {
Data::Struct(struct_data) => struct_data,
Data::Enum(_) => panic!("Stateful struct cannot be derived from an enum."),
Data::Union(_) => panic!("Stateful struct cannot be derived from a union.")
Data::Union(_) => panic!("Stateful struct cannot be derived from a union."),
};
// Check if generics contains T
// If it does make sure it is T: Copy
// If it isn't Copy, panic
// ----- Extract value type attribute ----------------------------------
const VALUE_T_NAME: &str = "value_type";
let mut value_type_ident: Option<Ident> = None;
for attribute in attrs.iter() {
// if the attribute is a named value
if let Meta::NameValue(meta) = &attribute.meta {
// if the name of the attribute is value_t
if meta.path.segments[0].ident == VALUE_T_NAME {
// if the value of the attribute is a literal
if let Expr::Lit(lit) = &meta.value {
// if the literal is a string
if let Lit::Str(lit) = &lit.lit {
value_type_ident = Some(Ident::new(lit.value().deref(), Span::call_site()));
} else {
panic!("{VALUE_T_NAME} must be set with a string literal.")
}
} else {
panic!("{VALUE_T_NAME} must be set with a literal.")
}
} else {
continue;
}
} else {
continue;
}
};
let value_type_ident = if let Some(val) = value_type_ident {
val
} else {
panic!("Need attribute {VALUE_T_NAME}: #[{VALUE_T_NAME} = \"type\"]")
};
// If it doesn't contain T, add it for use in the type that will be generated
// ----- Build publisher generics ----------------------------------
//TODO: Get rid of all this adding T stuff
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
// // Check if generics contains T
// const T_IDENT_STR: &str = "T";
// const COPY_IDENT_STR: &str = "Copy";
// let mut first_type_index: Option<usize> = None;
// let mut has_t = false;
// for (index, param) in og_generics.params.iter().enumerate() {
// // If the generic parameter is a type
// if let GenericParam::Type(param) = param {
// if first_type_index.is_none() {
// first_type_index = Some(index);
// }
// // If the generic parameter is T
// if param.ident == T_IDENT_STR {
// has_t = true;
// }
// }
// }
let stateful_name = format!("{ident}Stateful");
let stateful_ident = Ident::new(stateful_name.deref(), ident.span());
// let mut generics_with_t = og_generics.clone();
// // If T is not a generic parameter, add it
// if !has_t {
// let first_type_index = first_type_index.unwrap_or(0);
// let t_ident = Ident::new(T_IDENT_STR, Span::call_site());
// let copy_ident = Ident::new(COPY_IDENT_STR, Span::call_site());
// let mut t_bounds: Punctuated<TypeParamBound, Plus> = Punctuated::new();
//
// let t_bound = TraitBound {
// paren_token: None,
// modifier: TraitBoundModifier::None,
// lifetimes: None,
// path: copy_ident.into(),
// };
// t_bounds.push(t_bound.into());
//
// let t_param: TypeParam = TypeParam {
// attrs: Vec::new(),
// ident: t_ident,
// colon_token: Some(Colon::default()),
// bounds: t_bounds,
// eq_token: None,
// default: None,
// };
// generics_with_t
// .params
// .insert(first_type_index, t_param.into());
// }
eprintln!("input: {input:#?}");
eprintln!("impl_generics: {impl_generics:#?}");
let stateful_ident = Ident::new(format!("{ident}Stateful").deref(), ident.span());
let expanded = quote! {
#vis struct #stateful_ident #generics {
pub poll: #ident #ty_generics,
pub state: physical::transducer::State<T>,
// ----- Stateful struct ----------------------------------
#vis struct #stateful_ident #og_generics #og_where_clause {
pub poll: #ident #og_type_generics,
pub state: physical_node::transducer::State<#value_type_ident>,
}
// ----- Stateful impls ----------------------------------
impl #og_impl_generics physical_node::transducer::Stateful for #stateful_ident #og_type_generics #og_where_clause {
type Value = #value_type_ident;
#[inline(always)]
fn state_cell(&self) -> physical_node::cell::CellView<Self::Value> {
self.state.state_cell()
}
#[inline(always)]
fn state(&self) -> Self::Value {
self.state.state()
}
}
impl #og_impl_generics physical_node::transducer::input::Poll for #stateful_ident #og_type_generics #og_where_clause {
type Value = #value_type_ident;
#[inline(always)]
async fn poll(&self) -> Self::Value {
let value = self.poll.poll().await;
self.state.update(value);
value
}
}
// ----- Publish struct ----------------------------------
// ----- Publish impl ----------------------------------
// ----- StatePub struct ----------------------------------
// ----- StatePub impl ----------------------------------
};
TokenStream::from(expanded)