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