use crate::ident::GetIdent;
use syn::{
spanned::Spanned, Attribute, Ident, ImplItem, ImplItemMethod, Item, ItemFn, ItemMod, Result,
TraitItem, TraitItemMethod,
};
pub trait ItemLike: Spanned {
fn attrs(&self) -> Result<&[Attribute]>;
fn attrs_mut(&mut self) -> Result<&mut Vec<Attribute>>;
fn function_or_method(&self) -> Result<&dyn FunctionLike>;
fn constant(&self) -> Result<&dyn ConstLike>;
fn is_type(&self) -> bool;
fn is_macro(&self) -> bool;
}
impl ItemLike for Item {
fn attrs(&self) -> Result<&[Attribute]> {
use syn::Item::*;
use syn::*;
let attrs = match self {
Const(ItemConst { ref attrs, .. }) => attrs,
Enum(ItemEnum { ref attrs, .. }) => attrs,
ExternCrate(ItemExternCrate { ref attrs, .. }) => attrs,
Fn(ItemFn { ref attrs, .. }) => attrs,
ForeignMod(ItemForeignMod { ref attrs, .. }) => attrs,
Impl(ItemImpl { ref attrs, .. }) => attrs,
Macro(ItemMacro { ref attrs, .. }) => attrs,
Macro2(ItemMacro2 { ref attrs, .. }) => attrs,
Mod(ItemMod { ref attrs, .. }) => attrs,
Static(ItemStatic { ref attrs, .. }) => attrs,
Struct(ItemStruct { ref attrs, .. }) => attrs,
Trait(ItemTrait { ref attrs, .. }) => attrs,
TraitAlias(ItemTraitAlias { ref attrs, .. }) => attrs,
Type(ItemType { ref attrs, .. }) => attrs,
Union(ItemUnion { ref attrs, .. }) => attrs,
Use(ItemUse { ref attrs, .. }) => attrs,
other => {
return Err(Error::new_spanned(
other,
"this kind of item doesn't have attrs",
))
}
};
Ok(attrs)
}
fn attrs_mut(&mut self) -> Result<&mut Vec<Attribute>> {
use syn::Item::*;
use syn::*;
let attrs = match self {
Const(ItemConst { ref mut attrs, .. }) => attrs,
Enum(ItemEnum { ref mut attrs, .. }) => attrs,
ExternCrate(ItemExternCrate { ref mut attrs, .. }) => attrs,
Fn(ItemFn { ref mut attrs, .. }) => attrs,
ForeignMod(ItemForeignMod { ref mut attrs, .. }) => attrs,
Impl(ItemImpl { ref mut attrs, .. }) => attrs,
Macro(ItemMacro { ref mut attrs, .. }) => attrs,
Macro2(ItemMacro2 { ref mut attrs, .. }) => attrs,
Mod(ItemMod { ref mut attrs, .. }) => attrs,
Static(ItemStatic { ref mut attrs, .. }) => attrs,
Struct(ItemStruct { ref mut attrs, .. }) => attrs,
Trait(ItemTrait { ref mut attrs, .. }) => attrs,
TraitAlias(ItemTraitAlias { ref mut attrs, .. }) => attrs,
Type(ItemType { ref mut attrs, .. }) => attrs,
Union(ItemUnion { ref mut attrs, .. }) => attrs,
Use(ItemUse { ref mut attrs, .. }) => attrs,
other => {
return Err(Error::new_spanned(
other,
"this kind of item doesn't have attrs",
))
}
};
Ok(attrs)
}
fn function_or_method(&self) -> Result<&dyn FunctionLike> {
match self {
Item::Fn(f @ syn::ItemFn { .. }) => Ok(f),
other => Err(syn::Error::new_spanned(
other,
"this item is not a function or method",
)),
}
}
fn constant(&self) -> Result<&dyn ConstLike> {
match self {
Item::Const(c @ syn::ItemConst { .. }) => Ok(c),
other => Err(syn::Error::new_spanned(other, "this item is not a const")),
}
}
fn is_type(&self) -> bool {
matches!(self, Item::Type(_))
}
fn is_macro(&self) -> bool {
matches!(self, Item::Macro(_))
}
}
impl ItemLike for ImplItem {
fn attrs(&self) -> Result<&[Attribute]> {
use syn::ImplItem::*;
use syn::*;
let attrs = match self {
Const(ImplItemConst { ref attrs, .. }) => attrs,
Method(ImplItemMethod { ref attrs, .. }) => attrs,
Type(ImplItemType { ref attrs, .. }) => attrs,
Macro(ImplItemMacro { ref attrs, .. }) => attrs,
other => {
return Err(Error::new_spanned(
other,
"this kind of item doesn't have attrs",
))
}
};
Ok(attrs)
}
fn attrs_mut(&mut self) -> Result<&mut Vec<Attribute>> {
use syn::ImplItem::*;
use syn::*;
let attrs = match self {
Const(ImplItemConst { ref mut attrs, .. }) => attrs,
Method(ImplItemMethod { ref mut attrs, .. }) => attrs,
Type(ImplItemType { ref mut attrs, .. }) => attrs,
Macro(ImplItemMacro { ref mut attrs, .. }) => attrs,
other => {
return Err(Error::new_spanned(
other,
"this kind of item doesn't have attrs",
))
}
};
Ok(attrs)
}
fn function_or_method(&self) -> Result<&dyn FunctionLike> {
match self {
ImplItem::Method(f @ syn::ImplItemMethod { .. }) => Ok(f),
other => Err(syn::Error::new_spanned(
other,
"this item is not a function or method",
)),
}
}
fn constant(&self) -> Result<&dyn ConstLike> {
match self {
ImplItem::Const(c @ syn::ImplItemConst { .. }) => Ok(c),
other => Err(syn::Error::new_spanned(other, "this item is not a const")),
}
}
fn is_type(&self) -> bool {
matches!(self, ImplItem::Type(_))
}
fn is_macro(&self) -> bool {
matches!(self, ImplItem::Macro(_))
}
}
impl ItemLike for TraitItem {
fn attrs(&self) -> Result<&[Attribute]> {
use syn::TraitItem::*;
use syn::*;
let attrs = match self {
Const(TraitItemConst { ref attrs, .. }) => attrs,
Method(TraitItemMethod { ref attrs, .. }) => attrs,
Type(TraitItemType { ref attrs, .. }) => attrs,
Macro(TraitItemMacro { ref attrs, .. }) => attrs,
other => {
return Err(Error::new_spanned(
other,
"this kind of item doesn't have attrs",
))
}
};
Ok(attrs)
}
fn attrs_mut(&mut self) -> Result<&mut Vec<Attribute>> {
use syn::TraitItem::*;
use syn::*;
let attrs = match self {
Const(TraitItemConst { ref mut attrs, .. }) => attrs,
Method(TraitItemMethod { ref mut attrs, .. }) => attrs,
Type(TraitItemType { ref mut attrs, .. }) => attrs,
Macro(TraitItemMacro { ref mut attrs, .. }) => attrs,
other => {
return Err(Error::new_spanned(
other,
"this kind of impl item doesn't have attrs",
))
}
};
Ok(attrs)
}
fn function_or_method(&self) -> Result<&dyn FunctionLike> {
match self {
TraitItem::Method(f @ syn::TraitItemMethod { .. }) => Ok(f),
other => Err(syn::Error::new_spanned(
other,
"this item is not a function or method",
)),
}
}
fn constant(&self) -> Result<&dyn ConstLike> {
match self {
TraitItem::Const(c @ syn::TraitItemConst { .. }) => Ok(c),
other => Err(syn::Error::new_spanned(other, "this item is not a const")),
}
}
fn is_type(&self) -> bool {
matches!(self, TraitItem::Type(_))
}
fn is_macro(&self) -> bool {
matches!(self, TraitItem::Macro(_))
}
}
pub trait ItemAttrExt: ItemLike {
fn try_split_attr_mut<F, R>(&mut self, f: F) -> Result<R>
where
F: FnOnce(&mut Vec<Attribute>, &mut Self) -> Result<R>,
{
let mut attrs = std::mem::take(self.attrs_mut()?);
let result = f(&mut attrs, self);
let _temp = std::mem::replace(self.attrs_mut().unwrap(), attrs);
assert!(
_temp.is_empty(),
"attrs changed during replacement. this behavior must be a bug."
);
result
}
}
impl ItemAttrExt for Item {}
impl ItemAttrExt for ImplItem {}
impl ItemAttrExt for TraitItem {}
pub trait ItemModExt {
fn items(&self) -> Option<&[Item]>;
fn items_mut(&mut self) -> Option<&mut Vec<Item>>;
}
impl ItemModExt for ItemMod {
fn items(&self) -> Option<&[Item]> {
if let Some((_, content)) = self.content.as_ref() {
Some(content)
} else {
None
}
}
fn items_mut(&mut self) -> Option<&mut Vec<Item>> {
if let Some((_, content)) = self.content.as_mut() {
Some(content)
} else {
None
}
}
}
impl GetIdent for Item {
fn get_ident(&self) -> Option<&Ident> {
use syn::Item::*;
use syn::UseTree::*;
use syn::*;
#[allow(clippy::collapsible_match)]
let attrs = match self {
Const(ItemConst { ref ident, .. }) => ident,
Enum(ItemEnum { ref ident, .. }) => ident,
ExternCrate(ItemExternCrate { ref ident, .. }) => ident,
Fn(ItemFn { sig, .. }) => &sig.ident,
Impl(ItemImpl { .. }) => unimplemented!(),
Macro(ItemMacro { ref ident, .. }) => return ident.as_ref(),
Macro2(ItemMacro2 { ref ident, .. }) => ident,
Mod(ItemMod { ref ident, .. }) => ident,
Static(ItemStatic { ref ident, .. }) => ident,
Struct(ItemStruct { ref ident, .. }) => ident,
Trait(ItemTrait { ref ident, .. }) => ident,
TraitAlias(ItemTraitAlias { ref ident, .. }) => ident,
Type(ItemType { ref ident, .. }) => ident,
Union(ItemUnion { ref ident, .. }) => ident,
Use(ItemUse { ref tree, .. }) => match tree {
Name(UseName { ident }) => ident,
_ => return None,
},
_ => return None,
};
Some(attrs)
}
}
impl GetIdent for ImplItem {
fn get_ident(&self) -> Option<&Ident> {
use syn::ImplItem::*;
use syn::*;
let ident = match self {
Const(ImplItemConst { ref ident, .. }) => ident,
Method(ImplItemMethod { sig, .. }) => &sig.ident,
Type(ImplItemType { ref ident, .. }) => ident,
Macro(ImplItemMacro {
mac: syn::Macro { path, .. },
..
}) => return path.get_ident(),
_ => return None,
};
Some(ident)
}
}
impl GetIdent for TraitItem {
fn get_ident(&self) -> Option<&Ident> {
use syn::TraitItem::*;
use syn::*;
let ident = match self {
Const(TraitItemConst { ref ident, .. }) => ident,
Method(TraitItemMethod { sig, .. }) => &sig.ident,
Type(TraitItemType { ref ident, .. }) => ident,
Macro(TraitItemMacro {
mac: syn::Macro { path, .. },
..
}) => return path.get_ident(),
_ => return None,
};
Some(ident)
}
}
pub trait FunctionLike: Spanned {
fn attrs(&self) -> &[Attribute];
fn attrs_mut(&mut self) -> &mut Vec<Attribute>;
fn vis(&self) -> &syn::Visibility;
fn sig(&self) -> &syn::Signature;
fn block(&self) -> Option<&syn::Block>;
}
impl FunctionLike for ItemFn {
fn attrs(&self) -> &[Attribute] {
&self.attrs
}
fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
&mut self.attrs
}
fn vis(&self) -> &syn::Visibility {
&self.vis
}
fn sig(&self) -> &syn::Signature {
&self.sig
}
fn block(&self) -> Option<&syn::Block> {
Some(&self.block)
}
}
impl FunctionLike for ImplItemMethod {
fn attrs(&self) -> &[Attribute] {
&self.attrs
}
fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
&mut self.attrs
}
fn vis(&self) -> &syn::Visibility {
&self.vis
}
fn sig(&self) -> &syn::Signature {
&self.sig
}
fn block(&self) -> Option<&syn::Block> {
Some(&self.block)
}
}
impl FunctionLike for TraitItemMethod {
fn attrs(&self) -> &[Attribute] {
&self.attrs
}
fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
&mut self.attrs
}
fn vis(&self) -> &syn::Visibility {
&syn::Visibility::Inherited
}
fn sig(&self) -> &syn::Signature {
&self.sig
}
fn block(&self) -> Option<&syn::Block> {
self.default.as_ref()
}
}
pub trait ConstLike: Spanned {
fn attrs(&self) -> &[Attribute];
fn attrs_mut(&mut self) -> &mut Vec<Attribute>;
fn vis(&self) -> &syn::Visibility;
fn const_token(&self) -> &syn::token::Const;
fn ident(&self) -> &syn::Ident;
fn colon_token(&self) -> &syn::token::Colon;
fn ty(&self) -> &syn::Type;
}
impl ConstLike for syn::ItemConst {
fn attrs(&self) -> &[Attribute] {
&self.attrs
}
fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
&mut self.attrs
}
fn vis(&self) -> &syn::Visibility {
&self.vis
}
fn const_token(&self) -> &syn::token::Const {
&self.const_token
}
fn ident(&self) -> &syn::Ident {
&self.ident
}
fn colon_token(&self) -> &syn::token::Colon {
&self.colon_token
}
fn ty(&self) -> &syn::Type {
&self.ty
}
}
impl ConstLike for syn::ImplItemConst {
fn attrs(&self) -> &[Attribute] {
&self.attrs
}
fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
&mut self.attrs
}
fn vis(&self) -> &syn::Visibility {
&self.vis
}
fn const_token(&self) -> &syn::token::Const {
&self.const_token
}
fn ident(&self) -> &syn::Ident {
&self.ident
}
fn colon_token(&self) -> &syn::token::Colon {
&self.colon_token
}
fn ty(&self) -> &syn::Type {
&self.ty
}
}
impl ConstLike for syn::TraitItemConst {
fn attrs(&self) -> &[Attribute] {
&self.attrs
}
fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
&mut self.attrs
}
fn vis(&self) -> &syn::Visibility {
&syn::Visibility::Inherited
}
fn const_token(&self) -> &syn::token::Const {
&self.const_token
}
fn ident(&self) -> &syn::Ident {
&self.ident
}
fn colon_token(&self) -> &syn::token::Colon {
&self.colon_token
}
fn ty(&self) -> &syn::Type {
&self.ty
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::assert_quote_eq;
use quote::quote;
use syn::parse_quote;
#[test]
fn test_attrs() {
let mut item: Item = parse_quote!(
#[test]
type A = u32;
);
let expected: Attribute = parse_quote!(#[test]);
{
let attr = &item.attrs().unwrap()[0];
assert_quote_eq!(attr, expected);
}
{
let attr = &item.attrs_mut().unwrap()[0];
assert_quote_eq!(attr, expected);
}
}
#[test]
fn test_items() {
let module: ItemMod = parse_quote!(
mod m {
static x: usize = 0;
fn f() {}
}
);
let content = module.items().unwrap();
assert!(matches!(content[0], Item::Static(_)));
assert!(matches!(content[1], Item::Fn(_)));
}
#[test]
fn test_items_decl() {
let module: ItemMod = parse_quote!(
mod m;
);
assert!(module.items().is_none());
}
#[test]
fn test_function_like() {
let function: ItemFn = parse_quote!(
fn f(a: u8) -> Result<()> {}
);
let method: ImplItemMethod = parse_quote!(
fn f(a: u8) -> Result<()> {}
);
assert_eq!(quote!(#function).to_string(), quote!(#method).to_string());
}
}