1use crate::ident::GetIdent;
2use syn::{
3 spanned::Spanned, Attribute, Ident, ImplItem, ImplItemFn, Item, ItemFn, ItemMod, Result,
4 TraitItem, TraitItemFn,
5};
6
7pub trait ItemLike: Spanned {
9 fn attrs(&self) -> Result<&[Attribute]>;
11 fn attrs_mut(&mut self) -> Result<&mut Vec<Attribute>>;
13 fn function_or_method(&self) -> Result<&dyn FunctionLike>;
15 fn constant(&self) -> Result<&dyn ConstLike>;
17
18 fn is_type(&self) -> bool;
20 fn is_macro(&self) -> bool;
22}
23
24impl ItemLike for Item {
25 fn attrs(&self) -> Result<&[Attribute]> {
26 use syn::Item::*;
27 use syn::*;
28 let attrs = match self {
29 Const(ItemConst { ref attrs, .. }) => attrs,
30 Enum(ItemEnum { ref attrs, .. }) => attrs,
31 ExternCrate(ItemExternCrate { ref attrs, .. }) => attrs,
32 Fn(ItemFn { ref attrs, .. }) => attrs,
33 ForeignMod(ItemForeignMod { ref attrs, .. }) => attrs,
34 Impl(ItemImpl { ref attrs, .. }) => attrs,
35 Macro(ItemMacro { ref attrs, .. }) => attrs,
36 Mod(ItemMod { ref attrs, .. }) => attrs,
37 Static(ItemStatic { ref attrs, .. }) => attrs,
38 Struct(ItemStruct { ref attrs, .. }) => attrs,
39 Trait(ItemTrait { ref attrs, .. }) => attrs,
40 TraitAlias(ItemTraitAlias { ref attrs, .. }) => attrs,
41 Type(ItemType { ref attrs, .. }) => attrs,
42 Union(ItemUnion { ref attrs, .. }) => attrs,
43 Use(ItemUse { ref attrs, .. }) => attrs,
44 other => {
45 return Err(Error::new_spanned(
46 other,
47 "this kind of item doesn't have attrs",
48 ))
49 }
50 };
51 Ok(attrs)
52 }
53
54 fn attrs_mut(&mut self) -> Result<&mut Vec<Attribute>> {
55 use syn::Item::*;
56 use syn::*;
57 let attrs = match self {
58 Const(ItemConst { ref mut attrs, .. }) => attrs,
59 Enum(ItemEnum { ref mut attrs, .. }) => attrs,
60 ExternCrate(ItemExternCrate { ref mut attrs, .. }) => attrs,
61 Fn(ItemFn { ref mut attrs, .. }) => attrs,
62 ForeignMod(ItemForeignMod { ref mut attrs, .. }) => attrs,
63 Impl(ItemImpl { ref mut attrs, .. }) => attrs,
64 Macro(ItemMacro { ref mut attrs, .. }) => attrs,
65 Mod(ItemMod { ref mut attrs, .. }) => attrs,
66 Static(ItemStatic { ref mut attrs, .. }) => attrs,
67 Struct(ItemStruct { ref mut attrs, .. }) => attrs,
68 Trait(ItemTrait { ref mut attrs, .. }) => attrs,
69 TraitAlias(ItemTraitAlias { ref mut attrs, .. }) => attrs,
70 Type(ItemType { ref mut attrs, .. }) => attrs,
71 Union(ItemUnion { ref mut attrs, .. }) => attrs,
72 Use(ItemUse { ref mut attrs, .. }) => attrs,
73 other => {
74 return Err(Error::new_spanned(
75 other,
76 "this kind of item doesn't have attrs",
77 ))
78 }
79 };
80 Ok(attrs)
81 }
82
83 fn function_or_method(&self) -> Result<&dyn FunctionLike> {
84 match self {
85 Item::Fn(f @ syn::ItemFn { .. }) => Ok(f),
86 other => Err(syn::Error::new_spanned(
87 other,
88 "this item is not a function or method",
89 )),
90 }
91 }
92
93 fn constant(&self) -> Result<&dyn ConstLike> {
94 match self {
95 Item::Const(c @ syn::ItemConst { .. }) => Ok(c),
96 other => Err(syn::Error::new_spanned(other, "this item is not a const")),
97 }
98 }
99
100 fn is_type(&self) -> bool {
101 matches!(self, Item::Type(_))
102 }
103 fn is_macro(&self) -> bool {
104 matches!(self, Item::Macro(_))
105 }
106}
107
108impl ItemLike for ImplItem {
109 fn attrs(&self) -> Result<&[Attribute]> {
110 use syn::ImplItem::*;
111 use syn::*;
112 let attrs = match self {
113 Const(ImplItemConst { ref attrs, .. }) => attrs,
114 Fn(ImplItemFn { ref attrs, .. }) => attrs,
115 Type(ImplItemType { ref attrs, .. }) => attrs,
116 Macro(ImplItemMacro { ref attrs, .. }) => attrs,
117 other => {
118 return Err(Error::new_spanned(
119 other,
120 "this kind of item doesn't have attrs",
121 ))
122 }
123 };
124 Ok(attrs)
125 }
126
127 fn attrs_mut(&mut self) -> Result<&mut Vec<Attribute>> {
128 use syn::ImplItem::*;
129 use syn::*;
130 let attrs = match self {
131 Const(ImplItemConst { ref mut attrs, .. }) => attrs,
132 Fn(ImplItemFn { ref mut attrs, .. }) => attrs,
133 Type(ImplItemType { ref mut attrs, .. }) => attrs,
134 Macro(ImplItemMacro { ref mut attrs, .. }) => attrs,
135 other => {
136 return Err(Error::new_spanned(
137 other,
138 "this kind of item doesn't have attrs",
139 ))
140 }
141 };
142 Ok(attrs)
143 }
144
145 fn function_or_method(&self) -> Result<&dyn FunctionLike> {
146 match self {
147 ImplItem::Fn(f @ syn::ImplItemFn { .. }) => Ok(f),
148 other => Err(syn::Error::new_spanned(
149 other,
150 "this item is not a function or method",
151 )),
152 }
153 }
154
155 fn constant(&self) -> Result<&dyn ConstLike> {
156 match self {
157 ImplItem::Const(c @ syn::ImplItemConst { .. }) => Ok(c),
158 other => Err(syn::Error::new_spanned(other, "this item is not a const")),
159 }
160 }
161
162 fn is_type(&self) -> bool {
163 matches!(self, ImplItem::Type(_))
164 }
165 fn is_macro(&self) -> bool {
166 matches!(self, ImplItem::Macro(_))
167 }
168}
169
170impl ItemLike for TraitItem {
171 fn attrs(&self) -> Result<&[Attribute]> {
172 use syn::TraitItem::*;
173 use syn::*;
174 let attrs = match self {
175 Const(TraitItemConst { ref attrs, .. }) => attrs,
176 Fn(TraitItemFn { ref attrs, .. }) => attrs,
177 Type(TraitItemType { ref attrs, .. }) => attrs,
178 Macro(TraitItemMacro { ref attrs, .. }) => attrs,
179 other => {
180 return Err(Error::new_spanned(
181 other,
182 "this kind of item doesn't have attrs",
183 ))
184 }
185 };
186 Ok(attrs)
187 }
188
189 fn attrs_mut(&mut self) -> Result<&mut Vec<Attribute>> {
190 use syn::TraitItem::*;
191 use syn::*;
192 let attrs = match self {
193 Const(TraitItemConst { ref mut attrs, .. }) => attrs,
194 Fn(TraitItemFn { ref mut attrs, .. }) => attrs,
195 Type(TraitItemType { ref mut attrs, .. }) => attrs,
196 Macro(TraitItemMacro { ref mut attrs, .. }) => attrs,
197 other => {
198 return Err(Error::new_spanned(
199 other,
200 "this kind of impl item doesn't have attrs",
201 ))
202 }
203 };
204 Ok(attrs)
205 }
206
207 fn function_or_method(&self) -> Result<&dyn FunctionLike> {
208 match self {
209 TraitItem::Fn(f @ syn::TraitItemFn { .. }) => Ok(f),
210 other => Err(syn::Error::new_spanned(
211 other,
212 "this item is not a function or method",
213 )),
214 }
215 }
216
217 fn constant(&self) -> Result<&dyn ConstLike> {
218 match self {
219 TraitItem::Const(c @ syn::TraitItemConst { .. }) => Ok(c),
220 other => Err(syn::Error::new_spanned(other, "this item is not a const")),
221 }
222 }
223
224 fn is_type(&self) -> bool {
225 matches!(self, TraitItem::Type(_))
226 }
227 fn is_macro(&self) -> bool {
228 matches!(self, TraitItem::Macro(_))
229 }
230}
231
232pub trait ItemAttrExt: ItemLike {
234 fn try_split_attr_mut<F, R>(&mut self, f: F) -> Result<R>
247 where
248 F: FnOnce(&mut Vec<Attribute>, &mut Self) -> Result<R>,
249 {
250 let mut attrs = std::mem::take(self.attrs_mut()?);
251 let result = f(&mut attrs, self);
252 let _temp = std::mem::replace(self.attrs_mut().unwrap(), attrs);
253 assert!(
254 _temp.is_empty(),
255 "attrs changed during replacement. this behavior must be a bug."
256 );
257 result
258 }
259}
260
261impl ItemAttrExt for Item {}
262impl ItemAttrExt for ImplItem {}
263impl ItemAttrExt for TraitItem {}
264
265pub trait ItemModExt {
267 fn items(&self) -> Option<&[Item]>;
269 fn items_mut(&mut self) -> Option<&mut Vec<Item>>;
271}
272
273impl ItemModExt for ItemMod {
274 fn items(&self) -> Option<&[Item]> {
275 if let Some((_, content)) = self.content.as_ref() {
276 Some(content)
277 } else {
278 None
279 }
280 }
281 fn items_mut(&mut self) -> Option<&mut Vec<Item>> {
282 if let Some((_, content)) = self.content.as_mut() {
283 Some(content)
284 } else {
285 None
286 }
287 }
288}
289
290impl GetIdent for Item {
291 fn get_ident(&self) -> Option<&Ident> {
292 use syn::Item::*;
293 use syn::UseTree::*;
294 use syn::*;
295 #[allow(clippy::collapsible_match)]
296 let attrs = match self {
297 Const(ItemConst { ref ident, .. }) => ident,
298 Enum(ItemEnum { ref ident, .. }) => ident,
299 ExternCrate(ItemExternCrate { ref ident, .. }) => ident,
300 Fn(ItemFn { sig, .. }) => &sig.ident,
301 Impl(ItemImpl { .. }) => unimplemented!(),
302 Macro(ItemMacro { ref ident, .. }) => return ident.as_ref(),
303 Mod(ItemMod { ref ident, .. }) => ident,
304 Static(ItemStatic { ref ident, .. }) => ident,
305 Struct(ItemStruct { ref ident, .. }) => ident,
306 Trait(ItemTrait { ref ident, .. }) => ident,
307 TraitAlias(ItemTraitAlias { ref ident, .. }) => ident,
308 Type(ItemType { ref ident, .. }) => ident,
309 Union(ItemUnion { ref ident, .. }) => ident,
310 Use(ItemUse { ref tree, .. }) => match tree {
311 Name(UseName { ident }) => ident,
312 _ => return None,
313 },
314 _ => return None,
315 };
316 Some(attrs)
317 }
318}
319
320impl GetIdent for ImplItem {
321 fn get_ident(&self) -> Option<&Ident> {
322 use syn::ImplItem::*;
323 use syn::*;
324 let ident = match self {
325 Const(ImplItemConst { ref ident, .. }) => ident,
326 Fn(ImplItemFn { sig, .. }) => &sig.ident,
327 Type(ImplItemType { ref ident, .. }) => ident,
328 Macro(ImplItemMacro {
329 mac: syn::Macro { path, .. },
330 ..
331 }) => return path.get_ident(),
332 _ => return None,
333 };
334 Some(ident)
335 }
336}
337
338impl GetIdent for TraitItem {
339 fn get_ident(&self) -> Option<&Ident> {
340 use syn::TraitItem::*;
341 use syn::*;
342 let ident = match self {
343 Const(TraitItemConst { ref ident, .. }) => ident,
344 Fn(TraitItemFn { sig, .. }) => &sig.ident,
345 Type(TraitItemType { ref ident, .. }) => ident,
346 Macro(TraitItemMacro {
347 mac: syn::Macro { path, .. },
348 ..
349 }) => return path.get_ident(),
350 _ => return None,
351 };
352 Some(ident)
353 }
354}
355
356pub trait FunctionLike: Spanned {
358 fn attrs(&self) -> &[Attribute];
360 fn attrs_mut(&mut self) -> &mut Vec<Attribute>;
362 fn vis(&self) -> &syn::Visibility;
364
365 fn sig(&self) -> &syn::Signature;
366 fn block(&self) -> Option<&syn::Block>;
367}
368
369impl FunctionLike for ItemFn {
370 fn attrs(&self) -> &[Attribute] {
371 &self.attrs
372 }
373 fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
374 &mut self.attrs
375 }
376 fn vis(&self) -> &syn::Visibility {
377 &self.vis
378 }
379 fn sig(&self) -> &syn::Signature {
380 &self.sig
381 }
382 fn block(&self) -> Option<&syn::Block> {
383 Some(&self.block)
384 }
385}
386
387impl FunctionLike for ImplItemFn {
388 fn attrs(&self) -> &[Attribute] {
389 &self.attrs
390 }
391 fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
392 &mut self.attrs
393 }
394 fn vis(&self) -> &syn::Visibility {
395 &self.vis
396 }
397 fn sig(&self) -> &syn::Signature {
398 &self.sig
399 }
400 fn block(&self) -> Option<&syn::Block> {
401 Some(&self.block)
402 }
403}
404
405impl FunctionLike for TraitItemFn {
406 fn attrs(&self) -> &[Attribute] {
407 &self.attrs
408 }
409 fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
410 &mut self.attrs
411 }
412 fn vis(&self) -> &syn::Visibility {
413 &syn::Visibility::Inherited
414 }
415 fn sig(&self) -> &syn::Signature {
416 &self.sig
417 }
418 fn block(&self) -> Option<&syn::Block> {
419 self.default.as_ref()
420 }
421}
422
423pub trait ConstLike: Spanned {
425 fn attrs(&self) -> &[Attribute];
427 fn attrs_mut(&mut self) -> &mut Vec<Attribute>;
429 fn vis(&self) -> &syn::Visibility;
431 fn const_token(&self) -> &syn::token::Const;
433 fn ident(&self) -> &syn::Ident;
435 fn colon_token(&self) -> &syn::token::Colon;
437 fn ty(&self) -> &syn::Type;
439}
440
441impl ConstLike for syn::ItemConst {
442 fn attrs(&self) -> &[Attribute] {
443 &self.attrs
444 }
445 fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
446 &mut self.attrs
447 }
448 fn vis(&self) -> &syn::Visibility {
449 &self.vis
450 }
451 fn const_token(&self) -> &syn::token::Const {
452 &self.const_token
453 }
454 fn ident(&self) -> &syn::Ident {
455 &self.ident
456 }
457 fn colon_token(&self) -> &syn::token::Colon {
458 &self.colon_token
459 }
460 fn ty(&self) -> &syn::Type {
461 &self.ty
462 }
463}
464
465impl ConstLike for syn::ImplItemConst {
466 fn attrs(&self) -> &[Attribute] {
467 &self.attrs
468 }
469 fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
470 &mut self.attrs
471 }
472 fn vis(&self) -> &syn::Visibility {
473 &self.vis
474 }
475 fn const_token(&self) -> &syn::token::Const {
476 &self.const_token
477 }
478 fn ident(&self) -> &syn::Ident {
479 &self.ident
480 }
481 fn colon_token(&self) -> &syn::token::Colon {
482 &self.colon_token
483 }
484 fn ty(&self) -> &syn::Type {
485 &self.ty
486 }
487}
488
489impl ConstLike for syn::TraitItemConst {
490 fn attrs(&self) -> &[Attribute] {
491 &self.attrs
492 }
493 fn attrs_mut(&mut self) -> &mut Vec<Attribute> {
494 &mut self.attrs
495 }
496 fn vis(&self) -> &syn::Visibility {
497 &syn::Visibility::Inherited
498 }
499 fn const_token(&self) -> &syn::token::Const {
500 &self.const_token
501 }
502 fn ident(&self) -> &syn::Ident {
503 &self.ident
504 }
505 fn colon_token(&self) -> &syn::token::Colon {
506 &self.colon_token
507 }
508 fn ty(&self) -> &syn::Type {
509 &self.ty
510 }
511}
512
513#[cfg(test)]
514mod tests {
515 use super::*;
516 use crate::assert_quote_eq;
517 use quote::quote;
518 use syn::parse_quote;
519
520 #[test]
521 fn test_attrs() {
522 let mut item: Item = parse_quote!(
523 #[test]
524 type A = u32;
525 );
526 let expected: Attribute = parse_quote!(#[test]);
527 {
528 let attr = &item.attrs().unwrap()[0];
529 assert_quote_eq!(attr, expected);
530 }
531 {
532 let attr = &item.attrs_mut().unwrap()[0];
533 assert_quote_eq!(attr, expected);
534 }
535 }
536
537 #[test]
538 fn test_items() {
539 let module: ItemMod = parse_quote!(
540 mod m {
541 static x: usize = 0;
542 fn f() {}
543 }
544 );
545 let content = module.items().unwrap();
546 assert!(matches!(content[0], Item::Static(_)));
547 assert!(matches!(content[1], Item::Fn(_)));
548 }
549
550 #[test]
551 fn test_items_decl() {
552 let module: ItemMod = parse_quote!(
553 mod m;
554 );
555 assert!(module.items().is_none());
556 }
557
558 #[test]
559 fn test_function_like() {
560 let function: ItemFn = parse_quote!(
561 fn f(a: u8) -> Result<()> {}
562 );
563 let method: ImplItemFn = parse_quote!(
564 fn f(a: u8) -> Result<()> {}
565 );
566 assert_eq!(quote!(#function).to_string(), quote!(#method).to_string());
567 }
568}