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