extern crate proc_macro;
mod method;
use crate::method::{
generate_native_model_decode_body, generate_native_model_decode_upgrade_body,
generate_native_model_encode_body, generate_native_model_encode_downgrade_body,
generate_native_model_id, generate_native_model_version,
};
use proc_macro::TokenStream;
use quote::quote;
use syn::meta::ParseNestedMeta;
use syn::parse::{Parse, Result};
use syn::punctuated::Punctuated;
use syn::token;
use syn::{parse_macro_input, DeriveInput, LitInt, Path, Token};
#[derive(Default)]
pub(crate) struct ModelAttributes {
pub(crate) id: Option<LitInt>,
pub(crate) version: Option<LitInt>,
pub(crate) from: Option<Path>,
pub(crate) try_from: Option<(Path, Path)>,
}
impl ModelAttributes {
fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
if meta.path.is_ident("id") {
self.id = Some(meta.value()?.parse()?);
} else if meta.path.is_ident("version") {
self.version = Some(meta.value()?.parse()?);
} else if meta.path.is_ident("from") {
self.from = Some(meta.value()?.parse()?);
} else if meta.path.is_ident("try_from") {
let tuple_try_from: TupleTryFrom = meta.value()?.parse()?;
let mut fields = tuple_try_from.fields.into_iter();
self.try_from.replace((
fields.next().unwrap().clone(),
fields.next().unwrap().clone(),
));
} else {
panic!(
"Unknown attribute: {}",
meta.path.get_ident().unwrap().to_string()
);
}
Ok(())
}
}
#[derive(Default)]
pub(crate) struct TupleTryFrom {
pub(crate) _parent_token: token::Paren,
pub(crate) fields: Punctuated<Path, Token![,]>,
}
impl Parse for TupleTryFrom {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let content;
Ok(TupleTryFrom {
_parent_token: syn::parenthesized!(content in input),
fields: content.parse_terminated(Path::parse, Token![,])?,
})
}
}
#[proc_macro_attribute]
pub fn native_model(args: TokenStream, input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let struct_name = &ast.ident;
let mut attrs = ModelAttributes::default();
let model_attributes_parser = syn::meta::parser(|meta| attrs.parse(meta));
parse_macro_input!(args with model_attributes_parser);
let native_model_id_fn = generate_native_model_id(&attrs);
let native_model_version_fn = generate_native_model_version(&attrs);
let native_model_encode_body_fn = generate_native_model_encode_body();
let native_model_encode_downgrade_body_fn = generate_native_model_encode_downgrade_body(&attrs);
let native_model_decode_body_fn = generate_native_model_decode_body();
let native_model_decode_upgrade_body_fn = generate_native_model_decode_upgrade_body(&attrs);
let gen = quote! {
#ast
impl native_model::Model for #struct_name {
#native_model_id_fn
#native_model_version_fn
#native_model_encode_body_fn
#native_model_encode_downgrade_body_fn
#native_model_decode_body_fn
#native_model_decode_upgrade_body_fn
}
};
gen.into()
}