subcommand group syntax
This commit is contained in:
		@@ -2,6 +2,7 @@ pub mod suffixes {
 | 
			
		||||
    pub const COMMAND: &str = "COMMAND";
 | 
			
		||||
    pub const ARG: &str = "ARG";
 | 
			
		||||
    pub const SUBCOMMAND: &str = "SUBCOMMAND";
 | 
			
		||||
    pub const SUBCOMMAND_GROUP: &str = "GROUP";
 | 
			
		||||
    pub const CHECK: &str = "CHECK";
 | 
			
		||||
    pub const HOOK: &str = "HOOK";
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,13 @@ macro_rules! match_options {
 | 
			
		||||
 | 
			
		||||
#[proc_macro_attribute]
 | 
			
		||||
pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
 | 
			
		||||
    enum LastItem {
 | 
			
		||||
        Fun,
 | 
			
		||||
        SubFun,
 | 
			
		||||
        SubGroup,
 | 
			
		||||
        SubGroupFun,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let mut fun = parse_macro_input!(input as CommandFun);
 | 
			
		||||
 | 
			
		||||
    let _name = if !attr.is_empty() {
 | 
			
		||||
@@ -46,6 +53,7 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
 | 
			
		||||
 | 
			
		||||
    let mut hooks: Vec<Ident> = Vec::new();
 | 
			
		||||
    let mut options = Options::new();
 | 
			
		||||
    let mut last_desc = LastItem::Fun;
 | 
			
		||||
 | 
			
		||||
    for attribute in &fun.attributes {
 | 
			
		||||
        let span = attribute.span();
 | 
			
		||||
@@ -56,15 +64,39 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
 | 
			
		||||
 | 
			
		||||
        match name {
 | 
			
		||||
            "subcommand" => {
 | 
			
		||||
                options
 | 
			
		||||
                    .subcommands
 | 
			
		||||
                    .push(Subcommand::new(propagate_err!(attributes::parse(values))));
 | 
			
		||||
                let new_subcommand = Subcommand::new(propagate_err!(attributes::parse(values)));
 | 
			
		||||
 | 
			
		||||
                if let Some(subcommand_group) = options.subcommand_groups.last_mut() {
 | 
			
		||||
                    last_desc = LastItem::SubGroupFun;
 | 
			
		||||
                    subcommand_group.subcommands.push(new_subcommand);
 | 
			
		||||
                } else {
 | 
			
		||||
                    last_desc = LastItem::SubFun;
 | 
			
		||||
                    options.subcommands.push(new_subcommand);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            "subcommandgroup" => {
 | 
			
		||||
                let new_group = SubcommandGroup::new(propagate_err!(attributes::parse(values)));
 | 
			
		||||
                last_desc = LastItem::SubGroup;
 | 
			
		||||
 | 
			
		||||
                options.subcommand_groups.push(new_group);
 | 
			
		||||
            }
 | 
			
		||||
            "arg" => {
 | 
			
		||||
                if let Some(subcommand) = options.subcommands.last_mut() {
 | 
			
		||||
                    subcommand.cmd_args.push(propagate_err!(attributes::parse(values)));
 | 
			
		||||
                if let Some(subcommand_group) = options.subcommand_groups.last_mut() {
 | 
			
		||||
                    if let Some(subcommand) = subcommand_group.subcommands.last_mut() {
 | 
			
		||||
                        subcommand.cmd_args.push(propagate_err!(attributes::parse(values)));
 | 
			
		||||
                    } else {
 | 
			
		||||
                        if let Some(subcommand) = options.subcommands.last_mut() {
 | 
			
		||||
                            subcommand.cmd_args.push(propagate_err!(attributes::parse(values)));
 | 
			
		||||
                        } else {
 | 
			
		||||
                            options.cmd_args.push(propagate_err!(attributes::parse(values)));
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    options.cmd_args.push(propagate_err!(attributes::parse(values)));
 | 
			
		||||
                    if let Some(subcommand) = options.subcommands.last_mut() {
 | 
			
		||||
                        subcommand.cmd_args.push(propagate_err!(attributes::parse(values)));
 | 
			
		||||
                    } else {
 | 
			
		||||
                        options.cmd_args.push(propagate_err!(attributes::parse(values)));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            "example" => {
 | 
			
		||||
@@ -72,10 +104,36 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
 | 
			
		||||
            }
 | 
			
		||||
            "description" => {
 | 
			
		||||
                let line: String = propagate_err!(attributes::parse(values));
 | 
			
		||||
                if let Some(subcommand) = options.subcommands.last_mut() {
 | 
			
		||||
                    util::append_line(&mut subcommand.description, line);
 | 
			
		||||
                } else {
 | 
			
		||||
                    util::append_line(&mut options.description, line);
 | 
			
		||||
 | 
			
		||||
                match last_desc {
 | 
			
		||||
                    LastItem::Fun => {
 | 
			
		||||
                        util::append_line(&mut options.description, line);
 | 
			
		||||
                    }
 | 
			
		||||
                    LastItem::SubFun => {
 | 
			
		||||
                        util::append_line(
 | 
			
		||||
                            &mut options.subcommands.last_mut().unwrap().description,
 | 
			
		||||
                            line,
 | 
			
		||||
                        );
 | 
			
		||||
                    }
 | 
			
		||||
                    LastItem::SubGroup => {
 | 
			
		||||
                        util::append_line(
 | 
			
		||||
                            &mut options.subcommand_groups.last_mut().unwrap().description,
 | 
			
		||||
                            line,
 | 
			
		||||
                        );
 | 
			
		||||
                    }
 | 
			
		||||
                    LastItem::SubGroupFun => {
 | 
			
		||||
                        util::append_line(
 | 
			
		||||
                            &mut options
 | 
			
		||||
                                .subcommand_groups
 | 
			
		||||
                                .last_mut()
 | 
			
		||||
                                .unwrap()
 | 
			
		||||
                                .subcommands
 | 
			
		||||
                                .last_mut()
 | 
			
		||||
                                .unwrap()
 | 
			
		||||
                                .description,
 | 
			
		||||
                            line,
 | 
			
		||||
                        );
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            "hook" => {
 | 
			
		||||
@@ -101,120 +159,81 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
 | 
			
		||||
        supports_dm,
 | 
			
		||||
        mut cmd_args,
 | 
			
		||||
        mut subcommands,
 | 
			
		||||
        mut subcommand_groups,
 | 
			
		||||
    } = options;
 | 
			
		||||
 | 
			
		||||
    let visibility = fun.visibility;
 | 
			
		||||
    let name = fun.name.clone();
 | 
			
		||||
    let body = fun.body;
 | 
			
		||||
 | 
			
		||||
    let n = name.with_suffix(COMMAND);
 | 
			
		||||
 | 
			
		||||
    let cooked = fun.cooked.clone();
 | 
			
		||||
    let root_ident = name.with_suffix(COMMAND);
 | 
			
		||||
 | 
			
		||||
    let command_path = quote!(crate::framework::Command);
 | 
			
		||||
    let arg_path = quote!(crate::framework::Arg);
 | 
			
		||||
    let subcommand_path = ApplicationCommandOptionType::SubCommand;
 | 
			
		||||
 | 
			
		||||
    populate_fut_lifetimes_on_refs(&mut fun.args);
 | 
			
		||||
    let args = fun.args;
 | 
			
		||||
 | 
			
		||||
    let mut subcommand_group_idents = subcommand_groups
 | 
			
		||||
        .iter()
 | 
			
		||||
        .map(|subcommand| {
 | 
			
		||||
            root_ident
 | 
			
		||||
                .with_suffix(subcommand.name.replace("-", "_").as_str())
 | 
			
		||||
                .with_suffix(SUBCOMMAND_GROUP)
 | 
			
		||||
        })
 | 
			
		||||
        .collect::<Vec<Ident>>();
 | 
			
		||||
 | 
			
		||||
    let mut subcommand_idents = subcommands
 | 
			
		||||
        .iter()
 | 
			
		||||
        .map(|subcommand| {
 | 
			
		||||
            n.with_suffix(subcommand.name.replace("-", "_").as_str()).with_suffix(SUBCOMMAND)
 | 
			
		||||
            root_ident
 | 
			
		||||
                .with_suffix(subcommand.name.replace("-", "_").as_str())
 | 
			
		||||
                .with_suffix(SUBCOMMAND)
 | 
			
		||||
        })
 | 
			
		||||
        .collect::<Vec<Ident>>();
 | 
			
		||||
 | 
			
		||||
    let mut tokens = subcommands
 | 
			
		||||
        .iter_mut()
 | 
			
		||||
        .zip(subcommand_idents.iter())
 | 
			
		||||
        .map(|(subcommand, sc_ident)| {
 | 
			
		||||
            let arg_idents = subcommand
 | 
			
		||||
                .cmd_args
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|arg| {
 | 
			
		||||
                    n.with_suffix(subcommand.name.as_str())
 | 
			
		||||
                        .with_suffix(arg.name.as_str())
 | 
			
		||||
                        .with_suffix(ARG)
 | 
			
		||||
                })
 | 
			
		||||
                .collect::<Vec<Ident>>();
 | 
			
		||||
 | 
			
		||||
            let mut tokens = subcommand
 | 
			
		||||
                .cmd_args
 | 
			
		||||
                .iter_mut()
 | 
			
		||||
                .zip(arg_idents.iter())
 | 
			
		||||
                .map(|(arg, ident)| {
 | 
			
		||||
                    let Arg { name, description, kind, required } = arg;
 | 
			
		||||
 | 
			
		||||
                    quote! {
 | 
			
		||||
                        #(#cooked)*
 | 
			
		||||
                        #[allow(missing_docs)]
 | 
			
		||||
                        pub static #ident: #arg_path = #arg_path {
 | 
			
		||||
                            name: #name,
 | 
			
		||||
                            description: #description,
 | 
			
		||||
                            kind: #kind,
 | 
			
		||||
                            required: #required,
 | 
			
		||||
                            options: &[]
 | 
			
		||||
                        };
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
                .fold(quote! {}, |mut a, b| {
 | 
			
		||||
                    a.extend(b);
 | 
			
		||||
                    a
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            let Subcommand { name, description, .. } = subcommand;
 | 
			
		||||
 | 
			
		||||
            tokens.extend(quote! {
 | 
			
		||||
                #(#cooked)*
 | 
			
		||||
                #[allow(missing_docs)]
 | 
			
		||||
                pub static #sc_ident: #arg_path = #arg_path {
 | 
			
		||||
                    name: #name,
 | 
			
		||||
                    description: #description,
 | 
			
		||||
                    kind: #subcommand_path,
 | 
			
		||||
                    required: false,
 | 
			
		||||
                    options: &[#(&#arg_idents),*],
 | 
			
		||||
                };
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            tokens
 | 
			
		||||
        })
 | 
			
		||||
        .fold(quote! {}, |mut a, b| {
 | 
			
		||||
            a.extend(b);
 | 
			
		||||
            a
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    let mut arg_idents = cmd_args
 | 
			
		||||
        .iter()
 | 
			
		||||
        .map(|arg| n.with_suffix(arg.name.replace("-", "_").as_str()).with_suffix(ARG))
 | 
			
		||||
        .map(|arg| root_ident.with_suffix(arg.name.replace("-", "_").as_str()).with_suffix(ARG))
 | 
			
		||||
        .collect::<Vec<Ident>>();
 | 
			
		||||
 | 
			
		||||
    let arg_tokens = cmd_args
 | 
			
		||||
        .iter_mut()
 | 
			
		||||
        .zip(arg_idents.iter())
 | 
			
		||||
        .map(|(arg, ident)| {
 | 
			
		||||
            let Arg { name, description, kind, required } = arg;
 | 
			
		||||
    let mut tokens = quote! {};
 | 
			
		||||
 | 
			
		||||
            quote! {
 | 
			
		||||
                #(#cooked)*
 | 
			
		||||
                #[allow(missing_docs)]
 | 
			
		||||
                pub static #ident: #arg_path = #arg_path {
 | 
			
		||||
                    name: #name,
 | 
			
		||||
                    description: #description,
 | 
			
		||||
                    kind: #kind,
 | 
			
		||||
                    required: #required,
 | 
			
		||||
                    options: &[],
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .fold(quote! {}, |mut a, b| {
 | 
			
		||||
            a.extend(b);
 | 
			
		||||
            a
 | 
			
		||||
        });
 | 
			
		||||
    tokens.extend(
 | 
			
		||||
        subcommand_groups
 | 
			
		||||
            .iter_mut()
 | 
			
		||||
            .zip(subcommand_group_idents.iter())
 | 
			
		||||
            .map(|(group, group_ident)| group.as_tokens(group_ident))
 | 
			
		||||
            .fold(quote! {}, |mut a, b| {
 | 
			
		||||
                a.extend(b);
 | 
			
		||||
                a
 | 
			
		||||
            }),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    tokens.extend(arg_tokens);
 | 
			
		||||
    tokens.extend(
 | 
			
		||||
        subcommands
 | 
			
		||||
            .iter_mut()
 | 
			
		||||
            .zip(subcommand_idents.iter())
 | 
			
		||||
            .map(|(subcommand, sc_ident)| subcommand.as_tokens(sc_ident))
 | 
			
		||||
            .fold(quote! {}, |mut a, b| {
 | 
			
		||||
                a.extend(b);
 | 
			
		||||
                a
 | 
			
		||||
            }),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    tokens.extend(
 | 
			
		||||
        cmd_args.iter_mut().zip(arg_idents.iter()).map(|(arg, ident)| arg.as_tokens(ident)).fold(
 | 
			
		||||
            quote! {},
 | 
			
		||||
            |mut a, b| {
 | 
			
		||||
                a.extend(b);
 | 
			
		||||
                a
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    arg_idents.append(&mut subcommand_group_idents);
 | 
			
		||||
    arg_idents.append(&mut subcommand_idents);
 | 
			
		||||
 | 
			
		||||
    let args = fun.args;
 | 
			
		||||
 | 
			
		||||
    let variant = if args.len() == 2 {
 | 
			
		||||
        quote!(crate::framework::CommandFnType::Multi)
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -230,9 +249,8 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    tokens.extend(quote! {
 | 
			
		||||
        #(#cooked)*
 | 
			
		||||
        #[allow(missing_docs)]
 | 
			
		||||
        pub static #n: #command_path = #command_path {
 | 
			
		||||
        pub static #root_ident: #command_path = #command_path {
 | 
			
		||||
            fun: #variant(#name),
 | 
			
		||||
            names: &[#_name, #(#aliases),*],
 | 
			
		||||
            desc: #description,
 | 
			
		||||
@@ -243,10 +261,7 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
 | 
			
		||||
            args: &[#(&#arg_idents),*],
 | 
			
		||||
            hooks: &[#(&#hooks),*],
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    tokens.extend(quote! {
 | 
			
		||||
        #(#cooked)*
 | 
			
		||||
        #[allow(missing_docs)]
 | 
			
		||||
        #visibility fn #name<'fut> (#(#args),*) -> ::serenity::futures::future::BoxFuture<'fut, ()> {
 | 
			
		||||
            use ::serenity::futures::future::FutureExt;
 | 
			
		||||
@@ -269,7 +284,6 @@ pub fn check(_attr: TokenStream, input: TokenStream) -> TokenStream {
 | 
			
		||||
    let fn_name = n.with_suffix(CHECK);
 | 
			
		||||
    let visibility = fun.visibility;
 | 
			
		||||
 | 
			
		||||
    let cooked = fun.cooked;
 | 
			
		||||
    let body = fun.body;
 | 
			
		||||
    let ret = fun.ret;
 | 
			
		||||
    populate_fut_lifetimes_on_refs(&mut fun.args);
 | 
			
		||||
@@ -279,7 +293,6 @@ pub fn check(_attr: TokenStream, input: TokenStream) -> TokenStream {
 | 
			
		||||
    let uuid = Uuid::new_v4().as_u128();
 | 
			
		||||
 | 
			
		||||
    (quote! {
 | 
			
		||||
        #(#cooked)*
 | 
			
		||||
        #[allow(missing_docs)]
 | 
			
		||||
        #visibility fn #fn_name<'fut>(#(#args),*) -> ::serenity::futures::future::BoxFuture<'fut, #ret> {
 | 
			
		||||
            use ::serenity::futures::future::FutureExt;
 | 
			
		||||
@@ -291,7 +304,6 @@ pub fn check(_attr: TokenStream, input: TokenStream) -> TokenStream {
 | 
			
		||||
            }.boxed()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #(#cooked)*
 | 
			
		||||
        #[allow(missing_docs)]
 | 
			
		||||
        pub static #name: #hook_path = #hook_path {
 | 
			
		||||
            fun: #fn_name,
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,10 @@ use syn::{
 | 
			
		||||
    Attribute, Block, FnArg, Ident, Pat, ReturnType, Stmt, Token, Type, Visibility,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::util::{Argument, Parenthesised};
 | 
			
		||||
use crate::{
 | 
			
		||||
    consts::{ARG, SUBCOMMAND},
 | 
			
		||||
    util::{Argument, IdentExt2, Parenthesised},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
fn parse_argument(arg: FnArg) -> Result<Argument> {
 | 
			
		||||
    match arg {
 | 
			
		||||
@@ -38,43 +41,12 @@ fn parse_argument(arg: FnArg) -> Result<Argument> {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test if the attribute is cooked.
 | 
			
		||||
fn is_cooked(attr: &Attribute) -> bool {
 | 
			
		||||
    const COOKED_ATTRIBUTE_NAMES: &[&str] =
 | 
			
		||||
        &["cfg", "cfg_attr", "derive", "inline", "allow", "warn", "deny", "forbid"];
 | 
			
		||||
 | 
			
		||||
    COOKED_ATTRIBUTE_NAMES.iter().any(|n| attr.path.is_ident(n))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Removes cooked attributes from a vector of attributes. Uncooked attributes are left in the vector.
 | 
			
		||||
///
 | 
			
		||||
/// # Return
 | 
			
		||||
///
 | 
			
		||||
/// Returns a vector of cooked attributes that have been removed from the input vector.
 | 
			
		||||
fn remove_cooked(attrs: &mut Vec<Attribute>) -> Vec<Attribute> {
 | 
			
		||||
    let mut cooked = Vec::new();
 | 
			
		||||
 | 
			
		||||
    // FIXME: Replace with `Vec::drain_filter` once it is stable.
 | 
			
		||||
    let mut i = 0;
 | 
			
		||||
    while i < attrs.len() {
 | 
			
		||||
        if !is_cooked(&attrs[i]) {
 | 
			
		||||
            i += 1;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        cooked.push(attrs.remove(i));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cooked
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct CommandFun {
 | 
			
		||||
    /// `#[...]`-style attributes.
 | 
			
		||||
    pub attributes: Vec<Attribute>,
 | 
			
		||||
    /// Populated cooked attributes. These are attributes outside of the realm of this crate's procedural macros
 | 
			
		||||
    /// and will appear in generated output.
 | 
			
		||||
    pub cooked: Vec<Attribute>,
 | 
			
		||||
    pub visibility: Visibility,
 | 
			
		||||
    pub name: Ident,
 | 
			
		||||
    pub args: Vec<Argument>,
 | 
			
		||||
@@ -84,9 +56,7 @@ pub struct CommandFun {
 | 
			
		||||
 | 
			
		||||
impl Parse for CommandFun {
 | 
			
		||||
    fn parse(input: ParseStream<'_>) -> Result<Self> {
 | 
			
		||||
        let mut attributes = input.call(Attribute::parse_outer)?;
 | 
			
		||||
 | 
			
		||||
        let cooked = remove_cooked(&mut attributes);
 | 
			
		||||
        let attributes = input.call(Attribute::parse_outer)?;
 | 
			
		||||
 | 
			
		||||
        let visibility = input.parse::<Visibility>()?;
 | 
			
		||||
 | 
			
		||||
@@ -110,16 +80,15 @@ impl Parse for CommandFun {
 | 
			
		||||
 | 
			
		||||
        let args = args.into_iter().map(parse_argument).collect::<Result<Vec<_>>>()?;
 | 
			
		||||
 | 
			
		||||
        Ok(Self { attributes, cooked, visibility, name, args, ret, body })
 | 
			
		||||
        Ok(Self { attributes, visibility, name, args, ret, body })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for CommandFun {
 | 
			
		||||
    fn to_tokens(&self, stream: &mut TokenStream2) {
 | 
			
		||||
        let Self { attributes: _, cooked, visibility, name, args, ret, body } = self;
 | 
			
		||||
        let Self { attributes: _, visibility, name, args, ret, body } = self;
 | 
			
		||||
 | 
			
		||||
        stream.extend(quote! {
 | 
			
		||||
            #(#cooked)*
 | 
			
		||||
            #visibility async fn #name (#(#args),*) -> #ret {
 | 
			
		||||
                #(#body)*
 | 
			
		||||
            }
 | 
			
		||||
@@ -193,6 +162,24 @@ pub(crate) struct Arg {
 | 
			
		||||
    pub required: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Arg {
 | 
			
		||||
    pub fn as_tokens(&self, ident: &Ident) -> TokenStream2 {
 | 
			
		||||
        let arg_path = quote!(crate::framework::Arg);
 | 
			
		||||
        let Arg { name, description, kind, required } = self;
 | 
			
		||||
 | 
			
		||||
        quote! {
 | 
			
		||||
            #[allow(missing_docs)]
 | 
			
		||||
            pub static #ident: #arg_path = #arg_path {
 | 
			
		||||
                name: #name,
 | 
			
		||||
                description: #description,
 | 
			
		||||
                kind: #kind,
 | 
			
		||||
                required: #required,
 | 
			
		||||
                options: &[]
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Arg {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
@@ -211,6 +198,44 @@ pub(crate) struct Subcommand {
 | 
			
		||||
    pub cmd_args: Vec<Arg>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Subcommand {
 | 
			
		||||
    pub fn as_tokens(&mut self, ident: &Ident) -> TokenStream2 {
 | 
			
		||||
        let arg_path = quote!(crate::framework::Arg);
 | 
			
		||||
        let subcommand_path = ApplicationCommandOptionType::SubCommand;
 | 
			
		||||
 | 
			
		||||
        let arg_idents = self
 | 
			
		||||
            .cmd_args
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|arg| ident.with_suffix(arg.name.as_str()).with_suffix(ARG))
 | 
			
		||||
            .collect::<Vec<Ident>>();
 | 
			
		||||
 | 
			
		||||
        let mut tokens = self
 | 
			
		||||
            .cmd_args
 | 
			
		||||
            .iter_mut()
 | 
			
		||||
            .zip(arg_idents.iter())
 | 
			
		||||
            .map(|(arg, ident)| arg.as_tokens(ident))
 | 
			
		||||
            .fold(quote! {}, |mut a, b| {
 | 
			
		||||
                a.extend(b);
 | 
			
		||||
                a
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        let Subcommand { name, description, .. } = self;
 | 
			
		||||
 | 
			
		||||
        tokens.extend(quote! {
 | 
			
		||||
            #[allow(missing_docs)]
 | 
			
		||||
            pub static #ident: #arg_path = #arg_path {
 | 
			
		||||
                name: #name,
 | 
			
		||||
                description: #description,
 | 
			
		||||
                kind: #subcommand_path,
 | 
			
		||||
                required: false,
 | 
			
		||||
                options: &[#(&#arg_idents),*],
 | 
			
		||||
            };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        tokens
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Subcommand {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self { name: String::new(), description: String::new(), cmd_args: vec![] }
 | 
			
		||||
@@ -223,6 +248,68 @@ impl Subcommand {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub(crate) struct SubcommandGroup {
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub description: String,
 | 
			
		||||
    pub subcommands: Vec<Subcommand>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SubcommandGroup {
 | 
			
		||||
    pub fn as_tokens(&mut self, ident: &Ident) -> TokenStream2 {
 | 
			
		||||
        let arg_path = quote!(crate::framework::Arg);
 | 
			
		||||
        let subcommand_group_path = ApplicationCommandOptionType::SubCommandGroup;
 | 
			
		||||
 | 
			
		||||
        let arg_idents = self
 | 
			
		||||
            .subcommands
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|arg| {
 | 
			
		||||
                ident
 | 
			
		||||
                    .with_suffix(self.name.as_str())
 | 
			
		||||
                    .with_suffix(arg.name.as_str())
 | 
			
		||||
                    .with_suffix(SUBCOMMAND)
 | 
			
		||||
            })
 | 
			
		||||
            .collect::<Vec<Ident>>();
 | 
			
		||||
 | 
			
		||||
        let mut tokens = self
 | 
			
		||||
            .subcommands
 | 
			
		||||
            .iter_mut()
 | 
			
		||||
            .zip(arg_idents.iter())
 | 
			
		||||
            .map(|(subcommand, ident)| subcommand.as_tokens(ident))
 | 
			
		||||
            .fold(quote! {}, |mut a, b| {
 | 
			
		||||
                a.extend(b);
 | 
			
		||||
                a
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        let SubcommandGroup { name, description, .. } = self;
 | 
			
		||||
 | 
			
		||||
        tokens.extend(quote! {
 | 
			
		||||
            #[allow(missing_docs)]
 | 
			
		||||
            pub static #ident: #arg_path = #arg_path {
 | 
			
		||||
                name: #name,
 | 
			
		||||
                description: #description,
 | 
			
		||||
                kind: #subcommand_group_path,
 | 
			
		||||
                required: false,
 | 
			
		||||
                options: &[#(&#arg_idents),*],
 | 
			
		||||
            };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        tokens
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for SubcommandGroup {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self { name: String::new(), description: String::new(), subcommands: vec![] }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SubcommandGroup {
 | 
			
		||||
    pub(crate) fn new(name: String) -> Self {
 | 
			
		||||
        Self { name, ..Default::default() }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub(crate) struct Options {
 | 
			
		||||
    pub aliases: Vec<String>,
 | 
			
		||||
@@ -233,6 +320,7 @@ pub(crate) struct Options {
 | 
			
		||||
    pub supports_dm: bool,
 | 
			
		||||
    pub cmd_args: Vec<Arg>,
 | 
			
		||||
    pub subcommands: Vec<Subcommand>,
 | 
			
		||||
    pub subcommand_groups: Vec<SubcommandGroup>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Options {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user