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 pub value_domain_derivation: omena_abstract_value::ReducedClassValueDerivationV0,
848}
849
850#[derive(Debug, Serialize)]
851#[serde(rename_all = "camelCase")]
852pub struct ExpressionSemanticsEvaluatorCandidateV0 {
853 pub kind: &'static str,
854 pub file_path: String,
855 pub query_id: String,
856 pub payload: ExpressionSemanticsEvaluatorCandidatePayloadV0,
857}
858
859#[derive(Debug, Serialize)]
860#[serde(rename_all = "camelCase")]
861pub struct ExpressionSemanticsEvaluatorCandidatesV0 {
862 pub schema_version: &'static str,
863 pub input_version: String,
864 pub results: Vec<ExpressionSemanticsEvaluatorCandidateV0>,
865}
866
867#[derive(Debug, Serialize)]
868#[serde(rename_all = "camelCase")]
869pub struct ExpressionSemanticsCanonicalProducerSignalV0 {
870 pub schema_version: &'static str,
871 pub input_version: String,
872 pub canonical_bundle: ExpressionSemanticsCanonicalCandidateBundleV0,
873 pub evaluator_candidates: ExpressionSemanticsEvaluatorCandidatesV0,
874}
875
876#[derive(Debug, Serialize, Clone)]
877#[serde(rename_all = "camelCase")]
878pub struct SourceResolutionFragmentV0 {
879 query_id: String,
880 expression_id: String,
881 style_file_path: String,
882 value_certainty_shape_kind: String,
883 #[serde(skip_serializing_if = "Option::is_none")]
884 value_certainty_constraint_kind: Option<String>,
885 #[serde(skip_serializing_if = "Option::is_none")]
886 value_prefix: Option<String>,
887 #[serde(skip_serializing_if = "Option::is_none")]
888 value_suffix: Option<String>,
889 #[serde(skip_serializing_if = "Option::is_none")]
890 value_min_len: Option<usize>,
891 #[serde(skip_serializing_if = "Option::is_none")]
892 value_max_len: Option<usize>,
893 #[serde(skip_serializing_if = "Option::is_none")]
894 value_char_must: Option<String>,
895 #[serde(skip_serializing_if = "Option::is_none")]
896 value_char_may: Option<String>,
897 #[serde(skip_serializing_if = "Option::is_none")]
898 value_may_include_other_chars: Option<bool>,
899}
900
901#[derive(Debug, Serialize)]
902#[serde(rename_all = "camelCase")]
903pub struct SourceResolutionFragmentsV0 {
904 schema_version: &'static str,
905 input_version: String,
906 fragments: Vec<SourceResolutionFragmentV0>,
907}
908
909#[derive(Debug, Serialize, Default, Clone)]
910#[serde(rename_all = "camelCase")]
911pub struct ConstraintDetailCounts {
912 pub prefix_count: usize,
913 pub suffix_count: usize,
914 pub min_len_count: usize,
915 pub min_len_sum: usize,
916 pub max_len_count: usize,
917 pub max_len_sum: usize,
918 pub char_must_count: usize,
919 pub char_must_len_sum: usize,
920 pub char_may_count: usize,
921 pub char_may_len_sum: usize,
922 pub may_include_other_chars_count: usize,
923}
924
925fn collect_constraint_detail_counts(
926 counts: &mut ConstraintDetailCounts,
927 details: ConstraintDetailInput<'_>,
928) {
929 if details.prefix.is_some() {
930 counts.prefix_count += 1;
931 }
932 if details.suffix.is_some() {
933 counts.suffix_count += 1;
934 }
935 if let Some(value) = details.min_len {
936 counts.min_len_count += 1;
937 counts.min_len_sum += value;
938 }
939 if let Some(value) = details.max_len {
940 counts.max_len_count += 1;
941 counts.max_len_sum += value;
942 }
943 if let Some(value) = details.char_must {
944 counts.char_must_count += 1;
945 counts.char_must_len_sum += value.len();
946 }
947 if let Some(value) = details.char_may {
948 counts.char_may_count += 1;
949 counts.char_may_len_sum += value.len();
950 }
951 if details.may_include_other_chars == Some(true) {
952 counts.may_include_other_chars_count += 1;
953 }
954}
955
956pub(crate) struct ConstraintDetailInput<'a> {
957 pub(crate) prefix: Option<&'a String>,
958 pub(crate) suffix: Option<&'a String>,
959 pub(crate) min_len: Option<usize>,
960 pub(crate) max_len: Option<usize>,
961 pub(crate) char_must: Option<&'a String>,
962 pub(crate) char_may: Option<&'a String>,
963 pub(crate) may_include_other_chars: Option<bool>,
964}
965
966pub(crate) fn map_expression_value_domain_kind(facts: &StringTypeFactsV2) -> String {
967 omena_abstract_value::expression_value_domain_kind_from_facts(&abstract_value_facts(facts))
968}
969
970pub(crate) fn map_reduced_expression_value_domain_kind(facts: &StringTypeFactsV2) -> String {
971 omena_abstract_value::reduced_value_domain_kind_from_facts(&abstract_value_facts(facts))
972 .to_string()
973}
974
975pub(crate) fn map_reduced_expression_value_domain_derivation(
976 facts: &StringTypeFactsV2,
977) -> omena_abstract_value::ReducedClassValueDerivationV0 {
978 omena_abstract_value::reduced_class_value_derivation_from_facts(&abstract_value_facts(facts))
979}
980
981pub(crate) fn map_value_certainty(facts: &StringTypeFactsV2) -> Option<String> {
982 omena_abstract_value::value_certainty_from_facts(&abstract_value_facts(facts))
983 .map(str::to_string)
984}
985
986pub(crate) fn map_value_certainty_shape_kind(facts: &StringTypeFactsV2) -> String {
987 omena_abstract_value::value_certainty_shape_kind_from_facts(&abstract_value_facts(facts))
988 .to_string()
989}
990
991pub(crate) fn map_value_certainty_shape_label(facts: &StringTypeFactsV2) -> String {
992 omena_abstract_value::value_certainty_shape_label_from_facts(&abstract_value_facts(facts))
993}
994
995pub(crate) fn map_selector_certainty_shape_kind(
996 facts: &StringTypeFactsV2,
997 matched_selector_count: usize,
998 selector_universe_count: usize,
999) -> String {
1000 omena_abstract_value::selector_certainty_shape_kind_from_facts(
1001 &abstract_value_facts(facts),
1002 matched_selector_count,
1003 selector_universe_count,
1004 )
1005 .to_string()
1006}
1007
1008pub(crate) fn map_selector_certainty_shape_label(
1009 facts: &StringTypeFactsV2,
1010 matched_selector_count: usize,
1011 selector_universe_count: usize,
1012) -> String {
1013 omena_abstract_value::selector_certainty_shape_label_from_facts(
1014 &abstract_value_facts(facts),
1015 matched_selector_count,
1016 selector_universe_count,
1017 )
1018}
1019
1020pub(crate) fn map_selector_certainty(
1021 facts: &StringTypeFactsV2,
1022 matched_selector_count: usize,
1023 selector_universe_count: usize,
1024) -> String {
1025 omena_abstract_value::selector_certainty_from_facts(
1026 &abstract_value_facts(facts),
1027 matched_selector_count,
1028 selector_universe_count,
1029 )
1030 .to_string()
1031}
1032
1033pub(crate) fn finite_values_for_facts(facts: &StringTypeFactsV2) -> Option<Vec<String>> {
1034 omena_abstract_value::finite_values_from_facts(&abstract_value_facts(facts))
1035}
1036
1037fn abstract_value_facts(
1038 facts: &StringTypeFactsV2,
1039) -> omena_abstract_value::ExternalStringTypeFactsV0 {
1040 omena_abstract_value::ExternalStringTypeFactsV0 {
1041 kind: facts.kind.clone(),
1042 constraint_kind: facts.constraint_kind.clone(),
1043 values: facts.values.clone(),
1044 prefix: facts.prefix.clone(),
1045 suffix: facts.suffix.clone(),
1046 min_len: facts.min_len,
1047 max_len: facts.max_len,
1048 char_must: facts.char_must.clone(),
1049 char_may: facts.char_may.clone(),
1050 may_include_other_chars: facts.may_include_other_chars,
1051 }
1052}
1053
1054pub(crate) fn resolve_selector_names(
1055 style: &StyleAnalysisInputV2,
1056 facts: &StringTypeFactsV2,
1057) -> Vec<String> {
1058 match facts.kind.as_str() {
1059 "unknown" => Vec::new(),
1060 "top" => canonical_selector_names(style),
1061 "exact" | "finiteSet" => {
1062 let mut names = Vec::new();
1063 for value in facts.values.as_ref().into_iter().flatten() {
1064 push_canonical_match(style, value, &mut names);
1065 }
1066 names
1067 }
1068 "constrained" => resolve_constrained_selector_names(style, facts),
1069 _ => Vec::new(),
1070 }
1071}
1072
1073fn resolve_constrained_selector_names(
1074 style: &StyleAnalysisInputV2,
1075 facts: &StringTypeFactsV2,
1076) -> Vec<String> {
1077 let mut names = Vec::new();
1078
1079 for selector in &style.document.selectors {
1080 if !matches_selector_constraints(selector, facts) {
1081 continue;
1082 }
1083 let canonical_name = canonical_name_for_selector(style, selector);
1084 if let Some(canonical_name) = canonical_name
1085 && !names.contains(&canonical_name)
1086 {
1087 names.push(canonical_name);
1088 }
1089 }
1090
1091 names
1092}
1093
1094fn matches_selector_constraints(selector: &StyleSelectorV2, facts: &StringTypeFactsV2) -> bool {
1095 match facts.constraint_kind.as_deref() {
1096 Some("prefix") => facts
1097 .prefix
1098 .as_ref()
1099 .is_some_and(|prefix| selector.name.starts_with(prefix)),
1100 Some("suffix") => facts
1101 .suffix
1102 .as_ref()
1103 .is_some_and(|suffix| selector.name.ends_with(suffix)),
1104 Some("prefixSuffix") => {
1105 let prefix_ok = facts
1106 .prefix
1107 .as_ref()
1108 .is_none_or(|prefix| selector.name.starts_with(prefix));
1109 let suffix_ok = facts
1110 .suffix
1111 .as_ref()
1112 .is_none_or(|suffix| selector.name.ends_with(suffix));
1113 let min_len_ok = facts
1114 .min_len
1115 .is_none_or(|min_len| selector.name.len() >= min_len);
1116 let max_len_ok = facts
1117 .max_len
1118 .is_none_or(|max_len| selector.name.len() <= max_len);
1119 prefix_ok && suffix_ok && min_len_ok && max_len_ok
1120 }
1121 Some("charInclusion") => matches_char_constraints(
1122 &selector.name,
1123 facts.char_must.as_deref().unwrap_or(""),
1124 facts.char_may.as_deref().unwrap_or(""),
1125 facts.may_include_other_chars.unwrap_or(false),
1126 ),
1127 Some("composite") => {
1128 let prefix_ok = facts
1129 .prefix
1130 .as_ref()
1131 .is_none_or(|prefix| selector.name.starts_with(prefix));
1132 let suffix_ok = facts
1133 .suffix
1134 .as_ref()
1135 .is_none_or(|suffix| selector.name.ends_with(suffix));
1136 let min_len_ok = facts
1137 .min_len
1138 .is_none_or(|min_len| selector.name.len() >= min_len);
1139 let max_len_ok = facts
1140 .max_len
1141 .is_none_or(|max_len| selector.name.len() <= max_len);
1142 prefix_ok
1143 && suffix_ok
1144 && min_len_ok
1145 && max_len_ok
1146 && matches_char_constraints(
1147 &selector.name,
1148 facts.char_must.as_deref().unwrap_or(""),
1149 facts.char_may.as_deref().unwrap_or(""),
1150 facts.may_include_other_chars.unwrap_or(false),
1151 )
1152 }
1153 _ => false,
1154 }
1155}
1156
1157fn matches_char_constraints(
1158 value: &str,
1159 must_chars: &str,
1160 may_chars: &str,
1161 may_include_other_chars: bool,
1162) -> bool {
1163 let value_chars: std::collections::BTreeSet<char> = value.chars().collect();
1164 let must_set: std::collections::BTreeSet<char> = must_chars.chars().collect();
1165 let may_set: std::collections::BTreeSet<char> = may_chars.chars().collect();
1166
1167 if must_set.iter().any(|char| !value_chars.contains(char)) {
1168 return false;
1169 }
1170 if !may_include_other_chars && value_chars.iter().any(|char| !may_set.contains(char)) {
1171 return false;
1172 }
1173 true
1174}
1175
1176fn push_canonical_match(style: &StyleAnalysisInputV2, view_name: &str, names: &mut Vec<String>) {
1177 if let Some(canonical_name) = canonical_name_for_view_name(style, view_name)
1178 && !names.contains(&canonical_name)
1179 {
1180 names.push(canonical_name);
1181 }
1182}
1183
1184fn canonical_selector_names(style: &StyleAnalysisInputV2) -> Vec<String> {
1185 let mut names = Vec::new();
1186 for selector in &style.document.selectors {
1187 if selector.view_kind == "canonical"
1188 && let Some(canonical_name) = selector.canonical_name.clone()
1189 && !names.contains(&canonical_name)
1190 {
1191 names.push(canonical_name);
1192 }
1193 }
1194 names
1195}
1196
1197pub(crate) fn canonical_selector_count(style: &StyleAnalysisInputV2) -> usize {
1198 canonical_selector_names(style).len()
1199}
1200
1201fn canonical_name_for_selector(
1202 style: &StyleAnalysisInputV2,
1203 selector: &StyleSelectorV2,
1204) -> Option<String> {
1205 canonical_name_for_view_name(style, &selector.name)
1206}
1207
1208fn canonical_name_for_view_name(style: &StyleAnalysisInputV2, view_name: &str) -> Option<String> {
1209 let matched = style
1210 .document
1211 .selectors
1212 .iter()
1213 .find(|selector| selector.name == view_name)?;
1214 let canonical = style.document.selectors.iter().find(|selector| {
1215 selector.view_kind == "canonical" && selector.canonical_name == matched.canonical_name
1216 });
1217 canonical
1218 .and_then(|selector| selector.canonical_name.clone())
1219 .or_else(|| matched.canonical_name.clone())
1220 .or_else(|| Some(matched.name.clone()))
1221}