use std::collections::{BTreeMap, BTreeSet};
use engine_input_producers::{
EngineInputV2, ExpressionSemanticsEvaluatorCandidatePayloadV0,
summarize_expression_semantics_evaluator_candidates_input,
summarize_selector_usage_evaluator_candidates_input,
};
use serde::Serialize;
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SourceInputPromotionEvidenceSummaryV0 {
pub schema_version: &'static str,
pub product: &'static str,
pub input_version: String,
pub reference_site_identity: ReferenceSiteIdentityEvidenceV0,
pub certainty_reason: CertaintyReasonEvidenceV0,
pub binding_origin: BindingOriginEvidenceV0,
pub style_module_edge: StyleModuleEdgeEvidenceV0,
pub value_domain_explanation: ValueDomainExplanationEvidenceV0,
pub blocking_gaps: Vec<&'static str>,
pub next_priorities: Vec<&'static str>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ReferenceSiteIdentityEvidenceV0 {
pub status: &'static str,
pub selector_count: usize,
pub reference_site_count: usize,
pub direct_reference_site_count: usize,
pub expanded_reference_site_count: usize,
pub style_dependency_reference_site_count: usize,
pub editable_direct_site_count: usize,
pub reference_kind_counts: BTreeMap<String, usize>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CertaintyReasonEvidenceV0 {
pub status: &'static str,
pub expression_count: usize,
pub exact_count: usize,
pub inferred_count: usize,
pub possible_count: usize,
pub missing_reason_count: usize,
pub reason_counts: BTreeMap<String, usize>,
pub shape_kind_counts: BTreeMap<String, usize>,
pub shape_label_counts: BTreeMap<String, usize>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BindingOriginEvidenceV0 {
pub status: &'static str,
pub expression_count: usize,
pub direct_class_name_count: usize,
pub root_binding_count: usize,
pub access_path_count: usize,
pub access_path_segment_count: usize,
pub expression_kind_counts: BTreeMap<String, usize>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StyleModuleEdgeEvidenceV0 {
pub status: &'static str,
pub source_style_edge_count: usize,
pub distinct_style_module_count: usize,
pub missing_style_document_edge_count: usize,
pub composed_edge_count: usize,
pub imported_composed_edge_count: usize,
pub global_composed_edge_count: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ValueDomainExplanationEvidenceV0 {
pub status: &'static str,
pub expression_count: usize,
pub exact_expression_count: usize,
pub finite_value_expression_count: usize,
pub constrained_expression_count: usize,
pub unknown_expression_count: usize,
pub finite_value_count: usize,
pub derivation_count: usize,
pub derivation_step_count: usize,
pub value_domain_kind_counts: BTreeMap<String, usize>,
pub constraint_kind_counts: BTreeMap<String, usize>,
pub derivation_product_counts: BTreeMap<String, usize>,
pub derivation_reduced_kind_counts: BTreeMap<String, usize>,
pub derivation_operation_counts: BTreeMap<String, usize>,
}
pub fn summarize_omena_bridge_source_input_evidence(
input: &EngineInputV2,
) -> SourceInputPromotionEvidenceSummaryV0 {
let reference_site_identity = summarize_reference_site_identity(input);
let certainty_reason = summarize_certainty_reason(input);
let binding_origin = summarize_binding_origin(input);
let style_module_edge = summarize_style_module_edge(input);
let value_domain_explanation = summarize_value_domain_explanation(input);
let mut blocking_gaps = Vec::new();
if reference_site_identity.status == "gap" {
blocking_gaps.push("referenceSiteIdentity");
}
if certainty_reason.status == "gap" {
blocking_gaps.push("certaintyReason");
}
if binding_origin.status == "gap" {
blocking_gaps.push("bindingOrigin");
}
if style_module_edge.status == "gap" {
blocking_gaps.push("styleModuleEdge");
}
if value_domain_explanation.status == "gap" {
blocking_gaps.push("valueDomainExplanation");
}
SourceInputPromotionEvidenceSummaryV0 {
schema_version: "0",
product: "omena-semantic.source-input-evidence",
input_version: input.version.clone(),
reference_site_identity,
certainty_reason,
binding_origin,
style_module_edge,
value_domain_explanation,
blocking_gaps,
next_priorities: Vec::new(),
}
}
fn summarize_reference_site_identity(input: &EngineInputV2) -> ReferenceSiteIdentityEvidenceV0 {
let selector_usage = summarize_selector_usage_evaluator_candidates_input(input);
let selector_count = selector_usage.results.len();
let mut reference_site_count = 0usize;
let mut direct_reference_site_count = 0usize;
let mut expanded_reference_site_count = 0usize;
let mut style_dependency_reference_site_count = 0usize;
let mut editable_direct_site_count = 0usize;
let mut reference_kind_counts = BTreeMap::new();
for result in selector_usage.results {
editable_direct_site_count += result.payload.editable_direct_sites.len();
for site in result.payload.all_sites {
reference_site_count += 1;
if site.expansion == "direct" {
direct_reference_site_count += 1;
} else {
expanded_reference_site_count += 1;
}
if site.reference_kind == "styleDependency" {
style_dependency_reference_site_count += 1;
}
*reference_kind_counts
.entry(site.reference_kind)
.or_insert(0) += 1;
}
}
ReferenceSiteIdentityEvidenceV0 {
status: if reference_site_count > 0 {
"ready"
} else {
"gap"
},
selector_count,
reference_site_count,
direct_reference_site_count,
expanded_reference_site_count,
style_dependency_reference_site_count,
editable_direct_site_count,
reference_kind_counts,
}
}
fn summarize_certainty_reason(input: &EngineInputV2) -> CertaintyReasonEvidenceV0 {
let expression_semantics = summarize_expression_semantics_evaluator_candidates_input(input);
let mut expression_count = 0usize;
let mut exact_count = 0usize;
let mut inferred_count = 0usize;
let mut possible_count = 0usize;
let mut missing_reason_count = 0usize;
let mut reason_counts = BTreeMap::new();
let mut shape_kind_counts = BTreeMap::new();
let mut shape_label_counts = BTreeMap::new();
for result in expression_semantics.results {
expression_count += 1;
let payload = result.payload;
match payload.selector_certainty.as_str() {
"exact" => exact_count += 1,
"inferred" => inferred_count += 1,
"possible" => possible_count += 1,
_ => {}
}
*shape_kind_counts
.entry(payload.selector_certainty_shape_kind.clone())
.or_insert(0) += 1;
*shape_label_counts
.entry(payload.selector_certainty_shape_label.clone())
.or_insert(0) += 1;
if let Some(reason) = selector_certainty_reason(&payload) {
*reason_counts.entry(reason).or_insert(0) += 1;
} else {
missing_reason_count += 1;
}
}
CertaintyReasonEvidenceV0 {
status: if expression_count == 0 {
"gap"
} else if missing_reason_count == 0 {
"ready"
} else {
"partial"
},
expression_count,
exact_count,
inferred_count,
possible_count,
missing_reason_count,
reason_counts,
shape_kind_counts,
shape_label_counts,
}
}
fn summarize_binding_origin(input: &EngineInputV2) -> BindingOriginEvidenceV0 {
let mut expression_count = 0usize;
let mut direct_class_name_count = 0usize;
let mut root_binding_count = 0usize;
let mut access_path_count = 0usize;
let mut access_path_segment_count = 0usize;
let mut expression_kind_counts = BTreeMap::new();
for source in &input.sources {
for expression in &source.document.class_expressions {
expression_count += 1;
*expression_kind_counts
.entry(expression.kind.clone())
.or_insert(0) += 1;
if expression.class_name.is_some() {
direct_class_name_count += 1;
}
if expression.root_binding_decl_id.is_some() {
root_binding_count += 1;
}
if let Some(access_path) = &expression.access_path {
access_path_count += 1;
access_path_segment_count += access_path.len();
}
}
}
BindingOriginEvidenceV0 {
status: if expression_count == 0 {
"gap"
} else if direct_class_name_count + root_binding_count + access_path_count > 0 {
"ready"
} else {
"partial"
},
expression_count,
direct_class_name_count,
root_binding_count,
access_path_count,
access_path_segment_count,
expression_kind_counts,
}
}
fn summarize_style_module_edge(input: &EngineInputV2) -> StyleModuleEdgeEvidenceV0 {
let style_paths = input
.styles
.iter()
.map(|style| style.file_path.clone())
.collect::<BTreeSet<_>>();
let mut referenced_style_paths = BTreeSet::new();
let mut source_style_edge_count = 0usize;
let mut missing_style_document_edge_count = 0usize;
let mut composed_edge_count = 0usize;
let mut imported_composed_edge_count = 0usize;
let mut global_composed_edge_count = 0usize;
for source in &input.sources {
for expression in &source.document.class_expressions {
source_style_edge_count += 1;
referenced_style_paths.insert(expression.scss_module_path.clone());
if !style_paths.contains(&expression.scss_module_path) {
missing_style_document_edge_count += 1;
}
}
}
for style in &input.styles {
for selector in &style.document.selectors {
let Some(composes) = &selector.composes else {
continue;
};
composed_edge_count += composes.len();
for compose in composes {
if compose
.get("fromGlobal")
.and_then(|value| value.as_bool())
.unwrap_or(false)
{
global_composed_edge_count += 1;
} else if compose
.get("from")
.and_then(|value| value.as_str())
.is_some()
{
imported_composed_edge_count += 1;
}
}
}
}
StyleModuleEdgeEvidenceV0 {
status: if source_style_edge_count == 0 {
"gap"
} else if missing_style_document_edge_count == 0 {
"ready"
} else {
"partial"
},
source_style_edge_count,
distinct_style_module_count: referenced_style_paths.len(),
missing_style_document_edge_count,
composed_edge_count,
imported_composed_edge_count,
global_composed_edge_count,
}
}
fn summarize_value_domain_explanation(input: &EngineInputV2) -> ValueDomainExplanationEvidenceV0 {
let expression_semantics = summarize_expression_semantics_evaluator_candidates_input(input);
let mut expression_count = 0usize;
let mut exact_expression_count = 0usize;
let mut finite_value_expression_count = 0usize;
let mut constrained_expression_count = 0usize;
let mut unknown_expression_count = 0usize;
let mut finite_value_count = 0usize;
let mut derivation_count = 0usize;
let mut derivation_step_count = 0usize;
let mut value_domain_kind_counts = BTreeMap::new();
let mut constraint_kind_counts = BTreeMap::new();
let mut derivation_product_counts = BTreeMap::new();
let mut derivation_reduced_kind_counts = BTreeMap::new();
let mut derivation_operation_counts = BTreeMap::new();
for result in expression_semantics.results {
expression_count += 1;
let payload = result.payload;
*value_domain_kind_counts
.entry(payload.value_domain_kind.clone())
.or_insert(0) += 1;
match payload.value_domain_kind.as_str() {
"exact" => exact_expression_count += 1,
"finiteSet" => finite_value_expression_count += 1,
"constrained" => constrained_expression_count += 1,
"none" | "unknown" | "top" => unknown_expression_count += 1,
_ => {}
}
if let Some(values) = &payload.finite_values {
finite_value_count += values.len();
}
if let Some(kind) = &payload.value_constraint_kind {
*constraint_kind_counts.entry(kind.clone()).or_insert(0) += 1;
}
derivation_count += 1;
let derivation = payload.value_domain_derivation;
*derivation_product_counts
.entry(derivation.product.to_string())
.or_insert(0) += 1;
*derivation_reduced_kind_counts
.entry(derivation.reduced_kind.to_string())
.or_insert(0) += 1;
for step in derivation.steps {
derivation_step_count += 1;
*derivation_operation_counts
.entry(step.operation.to_string())
.or_insert(0) += 1;
}
}
ValueDomainExplanationEvidenceV0 {
status: if expression_count == 0 {
"gap"
} else if exact_expression_count
+ finite_value_expression_count
+ constrained_expression_count
> 0
&& derivation_count == expression_count
{
"ready"
} else {
"partial"
},
expression_count,
exact_expression_count,
finite_value_expression_count,
constrained_expression_count,
unknown_expression_count,
finite_value_count,
derivation_count,
derivation_step_count,
value_domain_kind_counts,
constraint_kind_counts,
derivation_product_counts,
derivation_reduced_kind_counts,
derivation_operation_counts,
}
}
fn selector_certainty_reason(
payload: &ExpressionSemanticsEvaluatorCandidatePayloadV0,
) -> Option<String> {
match payload.selector_certainty.as_str() {
"exact" => {
if payload.selector_names.len() == 1 {
Some("single selector matched".to_string())
} else {
Some("selector set exactly matched the proven value domain".to_string())
}
}
"inferred" => match payload.selector_constraint_kind.as_deref() {
Some("prefix" | "suffix" | "prefixSuffix" | "charInclusion" | "composite") => {
Some("constrained runtime shape matched a bounded selector set".to_string())
}
_ => Some("finite candidate values matched a bounded selector set".to_string()),
},
"possible" => {
if payload.selector_names.is_empty() {
Some("no selector could be proven for this value".to_string())
} else {
Some("analysis could not prove an exact selector set".to_string())
}
}
_ => None,
}
}