use proc_macro::TokenStream;
use quote::ToTokens;
use syn::DeriveInput;
const DEFAULT_RELIABLE_SER_ID: u64 = 150;
const DEFAULT_VERSION_ID: u32 = 1;
pub fn derive_arcon(input: TokenStream) -> TokenStream {
let input: DeriveInput = syn::parse(input).unwrap();
let name = &input.ident;
#[allow(unused)]
let (reliable_ser_id, version) = {
let arcon_attr = input.attrs.iter().find_map(|attr| match attr.parse_meta() {
Ok(m) => {
if m.path().is_ident("arcon") {
Some(m)
} else {
None
}
}
Err(e) => panic!("unable to parse attribute: {}", e),
});
if let Some(meta) = arcon_attr {
let meta_list = match meta {
syn::Meta::List(inner) => inner,
_ => panic!("attribute 'arcon' has incorrect type"),
};
arcon_attr_meta(meta_list)
} else {
let mut doc_attrs = Vec::new();
for i in input.attrs.iter() {
let attr = i.parse_meta().expect("failed to parse meta");
if attr.path().is_ident("doc") {
doc_attrs.push(attr.clone());
}
}
if doc_attrs.is_empty() {
(Some(DEFAULT_RELIABLE_SER_ID), Some(DEFAULT_VERSION_ID))
} else {
arcon_doc_attr(doc_attrs)
}
}
};
let reliable_ser_id = reliable_ser_id.expect("missing reliable_ser_id attr");
let version = version.expect("missing version attr");
let mut ids: Vec<proc_macro2::TokenStream> = Vec::with_capacity(2);
ids.push(quote! { const RELIABLE_SER_ID: ::arcon::SerId = #reliable_ser_id; });
ids.push(quote! { const VERSION_ID: ::arcon::VersionId = #version; });
let generics = &input.generics;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let output: proc_macro2::TokenStream = {
quote! {
impl #impl_generics ::arcon::ArconType for #name #ty_generics #where_clause {
#(#ids)*
}
}
};
proc_macro::TokenStream::from(output)
}
fn arcon_attr_meta(meta_list: syn::MetaList) -> (Option<u64>, Option<u32>) {
let mut reliable_ser_id = None;
let mut version = None;
for item in meta_list.nested {
let pair = match item {
syn::NestedMeta::Meta(syn::Meta::NameValue(ref pair)) => pair,
_ => panic!(
"unsupported attribute argument {:?}",
item.to_token_stream()
),
};
if pair.path.is_ident("reliable_ser_id") {
if let syn::Lit::Int(ref s) = pair.lit {
reliable_ser_id = Some(s.base10_parse::<u64>().unwrap());
} else {
panic!("reliable_ser_id must be an Int literal");
}
} else if pair.path.is_ident("version") {
if let syn::Lit::Int(ref s) = pair.lit {
version = Some(s.base10_parse::<u32>().unwrap());
} else {
panic!("version must be an Int literal");
}
} else {
panic!(
"unsupported attribute key '{}' found",
pair.path.to_token_stream()
)
}
}
(reliable_ser_id, version)
}
fn arcon_doc_attr(name_values: Vec<syn::Meta>) -> (Option<u64>, Option<u32>) {
let mut reliable_ser_id = None;
let mut version = None;
for attr in name_values {
let lit = match attr {
syn::Meta::NameValue(v) => v.lit,
_ => panic!("expected NameValue"),
};
if let syn::Lit::Str(s) = lit {
let value = s.value();
let attr_str = value.trim();
let str_parts: Vec<String> = attr_str
.trim()
.to_string()
.split(' ')
.map(|s| s.to_string())
.collect();
if str_parts.len() == 3 && str_parts[1] == "=" {
if str_parts[0] == "reliable_ser_id" {
reliable_ser_id = Some(str_parts[2].parse::<u64>().unwrap());
} else if str_parts[0] == "version" {
version = Some(str_parts[2].parse::<u32>().unwrap());
}
}
} else {
panic!("must be an Str literal");
}
}
(reliable_ser_id, version)
}