use std::ops::Deref;
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use syn::{Field, Fields, Ident, Index, Member, Token, spanned::Spanned};
pub struct Struct {
ident: Ident,
fields: Fields,
}
impl Struct {
pub fn new_as_self(fields: &Fields) -> Self {
Self {
ident: <Token![Self]>::default().into(),
fields: fields.clone(),
}
}
pub fn is_empty(&self) -> bool {
self.fields.is_empty()
}
pub fn num_fields(&self) -> usize {
self.fields.len()
}
pub fn fields(&self) -> impl Iterator<Item = &Field> + '_ {
self.fields.iter()
}
pub fn sized_fields(&self) -> impl Iterator<Item = &Field> + '_ {
self.fields().take(self.num_fields() - 1)
}
pub fn unsized_field(&self) -> Option<&Field> {
self.fields.iter().next_back()
}
pub fn members(&self) -> impl Iterator<Item = Member> + '_ {
self.fields
.iter()
.enumerate()
.map(|(i, f)| make_member(i, f))
}
pub fn sized_members(&self) -> impl Iterator<Item = Member> + '_ {
self.members().take(self.num_fields() - 1)
}
pub fn unsized_member(&self) -> Option<Member> {
self.fields
.iter()
.next_back()
.map(|f| make_member(self.num_fields() - 1, f))
}
pub fn builder<F: Fn(Member) -> Ident>(
&self,
f: F,
) -> StructBuilder<'_, F> {
StructBuilder {
target: self,
var_fn: f,
}
}
}
fn make_member(index: usize, field: &Field) -> Member {
match &field.ident {
Some(ident) => Member::Named(ident.clone()),
None => Member::Unnamed(Index {
index: index as u32,
span: field.ty.span(),
}),
}
}
pub struct StructBuilder<'a, F: Fn(Member) -> Ident> {
target: &'a Struct,
var_fn: F,
}
impl<F: Fn(Member) -> Ident> StructBuilder<'_, F> {
pub fn init_vars(&self) -> impl Iterator<Item = Ident> + '_ {
self.members().map(&self.var_fn)
}
pub fn sized_init_vars(&self) -> impl Iterator<Item = Ident> + '_ {
self.sized_members().map(&self.var_fn)
}
pub fn unsized_init_var(&self) -> Option<Ident> {
self.unsized_member().map(&self.var_fn)
}
}
impl<F: Fn(Member) -> Ident> Deref for StructBuilder<'_, F> {
type Target = Struct;
fn deref(&self) -> &Self::Target {
self.target
}
}
impl<F: Fn(Member) -> Ident> ToTokens for StructBuilder<'_, F> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let ident = &self.ident;
match self.fields {
Fields::Named(_) => {
let members = self.members();
let init_vars = self.init_vars();
quote! {
#ident { #(#members: #init_vars),* }
}
}
Fields::Unnamed(_) => {
let init_vars = self.init_vars();
quote! {
#ident ( #(#init_vars),* )
}
}
Fields::Unit => {
quote! { #ident }
}
}
.to_tokens(tokens);
}
}