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