1use 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}