114 lines
2.8 KiB
Rust
114 lines
2.8 KiB
Rust
|
#![deny(rust_2018_idioms)]
|
||
|
// FIXME: Remove this in a foreseeable future.
|
||
|
// Currently exists for backwards compatibility to previous Rust versions.
|
||
|
#![recursion_limit = "128"]
|
||
|
|
||
|
#[allow(unused_extern_crates)]
|
||
|
extern crate proc_macro;
|
||
|
|
||
|
use proc_macro::TokenStream;
|
||
|
use quote::quote;
|
||
|
use syn::{
|
||
|
parse::Error,
|
||
|
parse_macro_input, parse_quote,
|
||
|
spanned::Spanned,
|
||
|
Lit,
|
||
|
};
|
||
|
|
||
|
pub(crate) mod attributes;
|
||
|
pub(crate) mod consts;
|
||
|
pub(crate) mod structures;
|
||
|
|
||
|
#[macro_use]
|
||
|
pub(crate) mod util;
|
||
|
|
||
|
use attributes::*;
|
||
|
use consts::*;
|
||
|
use structures::*;
|
||
|
use util::*;
|
||
|
|
||
|
macro_rules! match_options {
|
||
|
($v:expr, $values:ident, $options:ident, $span:expr => [$($name:ident);*]) => {
|
||
|
match $v {
|
||
|
$(
|
||
|
stringify!($name) => $options.$name = propagate_err!($crate::attributes::parse($values)),
|
||
|
)*
|
||
|
_ => {
|
||
|
return Error::new($span, format_args!("invalid attribute: {:?}", $v))
|
||
|
.to_compile_error()
|
||
|
.into();
|
||
|
},
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
#[proc_macro_attribute]
|
||
|
pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||
|
let mut fun = parse_macro_input!(input as CommandFun);
|
||
|
|
||
|
let lit_name = if !attr.is_empty() {
|
||
|
parse_macro_input!(attr as Lit).to_str()
|
||
|
} else {
|
||
|
fun.name.to_string()
|
||
|
};
|
||
|
|
||
|
let mut options = Options::new();
|
||
|
|
||
|
for attribute in &fun.attributes {
|
||
|
let span = attribute.span();
|
||
|
let values = propagate_err!(parse_values(attribute));
|
||
|
|
||
|
let name = values.name.to_string();
|
||
|
let name = &name[..];
|
||
|
|
||
|
match_options!(name, values, options, span => [
|
||
|
permission_level;
|
||
|
supports_dm;
|
||
|
can_blacklist
|
||
|
]);
|
||
|
}
|
||
|
|
||
|
let Options {
|
||
|
permission_level,
|
||
|
supports_dm,
|
||
|
can_blacklist,
|
||
|
} = options;
|
||
|
|
||
|
propagate_err!(create_declaration_validations(&mut fun, DeclarFor::Command));
|
||
|
|
||
|
let res = parse_quote!(serenity::framework::standard::CommandResult);
|
||
|
create_return_type_validation(&mut fun, res);
|
||
|
|
||
|
let visibility = fun.visibility;
|
||
|
let name = fun.name.clone();
|
||
|
let body = fun.body;
|
||
|
let ret = fun.ret;
|
||
|
|
||
|
let n = name.with_suffix(COMMAND);
|
||
|
|
||
|
let cooked = fun.cooked.clone();
|
||
|
|
||
|
let command_path = quote!(crate::framework::Command);
|
||
|
|
||
|
populate_fut_lifetimes_on_refs(&mut fun.args);
|
||
|
let args = fun.args;
|
||
|
|
||
|
(quote! {
|
||
|
#(#cooked)*
|
||
|
pub static #n: #command_path = #command_path {
|
||
|
func: #name,
|
||
|
name: #lit_name,
|
||
|
required_perms: #permission_level,
|
||
|
supports_dm: #supports_dm,
|
||
|
can_blacklist: #can_blacklist,
|
||
|
};
|
||
|
|
||
|
#visibility fn #name<'fut> (#(#args),*) -> ::serenity::futures::future::BoxFuture<'fut, #ret> {
|
||
|
use ::serenity::futures::future::FutureExt;
|
||
|
|
||
|
async move { #(#body)* }.boxed()
|
||
|
}
|
||
|
})
|
||
|
.into()
|
||
|
}
|