Skip to main content

fluidattacks_blends_domain/syntax/
node.rs

1//! Typed syntax nodes: one enum variant per syntax label type.
2
3use alloc::collections::BTreeMap;
4use alloc::string::String;
5use alloc::vec::Vec;
6
7use strum::{EnumDiscriminants, IntoStaticStr};
8
9use crate::NodeId;
10
11#[derive(Clone, PartialEq, Eq, Debug)]
12pub struct FileStructData {
13    pub node: NodeId,
14    pub kind: String,
15    pub data: FileStructValue,
16    pub node_range: Option<Vec<NodeId>>,
17}
18
19#[derive(Clone, PartialEq, Eq, Debug)]
20pub enum FileStructValue {
21    MethodName(String),
22    Children(BTreeMap<String, FileStructData>),
23}
24
25#[derive(Clone, PartialEq, Eq, Debug)]
26pub struct FileInstanceData {
27    pub object: String,
28    pub source: String,
29    pub source_type: String,
30}
31
32#[derive(Clone, PartialEq, Eq, Debug, EnumDiscriminants)]
33#[strum_discriminants(name(SyntaxKind), derive(PartialOrd, Ord, Hash, IntoStaticStr))]
34pub enum SyntaxNode {
35    Annotation {
36        name: String,
37        arguments_id: Option<NodeId>,
38    },
39    Argument,
40    ArgumentList,
41    ArrayInitializer,
42    Assignment {
43        variable_id: NodeId,
44        value_id: Option<NodeId>,
45        operator: Option<String>,
46    },
47    Attribute {
48        name: String,
49    },
50    AwaitExpression {
51        expression_id: NodeId,
52    },
53    BinaryOperation {
54        operator: String,
55        left_id: Option<NodeId>,
56        right_id: Option<NodeId>,
57    },
58    Break,
59    CatchClause {
60        block_id: Option<NodeId>,
61        catch_declaration: Option<NodeId>,
62    },
63    CatchDeclaration,
64    Class {
65        name: String,
66        block_id: Option<NodeId>,
67        modifiers_id: Option<NodeId>,
68        inherited_class: Option<String>,
69        access_modifiers: Option<String>,
70    },
71    ClassBody,
72    Comment {
73        comment: String,
74    },
75    Continue,
76    Declaration {
77        name: String,
78        block_id: Option<NodeId>,
79        access_modifiers: Option<String>,
80    },
81    DeclarationBlock,
82    Default,
83    DoStatement {
84        block_id: NodeId,
85        condition_id: NodeId,
86    },
87    ElementAccess {
88        expression_id: NodeId,
89        arguments_id: Option<NodeId>,
90    },
91    ElseClause {
92        block_id: NodeId,
93    },
94    ExecutionBlock,
95    Export {
96        expression: Option<String>,
97        declaration_id: Option<NodeId>,
98    },
99    ExpressionStatement,
100    File,
101    FinallyClause {
102        block_id: Option<NodeId>,
103    },
104    ForEachStatement {
105        variable_id: NodeId,
106        iterable_item_id: NodeId,
107        block_id: Option<NodeId>,
108    },
109    ForStatement {
110        block_id: NodeId,
111        initializer_id: Option<NodeId>,
112        condition_id: Option<NodeId>,
113        update_id: Option<NodeId>,
114    },
115    If {
116        condition_id: NodeId,
117        true_id: Option<NodeId>,
118        false_id: Option<NodeId>,
119        initializer: Option<NodeId>,
120    },
121    Import {
122        expression: Option<String>,
123        alias: Option<String>,
124        method_name: Option<String>,
125        import_type: Option<String>,
126    },
127    JsxElement,
128    LambdaFunctionType,
129    Literal {
130        value: String,
131        value_type: String,
132    },
133    MemberAccess {
134        member: String,
135        expression: String,
136        expression_id: NodeId,
137        symbol_scope: Option<NodeId>,
138    },
139    Metadata {
140        path: String,
141        structure: BTreeMap<String, FileStructData>,
142        instances: BTreeMap<String, BTreeMap<String, FileInstanceData>>,
143        imports: Vec<String>,
144        package: Option<String>,
145    },
146    MethodDeclaration {
147        name: Option<String>,
148        access_modifiers: Option<String>,
149        block_id: Option<NodeId>,
150        modifiers_id: Option<NodeId>,
151        parameters_id: Option<NodeId>,
152    },
153    MethodInvocation {
154        expression: String,
155        object: Option<String>,
156        symbol_scope: Option<NodeId>,
157        expression_id: Option<NodeId>,
158        arguments_id: Option<NodeId>,
159        object_id: Option<NodeId>,
160        block_id: Option<NodeId>,
161        receiver_type_fqn: Option<String>,
162    },
163    MissingNode {
164        node_type: String,
165    },
166    Modifiers,
167    ModuleImport {
168        expression: String,
169        alias: Option<String>,
170    },
171    NamedArgument {
172        value_id: NodeId,
173        argument_name: Option<String>,
174    },
175    Namespace {
176        name: String,
177        block_id: Option<NodeId>,
178    },
179    NewExpression {
180        constructor_id: NodeId,
181        arguments_id: Option<NodeId>,
182    },
183    Object {
184        name: Option<String>,
185        tf_reference: Option<String>,
186    },
187    ObjectCreation {
188        name: String,
189        arguments_id: Option<NodeId>,
190        initializer_id: Option<NodeId>,
191    },
192    Pair {
193        key_id: NodeId,
194        value_id: NodeId,
195    },
196    Parameter {
197        variable: Option<String>,
198        variable_type: Option<String>,
199        value_id: Option<NodeId>,
200        parameter_mode: Option<String>,
201    },
202    ParameterList,
203    ParenthesizedExpression,
204    ReservedWord {
205        value: String,
206    },
207    RestPattern {
208        value_id: NodeId,
209    },
210    Return {
211        value_id: Option<NodeId>,
212    },
213    Selector {
214        selector_name: Option<String>,
215    },
216    SpreadElement {
217        value_id: NodeId,
218    },
219    SwitchBody,
220    SwitchSection {
221        case_expression: String,
222    },
223    SwitchStatement {
224        block_id: NodeId,
225        value_id: NodeId,
226    },
227    SymbolLookup {
228        symbol: String,
229        symbol_scope: Option<NodeId>,
230        value: Option<String>,
231    },
232    TernaryOperation {
233        condition_id: NodeId,
234        true_id: NodeId,
235        false_id: NodeId,
236    },
237    This {
238        value: String,
239    },
240    ThrowStatement {
241        expression_id: Option<NodeId>,
242    },
243    TryStatement {
244        block_id: NodeId,
245        resources_id: Option<NodeId>,
246    },
247    UnaryExpression {
248        operator: String,
249        operand_id: NodeId,
250    },
251    UsingStatement {
252        block_id: NodeId,
253        declaration_id: Option<NodeId>,
254    },
255    VariableDeclaration {
256        variable: String,
257        variable_type: Option<String>,
258        value_id: Option<NodeId>,
259        variable_id: Option<NodeId>,
260        access_modifier: Option<String>,
261    },
262    WhileStatement {
263        block_id: NodeId,
264        condition_id: Option<NodeId>,
265    },
266}
267
268impl SyntaxNode {
269    #[must_use]
270    pub const fn arguments_id(&self) -> Option<NodeId> {
271        match self {
272            Self::Annotation { arguments_id, .. }
273            | Self::ElementAccess { arguments_id, .. }
274            | Self::MethodInvocation { arguments_id, .. }
275            | Self::NewExpression { arguments_id, .. }
276            | Self::ObjectCreation { arguments_id, .. } => *arguments_id,
277            _ => None,
278        }
279    }
280
281    #[must_use]
282    pub const fn block_id(&self) -> Option<NodeId> {
283        match self {
284            Self::CatchClause { block_id, .. }
285            | Self::Class { block_id, .. }
286            | Self::Declaration { block_id, .. }
287            | Self::FinallyClause { block_id }
288            | Self::ForEachStatement { block_id, .. }
289            | Self::MethodDeclaration { block_id, .. }
290            | Self::MethodInvocation { block_id, .. }
291            | Self::Namespace { block_id, .. } => *block_id,
292            Self::DoStatement { block_id, .. }
293            | Self::ElseClause { block_id }
294            | Self::ForStatement { block_id, .. }
295            | Self::SwitchStatement { block_id, .. }
296            | Self::TryStatement { block_id, .. }
297            | Self::UsingStatement { block_id, .. }
298            | Self::WhileStatement { block_id, .. } => Some(*block_id),
299            _ => None,
300        }
301    }
302
303    #[must_use]
304    pub fn name(&self) -> Option<&str> {
305        match self {
306            Self::Annotation { name, .. }
307            | Self::Attribute { name }
308            | Self::Class { name, .. }
309            | Self::Declaration { name, .. }
310            | Self::Namespace { name, .. }
311            | Self::ObjectCreation { name, .. } => Some(name.as_str()),
312            Self::MethodDeclaration { name, .. } | Self::Object { name, .. } => name.as_deref(),
313            _ => None,
314        }
315    }
316
317    #[must_use]
318    pub fn variable(&self) -> Option<&str> {
319        match self {
320            Self::VariableDeclaration { variable, .. } => Some(variable.as_str()),
321            Self::Parameter { variable, .. } => variable.as_deref(),
322            _ => None,
323        }
324    }
325
326    #[must_use]
327    pub const fn symbol(&self) -> Option<&str> {
328        match self {
329            Self::SymbolLookup { symbol, .. } => Some(symbol.as_str()),
330            _ => None,
331        }
332    }
333
334    #[must_use]
335    pub fn expression(&self) -> Option<&str> {
336        match self {
337            Self::MemberAccess { expression, .. }
338            | Self::MethodInvocation { expression, .. }
339            | Self::ModuleImport { expression, .. } => Some(expression.as_str()),
340            Self::Export { expression, .. } | Self::Import { expression, .. } => {
341                expression.as_deref()
342            }
343            _ => None,
344        }
345    }
346
347    #[must_use]
348    pub fn alias(&self) -> Option<&str> {
349        match self {
350            Self::Import { alias, .. } | Self::ModuleImport { alias, .. } => alias.as_deref(),
351            _ => None,
352        }
353    }
354
355    #[must_use]
356    pub fn value(&self) -> Option<&str> {
357        match self {
358            Self::Literal { value, .. } | Self::ReservedWord { value } | Self::This { value } => {
359                Some(value.as_str())
360            }
361            Self::SymbolLookup { value, .. } => value.as_deref(),
362            _ => None,
363        }
364    }
365
366    #[must_use]
367    pub const fn object_id(&self) -> Option<NodeId> {
368        match self {
369            Self::MethodInvocation { object_id, .. } => *object_id,
370            _ => None,
371        }
372    }
373
374    #[must_use]
375    pub const fn value_id(&self) -> Option<NodeId> {
376        match self {
377            Self::Assignment { value_id, .. }
378            | Self::Parameter { value_id, .. }
379            | Self::Return { value_id }
380            | Self::VariableDeclaration { value_id, .. } => *value_id,
381            Self::NamedArgument { value_id, .. }
382            | Self::Pair { value_id, .. }
383            | Self::RestPattern { value_id }
384            | Self::SpreadElement { value_id }
385            | Self::SwitchStatement { value_id, .. } => Some(*value_id),
386            _ => None,
387        }
388    }
389
390    #[must_use]
391    pub const fn variable_id(&self) -> Option<NodeId> {
392        match self {
393            Self::VariableDeclaration { variable_id, .. } => *variable_id,
394            Self::Assignment { variable_id, .. } | Self::ForEachStatement { variable_id, .. } => {
395                Some(*variable_id)
396            }
397            _ => None,
398        }
399    }
400
401    #[must_use]
402    pub(crate) const fn symbol_scope(&self) -> Option<NodeId> {
403        match self {
404            Self::MemberAccess { symbol_scope, .. }
405            | Self::MethodInvocation { symbol_scope, .. }
406            | Self::SymbolLookup { symbol_scope, .. } => *symbol_scope,
407            _ => None,
408        }
409    }
410
411    #[must_use]
412    pub fn label_type(&self) -> &'static str {
413        SyntaxKind::from(self).into()
414    }
415}
416
417#[cfg(test)]
418mod tests {
419    use super::SyntaxNode;
420    use alloc::borrow::ToOwned;
421
422    #[test]
423    fn label_type_pins_the_contract_strings() {
424        assert_eq!(SyntaxNode::Default.label_type(), "Default");
425        assert_eq!(SyntaxNode::Break.label_type(), "Break");
426        assert_eq!(SyntaxNode::ArgumentList.label_type(), "ArgumentList");
427        assert_eq!(
428            SyntaxNode::Attribute {
429                name: "route".to_owned(),
430            }
431            .label_type(),
432            "Attribute"
433        );
434        assert_eq!(
435            SyntaxNode::MissingNode {
436                node_type: "stream".to_owned(),
437            }
438            .label_type(),
439            "MissingNode"
440        );
441    }
442}