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}