use syn::Ident;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields};
pub fn build_serde(input: &DeriveInput, _cur_crate: TokenStream) -> TokenStream {
let struct_name = &input.ident;
let (_impl_generics, _ty_generics, _where_clause) = input.generics.split_for_impl();
let fields = match input.data.clone() {
Data::Struct(DataStruct { fields: Fields::Named(fields), .. }) => fields.named,
_ => {panic!("this derive macro only works on structs with named fields")}
};
let impl_inner_struct = impl_innerstruct_trait(struct_name, &fields);
let impl_from_into = impl_serialize_deserialize(struct_name, &fields);
quote!
{
#[automatically_derived]
impl airone::serde::InnerStruct for #struct_name
{
#impl_inner_struct
}
#impl_from_into
}
}
fn impl_innerstruct_trait(struct_name: &Ident, fields: &Punctuated<Field, Comma>) -> TokenStream
{
let column_names_str = fields.iter().map(|f|{f.ident.as_ref().unwrap().to_string()});
let struct_name_str = struct_name.to_string();
let match_for_get = fields.iter().map(|field|{
let field_name = &field.ident.as_ref().unwrap();
let field_str = field_name.to_string();
let field_type = &field.ty;
quote!{
#field_str => {
let v = &self.#field_name as &dyn Any;
return v.downcast_ref::<V>().expect(
&format!(
"Can't convert provided generic type for field `{}` to `{}`",
stringify!(#field_name),
stringify!(#field_type)
)
);
}
}
});
let match_for_set = fields.iter().map(|field|{
let field_name = &field.ident;
let field_str = field_name.as_ref().unwrap().to_string();
let field_type: &syn::Type = &field.ty;
quote!{
#field_str => {
self.#field_name = (
*(&value as &dyn Any).downcast_ref::<#field_type>()
.expect(
&format!(
"Can't convert provided generic type for field `{}` to `{}`",
stringify!(#field_name),
stringify!(#field_type)
)
)
).clone()
}
}
});
let match_for_setstr = fields.iter().map(|field|{
let field_name = &field.ident;
let field_str = field_name.as_ref().unwrap().to_string();
quote!{
#field_str => {
self.#field_name = SerializableField::deserialize_field(value)?;
}
}
});
let setting_unknown_field_panic_handler = quote!{
panic!(
"Error trying to set field `{}`, but it doesn't exist in type \"{}\". \nAvailable fields are:\n{}",
key,
stringify!(#struct_name),
Self::COLUMNS.iter().map(|c|
{
format!(" -`{}`", c)
}).collect::<Vec<_>>().join("\n")
)
};
quote!{
const COLUMNS: &'static [&'static str] = &[
#(#column_names_str),*
];
const STRUCT_NAME: &'static str = #struct_name_str;
fn get<V: airone::serde::SerializableField>(&self, key: &str) -> &V {
use std::any::Any;
match key
{
#(#match_for_get),*
_ => {
panic!(
"Error trying to get field `{}`, but it doesn't exist in type \"{}\". \nAvailable fields are:\n{}",
key,
stringify!(#struct_name),
Self::COLUMNS.iter().map(|c|
{
format!(" -`{}`", c)
}).collect::<Vec<_>>().join("\n")
)
}
}
}
fn set<V: airone::serde::SerializableField>(&mut self, key: &str, value: V)
{
use std::any::Any;
match key
{
#(#match_for_set),*
_ => {#setting_unknown_field_panic_handler}
}
}
fn set_str(&mut self, key: &str, value: airone::serde::SerializedFieldValue) -> Result<(), airone::error::Error>
{
use airone::serde::SerializableField;
match key
{
#(#match_for_setstr),*
_ => {#setting_unknown_field_panic_handler}
}
Ok(())
}
}
}
fn impl_serialize_deserialize(struct_name: &Ident, fields: &Punctuated<Field, Comma>) -> TokenStream
{
let iter_deserialize = fields.iter().map(|f|{
let fieldname = f.ident.as_ref().unwrap();
quote!{
#fieldname: airone::serde::SerializableField::deserialize_field(values.next().unwrap())?
}
});
let iter_serialize = fields.iter().map(|f|{
let fieldname = f.ident.as_ref().unwrap();
quote!{
self.#fieldname.serialize_field()
}
});
quote!{
#[automatically_derived]
impl airone::serde::Deserialize for #struct_name
{
fn deserialize(value: &airone::serde::SerializedStruct) -> Result<Self, airone::error::Error>
{
use airone::serde::InnerStruct;
let values = value.get_values();
assert!(values.len()==Self::COLUMNS.len());
let mut values = values.iter().cloned();
Ok(
Self {
#(#iter_deserialize),*
}
)
}
}
#[automatically_derived]
impl airone::serde::Serialize for #struct_name
{
fn serialize(&self) -> airone::serde::SerializedStruct
{
use airone::serde::SerializableField;
airone::serde::SerializedStruct::new(
vec![
#(#iter_serialize),*
]
)
}
}
}
}