doc_macro/
generated_doc.rsuse proc_macro::TokenStream;
use quote::ToTokens;
use syn::{
AngleBracketedGenericArguments, Attribute, Expr, GenericArgument, ItemFn, Path, PathArguments,
ReturnType, Type, TypePath,
};
pub fn generated_doc_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut stream = TokenStream::new();
stream.extend(
format!("#[cfg_attr(all(doc, not(doctest)), generated_doc_inner({attr}))]")
.parse::<TokenStream>()
.unwrap(),
);
stream.extend(item);
stream
}
pub fn generated_doc_inner_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut item = syn::parse_macro_input!(item as ItemFn);
let storage;
let (arg, generate_docs_for) = if attr.is_empty() {
process_return_type(&item)
} else {
storage = syn::parse_macro_input!(attr as Type);
(&storage, GenerateDocsFor::JsonAndTs)
};
let windows_safe_name = arg
.to_token_stream()
.to_string()
.replace('<', "(")
.replace('>', ")");
let expr: Expr = syn::parse_str(&windows_safe_name).unwrap_or_else(|err| {
panic!("Failed to parse windows safe name {windows_safe_name}: {err}")
});
let attr: Attribute = match generate_docs_for {
GenerateDocsFor::Ts => syn::parse_quote!(#[doc = generated_docs!(#expr, ts)]),
GenerateDocsFor::JsonAndTs => {
syn::parse_quote!(#[doc = generated_docs!(#expr)])
}
};
item.attrs.push(attr);
item.into_token_stream().into()
}
#[derive(Clone, Copy)]
enum GenerateDocsFor {
Ts,
JsonAndTs,
}
fn process_return_type(item: &ItemFn) -> (&Type, GenerateDocsFor) {
if let ReturnType::Type(_, ty) = &item.sig.output {
if let Type::Path(TypePath {
path: Path { segments, .. },
..
}) = ty.as_ref()
{
let segment = segments
.last()
.expect("return type path shouldn't be empty");
let mut inner = (ty.as_ref(), segments);
if segment.ident != "Json" {
if let PathArguments::AngleBracketed(AngleBracketedGenericArguments {
args, ..
}) = &segment.arguments
{
let arg = args
.first()
.expect("return type generic list shouldn't be empty");
if let GenericArgument::Type(
ty @ Type::Path(TypePath {
path: Path { segments, .. },
..
}),
) = arg
{
inner = (ty, segments);
}
}
};
if let Some(segments) = inner.1.last() {
if segments.ident == "Bytes" {
return (inner.0, GenerateDocsFor::Ts);
}
if let PathArguments::AngleBracketed(AngleBracketedGenericArguments {
args, ..
}) = &segments.arguments
{
if let Some(GenericArgument::Type(t)) = args.first() {
return (t, GenerateDocsFor::JsonAndTs);
}
}
}
}
}
panic!(
"return type was expected to be `Json<_>` or `SomeGenericType<Json<_>>`, but it was `{:#?}`",
item.sig.output
)
}