diff --git a/pulse-macros/src/lib.rs b/pulse-macros/src/lib.rs index cd62b37..e852c81 100644 --- a/pulse-macros/src/lib.rs +++ b/pulse-macros/src/lib.rs @@ -1,11 +1,23 @@ use proc_macro::TokenStream; use quote::quote; -use syn::{Fields, ItemStruct, parse_macro_input}; +use syn::{Fields, ItemEnum, ItemStruct, parse_macro_input}; #[proc_macro_attribute] pub fn p_com(_: TokenStream, item: TokenStream) -> TokenStream { - let mut input = parse_macro_input!(item as ItemStruct); + let input = parse_macro_input!(item as syn::Item); + match input { + syn::Item::Struct(s) => expand_struct(s), + syn::Item::Enum(e) => expand_enum(e), + _ => { + return syn::Error::new_spanned(input, "p_com only supports structs and enums") + .to_compile_error() + .into(); + } + } +} + +fn expand_struct(mut input: ItemStruct) -> TokenStream { let name = &input.ident; match &mut input.fields { @@ -59,3 +71,104 @@ pub fn p_com(_: TokenStream, item: TokenStream) -> TokenStream { } }) } + +fn expand_enum(input: ItemEnum) -> TokenStream { + let name = &input.ident; + + let to_com = input.variants.iter().enumerate().map(|(i, variant)| { + let ident = &variant.ident; + let tag = i as u8; + + match &variant.fields { + Fields::Unit => quote! { + Self::#ident => { + vec.push(#tag); + } + }, + + Fields::Unnamed(fields) if fields.unnamed.len() == 1 => quote! { + Self::#ident(v) => { + vec.push(#tag); + vec.extend(v.to_com()); + } + }, + + Fields::Named(fields) => { + let names = fields.named.iter().map(|f| f.ident.as_ref().unwrap()); + + let names2 = fields.named.iter().map(|f| f.ident.as_ref().unwrap()); + + quote! { + Self::#ident { #( #names ),* } => { + vec.push(#tag); + #( vec.extend(#names2.to_com()); )* + } + } + } + + _ => { + panic!("tuple variants with >1 field are not supported"); + } + } + }); + + let from_com = input.variants.iter().enumerate().map(|(i, variant)| { + let ident = &variant.ident; + let tag = i as u8; + + match &variant.fields { + Fields::Unit => quote! { + #tag => Self::#ident, + }, + + Fields::Unnamed(fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed.first().unwrap().ty; + + quote! { + #tag => Self::#ident(<#ty>::from_com(com)), + } + } + + Fields::Named(fields) => { + let names = fields.named.iter().map(|f| f.ident.as_ref().unwrap()); + let tys = fields.named.iter().map(|f| &f.ty); + + quote! { + #tag => Self::#ident { + #( + #names: <#tys>::from_com(com), + )* + }, + } + } + + _ => panic!("tuple variants with >1 field are not supported"), + } + }); + + TokenStream::from(quote! { + #[derive(Debug, Clone)] + #input + + impl PulseCom for #name { + fn to_com(&self) -> Vec { + let mut vec = Vec::new(); + + match self { + #( #to_com )* + } + + vec + } + + fn from_com(com: &mut Vec) -> Self { + let kind = com.remove(0); + + match kind { + #( #from_com )* + _ => panic!("invalid {} discriminant {}", stringify!(#name), kind), + } + } + } + }) +} diff --git a/src/ptc.rs b/src/ptc.rs index b25b6ee..4dc117c 100644 --- a/src/ptc.rs +++ b/src/ptc.rs @@ -1,35 +1,35 @@ use pulse_macros::p_com; #[p_com] -struct WatchListItem { +pub struct WatchListItem { symbol: String, price: f64, trend: f64, } #[p_com] -struct ActivePosition { +pub struct ActivePosition { symbol: String, profit: f64, amount: f64, } #[p_com] -enum MarketTrend { +pub enum MarketTrend { Bullish, Bearish, Neutral, } #[p_com] -enum Volatility { +pub enum Volatility { Low, Medium, High, } #[p_com] -struct MarketOverview { +pub struct MarketOverview { trend: MarketTrend, volatility: Volatility, pressure: f64, @@ -38,7 +38,7 @@ struct MarketOverview { } #[p_com] -enum Feed { +pub enum Feed { Connected, Disconnected, Connecting, @@ -46,7 +46,7 @@ enum Feed { } #[p_com] -struct Status { +pub struct Status { feed: Feed, exchange: String, dex: String, @@ -54,13 +54,13 @@ struct Status { } #[p_com] -enum SignalKind { +pub enum SignalKind { Buy, Sell, } #[p_com] -enum SignalParameter { +pub enum SignalParameter { Lim, Stl, Tap, @@ -68,7 +68,7 @@ enum SignalParameter { } #[p_com] -struct Signal { +pub struct Signal { kind: SignalKind, symbol: String, param: SignalParameter, @@ -76,7 +76,7 @@ struct Signal { } #[p_com] -enum LogKind { +pub enum LogKind { Info, Warn, Err, @@ -84,27 +84,27 @@ enum LogKind { } #[p_com] -struct EventLog { +pub struct EventLog { kind: LogKind, name: String, message: String, } #[p_com] -enum AlertLevel { +pub enum AlertLevel { High, Medium, Low, } #[p_com] -struct Alert { +pub struct Alert { level: AlertLevel, message: String, } #[p_com] -enum InspectTarget { +pub enum InspectTarget { None, Symbol(WatchListItem), Position(ActivePosition),