mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 14:35:36 +01:00
Parsing the doc
comment as a doc
comment :)
This commit is contained in:
parent
e0654f7c0d
commit
4fe40f48b1
2 changed files with 52 additions and 79 deletions
|
@ -4,7 +4,7 @@ use proc_macro2::Span;
|
|||
use syn::{
|
||||
parse::{Parse, ParseBuffer, ParseStream},
|
||||
spanned::Spanned,
|
||||
Attribute, Ident, Lit, Path, Token,
|
||||
Attribute, Ident, Lit, LitStr, Path, Token,
|
||||
};
|
||||
|
||||
pub(crate) fn fold_attrs<A, R>(
|
||||
|
@ -18,14 +18,26 @@ pub(crate) fn fold_attrs<A, R>(
|
|||
.filter(filter)
|
||||
.flat_map(|attribute| {
|
||||
// FIXME: don't allocate here
|
||||
let attrs = match attribute.parse_args_with(|input: &ParseBuffer| {
|
||||
input.parse_terminated::<_, Token![,]>(Attr::parse)
|
||||
}) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => return vec![Err(err.into())],
|
||||
};
|
||||
|
||||
attrs.into_iter().map(&parse).collect()
|
||||
if crate::command_attr::is_doc_comment(&attribute) {
|
||||
vec![parse(Attr {
|
||||
key: Ident::new("description", Span::call_site()),
|
||||
value: AttrValue::Lit(
|
||||
LitStr::new(
|
||||
&crate::command_attr::parse_doc_comment(&attribute)
|
||||
.expect("it is doc comment"),
|
||||
Span::call_site(),
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
})]
|
||||
} else {
|
||||
match attribute.parse_args_with(|input: &ParseBuffer| {
|
||||
input.parse_terminated::<_, Token![,]>(Attr::parse)
|
||||
}) {
|
||||
Ok(ok) => ok.into_iter().map(&parse).collect(),
|
||||
Err(err) => vec![Err(err.into())],
|
||||
}
|
||||
}
|
||||
})
|
||||
.try_fold(init, |acc, r| r.and_then(|r| f(acc, r)))
|
||||
}
|
||||
|
|
|
@ -6,9 +6,8 @@ use crate::{
|
|||
Result,
|
||||
};
|
||||
|
||||
use proc_macro2::{Span, TokenTree};
|
||||
use quote::quote_spanned;
|
||||
use syn::{parse::Parser, spanned::Spanned, Attribute};
|
||||
use proc_macro2::Span;
|
||||
use syn::Attribute;
|
||||
|
||||
/// All attributes that can be used for `derive(BotCommands)`
|
||||
pub(crate) struct CommandAttrs {
|
||||
|
@ -50,31 +49,9 @@ impl CommandAttrs {
|
|||
pub fn from_attributes(attributes: &[Attribute]) -> Result<Self> {
|
||||
use CommandAttrKind::*;
|
||||
|
||||
let docs = attributes.iter().filter_map(|attr| {
|
||||
parse_doc_comment(attr)
|
||||
.or_else(|| parse_command_description(attr))
|
||||
.map(|doc| (doc, attr.span()))
|
||||
});
|
||||
|
||||
let mut attributes = attributes.to_vec();
|
||||
if docs.clone().count() != 0 {
|
||||
// Remove all command description attributes, to avoid duplication
|
||||
attributes.retain(|attr| !is_command_description(attr));
|
||||
let description = docs.clone().map(|(doc, _)| doc).collect::<Vec<_>>().join("\n");
|
||||
let sp = docs
|
||||
.map(|(_, sp)| sp)
|
||||
.reduce(|acc, sp| acc.join(sp).expect("The spans are in the same file"))
|
||||
.expect("There is at least one doc comment");
|
||||
// Insert a new command description attribute, with all descriptions and doc
|
||||
// comments
|
||||
let attr = Attribute::parse_outer
|
||||
.parse2(quote_spanned! { sp => #[command(description = #description)] })?;
|
||||
attributes.push(attr.into_iter().next().unwrap());
|
||||
}
|
||||
|
||||
fold_attrs(
|
||||
attributes.into_iter(),
|
||||
is_command_attribute,
|
||||
attributes.iter().cloned(),
|
||||
|attr| is_command_attribute(attr) || is_doc_comment(attr),
|
||||
CommandAttr::parse,
|
||||
Self {
|
||||
prefix: None,
|
||||
|
@ -96,9 +73,26 @@ impl CommandAttrs {
|
|||
}
|
||||
}
|
||||
|
||||
fn join_string(
|
||||
opt: &mut Option<(String, Span)>,
|
||||
new_str: String,
|
||||
sp: Span,
|
||||
) -> Result<()> {
|
||||
match opt {
|
||||
slot @ None => {
|
||||
*slot = Some((new_str, sp));
|
||||
Ok(())
|
||||
}
|
||||
Some((old_str, _)) => {
|
||||
*old_str = format!("{old_str}\n{new_str}");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match attr.kind {
|
||||
Prefix(p) => insert(&mut this.prefix, p, attr.sp),
|
||||
Description(d) => insert(&mut this.description, d, attr.sp),
|
||||
Description(d) => join_string(&mut this.description, d, attr.sp),
|
||||
RenameRule(r) => insert(&mut this.rename_rule, r, attr.sp),
|
||||
Rename(r) => insert(&mut this.rename, r, attr.sp),
|
||||
ParseWith(p) => insert(&mut this.parser, p, attr.sp),
|
||||
|
@ -148,51 +142,18 @@ fn is_command_attribute(a: &Attribute) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_command_description(attr: &Attribute) -> bool {
|
||||
for token in attr.tokens.clone() {
|
||||
if let TokenTree::Group(group) = token {
|
||||
for token in group.stream() {
|
||||
if let TokenTree::Ident(ident) = token {
|
||||
if ident == "description" {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) fn is_doc_comment(a: &Attribute) -> bool {
|
||||
match a.path.get_ident() {
|
||||
Some(ident) => ident == "doc",
|
||||
_ => false,
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn parse_command_description(attr: &Attribute) -> Option<String> {
|
||||
if is_command_attribute(attr) {
|
||||
for token in attr.tokens.clone() {
|
||||
if let TokenTree::Group(group) = token {
|
||||
for token in group.stream() {
|
||||
if let TokenTree::Ident(ident) = token {
|
||||
if ident == "description" {
|
||||
for token in group.stream() {
|
||||
if let TokenTree::Literal(lit) = token {
|
||||
let description = lit.to_string();
|
||||
return Some(
|
||||
lit.to_string().trim()[1..description.len() - 1]
|
||||
.replace(r"\n", "\n"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn parse_doc_comment(attr: &Attribute) -> Option<String> {
|
||||
#[allow(clippy::collapsible_match)]
|
||||
if let syn::Meta::NameValue(syn::MetaNameValue { lit, .. }) = attr.parse_meta().ok()? {
|
||||
if let syn::Lit::Str(s) = lit {
|
||||
pub(crate) fn parse_doc_comment(attr: &Attribute) -> Option<String> {
|
||||
if is_doc_comment(attr) {
|
||||
if let syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) =
|
||||
attr.parse_meta().ok()?
|
||||
{
|
||||
return Some(s.value().trim().replace(r"\n", "\n"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue