1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5mod expression_domain;
6mod expression_semantics;
7mod query_plan;
8mod selector_usage;
9mod semantic;
10mod source_resolution;
11mod source_side;
12#[cfg(test)]
13mod test_support;
14mod type_facts;
15
16pub use expression_domain::summarize_expression_domain_candidates_input;
17pub use expression_domain::summarize_expression_domain_canonical_candidate_bundle_input;
18pub use expression_domain::summarize_expression_domain_canonical_producer_signal_input;
19pub use expression_domain::summarize_expression_domain_evaluator_candidates_input;
20pub use expression_domain::summarize_expression_domain_flow_analysis_input;
21pub use expression_domain::summarize_expression_domain_fragments_input;
22pub use expression_domain::summarize_expression_domain_plan_input;
23pub use expression_semantics::summarize_expression_semantics_candidates_input;
24pub use expression_semantics::summarize_expression_semantics_canonical_candidate_bundle_input;
25pub use expression_semantics::summarize_expression_semantics_canonical_producer_signal_input;
26pub use expression_semantics::summarize_expression_semantics_evaluator_candidates_input;
27pub use expression_semantics::summarize_expression_semantics_fragments_input;
28pub use expression_semantics::summarize_expression_semantics_match_fragments_input;
29pub use expression_semantics::summarize_expression_semantics_query_fragments_input;
30pub use query_plan::summarize_query_plan_input;
31pub use selector_usage::summarize_selector_usage_candidates_input;
32pub use selector_usage::summarize_selector_usage_canonical_candidate_bundle_input;
33pub use selector_usage::summarize_selector_usage_canonical_producer_signal_input;
34pub use selector_usage::summarize_selector_usage_evaluator_candidates_input;
35pub use selector_usage::summarize_selector_usage_fragments_input;
36pub use selector_usage::summarize_selector_usage_plan_input;
37pub use selector_usage::summarize_selector_usage_query_fragments_input;
38pub use semantic::summarize_semantic_canonical_candidate_bundle_input;
39pub use semantic::summarize_semantic_canonical_producer_signal_input;
40pub use semantic::summarize_semantic_evaluator_candidates_input;
41pub use source_resolution::summarize_source_resolution_candidates_input;
42pub use source_resolution::summarize_source_resolution_canonical_candidate_bundle_input;
43pub use source_resolution::summarize_source_resolution_canonical_producer_signal_input;
44pub use source_resolution::summarize_source_resolution_evaluator_candidates_input;
45pub use source_resolution::summarize_source_resolution_fragments_input;
46pub use source_resolution::summarize_source_resolution_match_fragments_input;
47pub use source_resolution::summarize_source_resolution_plan_input;
48pub use source_resolution::summarize_source_resolution_query_fragments_input;
49pub use source_side::summarize_source_side_canonical_candidate_bundle_input;
50pub use source_side::summarize_source_side_canonical_producer_signal_input;
51pub use source_side::summarize_source_side_evaluator_candidates_input;
52pub use type_facts::summarize_type_fact_input;
53
54#[derive(Debug, Deserialize)]
55#[serde(rename_all = "camelCase")]
56pub struct EngineInputV2 {
57 pub version: String,
58 pub sources: Vec<SourceAnalysisInputV2>,
59 pub styles: Vec<StyleAnalysisInputV2>,
60 pub type_facts: Vec<TypeFactEntryV2>,
61}
62
63#[derive(Debug, Deserialize)]
64#[serde(rename_all = "camelCase")]
65pub struct SourceAnalysisInputV2 {
66 pub document: SourceDocumentV2,
67}
68
69#[derive(Debug, Deserialize)]
70#[serde(rename_all = "camelCase")]
71pub struct SourceDocumentV2 {
72 pub class_expressions: Vec<ClassExpressionInputV2>,
73}
74
75#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
76#[serde(rename_all = "camelCase")]
77pub struct PositionV2 {
78 pub line: usize,
79 pub character: usize,
80}
81
82#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
83#[serde(rename_all = "camelCase")]
84pub struct RangeV2 {
85 pub start: PositionV2,
86 pub end: PositionV2,
87}
88
89#[derive(Debug, Deserialize)]
90#[serde(rename_all = "camelCase")]
91pub struct BemSuffixInfoV2 {
92 pub raw_token_range: RangeV2,
93}
94
95#[derive(Debug, Deserialize)]
96#[serde(rename_all = "camelCase")]
97pub struct ClassExpressionInputV2 {
98 pub id: String,
99 pub kind: String,
100 pub scss_module_path: String,
101 pub range: RangeV2,
102 pub class_name: Option<String>,
103 pub root_binding_decl_id: Option<String>,
104 pub access_path: Option<Vec<String>>,
105}
106
107#[derive(Debug, Deserialize)]
108#[serde(rename_all = "camelCase")]
109pub struct StyleAnalysisInputV2 {
110 pub file_path: String,
111 pub document: StyleDocumentV2,
112}
113
114#[derive(Debug, Deserialize)]
115#[serde(rename_all = "camelCase")]
116pub struct StyleDocumentV2 {
117 pub selectors: Vec<StyleSelectorV2>,
118}
119
120#[derive(Debug, Deserialize)]
121#[serde(rename_all = "camelCase")]
122pub struct StyleSelectorV2 {
123 pub name: String,
124 pub view_kind: String,
125 pub canonical_name: Option<String>,
126 pub range: RangeV2,
127 pub nested_safety: Option<String>,
128 pub composes: Option<Vec<serde_json::Value>>,
129 pub bem_suffix: Option<BemSuffixInfoV2>,
130}
131
132#[derive(Debug, Deserialize)]
133#[serde(rename_all = "camelCase")]
134pub struct TypeFactEntryV2 {
135 pub file_path: String,
136 pub expression_id: String,
137 pub facts: StringTypeFactsV2,
138}
139
140#[derive(Debug, Deserialize)]
141#[serde(rename_all = "camelCase")]
142pub struct StringTypeFactsV2 {
143 pub kind: String,
144 pub constraint_kind: Option<String>,
145 pub values: Option<Vec<String>>,
146 pub prefix: Option<String>,
147 pub suffix: Option<String>,
148 pub min_len: Option<usize>,
149 pub max_len: Option<usize>,
150 pub char_must: Option<String>,
151 pub char_may: Option<String>,
152 pub may_include_other_chars: Option<bool>,
153}
154
155#[derive(Debug, Serialize)]
156#[serde(rename_all = "camelCase")]
157pub struct TypeFactInputSummaryV0 {
158 pub schema_version: &'static str,
159 pub input_version: String,
160 pub type_fact_count: usize,
161 pub distinct_fact_files: usize,
162 pub by_kind: BTreeMap<String, usize>,
163 pub constrained_kinds: BTreeMap<String, usize>,
164 pub finite_value_count: usize,
165}
166
167#[derive(Debug, Serialize)]
168#[serde(rename_all = "camelCase")]
169pub struct QueryPlanSummaryV0 {
170 schema_version: &'static str,
171 input_version: String,
172 expression_semantics_ids: Vec<String>,
173 source_expression_resolution_ids: Vec<String>,
174 selector_usage_ids: Vec<String>,
175 total_query_count: usize,
176}
177
178#[derive(Debug, Serialize, Clone)]
179#[serde(rename_all = "camelCase")]
180pub struct ExpressionDomainPlanSummaryV0 {
181 schema_version: &'static str,
182 input_version: String,
183 planned_expression_ids: Vec<String>,
184 value_domain_kinds: BTreeMap<String, usize>,
185 value_constraint_kinds: BTreeMap<String, usize>,
186 constraint_detail_counts: ConstraintDetailCounts,
187 finite_value_count: usize,
188}
189
190#[derive(Debug, Serialize, Clone)]
191#[serde(rename_all = "camelCase")]
192pub struct ExpressionDomainFragmentV0 {
193 pub expression_id: String,
194 pub file_path: String,
195 pub value_domain_kind: String,
196 #[serde(skip_serializing_if = "Option::is_none")]
197 pub value_constraint_kind: Option<String>,
198 #[serde(skip_serializing_if = "Option::is_none")]
199 pub value_prefix: Option<String>,
200 #[serde(skip_serializing_if = "Option::is_none")]
201 pub value_suffix: Option<String>,
202 #[serde(skip_serializing_if = "Option::is_none")]
203 pub value_min_len: Option<usize>,
204 #[serde(skip_serializing_if = "Option::is_none")]
205 pub value_max_len: Option<usize>,
206 #[serde(skip_serializing_if = "Option::is_none")]
207 pub value_char_must: Option<String>,
208 #[serde(skip_serializing_if = "Option::is_none")]
209 pub value_char_may: Option<String>,
210 #[serde(skip_serializing_if = "Option::is_none")]
211 pub value_may_include_other_chars: Option<bool>,
212 pub finite_value_count: usize,
213}
214
215#[derive(Debug, Serialize)]
216#[serde(rename_all = "camelCase")]
217pub struct ExpressionDomainFragmentsV0 {
218 pub schema_version: &'static str,
219 pub input_version: String,
220 pub fragments: Vec<ExpressionDomainFragmentV0>,
221}
222
223#[derive(Debug, Serialize, Clone)]
224#[serde(rename_all = "camelCase")]
225pub struct ExpressionDomainCandidateV0 {
226 pub expression_id: String,
227 pub file_path: String,
228 pub value_domain_kind: String,
229 #[serde(skip_serializing_if = "Option::is_none")]
230 pub value_constraint_kind: Option<String>,
231 #[serde(skip_serializing_if = "Option::is_none")]
232 pub value_prefix: Option<String>,
233 #[serde(skip_serializing_if = "Option::is_none")]
234 pub value_suffix: Option<String>,
235 #[serde(skip_serializing_if = "Option::is_none")]
236 pub value_min_len: Option<usize>,
237 #[serde(skip_serializing_if = "Option::is_none")]
238 pub value_max_len: Option<usize>,
239 #[serde(skip_serializing_if = "Option::is_none")]
240 pub value_char_must: Option<String>,
241 #[serde(skip_serializing_if = "Option::is_none")]
242 pub value_char_may: Option<String>,
243 #[serde(skip_serializing_if = "Option::is_none")]
244 pub value_may_include_other_chars: Option<bool>,
245 pub finite_value_count: usize,
246}
247
248#[derive(Debug, Serialize)]
249#[serde(rename_all = "camelCase")]
250pub struct ExpressionDomainCandidatesV0 {
251 pub schema_version: &'static str,
252 pub input_version: String,
253 pub candidates: Vec<ExpressionDomainCandidateV0>,
254}
255
256#[derive(Debug, Serialize)]
257#[serde(rename_all = "camelCase")]
258pub struct ExpressionDomainCanonicalCandidateBundleV0 {
259 pub schema_version: &'static str,
260 pub input_version: String,
261 pub plan_summary: ExpressionDomainPlanSummaryV0,
262 pub fragments: Vec<ExpressionDomainFragmentV0>,
263 pub candidates: Vec<ExpressionDomainCandidateV0>,
264}
265
266#[derive(Debug, Serialize)]
267#[serde(rename_all = "camelCase")]
268pub struct ExpressionDomainEvaluatorCandidatePayloadV0 {
269 pub expression_id: String,
270 pub value_domain_kind: String,
271 #[serde(skip_serializing_if = "Option::is_none")]
272 pub value_constraint_kind: Option<String>,
273 #[serde(skip_serializing_if = "Option::is_none")]
274 pub value_prefix: Option<String>,
275 #[serde(skip_serializing_if = "Option::is_none")]
276 pub value_suffix: Option<String>,
277 #[serde(skip_serializing_if = "Option::is_none")]
278 pub value_min_len: Option<usize>,
279 #[serde(skip_serializing_if = "Option::is_none")]
280 pub value_max_len: Option<usize>,
281 #[serde(skip_serializing_if = "Option::is_none")]
282 pub value_char_must: Option<String>,
283 #[serde(skip_serializing_if = "Option::is_none")]
284 pub value_char_may: Option<String>,
285 #[serde(skip_serializing_if = "Option::is_none")]
286 pub value_may_include_other_chars: Option<bool>,
287 pub finite_value_count: usize,
288 pub value_domain_derivation: omena_abstract_value::ReducedClassValueDerivationV0,
289}
290
291#[derive(Debug, Serialize)]
292#[serde(rename_all = "camelCase")]
293pub struct ExpressionDomainEvaluatorCandidateV0 {
294 pub kind: &'static str,
295 pub file_path: String,
296 pub query_id: String,
297 pub payload: ExpressionDomainEvaluatorCandidatePayloadV0,
298}
299
300#[derive(Debug, Serialize)]
301#[serde(rename_all = "camelCase")]
302pub struct ExpressionDomainEvaluatorCandidatesV0 {
303 pub schema_version: &'static str,
304 pub input_version: String,
305 pub results: Vec<ExpressionDomainEvaluatorCandidateV0>,
306}
307
308#[derive(Debug, Serialize)]
309#[serde(rename_all = "camelCase")]
310pub struct ExpressionDomainCanonicalProducerSignalV0 {
311 pub schema_version: &'static str,
312 pub input_version: String,
313 pub canonical_bundle: ExpressionDomainCanonicalCandidateBundleV0,
314 pub evaluator_candidates: ExpressionDomainEvaluatorCandidatesV0,
315}
316
317#[derive(Debug, Serialize)]
318#[serde(rename_all = "camelCase")]
319pub struct ExpressionDomainFlowAnalysisV0 {
320 pub schema_version: &'static str,
321 pub product: &'static str,
322 pub input_version: String,
323 pub analyses: Vec<ExpressionDomainFlowAnalysisEntryV0>,
324}
325
326#[derive(Debug, Serialize)]
327#[serde(rename_all = "camelCase")]
328pub struct ExpressionDomainFlowAnalysisEntryV0 {
329 pub graph_id: String,
330 pub file_path: String,
331 pub analysis: omena_abstract_value::ClassValueFlowAnalysisV0,
332}
333
334#[derive(Debug, Serialize)]
335#[serde(rename_all = "camelCase")]
336pub struct SelectorUsagePlanSummaryV0 {
337 schema_version: &'static str,
338 input_version: String,
339 canonical_selector_names: Vec<String>,
340 view_kind_counts: BTreeMap<String, usize>,
341 nested_safety_counts: BTreeMap<String, usize>,
342 composed_selector_count: usize,
343 total_composes_refs: usize,
344}
345
346#[derive(Debug, Clone, Serialize)]
347#[serde(rename_all = "camelCase")]
348pub struct SelectorUsageFragmentV0 {
349 pub ordinal: usize,
350 pub view_kind: String,
351 #[serde(skip_serializing_if = "Option::is_none")]
352 pub canonical_name: Option<String>,
353 #[serde(skip_serializing_if = "Option::is_none")]
354 pub nested_safety: Option<String>,
355 pub composes_count: usize,
356}
357
358#[derive(Debug, Serialize)]
359#[serde(rename_all = "camelCase")]
360pub struct SelectorUsageFragmentsV0 {
361 pub schema_version: &'static str,
362 pub input_version: String,
363 pub fragments: Vec<SelectorUsageFragmentV0>,
364}
365
366#[derive(Debug, Clone, Serialize)]
367#[serde(rename_all = "camelCase")]
368pub struct SelectorUsageQueryFragmentV0 {
369 pub query_id: String,
370 pub canonical_name: String,
371 #[serde(skip_serializing_if = "Option::is_none")]
372 pub nested_safety: Option<String>,
373 pub composes_count: usize,
374}
375
376#[derive(Debug, Serialize)]
377#[serde(rename_all = "camelCase")]
378pub struct SelectorUsageQueryFragmentsV0 {
379 pub schema_version: &'static str,
380 pub input_version: String,
381 pub fragments: Vec<SelectorUsageQueryFragmentV0>,
382}
383
384#[derive(Debug, Serialize, Clone)]
385#[serde(rename_all = "camelCase")]
386pub struct SelectorUsageCandidateV0 {
387 pub query_id: String,
388 pub canonical_name: String,
389 pub file_path: String,
390 pub total_references: usize,
391 pub direct_reference_count: usize,
392 pub editable_direct_reference_count: usize,
393 pub exact_reference_count: usize,
394 pub inferred_or_better_reference_count: usize,
395 pub has_expanded_references: bool,
396 pub has_style_dependency_references: bool,
397 pub has_any_references: bool,
398}
399
400#[derive(Debug, Serialize)]
401#[serde(rename_all = "camelCase")]
402pub struct SelectorUsageCandidatesV0 {
403 pub schema_version: &'static str,
404 pub input_version: String,
405 pub candidates: Vec<SelectorUsageCandidateV0>,
406}
407
408#[derive(Debug, Serialize, Clone)]
409#[serde(rename_all = "camelCase")]
410pub struct SelectorUsageEvaluatorCandidatePayloadV0 {
411 pub canonical_name: String,
412 pub total_references: usize,
413 pub direct_reference_count: usize,
414 pub editable_direct_reference_count: usize,
415 pub exact_reference_count: usize,
416 pub inferred_or_better_reference_count: usize,
417 pub has_expanded_references: bool,
418 pub has_style_dependency_references: bool,
419 pub has_any_references: bool,
420 pub all_sites: Vec<SelectorUsageReferenceSiteV0>,
421 pub editable_direct_sites: Vec<SelectorUsageEditableDirectSiteV0>,
422}
423
424#[derive(Debug, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
425#[serde(rename_all = "camelCase")]
426pub struct SelectorUsageReferenceSiteV0 {
427 pub file_path: String,
428 pub range: RangeV2,
429 pub expansion: String,
430 pub reference_kind: String,
431}
432
433#[derive(Debug, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
434#[serde(rename_all = "camelCase")]
435pub struct SelectorUsageEditableDirectSiteV0 {
436 pub file_path: String,
437 pub range: RangeV2,
438 pub class_name: String,
439}
440
441#[derive(Debug, Serialize, Clone)]
442#[serde(rename_all = "camelCase")]
443pub struct SelectorUsageEvaluatorCandidateV0 {
444 pub kind: &'static str,
445 pub file_path: String,
446 pub query_id: String,
447 pub payload: SelectorUsageEvaluatorCandidatePayloadV0,
448}
449
450#[derive(Debug, Serialize)]
451#[serde(rename_all = "camelCase")]
452pub struct SelectorUsageEvaluatorCandidatesV0 {
453 pub schema_version: &'static str,
454 pub input_version: String,
455 pub results: Vec<SelectorUsageEvaluatorCandidateV0>,
456}
457
458#[derive(Debug, Serialize)]
459#[serde(rename_all = "camelCase")]
460pub struct SelectorUsageCanonicalCandidateBundleV0 {
461 pub schema_version: &'static str,
462 pub input_version: String,
463 pub query_fragments: Vec<SelectorUsageQueryFragmentV0>,
464 pub fragments: Vec<SelectorUsageFragmentV0>,
465 pub candidates: Vec<SelectorUsageCandidateV0>,
466}
467
468#[derive(Debug, Serialize)]
469#[serde(rename_all = "camelCase")]
470pub struct SelectorUsageCanonicalProducerSignalV0 {
471 pub schema_version: &'static str,
472 pub input_version: String,
473 pub canonical_bundle: SelectorUsageCanonicalCandidateBundleV0,
474 pub evaluator_candidates: SelectorUsageEvaluatorCandidatesV0,
475}
476
477#[derive(Debug, Serialize)]
478#[serde(rename_all = "camelCase")]
479pub struct SourceResolutionPlanSummaryV0 {
480 schema_version: &'static str,
481 input_version: String,
482 planned_expression_ids: Vec<String>,
483 expression_kind_counts: BTreeMap<String, usize>,
484 distinct_style_file_paths: Vec<String>,
485 symbol_ref_with_binding_count: usize,
486 style_access_count: usize,
487 style_access_path_depth_sum: usize,
488}
489
490#[derive(Debug, Serialize, Clone)]
491#[serde(rename_all = "camelCase")]
492pub struct SourceResolutionQueryFragmentV0 {
493 pub query_id: String,
494 pub expression_id: String,
495 pub expression_kind: String,
496 pub style_file_path: String,
497}
498
499#[derive(Debug, Serialize)]
500#[serde(rename_all = "camelCase")]
501pub struct SourceResolutionQueryFragmentsV0 {
502 pub schema_version: &'static str,
503 pub input_version: String,
504 pub fragments: Vec<SourceResolutionQueryFragmentV0>,
505}
506
507#[derive(Debug, Serialize, Clone)]
508#[serde(rename_all = "camelCase")]
509pub struct SourceResolutionMatchFragmentV0 {
510 pub query_id: String,
511 pub expression_id: String,
512 pub style_file_path: String,
513 pub selector_names: Vec<String>,
514 #[serde(skip_serializing_if = "Option::is_none")]
515 pub finite_values: Option<Vec<String>>,
516}
517
518#[derive(Debug, Serialize)]
519#[serde(rename_all = "camelCase")]
520pub struct SourceResolutionMatchFragmentsV0 {
521 pub schema_version: &'static str,
522 pub input_version: String,
523 pub fragments: Vec<SourceResolutionMatchFragmentV0>,
524}
525
526#[derive(Debug, Serialize, Clone)]
527#[serde(rename_all = "camelCase")]
528pub struct SourceResolutionCandidateV0 {
529 pub query_id: String,
530 pub expression_id: String,
531 pub style_file_path: String,
532 pub selector_names: Vec<String>,
533 #[serde(skip_serializing_if = "Option::is_none")]
534 pub finite_values: Option<Vec<String>>,
535 pub selector_certainty: String,
536 #[serde(skip_serializing_if = "Option::is_none")]
537 pub value_certainty: Option<String>,
538 pub selector_certainty_shape_kind: String,
539 pub selector_certainty_shape_label: String,
540 pub value_certainty_shape_kind: String,
541 pub value_certainty_shape_label: String,
542 #[serde(skip_serializing_if = "Option::is_none")]
543 pub selector_constraint_kind: Option<String>,
544 #[serde(skip_serializing_if = "Option::is_none")]
545 pub value_certainty_constraint_kind: Option<String>,
546 #[serde(skip_serializing_if = "Option::is_none")]
547 pub value_prefix: Option<String>,
548 #[serde(skip_serializing_if = "Option::is_none")]
549 pub value_suffix: Option<String>,
550 #[serde(skip_serializing_if = "Option::is_none")]
551 pub value_min_len: Option<usize>,
552 #[serde(skip_serializing_if = "Option::is_none")]
553 pub value_max_len: Option<usize>,
554 #[serde(skip_serializing_if = "Option::is_none")]
555 pub value_char_must: Option<String>,
556 #[serde(skip_serializing_if = "Option::is_none")]
557 pub value_char_may: Option<String>,
558 #[serde(skip_serializing_if = "Option::is_none")]
559 pub value_may_include_other_chars: Option<bool>,
560}
561
562#[derive(Debug, Serialize)]
563#[serde(rename_all = "camelCase")]
564pub struct SourceResolutionCandidatesV0 {
565 pub schema_version: &'static str,
566 pub input_version: String,
567 pub candidates: Vec<SourceResolutionCandidateV0>,
568}
569
570#[derive(Debug, Serialize)]
571#[serde(rename_all = "camelCase")]
572pub struct SourceResolutionCanonicalCandidateBundleV0 {
573 pub schema_version: &'static str,
574 pub input_version: String,
575 pub query_fragments: Vec<SourceResolutionQueryFragmentV0>,
576 pub fragments: Vec<SourceResolutionFragmentV0>,
577 pub match_fragments: Vec<SourceResolutionMatchFragmentV0>,
578 pub candidates: Vec<SourceResolutionCandidateV0>,
579}
580
581#[derive(Debug, Serialize)]
582#[serde(rename_all = "camelCase")]
583pub struct SourceResolutionEvaluatorCandidatePayloadV0 {
584 pub expression_id: String,
585 pub style_file_path: String,
586 pub selector_names: Vec<String>,
587 #[serde(skip_serializing_if = "Option::is_none")]
588 pub finite_values: Option<Vec<String>>,
589 pub selector_certainty: String,
590 #[serde(skip_serializing_if = "Option::is_none")]
591 pub value_certainty: Option<String>,
592 pub selector_certainty_shape_kind: String,
593 pub selector_certainty_shape_label: String,
594 pub value_certainty_shape_kind: String,
595 pub value_certainty_shape_label: String,
596 #[serde(skip_serializing_if = "Option::is_none")]
597 pub selector_constraint_kind: Option<String>,
598 #[serde(skip_serializing_if = "Option::is_none")]
599 pub value_certainty_constraint_kind: Option<String>,
600 #[serde(skip_serializing_if = "Option::is_none")]
601 pub value_prefix: Option<String>,
602 #[serde(skip_serializing_if = "Option::is_none")]
603 pub value_suffix: Option<String>,
604 #[serde(skip_serializing_if = "Option::is_none")]
605 pub value_min_len: Option<usize>,
606 #[serde(skip_serializing_if = "Option::is_none")]
607 pub value_max_len: Option<usize>,
608 #[serde(skip_serializing_if = "Option::is_none")]
609 pub value_char_must: Option<String>,
610 #[serde(skip_serializing_if = "Option::is_none")]
611 pub value_char_may: Option<String>,
612 #[serde(skip_serializing_if = "Option::is_none")]
613 pub value_may_include_other_chars: Option<bool>,
614}
615
616#[derive(Debug, Serialize)]
617#[serde(rename_all = "camelCase")]
618pub struct SourceResolutionEvaluatorCandidateV0 {
619 pub kind: &'static str,
620 pub file_path: String,
621 pub query_id: String,
622 pub payload: SourceResolutionEvaluatorCandidatePayloadV0,
623}
624
625#[derive(Debug, Serialize)]
626#[serde(rename_all = "camelCase")]
627pub struct SourceResolutionEvaluatorCandidatesV0 {
628 pub schema_version: &'static str,
629 pub input_version: String,
630 pub results: Vec<SourceResolutionEvaluatorCandidateV0>,
631}
632
633#[derive(Debug, Serialize)]
634#[serde(rename_all = "camelCase")]
635pub struct SourceResolutionCanonicalProducerSignalV0 {
636 pub schema_version: &'static str,
637 pub input_version: String,
638 pub canonical_bundle: SourceResolutionCanonicalCandidateBundleV0,
639 pub evaluator_candidates: SourceResolutionEvaluatorCandidatesV0,
640}
641
642#[derive(Debug, Serialize)]
643#[serde(rename_all = "camelCase")]
644pub struct SourceSideCanonicalCandidateBundleV0 {
645 pub schema_version: &'static str,
646 pub input_version: String,
647 pub expression_semantics: ExpressionSemanticsCanonicalCandidateBundleV0,
648 pub source_resolution: SourceResolutionCanonicalCandidateBundleV0,
649}
650
651#[derive(Debug, Serialize)]
652#[serde(rename_all = "camelCase")]
653pub struct SourceSideEvaluatorCandidatesV0 {
654 pub schema_version: &'static str,
655 pub input_version: String,
656 pub expression_semantics: ExpressionSemanticsEvaluatorCandidatesV0,
657 pub source_resolution: SourceResolutionEvaluatorCandidatesV0,
658}
659
660#[derive(Debug, Serialize)]
661#[serde(rename_all = "camelCase")]
662pub struct SourceSideCanonicalProducerSignalV0 {
663 pub schema_version: &'static str,
664 pub input_version: String,
665 pub canonical_bundle: SourceSideCanonicalCandidateBundleV0,
666 pub evaluator_candidates: SourceSideEvaluatorCandidatesV0,
667}
668
669#[derive(Debug, Serialize)]
670#[serde(rename_all = "camelCase")]
671pub struct SemanticCanonicalCandidateBundleV0 {
672 pub schema_version: &'static str,
673 pub input_version: String,
674 pub source_side: SourceSideCanonicalCandidateBundleV0,
675 pub expression_domain: ExpressionDomainCanonicalCandidateBundleV0,
676}
677
678#[derive(Debug, Serialize)]
679#[serde(rename_all = "camelCase")]
680pub struct SemanticEvaluatorCandidatesV0 {
681 pub schema_version: &'static str,
682 pub input_version: String,
683 pub source_side: SourceSideEvaluatorCandidatesV0,
684 pub expression_domain: ExpressionDomainEvaluatorCandidatesV0,
685}
686
687#[derive(Debug, Serialize)]
688#[serde(rename_all = "camelCase")]
689pub struct SemanticCanonicalProducerSignalV0 {
690 pub schema_version: &'static str,
691 pub input_version: String,
692 pub canonical_bundle: SemanticCanonicalCandidateBundleV0,
693 pub evaluator_candidates: SemanticEvaluatorCandidatesV0,
694}
695
696#[derive(Debug, Serialize, Clone)]
697#[serde(rename_all = "camelCase")]
698pub struct ExpressionSemanticsFragmentV0 {
699 query_id: String,
700 expression_id: String,
701 expression_kind: String,
702 style_file_path: String,
703 value_domain_kind: String,
704 #[serde(skip_serializing_if = "Option::is_none")]
705 value_constraint_kind: Option<String>,
706 #[serde(skip_serializing_if = "Option::is_none")]
707 value_prefix: Option<String>,
708 #[serde(skip_serializing_if = "Option::is_none")]
709 value_suffix: Option<String>,
710 #[serde(skip_serializing_if = "Option::is_none")]
711 value_min_len: Option<usize>,
712 #[serde(skip_serializing_if = "Option::is_none")]
713 value_max_len: Option<usize>,
714 #[serde(skip_serializing_if = "Option::is_none")]
715 value_char_must: Option<String>,
716 #[serde(skip_serializing_if = "Option::is_none")]
717 value_char_may: Option<String>,
718 #[serde(skip_serializing_if = "Option::is_none")]
719 value_may_include_other_chars: Option<bool>,
720}
721
722#[derive(Debug, Serialize)]
723#[serde(rename_all = "camelCase")]
724pub struct ExpressionSemanticsFragmentsV0 {
725 schema_version: &'static str,
726 input_version: String,
727 fragments: Vec<ExpressionSemanticsFragmentV0>,
728}
729
730#[derive(Debug, Serialize, Clone)]
731#[serde(rename_all = "camelCase")]
732pub struct ExpressionSemanticsQueryFragmentV0 {
733 pub query_id: String,
734 pub expression_id: String,
735 pub expression_kind: String,
736 pub style_file_path: String,
737}
738
739#[derive(Debug, Serialize)]
740#[serde(rename_all = "camelCase")]
741pub struct ExpressionSemanticsQueryFragmentsV0 {
742 pub schema_version: &'static str,
743 pub input_version: String,
744 pub fragments: Vec<ExpressionSemanticsQueryFragmentV0>,
745}
746
747#[derive(Debug, Serialize, Clone)]
748#[serde(rename_all = "camelCase")]
749pub struct ExpressionSemanticsMatchFragmentV0 {
750 pub query_id: String,
751 pub expression_id: String,
752 pub style_file_path: String,
753 pub selector_names: Vec<String>,
754 pub candidate_names: Vec<String>,
755 #[serde(skip_serializing_if = "Option::is_none")]
756 pub finite_values: Option<Vec<String>>,
757}
758
759#[derive(Debug, Serialize)]
760#[serde(rename_all = "camelCase")]
761pub struct ExpressionSemanticsMatchFragmentsV0 {
762 pub schema_version: &'static str,
763 pub input_version: String,
764 pub fragments: Vec<ExpressionSemanticsMatchFragmentV0>,
765}
766
767#[derive(Debug, Serialize, Clone)]
768#[serde(rename_all = "camelCase")]
769pub struct ExpressionSemanticsCandidateV0 {
770 pub query_id: String,
771 pub expression_id: String,
772 pub expression_kind: String,
773 pub style_file_path: String,
774 pub selector_names: Vec<String>,
775 pub candidate_names: Vec<String>,
776 #[serde(skip_serializing_if = "Option::is_none")]
777 pub finite_values: Option<Vec<String>>,
778 pub value_domain_kind: String,
779 pub selector_certainty: String,
780 #[serde(skip_serializing_if = "Option::is_none")]
781 pub value_certainty: Option<String>,
782 pub selector_certainty_shape_kind: String,
783 pub selector_certainty_shape_label: String,
784 pub value_certainty_shape_kind: String,
785 pub value_certainty_shape_label: String,
786 #[serde(skip_serializing_if = "Option::is_none")]
787 pub selector_constraint_kind: Option<String>,
788 #[serde(skip_serializing_if = "Option::is_none")]
789 pub value_certainty_constraint_kind: Option<String>,
790 #[serde(skip_serializing_if = "Option::is_none")]
791 pub value_constraint_kind: Option<String>,
792 #[serde(skip_serializing_if = "Option::is_none")]
793 pub value_prefix: Option<String>,
794 #[serde(skip_serializing_if = "Option::is_none")]
795 pub value_suffix: Option<String>,
796 #[serde(skip_serializing_if = "Option::is_none")]
797 pub value_min_len: Option<usize>,
798 #[serde(skip_serializing_if = "Option::is_none")]
799 pub value_max_len: Option<usize>,
800 #[serde(skip_serializing_if = "Option::is_none")]
801 pub value_char_must: Option<String>,
802 #[serde(skip_serializing_if = "Option::is_none")]
803 pub value_char_may: Option<String>,
804 #[serde(skip_serializing_if = "Option::is_none")]
805 pub value_may_include_other_chars: Option<bool>,
806}
807
808#[derive(Debug, Serialize)]
809#[serde(rename_all = "camelCase")]
810pub struct ExpressionSemanticsCandidatesV0 {
811 pub schema_version: &'static str,
812 pub input_version: String,
813 pub candidates: Vec<ExpressionSemanticsCandidateV0>,
814}
815
816#[derive(Debug, Serialize)]
817#[serde(rename_all = "camelCase")]
818pub struct ExpressionSemanticsCanonicalCandidateBundleV0 {
819 pub schema_version: &'static str,
820 pub input_version: String,
821 pub query_fragments: Vec<ExpressionSemanticsQueryFragmentV0>,
822 pub fragments: Vec<ExpressionSemanticsFragmentV0>,
823 pub match_fragments: Vec<ExpressionSemanticsMatchFragmentV0>,
824 pub candidates: Vec<ExpressionSemanticsCandidateV0>,
825}
826
827#[derive(Debug, Serialize)]
828#[serde(rename_all = "camelCase")]
829pub struct ExpressionSemanticsEvaluatorCandidatePayloadV0 {
830 pub expression_id: String,
831 pub expression_kind: String,
832 pub style_file_path: String,
833 pub selector_names: Vec<String>,
834 pub candidate_names: Vec<String>,
835 #[serde(skip_serializing_if = "Option::is_none")]
836 pub finite_values: Option<Vec<String>>,
837 pub value_domain_kind: String,
838 pub selector_certainty: String,
839 #[serde(skip_serializing_if = "Option::is_none")]
840 pub value_certainty: Option<String>,
841 pub selector_certainty_shape_kind: String,
842 pub selector_certainty_shape_label: String,
843 pub value_certainty_shape_kind: String,
844 pub value_certainty_shape_label: String,
845 #[serde(skip_serializing_if = "Option::is_none")]
846 pub selector_constraint_kind: Option<String>,
847 #[serde(skip_serializing_if = "Option::is_none")]
848 pub value_certainty_constraint_kind: Option<String>,
849 #[serde(skip_serializing_if = "Option::is_none")]
850 pub value_constraint_kind: Option<String>,
851 #[serde(skip_serializing_if = "Option::is_none")]
852 pub value_prefix: Option<String>,
853 #[serde(skip_serializing_if = "Option::is_none")]
854 pub value_suffix: Option<String>,
855 #[serde(skip_serializing_if = "Option::is_none")]
856 pub value_min_len: Option<usize>,
857 #[serde(skip_serializing_if = "Option::is_none")]
858 pub value_max_len: Option<usize>,
859 #[serde(skip_serializing_if = "Option::is_none")]
860 pub value_char_must: Option<String>,
861 #[serde(skip_serializing_if = "Option::is_none")]
862 pub value_char_may: Option<String>,
863 #[serde(skip_serializing_if = "Option::is_none")]
864 pub value_may_include_other_chars: Option<bool>,
865 pub value_domain_derivation: omena_abstract_value::ReducedClassValueDerivationV0,
866}
867
868#[derive(Debug, Serialize)]
869#[serde(rename_all = "camelCase")]
870pub struct ExpressionSemanticsEvaluatorCandidateV0 {
871 pub kind: &'static str,
872 pub file_path: String,
873 pub query_id: String,
874 pub payload: ExpressionSemanticsEvaluatorCandidatePayloadV0,
875}
876
877#[derive(Debug, Serialize)]
878#[serde(rename_all = "camelCase")]
879pub struct ExpressionSemanticsEvaluatorCandidatesV0 {
880 pub schema_version: &'static str,
881 pub input_version: String,
882 pub results: Vec<ExpressionSemanticsEvaluatorCandidateV0>,
883}
884
885#[derive(Debug, Serialize)]
886#[serde(rename_all = "camelCase")]
887pub struct ExpressionSemanticsCanonicalProducerSignalV0 {
888 pub schema_version: &'static str,
889 pub input_version: String,
890 pub canonical_bundle: ExpressionSemanticsCanonicalCandidateBundleV0,
891 pub evaluator_candidates: ExpressionSemanticsEvaluatorCandidatesV0,
892}
893
894#[derive(Debug, Serialize, Clone)]
895#[serde(rename_all = "camelCase")]
896pub struct SourceResolutionFragmentV0 {
897 query_id: String,
898 expression_id: String,
899 style_file_path: String,
900 value_certainty_shape_kind: String,
901 #[serde(skip_serializing_if = "Option::is_none")]
902 value_certainty_constraint_kind: Option<String>,
903 #[serde(skip_serializing_if = "Option::is_none")]
904 value_prefix: Option<String>,
905 #[serde(skip_serializing_if = "Option::is_none")]
906 value_suffix: Option<String>,
907 #[serde(skip_serializing_if = "Option::is_none")]
908 value_min_len: Option<usize>,
909 #[serde(skip_serializing_if = "Option::is_none")]
910 value_max_len: Option<usize>,
911 #[serde(skip_serializing_if = "Option::is_none")]
912 value_char_must: Option<String>,
913 #[serde(skip_serializing_if = "Option::is_none")]
914 value_char_may: Option<String>,
915 #[serde(skip_serializing_if = "Option::is_none")]
916 value_may_include_other_chars: Option<bool>,
917}
918
919#[derive(Debug, Serialize)]
920#[serde(rename_all = "camelCase")]
921pub struct SourceResolutionFragmentsV0 {
922 schema_version: &'static str,
923 input_version: String,
924 fragments: Vec<SourceResolutionFragmentV0>,
925}
926
927#[derive(Debug, Serialize, Default, Clone)]
928#[serde(rename_all = "camelCase")]
929pub struct ConstraintDetailCounts {
930 pub prefix_count: usize,
931 pub suffix_count: usize,
932 pub min_len_count: usize,
933 pub min_len_sum: usize,
934 pub max_len_count: usize,
935 pub max_len_sum: usize,
936 pub char_must_count: usize,
937 pub char_must_len_sum: usize,
938 pub char_may_count: usize,
939 pub char_may_len_sum: usize,
940 pub may_include_other_chars_count: usize,
941}
942
943fn collect_constraint_detail_counts(
944 counts: &mut ConstraintDetailCounts,
945 details: ConstraintDetailInput<'_>,
946) {
947 if details.prefix.is_some() {
948 counts.prefix_count += 1;
949 }
950 if details.suffix.is_some() {
951 counts.suffix_count += 1;
952 }
953 if let Some(value) = details.min_len {
954 counts.min_len_count += 1;
955 counts.min_len_sum += value;
956 }
957 if let Some(value) = details.max_len {
958 counts.max_len_count += 1;
959 counts.max_len_sum += value;
960 }
961 if let Some(value) = details.char_must {
962 counts.char_must_count += 1;
963 counts.char_must_len_sum += value.len();
964 }
965 if let Some(value) = details.char_may {
966 counts.char_may_count += 1;
967 counts.char_may_len_sum += value.len();
968 }
969 if details.may_include_other_chars == Some(true) {
970 counts.may_include_other_chars_count += 1;
971 }
972}
973
974pub(crate) struct ConstraintDetailInput<'a> {
975 pub(crate) prefix: Option<&'a String>,
976 pub(crate) suffix: Option<&'a String>,
977 pub(crate) min_len: Option<usize>,
978 pub(crate) max_len: Option<usize>,
979 pub(crate) char_must: Option<&'a String>,
980 pub(crate) char_may: Option<&'a String>,
981 pub(crate) may_include_other_chars: Option<bool>,
982}
983
984pub(crate) fn map_expression_value_domain_kind(facts: &StringTypeFactsV2) -> String {
985 omena_abstract_value::expression_value_domain_kind_from_facts(&abstract_value_facts(facts))
986}
987
988pub(crate) fn map_reduced_expression_value_domain_kind(facts: &StringTypeFactsV2) -> String {
989 omena_abstract_value::reduced_value_domain_kind_from_facts(&abstract_value_facts(facts))
990 .to_string()
991}
992
993pub(crate) fn map_reduced_expression_value_domain_derivation(
994 facts: &StringTypeFactsV2,
995) -> omena_abstract_value::ReducedClassValueDerivationV0 {
996 omena_abstract_value::reduced_class_value_derivation_from_facts(&abstract_value_facts(facts))
997}
998
999pub(crate) fn map_value_certainty(facts: &StringTypeFactsV2) -> Option<String> {
1000 omena_abstract_value::value_certainty_from_facts(&abstract_value_facts(facts))
1001 .map(str::to_string)
1002}
1003
1004pub(crate) fn map_value_certainty_shape_kind(facts: &StringTypeFactsV2) -> String {
1005 omena_abstract_value::value_certainty_shape_kind_from_facts(&abstract_value_facts(facts))
1006 .to_string()
1007}
1008
1009pub(crate) fn map_value_certainty_shape_label(facts: &StringTypeFactsV2) -> String {
1010 omena_abstract_value::value_certainty_shape_label_from_facts(&abstract_value_facts(facts))
1011}
1012
1013pub(crate) fn map_selector_certainty_shape_kind(
1014 facts: &StringTypeFactsV2,
1015 matched_selector_count: usize,
1016 selector_universe_count: usize,
1017) -> String {
1018 omena_abstract_value::selector_certainty_shape_kind_from_facts(
1019 &abstract_value_facts(facts),
1020 matched_selector_count,
1021 selector_universe_count,
1022 )
1023 .to_string()
1024}
1025
1026pub(crate) fn map_selector_certainty_shape_label(
1027 facts: &StringTypeFactsV2,
1028 matched_selector_count: usize,
1029 selector_universe_count: usize,
1030) -> String {
1031 omena_abstract_value::selector_certainty_shape_label_from_facts(
1032 &abstract_value_facts(facts),
1033 matched_selector_count,
1034 selector_universe_count,
1035 )
1036}
1037
1038pub(crate) fn map_selector_certainty(
1039 facts: &StringTypeFactsV2,
1040 matched_selector_count: usize,
1041 selector_universe_count: usize,
1042) -> String {
1043 omena_abstract_value::selector_certainty_from_facts(
1044 &abstract_value_facts(facts),
1045 matched_selector_count,
1046 selector_universe_count,
1047 )
1048 .to_string()
1049}
1050
1051pub(crate) fn finite_values_for_facts(facts: &StringTypeFactsV2) -> Option<Vec<String>> {
1052 omena_abstract_value::finite_values_from_facts(&abstract_value_facts(facts))
1053}
1054
1055pub(crate) fn abstract_value_facts(
1056 facts: &StringTypeFactsV2,
1057) -> omena_abstract_value::ExternalStringTypeFactsV0 {
1058 omena_abstract_value::ExternalStringTypeFactsV0 {
1059 kind: facts.kind.clone(),
1060 constraint_kind: facts.constraint_kind.clone(),
1061 values: facts.values.clone(),
1062 prefix: facts.prefix.clone(),
1063 suffix: facts.suffix.clone(),
1064 min_len: facts.min_len,
1065 max_len: facts.max_len,
1066 char_must: facts.char_must.clone(),
1067 char_may: facts.char_may.clone(),
1068 may_include_other_chars: facts.may_include_other_chars,
1069 }
1070}
1071
1072pub(crate) fn resolve_selector_names(
1073 style: &StyleAnalysisInputV2,
1074 facts: &StringTypeFactsV2,
1075) -> Vec<String> {
1076 match facts.kind.as_str() {
1077 "unknown" => Vec::new(),
1078 "top" => canonical_selector_names(style),
1079 "exact" | "finiteSet" => {
1080 let mut names = Vec::new();
1081 for value in facts.values.as_ref().into_iter().flatten() {
1082 push_canonical_match(style, value, &mut names);
1083 }
1084 names
1085 }
1086 "constrained" => resolve_constrained_selector_names(style, facts),
1087 _ => Vec::new(),
1088 }
1089}
1090
1091fn resolve_constrained_selector_names(
1092 style: &StyleAnalysisInputV2,
1093 facts: &StringTypeFactsV2,
1094) -> Vec<String> {
1095 let mut names = Vec::new();
1096
1097 for selector in &style.document.selectors {
1098 if !matches_selector_constraints(selector, facts) {
1099 continue;
1100 }
1101 let canonical_name = canonical_name_for_selector(style, selector);
1102 if let Some(canonical_name) = canonical_name
1103 && !names.contains(&canonical_name)
1104 {
1105 names.push(canonical_name);
1106 }
1107 }
1108
1109 names
1110}
1111
1112fn matches_selector_constraints(selector: &StyleSelectorV2, facts: &StringTypeFactsV2) -> bool {
1113 match facts.constraint_kind.as_deref() {
1114 Some("prefix") => facts
1115 .prefix
1116 .as_ref()
1117 .is_some_and(|prefix| selector.name.starts_with(prefix)),
1118 Some("suffix") => facts
1119 .suffix
1120 .as_ref()
1121 .is_some_and(|suffix| selector.name.ends_with(suffix)),
1122 Some("prefixSuffix") => {
1123 let prefix_ok = facts
1124 .prefix
1125 .as_ref()
1126 .is_none_or(|prefix| selector.name.starts_with(prefix));
1127 let suffix_ok = facts
1128 .suffix
1129 .as_ref()
1130 .is_none_or(|suffix| selector.name.ends_with(suffix));
1131 let min_len_ok = facts
1132 .min_len
1133 .is_none_or(|min_len| selector.name.len() >= min_len);
1134 let max_len_ok = facts
1135 .max_len
1136 .is_none_or(|max_len| selector.name.len() <= max_len);
1137 prefix_ok && suffix_ok && min_len_ok && max_len_ok
1138 }
1139 Some("charInclusion") => matches_char_constraints(
1140 &selector.name,
1141 facts.char_must.as_deref().unwrap_or(""),
1142 facts.char_may.as_deref().unwrap_or(""),
1143 facts.may_include_other_chars.unwrap_or(false),
1144 ),
1145 Some("composite") => {
1146 let prefix_ok = facts
1147 .prefix
1148 .as_ref()
1149 .is_none_or(|prefix| selector.name.starts_with(prefix));
1150 let suffix_ok = facts
1151 .suffix
1152 .as_ref()
1153 .is_none_or(|suffix| selector.name.ends_with(suffix));
1154 let min_len_ok = facts
1155 .min_len
1156 .is_none_or(|min_len| selector.name.len() >= min_len);
1157 let max_len_ok = facts
1158 .max_len
1159 .is_none_or(|max_len| selector.name.len() <= max_len);
1160 prefix_ok
1161 && suffix_ok
1162 && min_len_ok
1163 && max_len_ok
1164 && matches_char_constraints(
1165 &selector.name,
1166 facts.char_must.as_deref().unwrap_or(""),
1167 facts.char_may.as_deref().unwrap_or(""),
1168 facts.may_include_other_chars.unwrap_or(false),
1169 )
1170 }
1171 _ => false,
1172 }
1173}
1174
1175fn matches_char_constraints(
1176 value: &str,
1177 must_chars: &str,
1178 may_chars: &str,
1179 may_include_other_chars: bool,
1180) -> bool {
1181 let value_chars: std::collections::BTreeSet<char> = value.chars().collect();
1182 let must_set: std::collections::BTreeSet<char> = must_chars.chars().collect();
1183 let may_set: std::collections::BTreeSet<char> = may_chars.chars().collect();
1184
1185 if must_set.iter().any(|char| !value_chars.contains(char)) {
1186 return false;
1187 }
1188 if !may_include_other_chars && value_chars.iter().any(|char| !may_set.contains(char)) {
1189 return false;
1190 }
1191 true
1192}
1193
1194fn push_canonical_match(style: &StyleAnalysisInputV2, view_name: &str, names: &mut Vec<String>) {
1195 if let Some(canonical_name) = canonical_name_for_view_name(style, view_name)
1196 && !names.contains(&canonical_name)
1197 {
1198 names.push(canonical_name);
1199 }
1200}
1201
1202fn canonical_selector_names(style: &StyleAnalysisInputV2) -> Vec<String> {
1203 let mut names = Vec::new();
1204 for selector in &style.document.selectors {
1205 if selector.view_kind == "canonical"
1206 && let Some(canonical_name) = selector.canonical_name.clone()
1207 && !names.contains(&canonical_name)
1208 {
1209 names.push(canonical_name);
1210 }
1211 }
1212 names
1213}
1214
1215pub(crate) fn canonical_selector_count(style: &StyleAnalysisInputV2) -> usize {
1216 canonical_selector_names(style).len()
1217}
1218
1219fn canonical_name_for_selector(
1220 style: &StyleAnalysisInputV2,
1221 selector: &StyleSelectorV2,
1222) -> Option<String> {
1223 canonical_name_for_view_name(style, &selector.name)
1224}
1225
1226fn canonical_name_for_view_name(style: &StyleAnalysisInputV2, view_name: &str) -> Option<String> {
1227 let matched = style
1228 .document
1229 .selectors
1230 .iter()
1231 .find(|selector| selector.name == view_name)?;
1232 let canonical = style.document.selectors.iter().find(|selector| {
1233 selector.view_kind == "canonical" && selector.canonical_name == matched.canonical_name
1234 });
1235 canonical
1236 .and_then(|selector| selector.canonical_name.clone())
1237 .or_else(|| matched.canonical_name.clone())
1238 .or_else(|| Some(matched.name.clone()))
1239}