Device macro to generate stateful and publish variants of device specific poll inputs.
This commit is contained in:
@ -21,3 +21,5 @@ workspace = true
|
||||
|
||||
[dev-dependencies.trybuild]
|
||||
workspace = true
|
||||
[dev-dependencies.physical-node]
|
||||
path = "../../node"
|
@ -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)
|
||||
|
@ -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)]
|
||||
struct ExamplePoll<'a, FirstT: Copy, SecondT> {
|
||||
#[value_type = "SecondT"]
|
||||
struct ExamplePoll<'a, FirstT, SecondT>
|
||||
where
|
||||
SecondT: Copy,
|
||||
{
|
||||
a: &'a i32,
|
||||
b: i32,
|
||||
first: FirstT,
|
||||
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() {}
|
||||
|
@ -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::*;
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -1,64 +1,8 @@
|
||||
pub mod input;
|
||||
pub mod output;
|
||||
#[cfg(feature = "embassy-sync")]
|
||||
mod sync;
|
||||
|
||||
pub use physical::transducer::*;
|
||||
|
||||
#[cfg(feature = "embassy-sync")]
|
||||
use embassy_sync::blocking_mutex::raw::RawMutex;
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
pub use sync::*;
|
||||
|
61
node/src/transducer/sync/input.rs
Normal file
61
node/src/transducer/sync/input.rs
Normal 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()
|
||||
}
|
||||
}
|
58
node/src/transducer/sync/mod.rs
Normal file
58
node/src/transducer/sync/mod.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
pub trait Poll {
|
||||
type Value: Copy;
|
||||
|
||||
pub trait Poll<T: Copy> {
|
||||
async fn poll() -> T;
|
||||
async fn poll(&self) -> Self::Value;
|
||||
}
|
||||
|
Reference in New Issue
Block a user