use quote::{format_ident, quote};
use syn::{FieldsNamed, FieldsUnnamed, Ident, Variant};
use syn::__private::TokenStream2;
use syn::Fields::*;
use crate::impl_display::write::WriteImplementor;
pub struct MatchArmImplementor<'a> {
enum_ident: &'a Ident,
message: &'a str
}
impl<'a> MatchArmImplementor<'a> {
pub fn new(enum_ident: &'a Ident, message: &'a str) -> Self {
MatchArmImplementor { enum_ident, message }
}
pub fn implement_default(self) -> TokenStream2 {
let write_implementation = WriteImplementor::new().implement(self.message.to_string());
quote! {
_ => #write_implementation
}
}
pub fn implement_for(self, variant: &Variant) -> TokenStream2 {
let ident = &variant.ident;
match &variant.fields {
Named(f) => self.implement_named(ident, f),
Unnamed(f) => self.implement_unnamed(ident, f),
Unit => self.implement_unit(ident)
}
}
fn implement_named(self, variant_ident: &Ident, fields: &FieldsNamed) -> TokenStream2 {
let field_names = fields.named
.iter()
.map(|f| f.ident.as_ref().unwrap());
let enum_ident = self.enum_ident;
let write_implementation = WriteImplementor::new().implement(self.message.to_string());
quote! {
#enum_ident :: #variant_ident { #(#field_names,)* } => #write_implementation
}
}
fn implement_unnamed(self, variant_ident: &Ident, fields: &FieldsUnnamed) -> TokenStream2 {
let field_names = (0..fields.unnamed.len())
.into_iter()
.map(|i| format!("_{}", i))
.map(|ident_str| {
let ident = format_ident!("{}", ident_str);
quote!(#ident)
});
let enum_ident = self.enum_ident;
let write_implementation = WriteImplementor::new().implement(self.message.to_string());
quote! {
#enum_ident :: #variant_ident ( #(#field_names,)* ) => #write_implementation
}
}
fn implement_unit(self, variant_ident: &Ident) -> TokenStream2 {
let enum_ident = self.enum_ident;
let write_implementation = WriteImplementor::new().implement(self.message.to_string());
quote! {
#enum_ident :: #variant_ident => #write_implementation
}
}
}
#[cfg(test)]
mod tests {
use syn::{parse_quote, Variant};
use crate::impl_display::match_arm::MatchArmImplementor;
use crate::test_helper::assert_tokens_are_equal;
#[test]
fn implement_default_works() {
let message = "something default: {print_cool_message()}";
let ts = implement_default(message);
let expected = r#"_ => write!(f, "something default: {}", print_cool_message())"#;
assert_tokens_are_equal(ts, expected)
}
#[test]
fn implement_named_works() {
let var = parse_quote!(Foo {val: usize});
let message = "Print the val: {val}";
let ts = implement_for(var, message);
let expected = r#"Enum::Foo { val, } => write!(f, "Print the val: {}", val)"#;
assert_tokens_are_equal(ts, expected)
}
#[test]
fn implement_unnamed_works() {
let var = parse_quote!(Foo (usize));
let message = "Print the val: {_0}";
let ts = implement_for(var, message);
let expected = r#"Enum::Foo( _0, ) => write!(f, "Print the val: {}", _0)"#;
assert_tokens_are_equal(ts, expected)
}
#[test]
fn implement_unit_works() {
let var = parse_quote!(Foo);
let message = "Print the enum: {self}";
let ts = implement_for(var, message);
let expected = r#"Enum::Foo => write!(f, "Print the enum: {}", self)"#;
assert_tokens_are_equal(ts, expected)
}
fn implement_default(message: &str) -> String {
MatchArmImplementor::new(&parse_quote!(Enum), message).implement_default().to_string()
}
fn implement_for(var: Variant, message: &str) -> String {
MatchArmImplementor::new(&parse_quote!(Enum), message).implement_for(&var).to_string()
}
}