use alloc::vec::Vec;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Generics, Ident};
use crate::{
binding::Bind as _,
bounds::{Context as BoundContext, Contribute as _},
field::{FieldId, Fields},
semantics::{Display, Variant},
};
use crate::format::ExpandContext as FormatContext;
use super::{Context, Expand};
pub struct DisplayImpl<'target> {
name: &'target Ident,
generics: &'target Generics,
fields: &'target Fields,
display: &'target Display,
}
impl<'target> DisplayImpl<'target> {
pub const fn new(name: &'target Ident, generics: &'target Generics, fields: &'target Fields, display: &'target Display) -> Self {
Self {
name,
generics,
fields,
display,
}
}
}
impl Expand for DisplayImpl<'_> {
type Context = Context;
fn expand_with(self, context: Self::Context) -> syn::Result<TokenStream> {
let Self {
name,
generics,
fields,
display,
} = self;
let root = context.root();
let inline = context.inline();
let mut generics = generics.clone();
let bound_context = BoundContext::new(fields, root);
display.contribute(&mut generics, &bound_context);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let body = DisplayBody { fields, display }.expand_with(context.clone())?;
Ok(quote! {
#[automatically_derived]
impl #impl_generics #root::fmt::Display for #name #ty_generics #where_clause {
#inline
fn fmt(&self, f: &mut #root::fmt::Formatter<'_>) -> #root::fmt::Result {
#body
}
}
})
}
}
struct DisplayBody<'target> {
fields: &'target Fields,
display: &'target Display,
}
impl Expand for DisplayBody<'_> {
type Context = Context;
fn expand_with(self, context: Self::Context) -> syn::Result<TokenStream> {
let Self { fields, display } = self;
let root = context.root();
match display {
Display::Format(format) => {
let selected = if format.arguments().is_empty() {
format.fields().to_vec()
} else {
(0..fields.len()).map(FieldId::from_index).collect()
};
let bindings = fields.bind(&selected);
let pattern = bindings.pattern();
let body = format.expand_with(FormatContext::new(root.clone()))?;
Ok(quote! {
let &Self #pattern = self;
#body
})
}
Display::Transparent(field) => {
let bindings = fields.bind(&[*field]);
let pattern = bindings.pattern();
let binding = bindings.ident(*field);
Ok(quote! {
let &Self #pattern = self;
#root::fmt::Display::fmt(#binding, f)
})
}
Display::Custom(path) => Ok(quote! { #path(self, f) }),
}
}
}
pub struct EnumDisplayImpl<'target> {
name: &'target Ident,
generics: &'target Generics,
variants: &'target [Variant],
}
impl<'target> EnumDisplayImpl<'target> {
pub const fn new(name: &'target Ident, generics: &'target Generics, variants: &'target [Variant]) -> Self {
Self { name, generics, variants }
}
}
impl Expand for EnumDisplayImpl<'_> {
type Context = Context;
fn expand_with(self, context: Self::Context) -> syn::Result<TokenStream> {
let Self { name, generics, variants } = self;
let root = context.root();
let inline = context.inline();
let mut generics = generics.clone();
for variant in variants {
let bound_context = BoundContext::new(variant.fields(), root);
variant.display().contribute(&mut generics, &bound_context);
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let arms = variants
.iter()
.map(|variant| VariantDisplayArm { variant }.expand_with(context.clone()))
.collect::<syn::Result<Vec<_>>>()?;
Ok(quote! {
#[automatically_derived]
impl #impl_generics #root::fmt::Display for #name #ty_generics #where_clause {
#inline
fn fmt(&self, f: &mut #root::fmt::Formatter<'_>) -> #root::fmt::Result {
match self {
#(#arms),*
}
}
}
})
}
}
struct VariantDisplayArm<'target> {
variant: &'target Variant,
}
impl Expand for VariantDisplayArm<'_> {
type Context = Context;
fn expand_with(self, context: Self::Context) -> syn::Result<TokenStream> {
let Self { variant } = self;
let name = variant.name();
let fields = variant.fields();
let root = context.root();
match variant.display() {
Display::Format(format) => {
let selected = if format.arguments().is_empty() {
format.fields().to_vec()
} else {
(0..fields.len()).map(FieldId::from_index).collect()
};
let bindings = fields.bind(&selected);
let pattern = bindings.pattern();
let body = format.expand_with(FormatContext::new(root.clone()))?;
Ok(quote! { &Self::#name #pattern => { #body } })
}
Display::Transparent(field) => {
let bindings = fields.bind(&[*field]);
let pattern = bindings.pattern();
let binding = bindings.ident(*field);
Ok(quote! {
&Self::#name #pattern => #root::fmt::Display::fmt(#binding, f)
})
}
Display::Custom(path) => {
let bindings = fields.bind(&[]);
let pattern = bindings.pattern();
Ok(quote! { &Self::#name #pattern => #path(self, f) })
}
}
}
}