use proc_macro2::TokenStream;
use quote::{quote, quote_spanned, ToTokens};
use syn::{spanned::Spanned, DataEnum, DeriveInput, Error, Ident, Result};
use super::parse::{ChoiceKind, ChoiceValue, ParsedVariant};
pub fn impl_command_option(input: DeriveInput) -> Result<TokenStream> {
let ident = &input.ident;
let input_span = input.span();
let (variants, kind) = match input.data {
syn::Data::Enum(DataEnum { variants, .. }) => {
ParsedVariant::from_variants(variants, input_span)?
}
_ => {
return Err(Error::new(
input_span,
"`#[derive(CommandOption)] can only be applied to enums",
))
}
};
let parsed_init = parsed_init(kind);
let match_expr = match_expr(kind);
let match_arms = variants.iter().map(variant_match_arm);
let value_match_arms = variants.iter().map(value_match_arm);
let choice_ty = match kind {
ChoiceKind::String => quote! { &'static str },
ChoiceKind::Integer => quote! { i64 },
ChoiceKind::Number => quote! { f64 },
};
Ok(quote! {
impl ::twilight_interactions::command::CommandOption for #ident {
fn from_option(
__value: ::twilight_model::application::interaction::application_command::CommandOptionValue,
__data: ::twilight_interactions::command::internal::CommandOptionData,
__resolved: ::std::option::Option<&::twilight_model::application::interaction::InteractionDataResolved>
) -> ::std::result::Result<Self, ::twilight_interactions::error::ParseOptionErrorType> {
#parsed_init
match #match_expr {
#(#match_arms,)*
__other => ::std::result::Result::Err(
::twilight_interactions::error::ParseOptionErrorType::InvalidChoice(
::std::string::ToString::to_string(__other)
)
)
}
}
}
impl #ident {
pub fn value(&self) -> #choice_ty {
match self {
#(#value_match_arms,)*
}
}
}
})
}
pub fn dummy_command_option(ident: Ident, error: Error) -> TokenStream {
let error = error.to_compile_error();
quote! {
#error
impl ::twilight_interactions::command::CommandOption for #ident {
fn from_option(
value: ::twilight_model::application::interaction::application_command::CommandOptionValue,
data: ::twilight_interactions::command::internal::CommandOptionData,
resolved: ::std::option::Option<&::twilight_model::application::interaction::InteractionDataResolved>
) -> ::std::result::Result<Self, ::twilight_interactions::error::ParseOptionErrorType> {
::std::unimplemented!()
}
}
}
}
fn parsed_init(kind: ChoiceKind) -> TokenStream {
match kind {
ChoiceKind::String => {
quote! { let __parsed: ::std::string::String = ::twilight_interactions::command::CommandOption::from_option(__value, ::std::default::Default::default(), __resolved)?; }
}
ChoiceKind::Integer => {
quote! { let __parsed: i64 = ::twilight_interactions::command::CommandOption::from_option(__value, ::std::default::Default::default(), __resolved)?; }
}
ChoiceKind::Number => {
quote! { let __parsed: f64 = ::twilight_interactions::command::CommandOption::from_option(__value, ::std::default::Default::default(), __resolved)?; }
}
}
}
fn match_expr(kind: ChoiceKind) -> TokenStream {
if kind == ChoiceKind::String {
quote! { __parsed.as_str() }
} else {
quote! { &__parsed }
}
}
fn variant_match_arm(variant: &ParsedVariant) -> TokenStream {
let ident = &variant.ident;
let span = variant.span;
let value = match &variant.attribute.value {
ChoiceValue::String(val) => val.to_token_stream(),
ChoiceValue::Int(val) => val.to_token_stream(),
ChoiceValue::Number(val) => quote! { __val if (__val - #val).abs() < f64::EPSILON },
};
quote_spanned! {span=>
#value => ::std::result::Result::Ok(Self::#ident)
}
}
fn value_match_arm(variant: &ParsedVariant) -> TokenStream {
let ident = &variant.ident;
let span = variant.span;
let value = match &variant.attribute.value {
ChoiceValue::String(val) => val.to_token_stream(),
ChoiceValue::Int(val) => val.to_token_stream(),
ChoiceValue::Number(val) => val.to_token_stream(),
};
quote_spanned! {span=>
Self::#ident => #value
}
}