use proc_macro::TokenStream;
use quote::{format_ident, quote};
#[proc_macro_derive(Fillable)]
pub fn fillable_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
let impl_fillable = impl_fillable_macro(&ast);
let gen = quote! {
#impl_fillable
};
gen.into()
}
fn impl_fillable_macro(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
let name = &ast.ident;
quote! {
impl Fillable for #name {
}
}
}
#[proc_macro_derive(HashAndPartialEqById)]
pub fn filler_derives(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let name = ast.ident;
let gen = quote! {
impl PartialEq for #name {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl std::hash::Hash for #name {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
};
gen.into()
}
#[proc_macro_derive(Filler)]
pub fn filler_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
let impl_filler_self = impl_filler_self_macro(&ast);
let gen = quote! {
#impl_filler_self
};
gen.into()
}
fn impl_filler_self_macro(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
let name = &ast.ident;
let fields = get_struct_fields(&ast);
let list_add_missing_data = list_add_missing_data(&fields);
let list_never_replace_data = list_never_replace_data(&fields);
let list_replace_data = list_replace_data(&fields);
quote! {
impl Filler<#name, #name> for #name{
fn add_missing_data(&mut self, source: &#name) {
if !self.check_mergeable(source) { return }
#list_add_missing_data
}
fn never_replace_data(&mut self, source: &#name) {
if !self.check_mergeable(source) { return }
#list_never_replace_data
}
fn replace_data(&mut self, source: &#name) {
#list_replace_data
}
}
}
}
fn list_add_missing_data(struct_fields: &[&syn::Field]) -> proc_macro2::TokenStream {
let mut parse_gen = quote! {};
for (_i, field) in struct_fields.iter().enumerate() {
let ident = field.ident.as_ref().unwrap();
parse_gen = quote! {
#parse_gen
self.#ident.add_missing_data(&source.#ident);
};
}
parse_gen
}
fn list_never_replace_data(struct_fields: &[&syn::Field]) -> proc_macro2::TokenStream {
let mut parse_gen = quote! {};
for (_i, field) in struct_fields.iter().enumerate() {
let ident = field.ident.as_ref().unwrap();
parse_gen = quote! {
#parse_gen
self.#ident.never_replace_data(&source.#ident);
};
}
parse_gen
}
fn list_replace_data(struct_fields: &[&syn::Field]) -> proc_macro2::TokenStream {
let mut parse_gen = quote! {};
for (_i, field) in struct_fields.iter().enumerate() {
let ident = field.ident.as_ref().unwrap();
parse_gen = quote! {
#parse_gen
self.#ident.replace_data(&source.#ident);
};
}
parse_gen
}
#[proc_macro_derive(HasUnknown, attributes(unknown_list))]
pub fn has_unknown_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
impl_has_unknown_macro(&ast)
}
fn impl_has_unknown_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let data = get_struct_data(&ast);
let mut parse_gen = quote! {};
for (field, _type) in &data {
parse_gen = quote! {
#parse_gen
self.#field.print_unknown(&prefix, unknown_map);
}
}
let mut parse_gen2 = quote! {};
for (field, _type) in &data {
parse_gen2 = quote! {
#parse_gen2
self.#field.combine_unknown(&prefix, unknown_combiner);
}
}
let gen = quote! {
impl HasUnknown for #name {
fn print_unknown(&self, prefix: &str, unknown_map: &mut HashMap<String, u32>){
let prefix = self.update_unknown_prefix(prefix,stringify!(#name));
#parse_gen
self.print_unknown_list(&self.unknown, &prefix, unknown_map);
}
fn combine_unknown(&self, prefix: &str, unknown_combiner: &mut HashMap<String, Vec<(String, Value)>>){
let prefix = self.update_unknown_prefix(prefix,stringify!(#name));
#parse_gen2
self.combine_unknown_list(&self.unknown, &prefix, unknown_combiner);
}
}
};
gen.into()
}
fn get_struct_data(ast: &syn::DeriveInput) -> Vec<(&syn::Ident, &syn::Type)> {
let mut list = Vec::new();
match &ast.data {
syn::Data::Struct(x) => match &x.fields {
syn::Fields::Named(x) => {
for field in &x.named {
list.push((field.ident.as_ref().unwrap(), &field.ty));
}
Some(())
}
syn::Fields::Unnamed(_) => None,
syn::Fields::Unit => None,
},
syn::Data::Enum(_x) => None,
syn::Data::Union(_x) => None,
};
list
}
fn get_struct_fields(ast: &syn::DeriveInput) -> Vec<&syn::Field> {
let mut list = Vec::new();
match &ast.data {
syn::Data::Struct(x) => match &x.fields {
syn::Fields::Named(x) => {
for field in &x.named {
list.push(field);
}
Some(())
}
syn::Fields::Unnamed(_) => None,
syn::Fields::Unit => None,
},
syn::Data::Enum(_x) => None,
syn::Data::Union(_x) => None,
};
list
}
#[proc_macro_derive(DeserializeBestEffort, attributes(serde))]
pub fn deserialize_best_effort_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
impl_deserialize_best_effort_macro(&ast)
}
fn impl_deserialize_best_effort_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let data = get_struct_data(&ast);
let fields = get_struct_fields(&ast);
let field_enum_and_field_visitor = impl_field_enum_visitor(&fields);
let struct_visitor = impl_struct_visitor(&data, name);
let fields_array = get_fields_array(&fields);
let visitor_name = get_visitor_name(&name);
let gen = quote! {
impl<'de> DeserializeBestEffort<'de> for #name {}
impl<'de> serde::de::Deserialize<'de> for #name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
#field_enum_and_field_visitor
#struct_visitor
#fields_array
deserializer.deserialize_struct(stringify!(#name), FIELDS, #visitor_name)
}
}
};
gen.into()
}
fn get_alias_attrs(struct_fields: &syn::Field) -> Vec<String> {
let mut lit_list = Vec::new();
for attr in &struct_fields.attrs {
let tokens = get_alias_attrs_variables(&attr);
for val in tokens {
let mut string_quote = "".to_string();
if let syn::Lit::Str(string_lit) = val {
string_quote = string_lit.value();
}
if string_quote.is_empty() {
continue;
}
lit_list.push(string_quote);
}
}
lit_list
}
fn get_alias_attrs_variables(attr: &syn::Attribute) -> Vec<syn::Lit> {
let meta_items = get_serde_meta_items(attr).unwrap();
let mut lit_list: Vec<syn::Lit> = Vec::new();
for meta_item in meta_items {
match meta_item {
syn::NestedMeta::Meta(syn::Meta::NameValue(m)) if m.path.is_ident("alias") => {
lit_list.push(m.lit);
}
_ => (),
};
}
lit_list
}
fn get_serde_meta_items(attr: &syn::Attribute) -> Result<Vec<syn::NestedMeta>, ()> {
if !attr.path.is_ident("serde") {
return Ok(Vec::new());
}
match attr.parse_meta() {
Ok(syn::Meta::List(meta)) => Ok(meta.nested.into_iter().collect()),
Ok(_other) => {
Err(())
}
Err(_err) => {
Err(())
}
}
}
fn get_fields_array(struct_fields: &[&syn::Field]) -> proc_macro2::TokenStream {
let mut parse_gen = quote! {};
for (i, field) in struct_fields.iter().enumerate() {
let ident = field.ident.as_ref().unwrap();
let field_ident = get_enum_ident(ident, i);
parse_gen = quote! {
#parse_gen stringify!(#field_ident),
};
let alias_ident = get_alias_attrs(field);
for alias in alias_ident {
parse_gen = quote! {
#parse_gen stringify!(#alias),
};
}
}
parse_gen = quote! {
const FIELDS: &'static [&'static str] = &[#parse_gen];
};
parse_gen
}
fn get_field_enum(struct_fields: &[&syn::Field]) -> proc_macro2::TokenStream {
let mut parse_gen = quote! {};
for (i, field) in struct_fields.iter().enumerate() {
let ident = field.ident.as_ref().unwrap();
let field_ident = get_enum_ident(ident, i);
parse_gen = quote! {
#parse_gen
#field_ident,
}
}
parse_gen = quote! {
enum Field {
#parse_gen
Unknown(String),
}
};
parse_gen
}
fn get_field_enum_match(struct_fields: &[&syn::Field]) -> proc_macro2::TokenStream {
let mut parse_gen = quote! {};
for (i, field) in struct_fields.iter().enumerate() {
let ident = field.ident.as_ref().unwrap();
let field_enum_ident = get_enum_ident(ident, i);
parse_gen = quote! {
#parse_gen
stringify!(#ident) => Ok(Field::#field_enum_ident),
};
let alias_ident = get_alias_attrs(field);
for alias in alias_ident {
parse_gen = quote! {
#parse_gen
#alias => Ok(Field::#field_enum_ident),
};
}
}
parse_gen = quote! {
match value {
#parse_gen
_ => Ok(Field::Unknown(value.to_string())),
}
};
parse_gen
}
fn impl_field_enum_visitor(struct_fields: &[&syn::Field]) -> proc_macro2::TokenStream {
let field_enum = get_field_enum(&struct_fields);
let field_enum_match = get_field_enum_match(&struct_fields);
let parse_gen = quote! {
#field_enum
impl<'de> serde::de::Deserialize<'de> for Field {
fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
where
D: serde::de::Deserializer<'de>,
{
struct FieldVisitor;
impl<'de> serde::de::Visitor<'de> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("Did not expect this... se default is not working.")
}
fn visit_str<E>(self, value: &str) -> Result<Field, E>
where
E: de::Error,
{
#field_enum_match
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}
};
parse_gen
}
fn impl_struct_visitor(
struct_data: &[(&syn::Ident, &syn::Type)],
name: &syn::Ident,
) -> proc_macro2::TokenStream {
let visit_seq = get_struct_visit_seq(&struct_data, &name);
let visit_map = get_struct_visit_map(&struct_data, &name);
let visitor_name = get_visitor_name(&name);
let parse_gen = quote! {
struct #visitor_name;
impl<'de> serde::de::Visitor<'de> for #visitor_name {
type Value = #name;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str(&format!("struct {}",stringify!(#visitor_name)))
}
#visit_seq
#visit_map
}
};
parse_gen
}
fn get_struct_visit_seq(
struct_data: &[(&syn::Ident, &syn::Type)],
name: &syn::Ident,
) -> proc_macro2::TokenStream {
let variable_init = set_struct_visit_seq_variable(&struct_data);
let create_object = set_struct_create_object(&struct_data, &name);
let parse_gen = quote! {
fn visit_seq<V>(self, mut seq: V) -> Result<#name, V::Error>
where
V: serde::de::SeqAccess<'de>,
{
#variable_init
Ok(#create_object)
}
};
parse_gen
}
fn set_struct_visit_seq_variable(
struct_data: &[(&syn::Ident, &syn::Type)],
) -> proc_macro2::TokenStream {
let mut parse_gen = quote! {};
for (i, (field, type_)) in struct_data.iter().enumerate() {
parse_gen = quote! {
#parse_gen
let #field:#type_ = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(#i, &self))?;
};
}
parse_gen
}
fn get_struct_visit_map(
struct_data: &[(&syn::Ident, &syn::Type)],
name: &syn::Ident,
) -> proc_macro2::TokenStream {
let variable_init = set_struct_visit_map_variable(&struct_data);
let enum_match_variable = set_struct_visit_map_enum_match(&struct_data);
let create_object = set_struct_create_object(&struct_data, &name);
let parse_gen = quote! {
fn visit_map<V>(self, mut map: V) -> Result<#name, V::Error>
where
V: serde::de::MapAccess<'de>,
{
#variable_init
while let Some(key) = map.next_key()? {
#enum_match_variable
}
Ok(#create_object)
}
};
parse_gen
}
fn set_struct_create_object(
struct_data: &[(&syn::Ident, &syn::Type)],
name: &syn::Ident,
) -> proc_macro2::TokenStream {
let mut parse_gen = quote! {};
for (field, _type) in struct_data {
parse_gen = quote! {
#parse_gen
#field,
}
}
parse_gen = quote! {
#name{
#parse_gen
}
};
parse_gen
}
fn set_struct_visit_map_variable(
struct_data: &[(&syn::Ident, &syn::Type)],
) -> proc_macro2::TokenStream {
let mut parse_gen = quote! {};
for (field, type_) in struct_data {
parse_gen = quote! {
#parse_gen
let mut #field:#type_ = Default::default();
}
}
parse_gen
}
fn set_struct_visit_map_enum_match(
struct_data: &[(&syn::Ident, &syn::Type)],
) -> proc_macro2::TokenStream {
let mut parse_gen = quote! {};
for (i, (field, _type)) in struct_data.iter().enumerate() {
let field_ident = get_enum_ident(field, i);
parse_gen = quote! {
#parse_gen
Field::#field_ident => {
let next_value = map.next_value().unwrap_or_default();
#field.add_data(stringify!(#field), next_value);
}
}
}
parse_gen = quote! {
match key {
#parse_gen
Field::Unknown(key_name) => {
let next_value = map.next_value().unwrap_or_default();
unknown.add_data(&key_name, next_value);
}
}
};
parse_gen
}
fn get_enum_ident(_ident: &syn::Ident, number: usize) -> syn::Ident {
format_ident!("Enum{}", number)
}
fn get_visitor_name(ident: &syn::Ident) -> syn::Ident {
format_ident!("Struct{}Visitor", ident)
}