use crate::error::DispatchError;
use crate::ir::ComponentNode;
use crate::resolve::{resolve_capabilities, ResolutionReport};
use crate::targets::{is_known_target, known_target_names, TargetId};
fn format_summary_line(entry: &crate::resolve::ResolvedCapability) -> String {
let outcome = serde_json::to_value(entry.outcome)
.ok()
.and_then(|v| v.as_str().map(str::to_string))
.unwrap_or_default();
let provided = serde_json::to_value(entry.provided_by)
.ok()
.and_then(|v| v.as_str().map(str::to_string))
.unwrap_or_default();
let criticality = serde_json::to_value(entry.criticality)
.ok()
.and_then(|v| v.as_str().map(str::to_string))
.unwrap_or_default();
let suffix = entry
.note
.as_ref()
.map(|n| format!(" — {n}"))
.unwrap_or_default();
format!(
" {:<28} {:<12} ({provided}, {criticality}){suffix}",
entry.capability, outcome
)
}
pub(super) fn print_resolution_summary(target_label: &str, report: &ResolutionReport) {
eprintln!("warble-dispatch: capability resolution for target '{target_label}':");
for entry in report {
eprintln!("{}", format_summary_line(entry));
}
}
pub fn resolve_node_capabilities(
node: &ComponentNode,
target_id: &str,
) -> Result<ResolutionReport, DispatchError> {
if !is_known_target(target_id) {
return Err(DispatchError(format!(
"target '{target_id}' has no capability profile (known targets: {})",
known_target_names().join(", ")
)));
}
let profile = TargetId::parse(target_id)
.expect("known target parses")
.profile();
resolve_capabilities(node, target_id, &profile)
}
pub(super) fn resolve_node_with_shared_binding(
node: &ComponentNode,
top: &crate::ir::ContextBinding,
target_id: &str,
profile: &crate::targets::CapabilityProfile,
) -> Result<ResolutionReport, DispatchError> {
if node.context_binding.resolved.is_some() || top.resolved.is_none() {
return crate::resolve::resolve_capabilities(node, target_id, profile);
}
let mut hydrated = node.clone();
hydrated.context_binding.resolved = top.resolved.clone();
crate::resolve::resolve_capabilities(&hydrated, target_id, profile)
}