use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use crate::input::Input;
use crate::names;
pub fn derive(input: &Input) -> TokenStream {
let name = &input.name;
let visibility = &input.visibility;
let attrs = &input.attrs.ref_;
let mut_attrs = &input.attrs.ref_mut;
let vec_name = names::vec_name(&input.name);
let ref_name = names::ref_name(&input.name);
let ref_mut_name = names::ref_mut_name(&input.name);
let fields_types = &input.fields.iter()
.map(|field| field.ty.clone())
.collect::<Vec<_>>();
let doc_url = format!("[`{0}`](struct.{0}.html)", name);
let vec_doc_url = format!("[`{0}`](struct.{0}.html)", vec_name);
let ref_doc_url = format!("[`{0}`](struct.{0}.html)", ref_name);
let ref_mut_doc_url = format!("[`{0}`](struct.{0}.html)", ref_mut_name);
let fields_names = &input.fields.iter()
.map(|field| field.ident.clone().unwrap())
.collect::<Vec<_>>();
let fields_names_hygienic = input.fields.iter()
.enumerate()
.map(|(i, _)| Ident::new(&format!("___soa_derive_private_{}", i), Span::call_site()))
.collect::<Vec<_>>();
let ref_fields_types = input.map_fields_nested_or(
|_, field_type| {
let field_ptr_type = names::ref_name(field_type);
quote! { #field_ptr_type<'a> }
},
|_, field_type| quote! { &'a #field_type },
).collect::<Vec<_>>();
let ref_mut_fields_types = input.map_fields_nested_or(
|_, field_type| {
let field_ptr_type = names::ref_mut_name(field_type);
quote! { #field_ptr_type<'a> }
},
|_, field_type| quote! { &'a mut #field_type },
).collect::<Vec<_>>();
let as_ref = input.map_fields_nested_or(
|ident, _| quote! { self.#ident.as_ref() },
|ident, _| quote! { &self.#ident },
).collect::<Vec<_>>();
let as_mut = input.map_fields_nested_or(
|ident, _| quote! { self.#ident.as_mut() },
|ident, _| quote! { &mut self.#ident },
).collect::<Vec<_>>();
let to_owned = input.map_fields_nested_or(
|ident, _| quote! { self.#ident.to_owned() },
|ident, _| quote! { self.#ident.clone() },
).collect::<Vec<_>>();
let ref_replace = input.map_fields_nested_or(
|ident, _| quote! { self.#ident.replace(field) },
|ident, _| quote! { ::std::mem::replace(&mut *self.#ident, field) },
).collect::<Vec<_>>();
quote! {
#[doc = #doc_url]
#(#[#attrs])*
#[derive(Copy, Clone)]
#visibility struct #ref_name<'a> {
#(
#[doc = stringify!(#fields_names)]
#[doc = #doc_url]
#[doc = #vec_doc_url]
pub #fields_names: #ref_fields_types,
)*
}
#[doc = #doc_url]
#(#[#mut_attrs])*
#visibility struct #ref_mut_name<'a> {
#(
#[doc = stringify!(#fields_names)]
#[doc = #doc_url]
#[doc = #vec_doc_url]
pub #fields_names: #ref_mut_fields_types,
)*
}
#[allow(dead_code)]
impl #name {
#[doc = #ref_doc_url]
#[doc = #doc_url]
#visibility fn as_ref(&self) -> #ref_name {
#ref_name {
#( #fields_names: #as_ref, )*
}
}
#[doc = #ref_mut_doc_url]
#[doc = #doc_url]
#visibility fn as_mut(&mut self) -> #ref_mut_name {
#ref_mut_name {
#( #fields_names: #as_mut, )*
}
}
}
impl<'a> #ref_name<'a> {
#[doc = #doc_url]
pub fn to_owned(&self) -> #name
where #( for<'b> #fields_types: Clone, )*
{
#name {
#( #fields_names: #to_owned, )*
}
}
}
impl<'a> From<#ref_name<'a>> for #name where #( for<'b> #fields_types: Clone, )* {
fn from(value: #ref_name<'a>) -> #name {
value.to_owned()
}
}
impl<'a> From<&'a #ref_name<'a>> for #name where #( for<'b> #fields_types: Clone, )* {
fn from(value: &'a #ref_name<'a>) -> #name {
value.to_owned()
}
}
impl<'a> #ref_mut_name<'a> {
#[doc = #doc_url]
pub fn to_owned(&self) -> #name
where #( for<'b> #fields_types: Clone, )*
{
#name {
#( #fields_names: #to_owned, )*
}
}
#[allow(clippy::forget_non_drop)]
pub fn replace(&mut self, val: #name) -> #name {
#(
let field = unsafe { ::std::ptr::read(&val.#fields_names) };
let #fields_names_hygienic = #ref_replace;
)*
::std::mem::forget(val);
#name{#(#fields_names: #fields_names_hygienic),*}
}
}
impl<'a> From<#ref_mut_name<'a>> for #name where #( for<'b> #fields_types: Clone, )* {
fn from(value: #ref_mut_name<'a>) -> #name {
value.to_owned()
}
}
impl<'a> From<&'a #ref_mut_name<'a>> for #name where #( for<'b> #fields_types: Clone, )* {
fn from(value: &'a #ref_mut_name<'a>) -> #name {
value.to_owned()
}
}
}
}