bytebraise_syntax/syntax/ast/
nodes.rs1use crate::ast_node;
2use crate::syntax::ast;
3use crate::syntax::ast::quoted_value::QuotedValue;
4use crate::syntax::ast::tokens::{
5 AssignmentOperator, DirectiveArgument, Identifier, PythonDefFunctionName, Varflag,
6};
7use crate::syntax::ast::{AstChildren, AstNode, AstToken, SyntaxKind, SyntaxNode, support, tokens};
8
9ast_node!(Assignment, AssignmentNode);
10impl Assignment {
11 pub fn left(&self) -> IdentifierExpression {
12 support::child(&self.syntax).unwrap()
13 }
14
15 pub fn right(&self) -> QuotedValue {
16 support::token(&self.syntax).unwrap()
17 }
18
19 pub fn op(&self) -> AssignmentOperator {
20 support::token(&self.syntax).unwrap()
21 }
22}
23
24ast_node!(Export, ExportNode);
25impl Export {
26 pub fn export_kw(&self) -> tokens::Export {
27 support::token(&self.syntax).unwrap()
28 }
29
30 pub fn var(&self) -> IdentifierExpression {
31 support::child(&self.syntax).unwrap()
32 }
33
34 pub fn assignment(&self) -> Option<Assignment> {
35 support::child(&self.syntax)
36 }
37}
38
39ast_node!(Include, IncludeNode);
40impl Include {
41 pub fn value(&self) -> tokens::UnquotedValue {
42 support::token(&self.syntax).unwrap()
43 }
44}
45
46ast_node!(Require, RequireNode);
47impl Require {
48 pub fn value(&self) -> tokens::UnquotedValue {
49 support::token(&self.syntax).unwrap()
50 }
51}
52
53ast_node!(Unset, UnsetNode);
54ast_node!(ExportFunctions, ExportFunctionsNode);
55ast_node!(Inherit, InheritNode);
56impl Inherit {
57 pub fn value(&self) -> tokens::UnquotedValue {
58 support::token(&self.syntax).unwrap()
59 }
60}
61
62ast_node!(PythonDef, PythonDefNode);
63impl PythonDef {
64 pub fn function_name(&self) -> PythonDefFunctionName {
66 support::token(&self.syntax).unwrap()
67 }
68}
69
70ast_node!(AddTask, AddTaskNode);
71impl AddTask {
72 pub fn task_name(&self) -> DirectiveArgument {
73 self.syntax
74 .children_with_tokens()
75 .filter_map(|it| it.into_token())
76 .take_while(|it| !matches!(it.kind(), SyntaxKind::After | SyntaxKind::Before))
78 .find_map(DirectiveArgument::cast)
79 .unwrap()
80 }
81
82 pub fn after(&self) -> Vec<DirectiveArgument> {
83 self.syntax
84 .children_with_tokens()
85 .filter_map(|it| it.into_token())
86 .skip_while(|it| !matches!(it.kind(), SyntaxKind::After))
87 .take_while(|it| !matches!(it.kind(), SyntaxKind::Before))
88 .filter_map(DirectiveArgument::cast)
89 .collect()
90 }
91
92 pub fn after_names(&self) -> Vec<String> {
93 self.after()
94 .into_iter()
95 .map(|t| t.syntax.text().to_string())
96 .collect()
97 }
98
99 pub fn before(&self) -> Vec<DirectiveArgument> {
100 self.syntax
101 .children_with_tokens()
102 .filter_map(|it| it.into_token())
103 .skip_while(|it| !matches!(it.kind(), SyntaxKind::Before))
104 .take_while(|it| !matches!(it.kind(), SyntaxKind::After))
105 .filter_map(DirectiveArgument::cast)
106 .collect()
107 }
108
109 pub fn before_names(&self) -> Vec<String> {
110 self.before()
111 .into_iter()
112 .map(|t| t.syntax.text().to_string())
113 .collect()
114 }
115}
116
117ast_node!(DelTask, DelTaskNode);
118ast_node!(AddHandler, AddHandlerNode);
119ast_node!(Comment, Comment);
120
121ast_node!(IdentifierExpression, IdentifierExpressionNode);
122impl IdentifierExpression {
123 pub fn identifier(&self) -> Identifier {
124 support::token(&self.syntax).unwrap()
125 }
126
127 pub fn varflag(&self) -> Option<Varflag> {
128 support::token(&self.syntax)
129 }
130}
131
132ast_node!(Root, RootNode);
133impl Root {
134 pub fn items(&self) -> AstChildren<RootItem> {
135 support::children(self.syntax())
136 }
137
138 pub fn tasks(&self) -> AstChildren<Task> {
139 support::children(self.syntax())
140 }
141
142 pub fn assignments(&self) -> AstChildren<Assignment> {
143 support::children(self.syntax())
144 }
145
146 pub fn identifier_assignments<'a, 'b: 'a>(
148 &'a self,
149 identifier: &'b str,
150 ) -> IdentifierAssignments<'a> {
151 IdentifierAssignments {
152 inner: self.assignments(),
153 identifier,
154 }
155 }
156}
157
158#[derive(Debug, Clone)]
159pub struct IdentifierAssignments<'a> {
160 inner: AstChildren<Assignment>,
161 identifier: &'a str,
162}
163
164impl<'a> Iterator for IdentifierAssignments<'a> {
165 type Item = Assignment;
166
167 fn next(&mut self) -> Option<Self::Item> {
168 let identifier = self.identifier;
169 self.inner.find(|i| i.left().syntax.text() == identifier)
170 }
171}
172
173#[derive(Debug, Clone, PartialEq, Eq, Hash)]
174pub enum RootItem {
175 Task(Task),
176 Comment(Comment),
177 Directive(Directive),
178 PythonDef(PythonDef),
179 Assignment(Assignment),
180}
181
182impl AstNode for RootItem {
183 fn can_cast(kind: SyntaxKind) -> bool
184 where
185 Self: Sized,
186 {
187 match kind {
188 SyntaxKind::TaskNode
189 | SyntaxKind::Comment
190 | SyntaxKind::PythonDefNode
191 | SyntaxKind::AssignmentNode => true,
192 k if Directive::can_cast(k) => true,
193 _ => false,
194 }
195 }
196
197 fn cast(syntax: SyntaxNode) -> Option<Self>
198 where
199 Self: Sized,
200 {
201 let res = match syntax.kind() {
202 SyntaxKind::TaskNode => RootItem::Task(Task { syntax }),
203 SyntaxKind::Comment => RootItem::Comment(Comment { syntax }),
204 SyntaxKind::PythonDefNode => RootItem::PythonDef(PythonDef { syntax }),
205 SyntaxKind::AssignmentNode => RootItem::Assignment(Assignment { syntax }),
206 k if Directive::can_cast(k) => RootItem::Directive(Directive::cast(syntax).unwrap()),
207 _ => return None,
208 };
209
210 Some(res)
211 }
212
213 fn syntax(&self) -> &SyntaxNode {
214 match self {
215 RootItem::Task(it) => &it.syntax,
216 RootItem::Comment(it) => &it.syntax,
217 RootItem::Directive(it) => it.syntax(),
218 RootItem::PythonDef(it) => &it.syntax,
219 RootItem::Assignment(it) => &it.syntax,
220 }
221 }
222}
223
224#[derive(Debug, Clone, PartialEq, Eq, Hash)]
225pub enum Directive {
226 AddHandler(AddHandler),
227 AddTask(AddTask),
228 DelTask(DelTask),
229 Unset(Unset),
230 Inherit(Inherit),
231 Include(Include),
232 Require(Require),
233 Export(Export),
234 ExportFunctions(ExportFunctions),
235}
236
237impl AstNode for Directive {
238 fn can_cast(kind: SyntaxKind) -> bool
239 where
240 Self: Sized,
241 {
242 use SyntaxKind::*;
243 match kind {
244 AddHandlerNode | AddTaskNode | DelTaskNode | UnsetNode | InheritNode | IncludeNode
245 | RequireNode | ExportNode | ExportFunctionsNode => true,
246 _ => false,
247 }
248 }
249
250 fn cast(syntax: SyntaxNode) -> Option<Self>
251 where
252 Self: Sized,
253 {
254 let res = match syntax.kind() {
255 SyntaxKind::AddHandlerNode => Directive::AddHandler(AddHandler { syntax }),
256 SyntaxKind::AddTaskNode => Directive::AddTask(AddTask { syntax }),
257 SyntaxKind::DelTaskNode => Directive::DelTask(DelTask { syntax }),
258 SyntaxKind::UnsetNode => Directive::Unset(Unset { syntax }),
259 SyntaxKind::InheritNode => Directive::Inherit(Inherit { syntax }),
260 SyntaxKind::IncludeNode => Directive::Include(Include { syntax }),
261 SyntaxKind::RequireNode => Directive::Require(Require { syntax }),
262 SyntaxKind::ExportNode => Directive::Export(Export { syntax }),
263 SyntaxKind::ExportFunctionsNode => {
264 Directive::ExportFunctions(ExportFunctions { syntax })
265 }
266 _ => return None,
267 };
268
269 Some(res)
270 }
271
272 fn syntax(&self) -> &SyntaxNode {
273 match self {
274 Directive::AddHandler(it) => &it.syntax,
275 Directive::AddTask(it) => &it.syntax,
276 Directive::DelTask(it) => &it.syntax,
277 Directive::Unset(it) => &it.syntax,
278 Directive::Inherit(it) => &it.syntax,
279 Directive::Include(it) => &it.syntax,
280 Directive::Require(it) => &it.syntax,
281 Directive::Export(it) => &it.syntax,
282 Directive::ExportFunctions(it) => &it.syntax,
283 }
284 }
285}
286
287ast_node!(Task, TaskNode);
288impl Task {
289 pub fn name(&self) -> Option<Identifier> {
290 let id_node: Option<IdentifierExpression> = support::child(self.syntax());
291 id_node.map(|id| id.identifier())
292 }
293
294 pub fn body(&self) -> tokens::Task {
295 support::token(&self.syntax).unwrap()
296 }
297
298 pub fn name_or_anonymous(&self) -> String {
299 self.name()
300 .as_ref()
301 .map(|n| n.text().to_string())
302 .unwrap_or(String::from("__anonymous"))
303 }
304
305 pub fn is_python(&self) -> bool {
306 self.syntax
307 .children_with_tokens()
308 .filter_map(|it| it.into_token())
309 .find_map(ast::tokens::Python::cast)
310 .is_some()
311 }
312
313 pub fn is_fakeroot(&self) -> bool {
314 self.syntax
315 .children_with_tokens()
316 .filter_map(|it| it.into_token())
317 .find_map(ast::tokens::Fakeroot::cast)
318 .is_some()
319 }
320
321 pub fn is_anonymous_python(&self) -> bool {
322 self.is_python()
323 && matches!(
324 self.name().as_ref().map(|n| n.text()),
325 Some("__anonymous") | None
326 )
327 }
328}