Skip to main content

omena_parser/
cst.rs

1//! Typed CST wrappers over the raw parser syntax tree.
2//!
3//! These wrappers give downstream parser consumers stable node categories
4//! without exposing the full cstree traversal contract at every call site.
5
6use cstree::{syntax::SyntaxNode, text::TextRange};
7use omena_syntax::SyntaxKind;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct ParsedCst {
11    root: SyntaxNode<SyntaxKind>,
12}
13
14impl ParsedCst {
15    pub fn new(root: SyntaxNode<SyntaxKind>) -> Self {
16        Self { root }
17    }
18
19    pub fn root(&self) -> &SyntaxNode<SyntaxKind> {
20        &self.root
21    }
22
23    pub fn stylesheet(&self) -> Option<StylesheetCstNode> {
24        self.first_node(StylesheetCstNode::cast)
25    }
26
27    pub fn rules(&self) -> Vec<RuleCstNode> {
28        self.nodes(RuleCstNode::cast)
29    }
30
31    pub fn selectors(&self) -> Vec<SelectorCstNode> {
32        self.nodes(SelectorCstNode::cast)
33    }
34
35    pub fn declarations(&self) -> Vec<DeclarationCstNode> {
36        self.nodes(DeclarationCstNode::cast)
37    }
38
39    pub fn declaration_lists(&self) -> Vec<DeclarationListCstNode> {
40        self.nodes(DeclarationListCstNode::cast)
41    }
42
43    pub fn values(&self) -> Vec<ValueCstNode> {
44        self.nodes(ValueCstNode::cast)
45    }
46
47    pub fn url_values(&self) -> Vec<UrlValueCstNode> {
48        self.nodes(UrlValueCstNode::cast)
49    }
50
51    pub fn component_values(&self) -> Vec<ComponentValueCstNode> {
52        self.nodes(ComponentValueCstNode::cast)
53    }
54
55    pub fn simple_blocks(&self) -> Vec<SimpleBlockCstNode> {
56        self.nodes(SimpleBlockCstNode::cast)
57    }
58
59    pub fn component_value_lists(&self) -> Vec<ComponentValueListCstNode> {
60        self.nodes(ComponentValueListCstNode::cast)
61    }
62
63    pub fn comma_separated_component_value_lists(
64        &self,
65    ) -> Vec<CommaSeparatedComponentValueListCstNode> {
66        self.nodes(CommaSeparatedComponentValueListCstNode::cast)
67    }
68
69    pub fn custom_property_values(&self) -> Vec<CustomPropertyValueCstNode> {
70        self.nodes(CustomPropertyValueCstNode::cast)
71    }
72
73    pub fn at_rules(&self) -> Vec<AtRuleCstNode> {
74        self.nodes(AtRuleCstNode::cast)
75    }
76
77    pub fn bogus_nodes(&self) -> Vec<BogusCstNode> {
78        self.nodes(BogusCstNode::cast)
79    }
80
81    pub fn has_bogus_nodes(&self) -> bool {
82        self.first_node(BogusCstNode::cast).is_some()
83    }
84
85    fn first_node<T>(&self, cast: impl Fn(SyntaxNode<SyntaxKind>) -> Option<T>) -> Option<T> {
86        let mut nodes = Vec::new();
87        collect_typed_nodes(&self.root, &cast, &mut nodes);
88        nodes.into_iter().next()
89    }
90
91    fn nodes<T>(&self, cast: impl Fn(SyntaxNode<SyntaxKind>) -> Option<T>) -> Vec<T> {
92        let mut nodes = Vec::new();
93        collect_typed_nodes(&self.root, &cast, &mut nodes);
94        nodes
95    }
96}
97
98pub trait TypedCstNode: Sized {
99    fn cast(syntax: SyntaxNode<SyntaxKind>) -> Option<Self>;
100    fn syntax(&self) -> &SyntaxNode<SyntaxKind>;
101
102    fn kind(&self) -> SyntaxKind {
103        self.syntax().kind()
104    }
105
106    fn text_range(&self) -> TextRange {
107        self.syntax().text_range()
108    }
109
110    fn into_syntax(self) -> SyntaxNode<SyntaxKind>;
111}
112
113macro_rules! typed_cst_node {
114    ($name:ident, $kind:expr) => {
115        #[derive(Debug, Clone, PartialEq, Eq)]
116        pub struct $name {
117            syntax: SyntaxNode<SyntaxKind>,
118        }
119
120        impl $name {
121            pub const KIND: SyntaxKind = $kind;
122        }
123
124        impl TypedCstNode for $name {
125            fn cast(syntax: SyntaxNode<SyntaxKind>) -> Option<Self> {
126                (syntax.kind() == Self::KIND).then_some(Self { syntax })
127            }
128
129            fn syntax(&self) -> &SyntaxNode<SyntaxKind> {
130                &self.syntax
131            }
132
133            fn into_syntax(self) -> SyntaxNode<SyntaxKind> {
134                self.syntax
135            }
136        }
137    };
138}
139
140typed_cst_node!(StylesheetCstNode, SyntaxKind::Stylesheet);
141typed_cst_node!(RuleCstNode, SyntaxKind::Rule);
142typed_cst_node!(SelectorCstNode, SyntaxKind::Selector);
143typed_cst_node!(DeclarationCstNode, SyntaxKind::Declaration);
144typed_cst_node!(DeclarationListCstNode, SyntaxKind::DeclarationList);
145typed_cst_node!(ValueCstNode, SyntaxKind::Value);
146typed_cst_node!(UrlValueCstNode, SyntaxKind::UrlValue);
147typed_cst_node!(ComponentValueCstNode, SyntaxKind::ComponentValue);
148typed_cst_node!(SimpleBlockCstNode, SyntaxKind::SimpleBlock);
149typed_cst_node!(ComponentValueListCstNode, SyntaxKind::ComponentValueList);
150typed_cst_node!(
151    CommaSeparatedComponentValueListCstNode,
152    SyntaxKind::CommaSeparatedComponentValueList
153);
154typed_cst_node!(CustomPropertyValueCstNode, SyntaxKind::CustomPropertyValue);
155
156#[derive(Debug, Clone, PartialEq, Eq)]
157pub struct AtRuleCstNode {
158    syntax: SyntaxNode<SyntaxKind>,
159}
160
161#[derive(Debug, Clone, PartialEq, Eq)]
162pub struct BogusCstNode {
163    syntax: SyntaxNode<SyntaxKind>,
164}
165
166impl TypedCstNode for AtRuleCstNode {
167    fn cast(syntax: SyntaxNode<SyntaxKind>) -> Option<Self> {
168        is_at_rule_node_kind(syntax.kind()).then_some(Self { syntax })
169    }
170
171    fn syntax(&self) -> &SyntaxNode<SyntaxKind> {
172        &self.syntax
173    }
174
175    fn into_syntax(self) -> SyntaxNode<SyntaxKind> {
176        self.syntax
177    }
178}
179
180impl TypedCstNode for BogusCstNode {
181    fn cast(syntax: SyntaxNode<SyntaxKind>) -> Option<Self> {
182        syntax.kind().is_bogus().then_some(Self { syntax })
183    }
184
185    fn syntax(&self) -> &SyntaxNode<SyntaxKind> {
186        &self.syntax
187    }
188
189    fn into_syntax(self) -> SyntaxNode<SyntaxKind> {
190        self.syntax
191    }
192}
193
194pub fn is_at_rule_node_kind(kind: SyntaxKind) -> bool {
195    matches!(
196        kind,
197        SyntaxKind::AtRule
198            | SyntaxKind::MediaRule
199            | SyntaxKind::SupportsRule
200            | SyntaxKind::ContainerRule
201            | SyntaxKind::LayerRule
202            | SyntaxKind::ScopeRule
203            | SyntaxKind::KeyframesRule
204            | SyntaxKind::FontFaceRule
205            | SyntaxKind::PageRule
206            | SyntaxKind::NamespaceRule
207            | SyntaxKind::ImportRule
208            | SyntaxKind::CharsetRule
209            | SyntaxKind::PropertyRule
210            | SyntaxKind::FunctionRule
211            | SyntaxKind::StartingStyleRule
212            | SyntaxKind::PageMarginRule
213            | SyntaxKind::WhenRule
214            | SyntaxKind::ElseRule
215            | SyntaxKind::IfRule
216            | SyntaxKind::CounterStyleRule
217            | SyntaxKind::FontPaletteValuesRule
218            | SyntaxKind::ColorProfileRule
219            | SyntaxKind::PositionTryRule
220            | SyntaxKind::FontFeatureValuesRule
221            | SyntaxKind::FontFeatureValuesStylisticRule
222            | SyntaxKind::FontFeatureValuesStylesetRule
223            | SyntaxKind::FontFeatureValuesCharacterVariantRule
224            | SyntaxKind::FontFeatureValuesSwashRule
225            | SyntaxKind::FontFeatureValuesOrnamentsRule
226            | SyntaxKind::FontFeatureValuesAnnotationRule
227            | SyntaxKind::FontFeatureValuesHistoricalFormsRule
228            | SyntaxKind::ViewTransitionRule
229            | SyntaxKind::NestRule
230            | SyntaxKind::CustomMediaRule
231            | SyntaxKind::ScssUseRule
232            | SyntaxKind::ScssForwardRule
233            | SyntaxKind::ScssMixinDeclaration
234            | SyntaxKind::ScssIncludeRule
235            | SyntaxKind::ScssFunctionDeclaration
236            | SyntaxKind::ScssReturnRule
237            | SyntaxKind::ScssAtRootRule
238            | SyntaxKind::ScssErrorRule
239            | SyntaxKind::ScssWarnRule
240            | SyntaxKind::ScssDebugRule
241            | SyntaxKind::ScssContentRule
242    )
243}
244
245fn collect_typed_nodes<T>(
246    node: &SyntaxNode<SyntaxKind>,
247    cast: &impl Fn(SyntaxNode<SyntaxKind>) -> Option<T>,
248    nodes: &mut Vec<T>,
249) {
250    if let Some(typed) = cast(node.clone()) {
251        nodes.push(typed);
252    }
253    for child in node.children() {
254        collect_typed_nodes(child, cast, nodes);
255    }
256}