use super::*;
use omena_query_checker_orchestrator::{
AbstractClassValueV0, OmenaQueryCheckerKLimitedFlowContextV0,
run_omena_query_checker_k_limited_flow_m_tier_gate_v0,
};
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(
tag = "kind",
rename_all = "camelCase",
rename_all_fields = "camelCase"
)]
pub enum OmenaQueryDynamicClassValueInputV0 {
Bottom,
Exact {
value: String,
},
FiniteSet {
values: Vec<String>,
},
Prefix {
prefix: String,
},
Suffix {
suffix: String,
},
PrefixSuffix {
prefix: String,
suffix: String,
#[serde(default)]
min_length: usize,
},
Top,
}
impl OmenaQueryDynamicClassValueInputV0 {
fn into_abstract_class_value(self) -> AbstractClassValueV0 {
match self {
Self::Bottom => AbstractClassValueV0::Bottom,
Self::Exact { value } => AbstractClassValueV0::Exact { value },
Self::FiniteSet { values } => AbstractClassValueV0::FiniteSet { values },
Self::Prefix { prefix } => AbstractClassValueV0::Prefix {
prefix,
provenance: None,
},
Self::Suffix { suffix } => AbstractClassValueV0::Suffix {
suffix,
provenance: None,
},
Self::PrefixSuffix {
prefix,
suffix,
min_length,
} => AbstractClassValueV0::PrefixSuffix {
prefix,
suffix,
min_length,
provenance: None,
},
Self::Top => AbstractClassValueV0::Top,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OmenaQueryDynamicClassnameCallSiteV0 {
pub callee_key: String,
pub call_site_stack: Vec<String>,
pub exit_value: OmenaQueryDynamicClassValueInputV0,
pub reference_range: ParserRangeV0,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OmenaQueryDynamicClassnameMTierInputV0 {
pub source_uri: String,
pub selector_universe: Vec<String>,
pub max_context_depth: usize,
pub call_sites: Vec<OmenaQueryDynamicClassnameCallSiteV0>,
}
pub fn summarize_omena_query_dynamic_classname_m_tier_diagnostics_with_context_depth(
input: &OmenaQueryDynamicClassnameMTierInputV0,
) -> OmenaQuerySourceDiagnosticsForFileV0 {
let mut diagnostics = collect_omena_query_dynamic_classname_m_tier_diagnostics(
&input.call_sites,
&input.selector_universe,
input.max_context_depth,
);
diagnostics.sort_by(|left, right| {
(
left.range.start.line,
left.range.start.character,
left.code,
&left.message,
)
.cmp(&(
right.range.start.line,
right.range.start.character,
right.code,
&right.message,
))
});
apply_omena_query_checker_product_gate_to_source_diagnostics(&mut diagnostics);
OmenaQuerySourceDiagnosticsForFileV0 {
schema_version: "0",
product: "omena-query.diagnostics-for-file",
file_uri: input.source_uri.clone(),
file_kind: "source",
diagnostic_count: diagnostics.len(),
diagnostics,
ready_surfaces: vec![
"dynamicClassnameMTierDiagnostics",
"kLimitedCallSiteFlow",
"checkerMTierEvaluation",
"checkerProductDiagnosticGate",
],
}
}
pub(super) const OMENA_QUERY_WORKSPACE_DYNAMIC_CLASSNAME_CONTEXT_DEPTH: usize = 2;
pub(super) fn harvest_omena_query_dynamic_classname_m_tier_diagnostics(
source_path: &str,
source: &str,
type_fact_targets: &[OmenaQuerySourceTypeFactTargetV0],
union_selector_universe: &[String],
selector_universe_by_uri: &BTreeMap<String, Vec<String>>,
max_context_depth: usize,
) -> Vec<OmenaQuerySourceDiagnosticV0> {
let mut call_sites_by_scope: BTreeMap<
Option<String>,
Vec<OmenaQueryDynamicClassnameCallSiteV0>,
> = BTreeMap::new();
for target in type_fact_targets {
let Some(exit_value) =
harvested_abstract_class_value(target.prefix.as_str(), target.suffix.as_str())
else {
continue;
};
let callee_key = harvested_callee_key(target.expression_id.as_str());
call_sites_by_scope
.entry(target.target_style_uri.clone())
.or_default()
.push(OmenaQueryDynamicClassnameCallSiteV0 {
callee_key,
call_site_stack: vec![
source_path.to_string(),
format!("{}:{}", target.byte_span.start, target.byte_span.end),
],
exit_value,
reference_range: parser_range_for_byte_span(source, target.byte_span),
});
}
if call_sites_by_scope.is_empty() {
return Vec::new();
}
let mut diagnostics = Vec::new();
for (scope_uri, call_sites) in &call_sites_by_scope {
let scoped_universe = match scope_uri {
Some(uri) => selector_universe_by_uri
.get(uri)
.map(Vec::as_slice)
.unwrap_or(union_selector_universe),
None => union_selector_universe,
};
diagnostics.extend(collect_omena_query_dynamic_classname_m_tier_diagnostics(
call_sites,
scoped_universe,
max_context_depth,
));
}
diagnostics.retain(|diagnostic| diagnostic.code != "noImpreciseValue");
diagnostics
}
fn harvested_abstract_class_value(
prefix: &str,
suffix: &str,
) -> Option<OmenaQueryDynamicClassValueInputV0> {
match (prefix.is_empty(), suffix.is_empty()) {
(true, true) => None,
(false, true) => Some(OmenaQueryDynamicClassValueInputV0::Prefix {
prefix: prefix.to_string(),
}),
(true, false) => Some(OmenaQueryDynamicClassValueInputV0::Suffix {
suffix: suffix.to_string(),
}),
(false, false) => Some(OmenaQueryDynamicClassValueInputV0::PrefixSuffix {
prefix: prefix.to_string(),
suffix: suffix.to_string(),
min_length: prefix.len() + suffix.len(),
}),
}
}
fn harvested_callee_key(expression_id: &str) -> String {
let trimmed = expression_id
.strip_prefix("omena-bridge-source-type-fact:")
.unwrap_or(expression_id);
let mut segments = trimmed.rsplitn(3, ':');
let _end = segments.next();
let _start = segments.next();
match segments.next() {
Some(path) if !path.is_empty() => path.to_string(),
_ => trimmed.to_string(),
}
}
pub(super) fn collect_omena_query_dynamic_classname_m_tier_diagnostics(
call_sites: &[OmenaQueryDynamicClassnameCallSiteV0],
selector_universe: &[String],
max_context_depth: usize,
) -> Vec<OmenaQuerySourceDiagnosticV0> {
let mut range_by_context: BTreeMap<(String, Vec<String>), ParserRangeV0> = BTreeMap::new();
let contexts = call_sites
.iter()
.map(|call_site| {
range_by_context.insert(
(
call_site.callee_key.clone(),
call_site.call_site_stack.clone(),
),
call_site.reference_range,
);
OmenaQueryCheckerKLimitedFlowContextV0 {
callee_key: call_site.callee_key.clone(),
call_site_stack: call_site.call_site_stack.clone(),
exit_value: call_site.exit_value.clone().into_abstract_class_value(),
}
})
.collect::<Vec<_>>();
let gate = run_omena_query_checker_k_limited_flow_m_tier_gate_v0(
&contexts,
selector_universe,
max_context_depth,
);
let mut diagnostics = Vec::new();
if gate.enforcement_passed {
for context in &gate.contexts {
let range = range_by_context
.get(&(context.callee_key.clone(), context.call_site_stack.clone()))
.copied()
.unwrap_or_default();
for evaluation in &context.evaluations {
diagnostics.push(OmenaQuerySourceDiagnosticV0 {
code: dynamic_classname_m_tier_diagnostic_code(evaluation.rule_code_name),
severity: evaluation.severity_name,
provenance: vec![
"omena-query-checker-orchestrator.k-limited-flow-m-tier-gate",
"omena-abstract-value.k-limited-call-site-flow",
"omena-checker.m-tier-rules",
"omena-query.dynamic-classname",
],
range,
message: evaluation.message.clone(),
create_selector: None,
});
}
}
}
diagnostics
}
fn dynamic_classname_m_tier_diagnostic_code(rule_code_name: &str) -> &'static str {
match rule_code_name {
"no-unknown-dynamic-class" => "noUnknownDynamicClass",
"no-imprecise-value" => "noImpreciseValue",
"no-impossible-selector" => "noImpossibleSelector",
_ => "dynamicClassDomain",
}
}