1use either::Either;
5
6use crate::{
7 SyntaxElement, SyntaxToken, T,
8 ast::{self, AstChildren, AstNode, AstToken, support},
9 syntax_node::SyntaxElementChildren,
10};
11
12pub trait HasName: AstNode {
13 fn name(&self) -> Option<ast::Name> {
14 support::child(self.syntax())
15 }
16}
17
18pub trait HasVisibility: AstNode {
19 fn visibility(&self) -> Option<ast::Visibility> {
20 support::child(self.syntax())
21 }
22}
23
24pub trait HasLoopBody: AstNode {
25 fn loop_body(&self) -> Option<ast::BlockExpr> {
26 support::child(self.syntax())
27 }
28
29 fn label(&self) -> Option<ast::Label> {
30 support::child(self.syntax())
31 }
32}
33
34pub trait HasArgList: AstNode {
35 fn arg_list(&self) -> Option<ast::ArgList> {
36 support::child(self.syntax())
37 }
38}
39
40pub trait HasModuleItem: AstNode {
41 fn items(&self) -> AstChildren<ast::Item> {
42 support::children(self.syntax())
43 }
44}
45
46pub trait HasGenericParams: AstNode {
47 fn generic_param_list(&self) -> Option<ast::GenericParamList> {
48 support::child(self.syntax())
49 }
50
51 fn where_clause(&self) -> Option<ast::WhereClause> {
52 support::child(self.syntax())
53 }
54}
55pub trait HasGenericArgs: AstNode {
56 fn generic_arg_list(&self) -> Option<ast::GenericArgList> {
57 support::child(self.syntax())
58 }
59}
60
61pub trait HasTypeBounds: AstNode {
62 fn type_bound_list(&self) -> Option<ast::TypeBoundList> {
63 support::child(self.syntax())
64 }
65
66 fn colon_token(&self) -> Option<SyntaxToken> {
67 support::token(self.syntax(), T![:])
68 }
69}
70
71pub trait HasAttrs: AstNode {
72 fn attrs(&self) -> AstChildren<ast::Attr> {
73 support::children(self.syntax())
74 }
75 fn has_atom_attr(&self, atom: &str) -> bool {
76 self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom)
77 }
78
79 fn attrs_including_inner(self) -> impl Iterator<Item = ast::Attr>
82 where
83 Self: Sized,
84 {
85 let inner_attrs_node = if let Some(it) =
86 support::child::<ast::BlockExpr>(self.syntax()).and_then(|it| it.stmt_list())
87 {
88 Some(it.syntax)
89 } else if let Some(it) = support::child::<ast::MatchArmList>(self.syntax()) {
90 Some(it.syntax)
91 } else if let Some(it) = support::child::<ast::AssocItemList>(self.syntax()) {
92 Some(it.syntax)
93 } else if let Some(it) = support::child::<ast::ItemList>(self.syntax()) {
94 Some(it.syntax)
95 } else if let Some(it) = support::child::<ast::ExternItemList>(self.syntax()) {
96 Some(it.syntax)
97 } else if let Some(it) = support::child::<ast::MacroItems>(self.syntax()) {
98 Some(it.syntax)
99 } else {
100 None
101 };
102
103 self.attrs().chain(inner_attrs_node.into_iter().flat_map(|it| support::children(&it)))
104 }
105}
106
107pub trait HasDocComments: HasAttrs {
108 fn doc_comments(&self) -> DocCommentIter {
109 DocCommentIter { iter: self.syntax().children_with_tokens() }
110 }
111}
112
113impl DocCommentIter {
114 pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> DocCommentIter {
115 DocCommentIter { iter: syntax_node.children_with_tokens() }
116 }
117
118 #[cfg(test)]
119 pub fn doc_comment_text(self) -> Option<String> {
120 let docs = itertools::Itertools::join(
121 &mut self.filter_map(|comment| comment.doc_comment().map(ToOwned::to_owned)),
122 "\n",
123 );
124 if docs.is_empty() { None } else { Some(docs) }
125 }
126}
127
128pub struct DocCommentIter {
129 iter: SyntaxElementChildren,
130}
131
132impl Iterator for DocCommentIter {
133 type Item = ast::Comment;
134 fn next(&mut self) -> Option<ast::Comment> {
135 self.iter.by_ref().find_map(|el| {
136 el.into_token().and_then(ast::Comment::cast).filter(ast::Comment::is_doc)
137 })
138 }
139}
140
141pub struct AttrDocCommentIter {
142 iter: SyntaxElementChildren,
143}
144
145impl AttrDocCommentIter {
146 pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> AttrDocCommentIter {
147 AttrDocCommentIter { iter: syntax_node.children_with_tokens() }
148 }
149}
150
151impl Iterator for AttrDocCommentIter {
152 type Item = Either<ast::Attr, ast::Comment>;
153 fn next(&mut self) -> Option<Self::Item> {
154 self.iter.by_ref().find_map(|el| match el {
155 SyntaxElement::Node(node) => ast::Attr::cast(node).map(Either::Left),
156 SyntaxElement::Token(tok) => {
157 ast::Comment::cast(tok).filter(ast::Comment::is_doc).map(Either::Right)
158 }
159 })
160 }
161}
162
163impl<A: HasName, B: HasName> HasName for Either<A, B> {}