diff --git a/macros/node-poll-variants/Cargo.toml b/macros/node-poll-variants/Cargo.toml index 85c0a16..332edc4 100644 --- a/macros/node-poll-variants/Cargo.toml +++ b/macros/node-poll-variants/Cargo.toml @@ -20,4 +20,6 @@ workspace = true workspace = true [dev-dependencies.trybuild] -workspace = true \ No newline at end of file +workspace = true +[dev-dependencies.physical-node] +path = "../../node" \ No newline at end of file diff --git a/macros/node-poll-variants/src/lib.rs b/macros/node-poll-variants/src/lib.rs index a75bde3..03675f3 100644 --- a/macros/node-poll-variants/src/lib.rs +++ b/macros/node-poll-variants/src/lib.rs @@ -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 = 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 = 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 = 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, + // ----- 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.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) diff --git a/macros/node-poll-variants/tests/generate.rs b/macros/node-poll-variants/tests/generate.rs index d24d483..01cf375 100644 --- a/macros/node-poll-variants/tests/generate.rs +++ b/macros/node-poll-variants/tests/generate.rs @@ -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() {} diff --git a/node/src/transducer/input.rs b/node/src/transducer/input.rs index dedbf72..84eb5d9 100644 --- a/node/src/transducer/input.rs +++ b/node/src/transducer/input.rs @@ -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, - pub publisher: Publisher, -} - -impl - StatefulPublisher -{ - #[inline(always)] - pub fn new( - state: State, - publisher: Publisher, - ) -> Self { - Self { state, publisher } - } - - #[inline(always)] - pub fn update(&self, value: T) { - self.state.update(value); - self.publisher.update(value); - } -} - -impl Stateful - for StatefulPublisher -{ - type Value = T; - - #[inline(always)] - fn state_cell(&self) -> CellView { - self.state.state_cell() - } - - #[inline(always)] - fn state(&self) -> Self::Value { - self.state.state() - } -} - -impl - Publish for StatefulPublisher -{ - type Value = T; - type Mutex = MutexT; - - #[inline(always)] - fn subscribe( - &self, - ) -> Result, Error> { - self.publisher.subscribe() - } -} diff --git a/node/src/transducer/mod.rs b/node/src/transducer/mod.rs index a9ac243..e8e7c3d 100644 --- a/node/src/transducer/mod.rs +++ b/node/src/transducer/mod.rs @@ -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 { - type Value: Copy; - type Mutex: RawMutex; - - fn subscribe( - &self, - ) -> Result, pubsub::Error>; -} - -#[cfg(feature = "embassy-sync")] -pub struct Publisher { - channel: PubSubChannel, -} - -impl - Publisher -{ - #[inline(always)] - pub fn new(channel: PubSubChannel) -> Self { - Self { channel } - } - - #[inline(always)] - pub fn update(&self, value: T) { - self.channel.publish_immediate(value); - } -} - -impl - Publish for Publisher -{ - type Value = T; - type Mutex = MutexT; - - #[inline(always)] - fn subscribe( - &self, - ) -> Result, pubsub::Error> { - self.channel.subscriber() - } -} - -impl -From> -for Publisher -{ - #[inline(always)] - fn from(channel: PubSubChannel) -> Self { - Publisher::new(channel) - } -} +pub use sync::*; diff --git a/node/src/transducer/sync/input.rs b/node/src/transducer/sync/input.rs new file mode 100644 index 0000000..e66a306 --- /dev/null +++ b/node/src/transducer/sync/input.rs @@ -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, + pub publisher: Publisher, +} + +impl + StatefulPublisher +{ + #[inline(always)] + pub fn new(state: State, publisher: Publisher) -> Self { + Self { state, publisher } + } + + #[inline(always)] + pub fn update(&self, value: T) { + self.state.update(value); + self.publisher.update(value); + } +} + +impl Stateful + for StatefulPublisher +{ + type Value = T; + + #[inline(always)] + fn state_cell(&self) -> CellView { + self.state.state_cell() + } + + #[inline(always)] + fn state(&self) -> Self::Value { + self.state.state() + } +} + +impl + Publish for StatefulPublisher +{ + type Value = T; + type Mutex = MutexT; + + #[inline(always)] + fn subscribe( + &self, + ) -> Result, Error> { + self.publisher.subscribe() + } +} diff --git a/node/src/transducer/sync/mod.rs b/node/src/transducer/sync/mod.rs new file mode 100644 index 0000000..0ac16ba --- /dev/null +++ b/node/src/transducer/sync/mod.rs @@ -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 { + type Value: Copy; + type Mutex: RawMutex; + + fn subscribe( + &self, + ) -> Result, pubsub::Error>; +} + +pub struct Publisher { + channel: PubSubChannel, +} + +impl +Publisher +{ + #[inline(always)] + pub fn new(channel: PubSubChannel) -> Self { + Self { channel } + } + + #[inline(always)] + pub fn update(&self, value: T) { + self.channel.publish_immediate(value); + } +} + +impl +Publish for Publisher +{ + type Value = T; + type Mutex = MutexT; + + #[inline(always)] + fn subscribe( + &self, + ) -> Result, pubsub::Error> { + self.channel.subscriber() + } +} + +impl +From> +for Publisher +{ + #[inline(always)] + fn from(channel: PubSubChannel) -> Self { + Publisher::new(channel) + } +} \ No newline at end of file diff --git a/src/transducer/input.rs b/src/transducer/input.rs index d03cc81..43b8fa4 100644 --- a/src/transducer/input.rs +++ b/src/transducer/input.rs @@ -1,4 +1,5 @@ +pub trait Poll { + type Value: Copy; -pub trait Poll { - async fn poll() -> T; + async fn poll(&self) -> Self::Value; }