subcommand group syntax
This commit is contained in:
parent
d84d7ab62b
commit
379e488f7a
@ -2,6 +2,7 @@ pub mod suffixes {
|
|||||||
pub const COMMAND: &str = "COMMAND";
|
pub const COMMAND: &str = "COMMAND";
|
||||||
pub const ARG: &str = "ARG";
|
pub const ARG: &str = "ARG";
|
||||||
pub const SUBCOMMAND: &str = "SUBCOMMAND";
|
pub const SUBCOMMAND: &str = "SUBCOMMAND";
|
||||||
|
pub const SUBCOMMAND_GROUP: &str = "GROUP";
|
||||||
pub const CHECK: &str = "CHECK";
|
pub const CHECK: &str = "CHECK";
|
||||||
pub const HOOK: &str = "HOOK";
|
pub const HOOK: &str = "HOOK";
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,13 @@ macro_rules! match_options {
|
|||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
enum LastItem {
|
||||||
|
Fun,
|
||||||
|
SubFun,
|
||||||
|
SubGroup,
|
||||||
|
SubGroupFun,
|
||||||
|
}
|
||||||
|
|
||||||
let mut fun = parse_macro_input!(input as CommandFun);
|
let mut fun = parse_macro_input!(input as CommandFun);
|
||||||
|
|
||||||
let _name = if !attr.is_empty() {
|
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 hooks: Vec<Ident> = Vec::new();
|
||||||
let mut options = Options::new();
|
let mut options = Options::new();
|
||||||
|
let mut last_desc = LastItem::Fun;
|
||||||
|
|
||||||
for attribute in &fun.attributes {
|
for attribute in &fun.attributes {
|
||||||
let span = attribute.span();
|
let span = attribute.span();
|
||||||
@ -56,27 +64,77 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
match name {
|
match name {
|
||||||
"subcommand" => {
|
"subcommand" => {
|
||||||
options
|
let new_subcommand = Subcommand::new(propagate_err!(attributes::parse(values)));
|
||||||
.subcommands
|
|
||||||
.push(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" => {
|
"arg" => {
|
||||||
|
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() {
|
if let Some(subcommand) = options.subcommands.last_mut() {
|
||||||
subcommand.cmd_args.push(propagate_err!(attributes::parse(values)));
|
subcommand.cmd_args.push(propagate_err!(attributes::parse(values)));
|
||||||
} else {
|
} else {
|
||||||
options.cmd_args.push(propagate_err!(attributes::parse(values)));
|
options.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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
"example" => {
|
"example" => {
|
||||||
options.examples.push(propagate_err!(attributes::parse(values)));
|
options.examples.push(propagate_err!(attributes::parse(values)));
|
||||||
}
|
}
|
||||||
"description" => {
|
"description" => {
|
||||||
let line: String = propagate_err!(attributes::parse(values));
|
let line: String = propagate_err!(attributes::parse(values));
|
||||||
if let Some(subcommand) = options.subcommands.last_mut() {
|
|
||||||
util::append_line(&mut subcommand.description, line);
|
match last_desc {
|
||||||
} else {
|
LastItem::Fun => {
|
||||||
util::append_line(&mut options.description, line);
|
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" => {
|
"hook" => {
|
||||||
hooks.push(propagate_err!(attributes::parse(values)));
|
hooks.push(propagate_err!(attributes::parse(values)));
|
||||||
@ -101,120 +159,81 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
supports_dm,
|
supports_dm,
|
||||||
mut cmd_args,
|
mut cmd_args,
|
||||||
mut subcommands,
|
mut subcommands,
|
||||||
|
mut subcommand_groups,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
let visibility = fun.visibility;
|
let visibility = fun.visibility;
|
||||||
let name = fun.name.clone();
|
let name = fun.name.clone();
|
||||||
let body = fun.body;
|
let body = fun.body;
|
||||||
|
|
||||||
let n = name.with_suffix(COMMAND);
|
let root_ident = name.with_suffix(COMMAND);
|
||||||
|
|
||||||
let cooked = fun.cooked.clone();
|
|
||||||
|
|
||||||
let command_path = quote!(crate::framework::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);
|
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
|
let mut subcommand_idents = subcommands
|
||||||
.iter()
|
.iter()
|
||||||
.map(|subcommand| {
|
.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>>();
|
.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
|
let mut arg_idents = cmd_args
|
||||||
.iter()
|
.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>>();
|
.collect::<Vec<Ident>>();
|
||||||
|
|
||||||
let arg_tokens = cmd_args
|
let mut tokens = quote! {};
|
||||||
.iter_mut()
|
|
||||||
.zip(arg_idents.iter())
|
|
||||||
.map(|(arg, ident)| {
|
|
||||||
let Arg { name, description, kind, required } = arg;
|
|
||||||
|
|
||||||
quote! {
|
tokens.extend(
|
||||||
#(#cooked)*
|
subcommand_groups
|
||||||
#[allow(missing_docs)]
|
.iter_mut()
|
||||||
pub static #ident: #arg_path = #arg_path {
|
.zip(subcommand_group_idents.iter())
|
||||||
name: #name,
|
.map(|(group, group_ident)| group.as_tokens(group_ident))
|
||||||
description: #description,
|
|
||||||
kind: #kind,
|
|
||||||
required: #required,
|
|
||||||
options: &[],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.fold(quote! {}, |mut a, b| {
|
.fold(quote! {}, |mut a, b| {
|
||||||
a.extend(b);
|
a.extend(b);
|
||||||
a
|
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);
|
arg_idents.append(&mut subcommand_idents);
|
||||||
|
|
||||||
|
let args = fun.args;
|
||||||
|
|
||||||
let variant = if args.len() == 2 {
|
let variant = if args.len() == 2 {
|
||||||
quote!(crate::framework::CommandFnType::Multi)
|
quote!(crate::framework::CommandFnType::Multi)
|
||||||
} else {
|
} else {
|
||||||
@ -230,9 +249,8 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
};
|
};
|
||||||
|
|
||||||
tokens.extend(quote! {
|
tokens.extend(quote! {
|
||||||
#(#cooked)*
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub static #n: #command_path = #command_path {
|
pub static #root_ident: #command_path = #command_path {
|
||||||
fun: #variant(#name),
|
fun: #variant(#name),
|
||||||
names: &[#_name, #(#aliases),*],
|
names: &[#_name, #(#aliases),*],
|
||||||
desc: #description,
|
desc: #description,
|
||||||
@ -243,10 +261,7 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
args: &[#(&#arg_idents),*],
|
args: &[#(&#arg_idents),*],
|
||||||
hooks: &[#(&#hooks),*],
|
hooks: &[#(&#hooks),*],
|
||||||
};
|
};
|
||||||
});
|
|
||||||
|
|
||||||
tokens.extend(quote! {
|
|
||||||
#(#cooked)*
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#visibility fn #name<'fut> (#(#args),*) -> ::serenity::futures::future::BoxFuture<'fut, ()> {
|
#visibility fn #name<'fut> (#(#args),*) -> ::serenity::futures::future::BoxFuture<'fut, ()> {
|
||||||
use ::serenity::futures::future::FutureExt;
|
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 fn_name = n.with_suffix(CHECK);
|
||||||
let visibility = fun.visibility;
|
let visibility = fun.visibility;
|
||||||
|
|
||||||
let cooked = fun.cooked;
|
|
||||||
let body = fun.body;
|
let body = fun.body;
|
||||||
let ret = fun.ret;
|
let ret = fun.ret;
|
||||||
populate_fut_lifetimes_on_refs(&mut fun.args);
|
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();
|
let uuid = Uuid::new_v4().as_u128();
|
||||||
|
|
||||||
(quote! {
|
(quote! {
|
||||||
#(#cooked)*
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#visibility fn #fn_name<'fut>(#(#args),*) -> ::serenity::futures::future::BoxFuture<'fut, #ret> {
|
#visibility fn #fn_name<'fut>(#(#args),*) -> ::serenity::futures::future::BoxFuture<'fut, #ret> {
|
||||||
use ::serenity::futures::future::FutureExt;
|
use ::serenity::futures::future::FutureExt;
|
||||||
@ -291,7 +304,6 @@ pub fn check(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
}.boxed()
|
}.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
#(#cooked)*
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub static #name: #hook_path = #hook_path {
|
pub static #name: #hook_path = #hook_path {
|
||||||
fun: #fn_name,
|
fun: #fn_name,
|
||||||
|
@ -7,7 +7,10 @@ use syn::{
|
|||||||
Attribute, Block, FnArg, Ident, Pat, ReturnType, Stmt, Token, Type, Visibility,
|
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> {
|
fn parse_argument(arg: FnArg) -> Result<Argument> {
|
||||||
match arg {
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct CommandFun {
|
pub struct CommandFun {
|
||||||
/// `#[...]`-style attributes.
|
/// `#[...]`-style attributes.
|
||||||
pub attributes: Vec<Attribute>,
|
pub attributes: Vec<Attribute>,
|
||||||
/// Populated cooked attributes. These are attributes outside of the realm of this crate's procedural macros
|
/// Populated cooked attributes. These are attributes outside of the realm of this crate's procedural macros
|
||||||
/// and will appear in generated output.
|
/// and will appear in generated output.
|
||||||
pub cooked: Vec<Attribute>,
|
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub args: Vec<Argument>,
|
pub args: Vec<Argument>,
|
||||||
@ -84,9 +56,7 @@ pub struct CommandFun {
|
|||||||
|
|
||||||
impl Parse for CommandFun {
|
impl Parse for CommandFun {
|
||||||
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||||
let mut attributes = input.call(Attribute::parse_outer)?;
|
let attributes = input.call(Attribute::parse_outer)?;
|
||||||
|
|
||||||
let cooked = remove_cooked(&mut attributes);
|
|
||||||
|
|
||||||
let visibility = input.parse::<Visibility>()?;
|
let visibility = input.parse::<Visibility>()?;
|
||||||
|
|
||||||
@ -110,16 +80,15 @@ impl Parse for CommandFun {
|
|||||||
|
|
||||||
let args = args.into_iter().map(parse_argument).collect::<Result<Vec<_>>>()?;
|
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 {
|
impl ToTokens for CommandFun {
|
||||||
fn to_tokens(&self, stream: &mut TokenStream2) {
|
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! {
|
stream.extend(quote! {
|
||||||
#(#cooked)*
|
|
||||||
#visibility async fn #name (#(#args),*) -> #ret {
|
#visibility async fn #name (#(#args),*) -> #ret {
|
||||||
#(#body)*
|
#(#body)*
|
||||||
}
|
}
|
||||||
@ -193,6 +162,24 @@ pub(crate) struct Arg {
|
|||||||
pub required: bool,
|
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 {
|
impl Default for Arg {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -211,6 +198,44 @@ pub(crate) struct Subcommand {
|
|||||||
pub cmd_args: Vec<Arg>,
|
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 {
|
impl Default for Subcommand {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { name: String::new(), description: String::new(), cmd_args: vec![] }
|
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)]
|
#[derive(Debug, Default)]
|
||||||
pub(crate) struct Options {
|
pub(crate) struct Options {
|
||||||
pub aliases: Vec<String>,
|
pub aliases: Vec<String>,
|
||||||
@ -233,6 +320,7 @@ pub(crate) struct Options {
|
|||||||
pub supports_dm: bool,
|
pub supports_dm: bool,
|
||||||
pub cmd_args: Vec<Arg>,
|
pub cmd_args: Vec<Arg>,
|
||||||
pub subcommands: Vec<Subcommand>,
|
pub subcommands: Vec<Subcommand>,
|
||||||
|
pub subcommand_groups: Vec<SubcommandGroup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
pub mod info_cmds;
|
pub mod info_cmds;
|
||||||
pub mod moderation_cmds;
|
pub mod moderation_cmds;
|
||||||
pub mod reminder_cmds;
|
pub mod reminder_cmds;
|
||||||
//pub mod todo_cmds;
|
pub mod todo_cmds;
|
||||||
|
@ -1,443 +1,44 @@
|
|||||||
use std::{convert::TryFrom, fmt};
|
|
||||||
|
|
||||||
use regex_command_attr::command;
|
use regex_command_attr::command;
|
||||||
use serenity::{
|
use serenity::client::Context;
|
||||||
async_trait,
|
|
||||||
client::Context,
|
|
||||||
constants::MESSAGE_CODE_LIMIT,
|
|
||||||
model::{
|
|
||||||
channel::Message,
|
|
||||||
id::{ChannelId, GuildId, UserId},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use sqlx::MySqlPool;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::framework::{CommandInvoke, CommandOptions};
|
||||||
command_help, get_ctx_data,
|
|
||||||
models::{user_data::UserData, CtxData},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[command]
|
||||||
struct TodoNotFound;
|
#[description("Manage todo lists")]
|
||||||
|
#[subcommandgroup("server")]
|
||||||
impl std::error::Error for TodoNotFound {}
|
#[description("Manage the server todo list")]
|
||||||
impl fmt::Display for TodoNotFound {
|
#[subcommand("add")]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
#[description("Add an item to the server todo list")]
|
||||||
write!(f, "Todo not found")
|
#[arg(
|
||||||
}
|
name = "task",
|
||||||
}
|
description = "The task to add to the todo list",
|
||||||
|
kind = "String",
|
||||||
struct Todo {
|
required = true
|
||||||
id: u32,
|
)]
|
||||||
value: String,
|
#[subcommand("view")]
|
||||||
}
|
#[description("View and remove from the server todo list")]
|
||||||
|
#[subcommandgroup("channel")]
|
||||||
struct TodoTarget {
|
#[description("Manage the channel todo list")]
|
||||||
user: UserId,
|
#[subcommand("add")]
|
||||||
guild: Option<GuildId>,
|
#[description("Add to the channel todo list")]
|
||||||
channel: Option<ChannelId>,
|
#[arg(
|
||||||
}
|
name = "task",
|
||||||
|
description = "The task to add to the todo list",
|
||||||
impl TodoTarget {
|
kind = "String",
|
||||||
pub fn command(&self, subcommand_opt: Option<SubCommand>) -> String {
|
required = true
|
||||||
let context = if self.channel.is_some() {
|
)]
|
||||||
"channel"
|
#[subcommand("view")]
|
||||||
} else if self.guild.is_some() {
|
#[description("View and remove from the channel todo list")]
|
||||||
"guild"
|
#[subcommandgroup("user")]
|
||||||
} else {
|
#[description("Manage your personal todo list")]
|
||||||
"user"
|
#[subcommand("add")]
|
||||||
};
|
#[description("Add to your personal todo list")]
|
||||||
|
#[arg(
|
||||||
if let Some(subcommand) = subcommand_opt {
|
name = "task",
|
||||||
format!("todo {} {}", context, subcommand.to_string())
|
description = "The task to add to the todo list",
|
||||||
} else {
|
kind = "String",
|
||||||
format!("todo {}", context)
|
required = true
|
||||||
}
|
)]
|
||||||
}
|
#[subcommand("view")]
|
||||||
|
#[description("View and remove from your personal todo list")]
|
||||||
pub fn name(&self) -> String {
|
async fn todo(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) {}
|
||||||
if self.channel.is_some() {
|
|
||||||
"Channel"
|
|
||||||
} else if self.guild.is_some() {
|
|
||||||
"Guild"
|
|
||||||
} else {
|
|
||||||
"User"
|
|
||||||
}
|
|
||||||
.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn view(
|
|
||||||
&self,
|
|
||||||
pool: MySqlPool,
|
|
||||||
) -> Result<Vec<Todo>, Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
Ok(if let Some(cid) = self.channel {
|
|
||||||
sqlx::query_as!(
|
|
||||||
Todo,
|
|
||||||
"
|
|
||||||
SELECT id, value FROM todos WHERE channel_id = (SELECT id FROM channels WHERE channel = ?)
|
|
||||||
",
|
|
||||||
cid.as_u64()
|
|
||||||
)
|
|
||||||
.fetch_all(&pool)
|
|
||||||
.await?
|
|
||||||
} else if let Some(gid) = self.guild {
|
|
||||||
sqlx::query_as!(
|
|
||||||
Todo,
|
|
||||||
"
|
|
||||||
SELECT id, value FROM todos WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND channel_id IS NULL
|
|
||||||
",
|
|
||||||
gid.as_u64()
|
|
||||||
)
|
|
||||||
.fetch_all(&pool)
|
|
||||||
.await?
|
|
||||||
} else {
|
|
||||||
sqlx::query_as!(
|
|
||||||
Todo,
|
|
||||||
"
|
|
||||||
SELECT id, value FROM todos WHERE user_id = (SELECT id FROM users WHERE user = ?) AND guild_id IS NULL
|
|
||||||
",
|
|
||||||
self.user.as_u64()
|
|
||||||
)
|
|
||||||
.fetch_all(&pool)
|
|
||||||
.await?
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add(
|
|
||||||
&self,
|
|
||||||
value: String,
|
|
||||||
pool: MySqlPool,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
if let (Some(cid), Some(gid)) = (self.channel, self.guild) {
|
|
||||||
sqlx::query!(
|
|
||||||
"
|
|
||||||
INSERT INTO todos (user_id, guild_id, channel_id, value) VALUES (
|
|
||||||
(SELECT id FROM users WHERE user = ?),
|
|
||||||
(SELECT id FROM guilds WHERE guild = ?),
|
|
||||||
(SELECT id FROM channels WHERE channel = ?),
|
|
||||||
?
|
|
||||||
)
|
|
||||||
",
|
|
||||||
self.user.as_u64(),
|
|
||||||
gid.as_u64(),
|
|
||||||
cid.as_u64(),
|
|
||||||
value
|
|
||||||
)
|
|
||||||
.execute(&pool)
|
|
||||||
.await?;
|
|
||||||
} else if let Some(gid) = self.guild {
|
|
||||||
sqlx::query!(
|
|
||||||
"
|
|
||||||
INSERT INTO todos (user_id, guild_id, value) VALUES (
|
|
||||||
(SELECT id FROM users WHERE user = ?),
|
|
||||||
(SELECT id FROM guilds WHERE guild = ?),
|
|
||||||
?
|
|
||||||
)
|
|
||||||
",
|
|
||||||
self.user.as_u64(),
|
|
||||||
gid.as_u64(),
|
|
||||||
value
|
|
||||||
)
|
|
||||||
.execute(&pool)
|
|
||||||
.await?;
|
|
||||||
} else {
|
|
||||||
sqlx::query!(
|
|
||||||
"
|
|
||||||
INSERT INTO todos (user_id, value) VALUES (
|
|
||||||
(SELECT id FROM users WHERE user = ?),
|
|
||||||
?
|
|
||||||
)
|
|
||||||
",
|
|
||||||
self.user.as_u64(),
|
|
||||||
value
|
|
||||||
)
|
|
||||||
.execute(&pool)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn remove(
|
|
||||||
&self,
|
|
||||||
num: usize,
|
|
||||||
pool: &MySqlPool,
|
|
||||||
) -> Result<Todo, Box<dyn std::error::Error + Sync + Send>> {
|
|
||||||
let todos = self.view(pool.clone()).await?;
|
|
||||||
|
|
||||||
if let Some(removal_todo) = todos.get(num) {
|
|
||||||
let deleting = sqlx::query_as!(
|
|
||||||
Todo,
|
|
||||||
"
|
|
||||||
SELECT id, value FROM todos WHERE id = ?
|
|
||||||
",
|
|
||||||
removal_todo.id
|
|
||||||
)
|
|
||||||
.fetch_one(&pool.clone())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
sqlx::query!(
|
|
||||||
"
|
|
||||||
DELETE FROM todos WHERE id = ?
|
|
||||||
",
|
|
||||||
removal_todo.id
|
|
||||||
)
|
|
||||||
.execute(pool)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(deleting)
|
|
||||||
} else {
|
|
||||||
Err(Box::new(TodoNotFound))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn clear(
|
|
||||||
&self,
|
|
||||||
pool: &MySqlPool,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error + Sync + Send>> {
|
|
||||||
if let Some(cid) = self.channel {
|
|
||||||
sqlx::query!(
|
|
||||||
"
|
|
||||||
DELETE FROM todos WHERE channel_id = (SELECT id FROM channels WHERE channel = ?)
|
|
||||||
",
|
|
||||||
cid.as_u64()
|
|
||||||
)
|
|
||||||
.execute(pool)
|
|
||||||
.await?;
|
|
||||||
} else if let Some(gid) = self.guild {
|
|
||||||
sqlx::query!(
|
|
||||||
"
|
|
||||||
DELETE FROM todos WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND channel_id IS NULL
|
|
||||||
",
|
|
||||||
gid.as_u64()
|
|
||||||
)
|
|
||||||
.execute(pool)
|
|
||||||
.await?;
|
|
||||||
} else {
|
|
||||||
sqlx::query!(
|
|
||||||
"
|
|
||||||
DELETE FROM todos WHERE user_id = (SELECT id FROM users WHERE user = ?) AND guild_id IS NULL
|
|
||||||
",
|
|
||||||
self.user.as_u64()
|
|
||||||
)
|
|
||||||
.execute(pool)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn execute(&self, ctx: &Context, msg: &Message, subcommand: SubCommand, extra: String) {
|
|
||||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
|
||||||
|
|
||||||
let user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
|
|
||||||
let prefix = ctx.prefix(msg.guild_id).await;
|
|
||||||
|
|
||||||
match subcommand {
|
|
||||||
SubCommand::View => {
|
|
||||||
let todo_items = self.view(pool).await.unwrap();
|
|
||||||
let mut todo_groups = vec!["".to_string()];
|
|
||||||
let mut char_count = 0;
|
|
||||||
|
|
||||||
todo_items.iter().enumerate().for_each(|(count, todo)| {
|
|
||||||
let display = format!("{}: {}\n", count + 1, todo.value);
|
|
||||||
|
|
||||||
if char_count + display.len() > MESSAGE_CODE_LIMIT as usize {
|
|
||||||
char_count = display.len();
|
|
||||||
|
|
||||||
todo_groups.push(display);
|
|
||||||
} else {
|
|
||||||
char_count += display.len();
|
|
||||||
|
|
||||||
let last_group = todo_groups.pop().unwrap();
|
|
||||||
|
|
||||||
todo_groups.push(format!("{}{}", last_group, display));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for group in todo_groups {
|
|
||||||
let _ = msg
|
|
||||||
.channel_id
|
|
||||||
.send_message(&ctx, |m| {
|
|
||||||
m.embed(|e| e.title(format!("{} Todo", self.name())).description(group))
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SubCommand::Add => {
|
|
||||||
let content = lm
|
|
||||||
.get(&user_data.language, "todo/added")
|
|
||||||
.replacen("{name}", &extra, 1);
|
|
||||||
|
|
||||||
self.add(extra, pool).await.unwrap();
|
|
||||||
|
|
||||||
let _ = msg.channel_id.say(&ctx, content).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
SubCommand::Remove => {
|
|
||||||
if let Ok(num) = extra.parse::<usize>() {
|
|
||||||
if let Ok(todo) = self.remove(num - 1, &pool).await {
|
|
||||||
let content = lm.get(&user_data.language, "todo/removed").replacen(
|
|
||||||
"{}",
|
|
||||||
&todo.value,
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
|
|
||||||
let _ = msg.channel_id.say(&ctx, content).await;
|
|
||||||
} else {
|
|
||||||
let _ = msg
|
|
||||||
.channel_id
|
|
||||||
.say(&ctx, lm.get(&user_data.language, "todo/error_index"))
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let content = lm
|
|
||||||
.get(&user_data.language, "todo/error_value")
|
|
||||||
.replacen("{prefix}", &prefix, 1)
|
|
||||||
.replacen("{command}", &self.command(Some(subcommand)), 1);
|
|
||||||
|
|
||||||
let _ = msg.channel_id.say(&ctx, content).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SubCommand::Clear => {
|
|
||||||
self.clear(&pool).await.unwrap();
|
|
||||||
|
|
||||||
let content = lm.get(&user_data.language, "todo/cleared");
|
|
||||||
|
|
||||||
let _ = msg.channel_id.say(&ctx, content).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SubCommand {
|
|
||||||
View,
|
|
||||||
Add,
|
|
||||||
Remove,
|
|
||||||
Clear,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Option<&str>> for SubCommand {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(value: Option<&str>) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
Some("add") => Ok(SubCommand::Add),
|
|
||||||
|
|
||||||
Some("remove") => Ok(SubCommand::Remove),
|
|
||||||
|
|
||||||
Some("clear") => Ok(SubCommand::Clear),
|
|
||||||
|
|
||||||
None | Some("") => Ok(SubCommand::View),
|
|
||||||
|
|
||||||
Some(_unrecognised) => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToString for SubCommand {
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
match self {
|
|
||||||
SubCommand::View => "",
|
|
||||||
SubCommand::Add => "add",
|
|
||||||
SubCommand::Remove => "remove",
|
|
||||||
SubCommand::Clear => "clear",
|
|
||||||
}
|
|
||||||
.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
trait Execute {
|
|
||||||
async fn execute(self, ctx: &Context, msg: &Message, extra: String, target: TodoTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Execute for Result<SubCommand, ()> {
|
|
||||||
async fn execute(self, ctx: &Context, msg: &Message, extra: String, target: TodoTarget) {
|
|
||||||
if let Ok(subcommand) = self {
|
|
||||||
target.execute(ctx, msg, subcommand, extra).await;
|
|
||||||
} else {
|
|
||||||
show_help(ctx, msg, Some(target)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command("todo")]
|
|
||||||
async fn todo_user(ctx: &Context, msg: &Message, args: String) {
|
|
||||||
let mut split = args.split(' ');
|
|
||||||
|
|
||||||
let target = TodoTarget {
|
|
||||||
user: msg.author.id,
|
|
||||||
guild: None,
|
|
||||||
channel: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let subcommand_opt = SubCommand::try_from(split.next());
|
|
||||||
|
|
||||||
subcommand_opt
|
|
||||||
.execute(ctx, msg, split.collect::<Vec<&str>>().join(" "), target)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command("todoc")]
|
|
||||||
#[supports_dm(false)]
|
|
||||||
#[permission_level(Managed)]
|
|
||||||
async fn todo_channel(ctx: &Context, msg: &Message, args: String) {
|
|
||||||
let mut split = args.split(' ');
|
|
||||||
|
|
||||||
let target = TodoTarget {
|
|
||||||
user: msg.author.id,
|
|
||||||
guild: msg.guild_id,
|
|
||||||
channel: Some(msg.channel_id),
|
|
||||||
};
|
|
||||||
|
|
||||||
let subcommand_opt = SubCommand::try_from(split.next());
|
|
||||||
|
|
||||||
subcommand_opt
|
|
||||||
.execute(ctx, msg, split.collect::<Vec<&str>>().join(" "), target)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command("todos")]
|
|
||||||
#[supports_dm(false)]
|
|
||||||
#[permission_level(Managed)]
|
|
||||||
async fn todo_guild(ctx: &Context, msg: &Message, args: String) {
|
|
||||||
let mut split = args.split(' ');
|
|
||||||
|
|
||||||
let target = TodoTarget {
|
|
||||||
user: msg.author.id,
|
|
||||||
guild: msg.guild_id,
|
|
||||||
channel: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let subcommand_opt = SubCommand::try_from(split.next());
|
|
||||||
|
|
||||||
subcommand_opt
|
|
||||||
.execute(ctx, msg, split.collect::<Vec<&str>>().join(" "), target)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn show_help(ctx: &Context, msg: &Message, target: Option<TodoTarget>) {
|
|
||||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
|
||||||
|
|
||||||
let language = UserData::language_of(&msg.author, &pool);
|
|
||||||
let prefix = ctx.prefix(msg.guild_id);
|
|
||||||
|
|
||||||
let command = match target {
|
|
||||||
None => "todo",
|
|
||||||
Some(t) => {
|
|
||||||
if t.channel.is_some() {
|
|
||||||
"todoc"
|
|
||||||
} else if t.guild.is_some() {
|
|
||||||
"todos"
|
|
||||||
} else {
|
|
||||||
"todo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
command_help(ctx, msg, lm, &prefix.await, &language.await, command).await;
|
|
||||||
}
|
|
||||||
|
12
src/main.rs
12
src/main.rs
@ -36,7 +36,7 @@ use sqlx::mysql::MySqlPool;
|
|||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::{info_cmds, moderation_cmds, reminder_cmds},
|
commands::{info_cmds, moderation_cmds, reminder_cmds, todo_cmds},
|
||||||
component_models::ComponentDataModel,
|
component_models::ComponentDataModel,
|
||||||
consts::{CNC_GUILD, DEFAULT_PREFIX, SUBSCRIPTION_ROLES, THEME_COLOR},
|
consts::{CNC_GUILD, DEFAULT_PREFIX, SUBSCRIPTION_ROLES, THEME_COLOR},
|
||||||
framework::RegexFramework,
|
framework::RegexFramework,
|
||||||
@ -318,16 +318,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||||||
.add_command(&reminder_cmds::PAUSE_COMMAND)
|
.add_command(&reminder_cmds::PAUSE_COMMAND)
|
||||||
.add_command(&reminder_cmds::OFFSET_COMMAND)
|
.add_command(&reminder_cmds::OFFSET_COMMAND)
|
||||||
.add_command(&reminder_cmds::NUDGE_COMMAND)
|
.add_command(&reminder_cmds::NUDGE_COMMAND)
|
||||||
/*
|
|
||||||
// to-do commands
|
// to-do commands
|
||||||
.add_command("todo", &todo_cmds::TODO_USER_COMMAND)
|
.add_command(&todo_cmds::TODO_COMMAND)
|
||||||
.add_command("todo user", &todo_cmds::TODO_USER_COMMAND)
|
|
||||||
.add_command("todoc", &todo_cmds::TODO_CHANNEL_COMMAND)
|
|
||||||
.add_command("todo channel", &todo_cmds::TODO_CHANNEL_COMMAND)
|
|
||||||
.add_command("todos", &todo_cmds::TODO_GUILD_COMMAND)
|
|
||||||
.add_command("todo server", &todo_cmds::TODO_GUILD_COMMAND)
|
|
||||||
.add_command("todo guild", &todo_cmds::TODO_GUILD_COMMAND)
|
|
||||||
*/
|
|
||||||
// moderation commands
|
// moderation commands
|
||||||
.add_command(&moderation_cmds::BLACKLIST_COMMAND)
|
.add_command(&moderation_cmds::BLACKLIST_COMMAND)
|
||||||
.add_command(&moderation_cmds::RESTRICT_COMMAND)
|
.add_command(&moderation_cmds::RESTRICT_COMMAND)
|
||||||
|
Loading…
Reference in New Issue
Block a user