serenity_commands_macros/
lib.rs

1#![allow(missing_docs)]
2//! Macros for the `serenity_commands` crate.
3//!
4//! An implementation detail. Do not use directly.
5
6mod basic_option;
7mod command;
8mod commands;
9mod field;
10mod sub_command;
11mod sub_command_group;
12mod utils;
13mod variant;
14
15use darling::{ast::NestedMeta, Error, FromDeriveInput, FromMeta};
16use proc_macro2::TokenStream;
17use quote::{quote, quote_spanned, ToTokens};
18use syn::{
19    parse::{Parse, Parser},
20    parse_macro_input,
21    punctuated::Punctuated,
22    token::Paren,
23    Expr, Ident, MacroDelimiter, Meta, Token,
24};
25
26#[derive(Debug)]
27struct DetachedMethodCall {
28    method: Ident,
29    #[allow(dead_code)]
30    paren_token: Paren,
31    args: Punctuated<Expr, Token![,]>,
32}
33
34impl Parse for DetachedMethodCall {
35    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
36        let content;
37
38        Ok(Self {
39            method: input.parse()?,
40            paren_token: syn::parenthesized!(content in input),
41            args: content.parse_terminated(Expr::parse, Token![,])?,
42        })
43    }
44}
45
46impl FromMeta for DetachedMethodCall {
47    fn from_meta(item: &Meta) -> darling::Result<Self> {
48        let Meta::List(list) = item else {
49            return Err(Error::unsupported_format("non-meta list"));
50        };
51
52        let method = list
53            .path
54            .get_ident()
55            .cloned()
56            .ok_or_else(|| Error::unsupported_format("non-ident path"))?;
57
58        let MacroDelimiter::Paren(paren_token) = list.delimiter else {
59            return Err(Error::unsupported_format("non-parenthesized arguments"));
60        };
61
62        let args = Punctuated::<Expr, Token![,]>::parse_terminated.parse2(list.tokens.clone())?;
63
64        Ok(Self {
65            method,
66            paren_token,
67            args,
68        })
69    }
70}
71
72#[derive(Debug)]
73struct BuilderMethodList {
74    methods: Vec<DetachedMethodCall>,
75}
76
77impl FromMeta for BuilderMethodList {
78    fn from_list(items: &[NestedMeta]) -> darling::Result<Self> {
79        let methods = items
80            .iter()
81            .map(DetachedMethodCall::from_nested_meta)
82            .collect::<darling::Result<_>>()?;
83
84        Ok(Self { methods })
85    }
86}
87
88impl ToTokens for BuilderMethodList {
89    fn to_tokens(&self, tokens: &mut TokenStream) {
90        let methods = self.methods.iter().map(|method| {
91            let method_name = &method.method;
92            let args = &method.args;
93
94            quote_spanned! {method_name.span()=>
95                .#method_name(#args)
96            }
97        });
98
99        tokens.extend(quote! {
100            #(#methods)*
101        });
102    }
103}
104
105#[proc_macro_derive(Commands, attributes(command))]
106pub fn derive_commands(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
107    commands::Args::from_derive_input(&parse_macro_input!(tokens))
108        .map_or_else(Error::write_errors, ToTokens::into_token_stream)
109        .into()
110}
111
112#[proc_macro_derive(Command, attributes(command))]
113pub fn derive_command(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
114    command::Args::from_derive_input(&parse_macro_input!(tokens))
115        .map_or_else(Error::write_errors, ToTokens::into_token_stream)
116        .into()
117}
118
119#[proc_macro_derive(SubCommandGroup, attributes(command))]
120pub fn derive_sub_command_group(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
121    sub_command_group::Args::from_derive_input(&parse_macro_input!(tokens))
122        .map_or_else(Error::write_errors, ToTokens::into_token_stream)
123        .into()
124}
125
126#[proc_macro_derive(SubCommand, attributes(command))]
127pub fn derive_sub_command(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
128    sub_command::Args::from_derive_input(&parse_macro_input!(tokens))
129        .map_or_else(Error::write_errors, ToTokens::into_token_stream)
130        .into()
131}
132
133#[proc_macro_derive(BasicOption, attributes(option))]
134pub fn derive_basic_option(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
135    basic_option::Args::from_derive_input(&parse_macro_input!(tokens))
136        .map_or_else(Error::write_errors, ToTokens::into_token_stream)
137        .into()
138}