Skip to main content

engine_input_producers/
lib.rs

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