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 block_id(&self) -> Option<NodeId> {
271        match self {
272            Self::CatchClause { block_id, .. }
273            | Self::Class { block_id, .. }
274            | Self::Declaration { block_id, .. }
275            | Self::FinallyClause { block_id }
276            | Self::ForEachStatement { block_id, .. }
277            | Self::MethodDeclaration { block_id, .. }
278            | Self::MethodInvocation { block_id, .. }
279            | Self::Namespace { block_id, .. } => *block_id,
280            Self::DoStatement { block_id, .. }
281            | Self::ElseClause { block_id }
282            | Self::ForStatement { block_id, .. }
283            | Self::SwitchStatement { block_id, .. }
284            | Self::TryStatement { block_id, .. }
285            | Self::UsingStatement { block_id, .. }
286            | Self::WhileStatement { block_id, .. } => Some(*block_id),
287            _ => None,
288        }
289    }
290
291    #[must_use]
292    pub fn name(&self) -> Option<&str> {
293        match self {
294            Self::Annotation { name, .. }
295            | Self::Attribute { name }
296            | Self::Class { name, .. }
297            | Self::Declaration { name, .. }
298            | Self::Namespace { name, .. }
299            | Self::ObjectCreation { name, .. } => Some(name.as_str()),
300            Self::MethodDeclaration { name, .. } | Self::Object { name, .. } => name.as_deref(),
301            _ => None,
302        }
303    }
304
305    #[must_use]
306    pub fn variable(&self) -> Option<&str> {
307        match self {
308            Self::VariableDeclaration { variable, .. } => Some(variable.as_str()),
309            Self::Parameter { variable, .. } => variable.as_deref(),
310            _ => None,
311        }
312    }
313
314    #[must_use]
315    pub const fn symbol(&self) -> Option<&str> {
316        match self {
317            Self::SymbolLookup { symbol, .. } => Some(symbol.as_str()),
318            _ => None,
319        }
320    }
321
322    #[must_use]
323    pub fn expression(&self) -> Option<&str> {
324        match self {
325            Self::MemberAccess { expression, .. }
326            | Self::MethodInvocation { expression, .. }
327            | Self::ModuleImport { expression, .. } => Some(expression.as_str()),
328            Self::Export { expression, .. } | Self::Import { expression, .. } => {
329                expression.as_deref()
330            }
331            _ => None,
332        }
333    }
334
335    #[must_use]
336    pub fn alias(&self) -> Option<&str> {
337        match self {
338            Self::Import { alias, .. } | Self::ModuleImport { alias, .. } => alias.as_deref(),
339            _ => None,
340        }
341    }
342
343    #[must_use]
344    pub fn value(&self) -> Option<&str> {
345        match self {
346            Self::Literal { value, .. } | Self::ReservedWord { value } | Self::This { value } => {
347                Some(value.as_str())
348            }
349            Self::SymbolLookup { value, .. } => value.as_deref(),
350            _ => None,
351        }
352    }
353
354    #[must_use]
355    pub const fn value_id(&self) -> Option<NodeId> {
356        match self {
357            Self::Assignment { value_id, .. }
358            | Self::Parameter { value_id, .. }
359            | Self::Return { value_id }
360            | Self::VariableDeclaration { value_id, .. } => *value_id,
361            Self::NamedArgument { value_id, .. }
362            | Self::Pair { value_id, .. }
363            | Self::RestPattern { value_id }
364            | Self::SpreadElement { value_id }
365            | Self::SwitchStatement { value_id, .. } => Some(*value_id),
366            _ => None,
367        }
368    }
369
370    #[must_use]
371    pub const fn variable_id(&self) -> Option<NodeId> {
372        match self {
373            Self::VariableDeclaration { variable_id, .. } => *variable_id,
374            Self::Assignment { variable_id, .. } | Self::ForEachStatement { variable_id, .. } => {
375                Some(*variable_id)
376            }
377            _ => None,
378        }
379    }
380
381    #[must_use]
382    pub(crate) const fn symbol_scope(&self) -> Option<NodeId> {
383        match self {
384            Self::MemberAccess { symbol_scope, .. }
385            | Self::MethodInvocation { symbol_scope, .. }
386            | Self::SymbolLookup { symbol_scope, .. } => *symbol_scope,
387            _ => None,
388        }
389    }
390
391    #[must_use]
392    pub fn label_type(&self) -> &'static str {
393        SyntaxKind::from(self).into()
394    }
395}
396
397#[cfg(test)]
398mod tests {
399    use super::SyntaxNode;
400    use alloc::borrow::ToOwned;
401
402    #[test]
403    fn label_type_pins_the_contract_strings() {
404        assert_eq!(SyntaxNode::Default.label_type(), "Default");
405        assert_eq!(SyntaxNode::Break.label_type(), "Break");
406        assert_eq!(SyntaxNode::ArgumentList.label_type(), "ArgumentList");
407        assert_eq!(
408            SyntaxNode::Attribute {
409                name: "route".to_owned(),
410            }
411            .label_type(),
412            "Attribute"
413        );
414        assert_eq!(
415            SyntaxNode::MissingNode {
416                node_type: "stream".to_owned(),
417            }
418            .label_type(),
419            "MissingNode"
420        );
421    }
422}