use anyhow::{Context, Result};
use crate::resolver::ResolutionCore;
use crate::resolver::version_resolver::VersionResolutionService;
use crate::version::conflict::{ConflictDetector, VersionConflict};
use super::registry::{ResourceRegistry, TransitiveChangeTracker, parse_resource_id_string};
pub fn get_variant_inputs_for_resource(
change_tracker: &TransitiveChangeTracker,
resource_id: &str,
) -> Option<serde_json::Value> {
change_tracker
.get_changed_resources()
.get(resource_id)
.and_then(|(_, _, _, variant_inputs)| variant_inputs.clone())
}
pub async fn reextract_transitive_deps(
core: &ResolutionCore,
version_service: &mut VersionResolutionService,
change_tracker: &mut TransitiveChangeTracker,
) -> Result<usize> {
use crate::resolver::transitive_extractor::extract_transitive_deps;
let mut count = 0;
let changed: Vec<(String, String, String)> = change_tracker
.get_changed_resources()
.iter()
.map(|(id, (_, new_ver, new_sha, _))| (id.clone(), new_ver.clone(), new_sha.clone()))
.collect();
for (resource_id, new_version, new_sha) in changed {
let (source_name, resource_path) = parse_resource_id_string(&resource_id)?;
tracing::debug!(
"Re-extracting transitive deps for {}: version={}, sha={}",
resource_id,
new_version,
&new_sha[..8.min(new_sha.len())]
);
let source_url = core
.source_manager()
.get_source_url(source_name)
.ok_or_else(|| anyhow::anyhow!("Source '{}' not found", source_name))?;
let worktree_path = core
.cache()
.get_or_create_worktree_for_sha(source_name, &source_url, &new_sha, Some(source_name))
.await?;
let variant_inputs = get_variant_inputs_for_resource(change_tracker, &resource_id);
let transitive_deps =
extract_transitive_deps(&worktree_path, resource_path, variant_inputs.as_ref()).await?;
for (_resource_type, specs) in transitive_deps {
for spec in specs {
if matches!(spec.install, Some(false)) {
continue;
}
let dep_source = source_name;
let dep_version = spec.version.as_deref();
version_service
.prepare_additional_version(core, dep_source, dep_version)
.await
.with_context(|| {
format!(
"Failed to prepare transitive dependency '{}' from {}",
spec.path, resource_id
)
})?;
count += 1;
tracing::debug!(
" Re-resolved transitive dep: {} from source {} version {}",
spec.path,
dep_source,
dep_version.unwrap_or("HEAD")
);
}
}
}
change_tracker.clear();
Ok(count)
}
pub fn detect_conflicts_after_changes(
resource_registry: &ResourceRegistry,
) -> Vec<VersionConflict> {
tracing::debug!("Detecting conflicts after version changes...");
let mut detector = ConflictDetector::new();
for resource in resource_registry.all_resources() {
for required_by in &resource.required_by {
detector.add_requirement(
resource.resource_id.clone(),
required_by,
&resource.version_constraint,
&resource.sha,
);
}
}
let conflicts = detector.detect_conflicts();
if conflicts.is_empty() {
tracing::debug!("No conflicts detected after changes");
} else {
tracing::debug!(
"Detected {} conflict(s) after changes: {:?}",
conflicts.len(),
conflicts.iter().map(|c| &c.resource).collect::<Vec<_>>()
);
}
conflicts
}