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

@ -21,3 +21,5 @@ workspace = true
[dev-dependencies.trybuild] [dev-dependencies.trybuild]
workspace = true workspace = true
[dev-dependencies.physical-node]
path = "../../node"

View File

@ -1,45 +1,155 @@
use proc_macro::TokenStream; use proc_macro::TokenStream;
use std::ops::Deref;
use quote::quote; 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 // Struct name: Ads1256PollStateful, Ads1256PollPublish, Ads1256PollStatePub
#[proc_macro_derive(PollVariants)] #[proc_macro_derive(PollVariants, attributes(value_type))]
pub fn transducer_macro(input: TokenStream) -> TokenStream { pub fn poll_variant_macro(input: TokenStream) -> TokenStream {
// ----- Parse input ----------------------------------
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
let DeriveInput { let attrs = &input.attrs;
attrs, let vis = &input.vis;
vis, let ident = &input.ident;
ident, let data = &input.data;
generics, let og_generics = &input.generics;
data, let (og_impl_generics, og_type_generics, og_where_clause) = &og_generics.split_for_impl();
} = &input;
// Check that item the macro was used on is valid // ----- Check that item the macro was used on is a struct ----------------------------------
match data { match data {
Data::Struct(struct_data) => struct_data, Data::Struct(struct_data) => struct_data,
Data::Enum(_) => panic!("Stateful struct cannot be derived from an enum."), 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 // ----- Extract value type attribute ----------------------------------
// If it isn't Copy, panic 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 mut generics_with_t = og_generics.clone();
let stateful_ident = Ident::new(stateful_name.deref(), ident.span()); // // 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:#?}"); let stateful_ident = Ident::new(format!("{ident}Stateful").deref(), ident.span());
eprintln!("impl_generics: {impl_generics:#?}");
let expanded = quote! { let expanded = quote! {
#vis struct #stateful_ident #generics { // ----- Stateful struct ----------------------------------
pub poll: #ident #ty_generics, #vis struct #stateful_ident #og_generics #og_where_clause {
pub state: physical::transducer::State<T>, 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) TokenStream::from(expanded)

View File

@ -1,11 +1,29 @@
use node_poll_variants::{PollVariants}; #![feature(async_fn_in_trait, impl_trait_projections)]
use node_poll_variants::PollVariants;
use physical_node::transducer::input::Poll;
#[derive(PollVariants)] #[derive(PollVariants)]
struct ExamplePoll<'a, FirstT: Copy, SecondT> { #[value_type = "SecondT"]
struct ExamplePoll<'a, FirstT, SecondT>
where
SecondT: Copy,
{
a: &'a i32, a: &'a i32,
b: i32, b: i32,
first: FirstT, first: FirstT,
second: SecondT, second: SecondT,
} }
impl<'a, FirstT, SecondT> Poll for ExamplePoll<'a, FirstT, SecondT>
where
SecondT: Copy,
{
type Value = SecondT;
async fn poll(&self) -> Self::Value {
self.second
}
}
fn main() {} fn main() {}

View File

@ -1,68 +1 @@
use crate::cell::CellView;
use crate::transducer::{Publish, Publisher};
use core::cell::Cell;
#[cfg(feature = "embassy-sync")]
use embassy_sync::blocking_mutex::raw::RawMutex;
#[cfg(feature = "embassy-sync")]
use embassy_sync::pubsub::{Error, PubSubChannel, Subscriber};
pub use physical::transducer::input::*; pub use physical::transducer::input::*;
use physical::transducer::{State, Stateful};
#[cfg(feature = "embassy-sync")]
pub struct StatefulPublisher<
T: Copy,
MutexT: RawMutex,
const CAPACITY: usize,
const NUM_SUBS: usize,
> {
pub state: State<T>,
pub publisher: Publisher<T, MutexT, CAPACITY, NUM_SUBS>,
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
StatefulPublisher<T, MutexT, CAPACITY, NUM_SUBS>
{
#[inline(always)]
pub fn new(
state: State<T>,
publisher: Publisher<T, MutexT, CAPACITY, NUM_SUBS>,
) -> Self {
Self { state, publisher }
}
#[inline(always)]
pub fn update(&self, value: T) {
self.state.update(value);
self.publisher.update(value);
}
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize> Stateful
for StatefulPublisher<T, MutexT, CAPACITY, NUM_SUBS>
{
type Value = T;
#[inline(always)]
fn state_cell(&self) -> CellView<Self::Value> {
self.state.state_cell()
}
#[inline(always)]
fn state(&self) -> Self::Value {
self.state.state()
}
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
Publish<CAPACITY, NUM_SUBS> for StatefulPublisher<T, MutexT, CAPACITY, NUM_SUBS>
{
type Value = T;
type Mutex = MutexT;
#[inline(always)]
fn subscribe(
&self,
) -> Result<Subscriber<Self::Mutex, Self::Value, CAPACITY, NUM_SUBS, 0>, Error> {
self.publisher.subscribe()
}
}

View File

@ -1,64 +1,8 @@
pub mod input; pub mod input;
pub mod output; pub mod output;
#[cfg(feature = "embassy-sync")]
mod sync;
pub use physical::transducer::*; pub use physical::transducer::*;
#[cfg(feature = "embassy-sync")] #[cfg(feature = "embassy-sync")]
use embassy_sync::blocking_mutex::raw::RawMutex; pub use sync::*;
#[cfg(feature = "embassy-sync")]
use embassy_sync::pubsub;
#[cfg(feature = "embassy-sync")]
use embassy_sync::pubsub::{PubSubBehavior, PubSubChannel, Subscriber};
#[cfg(feature = "embassy-sync")]
pub trait Publish<const CAPACITY: usize, const NUM_SUBS: usize> {
type Value: Copy;
type Mutex: RawMutex;
fn subscribe(
&self,
) -> Result<Subscriber<Self::Mutex, Self::Value, CAPACITY, NUM_SUBS, 0>, pubsub::Error>;
}
#[cfg(feature = "embassy-sync")]
pub struct Publisher<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize> {
channel: PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>,
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
Publisher<T, MutexT, CAPACITY, NUM_SUBS>
{
#[inline(always)]
pub fn new(channel: PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>) -> Self {
Self { channel }
}
#[inline(always)]
pub fn update(&self, value: T) {
self.channel.publish_immediate(value);
}
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
Publish<CAPACITY, NUM_SUBS> for Publisher<T, MutexT, CAPACITY, NUM_SUBS>
{
type Value = T;
type Mutex = MutexT;
#[inline(always)]
fn subscribe(
&self,
) -> Result<Subscriber<Self::Mutex, Self::Value, CAPACITY, NUM_SUBS, 0>, pubsub::Error> {
self.channel.subscriber()
}
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
From<PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>>
for Publisher<T, MutexT, CAPACITY, NUM_SUBS>
{
#[inline(always)]
fn from(channel: PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>) -> Self {
Publisher::new(channel)
}
}

View File

@ -0,0 +1,61 @@
use crate::cell::CellView;
use crate::transducer::{Publish, Publisher};
use core::cell::Cell;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::pubsub::{Error, PubSubChannel, Subscriber};
use physical::transducer::{State, Stateful};
pub struct StatefulPublisher<
T: Copy,
MutexT: RawMutex,
const CAPACITY: usize,
const NUM_SUBS: usize,
> {
pub state: State<T>,
pub publisher: Publisher<T, MutexT, CAPACITY, NUM_SUBS>,
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
StatefulPublisher<T, MutexT, CAPACITY, NUM_SUBS>
{
#[inline(always)]
pub fn new(state: State<T>, publisher: Publisher<T, MutexT, CAPACITY, NUM_SUBS>) -> Self {
Self { state, publisher }
}
#[inline(always)]
pub fn update(&self, value: T) {
self.state.update(value);
self.publisher.update(value);
}
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize> Stateful
for StatefulPublisher<T, MutexT, CAPACITY, NUM_SUBS>
{
type Value = T;
#[inline(always)]
fn state_cell(&self) -> CellView<Self::Value> {
self.state.state_cell()
}
#[inline(always)]
fn state(&self) -> Self::Value {
self.state.state()
}
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
Publish<CAPACITY, NUM_SUBS> for StatefulPublisher<T, MutexT, CAPACITY, NUM_SUBS>
{
type Value = T;
type Mutex = MutexT;
#[inline(always)]
fn subscribe(
&self,
) -> Result<Subscriber<Self::Mutex, Self::Value, CAPACITY, NUM_SUBS, 0>, Error> {
self.publisher.subscribe()
}
}

View File

@ -0,0 +1,58 @@
mod input;
pub use input::*;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::pubsub;
use embassy_sync::pubsub::{PubSubBehavior, PubSubChannel, Subscriber};
pub trait Publish<const CAPACITY: usize, const NUM_SUBS: usize> {
type Value: Copy;
type Mutex: RawMutex;
fn subscribe(
&self,
) -> Result<Subscriber<Self::Mutex, Self::Value, CAPACITY, NUM_SUBS, 0>, pubsub::Error>;
}
pub struct Publisher<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize> {
channel: PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>,
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
Publisher<T, MutexT, CAPACITY, NUM_SUBS>
{
#[inline(always)]
pub fn new(channel: PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>) -> Self {
Self { channel }
}
#[inline(always)]
pub fn update(&self, value: T) {
self.channel.publish_immediate(value);
}
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
Publish<CAPACITY, NUM_SUBS> for Publisher<T, MutexT, CAPACITY, NUM_SUBS>
{
type Value = T;
type Mutex = MutexT;
#[inline(always)]
fn subscribe(
&self,
) -> Result<Subscriber<Self::Mutex, Self::Value, CAPACITY, NUM_SUBS, 0>, pubsub::Error> {
self.channel.subscriber()
}
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
From<PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>>
for Publisher<T, MutexT, CAPACITY, NUM_SUBS>
{
#[inline(always)]
fn from(channel: PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>) -> Self {
Publisher::new(channel)
}
}

View File

@ -1,4 +1,5 @@
pub trait Poll {
type Value: Copy;
pub trait Poll<T: Copy> { async fn poll(&self) -> Self::Value;
async fn poll() -> T;
} }