1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
//! This crate exposes the 'Candle' macro,
//! It combines multiple types that implement 'CandleComponent'
//! into a single 'ModularCandle' struct
//! which can then be used as the output type of some aggregation process.
//! It also exposes getter methods for each 'CandleComponent' for convenience.
//! The name of the getter method is equivalent to the field name.
//! e.g.:
//! struct MyCandle {
//! open: Open,
//! }
//! with the derive macro will create a "fn open(&self)" method which gets the inner value
//!
//! When deriving the 'Candle' macro, make sure the following things are in scope:
//! - Trade
//! - ModularCandle
//! - CandleComponent
#![deny(missing_docs)]
use proc_macro::TokenStream;
use quote::{__private::Span, quote};
use syn::{
self, AngleBracketedGenericArguments, Data, DataStruct, Fields, GenericArgument, Ident, Type,
TypePath,
};
/// The 'Candle' macro takes a named struct,
/// that has multiple fields of type 'CandleComponent'
/// to automatically generate a struct that implements
/// the 'ModularCandle' trait, which means it can then be used
/// in the aggregation process.
/// It also exposes getter functions for each 'CandleComponent' for convenience.
#[proc_macro_derive(Candle)]
pub fn candle_macro_derive(input: TokenStream) -> TokenStream {
// Construct a representation of Rust code as a syntax tree
// that we can manipulate
let ast = syn::parse(input).unwrap();
// Build the trait implementation
impl_candle_macro(&ast)
}
fn phantom_path_to_type(path: &syn::Path) -> Option<Ident> {
if path.leading_colon.is_some() {
return None;
}
let mut it = path.segments.iter();
let segment = it.next()?;
match &segment.arguments {
syn::PathArguments::AngleBracketed(AngleBracketedGenericArguments { args: x, .. }) => {
match x.first() {
Some(GenericArgument::Type(Type::Path(TypePath {
path: syn::Path { segments: segs, .. },
..
}))) => segs.first().and_then(|x| Some(x.ident.clone())),
_ => None,
}
}
_ => None,
}
}
fn impl_candle_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let components = match &ast.data {
Data::Struct(DataStruct {
fields: Fields::Named(fields),
..
}) => &fields.named,
_ => panic!("Use a named struct"),
};
let default_output_type = Ident::new("f64", Span::call_site());
let mut input_type = Some(Ident::new("Trade", Span::call_site()));
let mut value_idents = vec![];
let mut value_types = vec![];
for c in components {
if let syn::Field {
ty: syn::Type::Path(syn::TypePath { path: p, .. }),
ident,
..
} = c
{
if ident.clone().unwrap().to_string().as_str() == "input" {
input_type = phantom_path_to_type(&p);
} else {
if let Some(type_) = phantom_path_to_type(&p) {
value_idents.push(ident);
value_types.push(type_);
} else {
value_idents.push(ident);
value_types.push(default_output_type.clone());
}
}
}
}
let fn_names0 = value_idents.clone();
let fn_names1 = fn_names0.clone();
let fn_names2 = fn_names1.clone();
let input_name = input_type.expect("No PhantomData for input attribute type!");
println!("impl building for {name}");
let gen = quote! {
impl #name {
#(
pub fn #fn_names0(&self) -> #value_types {
self.#fn_names0.value()
}
)*
}
impl ModularCandle<#input_name> for #name {
fn update(&mut self, trade: &#input_name) {
#(
self.#fn_names1.update(trade);
)*
}
fn reset(&mut self) {
#(
self.#fn_names2.reset();
)*
}
}
};
gen.into()
}