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