use crate::headeritem::HeaderItem;
use proc_macro2::TokenStream as TokenStream2;
use quote::ToTokens;
use syn::parse::{Error, Parse, ParseStream, Result};
#[derive(Debug, PartialEq)]
pub(crate) struct DocItem {
header_item: HeaderItem,
syn_item: syn::Item,
}
impl Parse for DocItem {
fn parse(input: ParseStream) -> Result<Self> {
let mut item = input.parse::<syn::Item>()?;
fn use_ident(tree: &syn::UseTree) -> Result<String> {
match tree {
syn::UseTree::Name(name) => Ok(name.ident.to_string()),
syn::UseTree::Path(path) => use_ident(path.tree.as_ref()),
syn::UseTree::Rename(rename) => Ok(rename.rename.to_string()),
_ => Err(Error::new_spanned(
tree,
"only single-item 'use' statements are supported",
)),
}
}
let (name, attrs) = match &mut item {
syn::Item::Fn(item) => (item.sig.ident.to_string(), &mut item.attrs),
syn::Item::Const(item) => (item.ident.to_string(), &mut item.attrs),
syn::Item::Static(item) => (item.ident.to_string(), &mut item.attrs),
syn::Item::Struct(item) => (item.ident.to_string(), &mut item.attrs),
syn::Item::Enum(item) => (item.ident.to_string(), &mut item.attrs),
syn::Item::Union(item) => (item.ident.to_string(), &mut item.attrs),
syn::Item::Type(item) => (item.ident.to_string(), &mut item.attrs),
syn::Item::Use(item) => (use_ident(&item.tree)?, &mut item.attrs),
_ => {
return Err(Error::new_spanned(
item,
"cannot determine header content from this item",
));
}
};
Ok(DocItem {
header_item: HeaderItem::from_attrs(name, attrs)?,
syn_item: item,
})
}
}
impl DocItem {
pub(crate) fn to_tokens(&self, tokens: &mut TokenStream2) {
self.syn_item.to_tokens(tokens);
self.header_item.to_tokens(tokens);
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parsing_fn() {
let di: DocItem = syn::parse_quote! {
pub unsafe extern "C" fn add(x: u32, y: u32) -> u32 {}
};
assert_eq!(
di.header_item,
HeaderItem {
order: 100,
name: "add".into(),
content: "// A docstring".into(),
}
);
}
#[test]
fn test_parsing_const() {
let di: DocItem = syn::parse_quote! {
pub const X: usize = 13;
};
assert_eq!(
di.header_item,
HeaderItem {
order: 100,
name: "X".into(),
content: "// A docstring".into(),
}
);
}
#[test]
fn test_parsing_static() {
let di: DocItem = syn::parse_quote! {
pub static X: usize = 13;
};
assert_eq!(
di.header_item,
HeaderItem {
order: 100,
name: "X".into(),
content: "// A docstring".into(),
}
);
}
#[test]
fn test_parsing_struct() {
let di: DocItem = syn::parse_quote! {
pub struct Foo {}
};
assert_eq!(
di.header_item,
HeaderItem {
order: 100,
name: "Foo".into(),
content: "// A docstring".into(),
}
);
}
#[test]
fn test_parsing_enum() {
let di: DocItem = syn::parse_quote! {
pub enum Foo {}
};
assert_eq!(
di.header_item,
HeaderItem {
order: 100,
name: "Foo".into(),
content: "// A docstring".into(),
}
);
}
#[test]
fn test_parsing_union() {
let di: DocItem = syn::parse_quote! {
pub union Foo {}
};
assert_eq!(
di.header_item,
HeaderItem {
order: 100,
name: "Foo".into(),
content: "// A docstring".into(),
}
);
}
#[test]
fn test_parsing_type() {
let di: DocItem = syn::parse_quote! {
pub type Foo = Bar;
};
assert_eq!(
di.header_item,
HeaderItem {
order: 100,
name: "Foo".into(),
content: "// A docstring".into(),
}
);
}
#[test]
fn test_parsing_use_name() {
let di: DocItem = syn::parse_quote! {
use foo;
};
assert_eq!(
di.header_item,
HeaderItem {
order: 100,
name: "foo".into(),
content: "// A docstring".into(),
}
);
}
#[test]
fn test_parsing_use_path() {
let di: DocItem = syn::parse_quote! {
pub use xxx::foo;
};
assert_eq!(
di.header_item,
HeaderItem {
order: 100,
name: "foo".into(),
content: "// A docstring".into(),
}
);
}
#[test]
fn test_parsing_use_rename() {
let di: DocItem = syn::parse_quote! {
use xxx::foo as bar;
};
assert_eq!(
di.header_item,
HeaderItem {
order: 100,
name: "bar".into(),
content: "// A docstring".into(),
}
);
}
#[test]
fn test_parsing_type_with_attrs() {
let di: DocItem = syn::parse_quote! {
#[ffizz(name="bar", order=10)]
fn foo() {}
};
assert_eq!(
di.header_item,
HeaderItem {
order: 10,
name: "bar".into(),
content: "// A docstring".into(),
}
);
}
}