Skip to content

Commit

Permalink
Interpret doc-comments as the description attribute (serenity-rs#724)
Browse files Browse the repository at this point in the history
  • Loading branch information
arqunis committed Jan 13, 2020
1 parent ef2fa87 commit d53f274
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 11 deletions.
35 changes: 30 additions & 5 deletions command_attr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ macro_rules! match_options {
/// | `#[owner_privilege]` </br> `#[owner_privilege(b)]` | If owners can bypass certain options. | `b` is a boolean. If no boolean is provided, the value is assumed to be `true`. |
/// | `#[sub_commands(commands)]` | The sub or children commands of this command. They are executed in the form: `this-command sub-command`. | `commands` is a comma separated list of identifiers referencing functions marked by the `#[command]` macro. |
///
/// Documentation comments (`///`) applied onto the function are interpreted as sugar for the
/// `#[description]` option. When more than one application of the option is performed,
/// the text is delimited by newlines. This mimics the behaviour of regular doc-comments,
/// which are sugar for the `#[doc = "..."]` attribute.
///
/// # Notes
/// The name of the command is parsed from the applied function,
/// or may be specified inside the `#[command]` attribute, a lá `#[command("foobar")]`.
Expand Down Expand Up @@ -122,12 +127,22 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
.examples
.push(propagate_err!(attributes::parse(values)));
}
"description" => {
let arg: String = propagate_err!(attributes::parse(values));

if let Some(desc) = &mut options.description.0 {
use std::fmt::Write;

let _ = write!(desc, "\n{}", arg.trim_matches(' '));
} else {
options.description = AsOption(Some(arg));
}
}
_ => {
match_options!(name, values, options, span => [
checks;
bucket;
aliases;
description;
delimiters;
usage;
min_args;
Expand Down Expand Up @@ -562,6 +577,17 @@ pub fn group(attr: TokenStream, input: TokenStream) -> TokenStream {
"prefix" => {
options.prefixes = vec![propagate_err!(attributes::parse(values))];
}
"description" => {
let arg: String = propagate_err!(attributes::parse(values));

if let Some(desc) = &mut options.description.0 {
use std::fmt::Write;

let _ = write!(desc, "\n{}", arg.trim_matches(' '));
} else {
options.description = AsOption(Some(arg));
}
}
_ => match_options!(name, values, options, span => [
prefixes;
only_in;
Expand All @@ -572,7 +598,6 @@ pub fn group(attr: TokenStream, input: TokenStream) -> TokenStream {
required_permissions;
checks;
default_command;
description;
commands;
sub_groups
]),
Expand Down Expand Up @@ -600,10 +625,10 @@ pub fn group(attr: TokenStream, input: TokenStream) -> TokenStream {
let n = group.name.with_suffix(GROUP);

let default_command = default_command.map(|ident| {
let i = ident.with_suffix(COMMAND);
let i = ident.with_suffix(COMMAND);

quote!(&#i)
});
quote!(&#i)
});

let commands = commands
.into_iter()
Expand Down
22 changes: 16 additions & 6 deletions command_attr/src/structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use syn::{
braced,
parse::{Error, Parse, ParseStream, Result},
spanned::Spanned,
Attribute, Block, FnArg, Ident, Pat, ReturnType, Stmt, Token, Type, Visibility,
Attribute, Block, FnArg, Ident, Pat, Path, PathSegment, ReturnType, Stmt, Token, Type,
Visibility,
};

#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -91,7 +92,7 @@ fn parse_argument(arg: FnArg) -> Result<Argument> {
pub struct CommandFun {
/// `#[...]`-style attributes.
pub attributes: Vec<Attribute>,
/// Populated by either `#[cfg(...)]` or `#[doc = "..."]` (the desugared form of doc-comments) type of attributes.
/// Populated by `#[cfg(...)]` type attributes.
pub cooked: Vec<Attribute>,
pub visibility: Visibility,
pub name: Ident,
Expand All @@ -104,9 +105,18 @@ impl Parse for CommandFun {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let attributes = input.call(Attribute::parse_outer)?;

let (cooked, attributes): (Vec<_>, Vec<_>) = attributes
.into_iter()
.partition(|a| a.path.is_ident("cfg") || a.path.is_ident("doc"));
let (cooked, mut attributes): (Vec<_>, Vec<_>) =
attributes.into_iter().partition(|a| a.path.is_ident("cfg"));

for attr in &mut attributes {
// Rename documentation comment attributes (`#[doc = "..."]`) to `#[description = "..."]`.
if attr.path.is_ident("doc") {
attr.path = Path::from(PathSegment::from(Ident::new(
"description",
Span::call_site(),
)));
}
}

let visibility = input.parse::<Visibility>()?;

Expand Down Expand Up @@ -149,8 +159,8 @@ impl Parse for CommandFun {
impl ToTokens for CommandFun {
fn to_tokens(&self, stream: &mut TokenStream2) {
let Self {
cooked,
attributes: _,
cooked,
visibility,
name,
args,
Expand Down

0 comments on commit d53f274

Please sign in to comment.