use proc_macro::TokenStream;
use quote::quote;
use syn::{punctuated::Punctuated, token::Comma, Field, Ident, Variant};
pub(crate) fn derive_named(name: Ident, fields: Punctuated<Field, Comma>) -> TokenStream {
let name_s = name.to_string();
if name_s.contains("InvalidRequest") {
return derive_invalid_request(name);
}
let num_fields = fields.len();
if num_fields != 1 {
panic!("structs with named field must have exactly 1 field");
}
let field_name = fields
.first()
.expect("we just checked that there is exactly 1 field")
.ident
.as_ref()
.expect("field must be named since we matched on named struct");
derive_named_newtype(name, field_name)
}
fn derive_invalid_request(name: Ident) -> TokenStream {
quote! {
impl ::kanin::error::FromError<::kanin::error::RequestError> for #name {
fn from_error(error: ::kanin::error::RequestError) -> Self {
#name {
error: format!("{:#}", error)
}
}
}
}
.into()
}
pub(crate) fn derive_unnamed(name: Ident, fields: Punctuated<Field, Comma>) -> TokenStream {
if fields.len() != 1 {
panic!("only tuple structs with a single field are supported",);
}
quote! {
impl ::kanin::error::FromError<::kanin::HandlerError> for #name {
fn from_error(error: ::kanin::HandlerError) -> Self {
Self(::kanin::error::FromError::from_error(error))
}
}
}
.into()
}
fn derive_named_newtype(name: Ident, field_name: &Ident) -> TokenStream {
quote! {
impl ::kanin::error::FromError<::kanin::HandlerError> for #name {
fn from_error(error: ::kanin::HandlerError) -> Self {
Self {
#field_name: ::kanin::error::FromError::from_error(error)
}
}
}
}
.into()
}
pub(crate) fn derive_enum(name: Ident, variants: Punctuated<Variant, Comma>) -> TokenStream {
let invalid_request_name = &variants
.iter()
.find(|v| v.ident.to_string().contains("InvalidRequest"))
.expect("enum missing a variant containing \"InvalidRequest\"")
.ident;
quote! {
impl ::kanin::error::FromError<::kanin::HandlerError> for #name {
fn from_error(error: ::kanin::HandlerError) -> Self {
match error {
::kanin::HandlerError::InvalidRequest(e) => {
Self::#invalid_request_name(::kanin::error::FromError::from_error(e))
},
}
}
}
}
.into()
}