use anyhow::Result;
use std::collections::HashMap;
use crate::core::ResourceType;
use crate::manifest::{DetailedDependency, ResourceDependency};
use crate::version::conflict::{ConflictDetector, VersionConflict};
use super::types::DependencyKey;
#[allow(dead_code)] pub struct ConflictService {
detector: ConflictDetector,
}
impl ConflictService {
pub fn new() -> Self {
Self {
detector: ConflictDetector::new(),
}
}
pub fn detect_version_conflicts(
&mut self,
dependencies: &HashMap<DependencyKey, DetailedDependency>,
) -> Result<Vec<VersionConflict>> {
let mut conflicts = Vec::new();
let mut grouped: HashMap<(ResourceType, String, String, String), Vec<_>> = HashMap::new();
for (key, dep) in dependencies {
let source = dep.source.clone().unwrap_or_default();
let tool = dep.tool.clone().unwrap_or_default();
let group_key = (
key.0, dep.path.clone(),
source,
tool,
);
grouped.entry(group_key).or_default().push((key, dep));
}
for ((resource_type, path, source, _tool), deps) in grouped {
if deps.len() > 1 {
let mut conflicting_requirements = Vec::new();
for (key, dep) in &deps {
let requirement = dep.version.clone().unwrap_or_else(|| "latest".to_string());
conflicting_requirements.push(
crate::version::conflict::ConflictingRequirement {
required_by: format!("{}/{}", key.0, key.1), requirement,
resolved_version: None,
},
);
}
conflicts.push(VersionConflict {
resource: format!("{}/{}/{}", resource_type, path, source),
conflicting_requirements,
});
}
}
Ok(conflicts)
}
pub fn detect_path_conflicts(
dependencies: &HashMap<DependencyKey, DetailedDependency>,
) -> Vec<(String, Vec<String>)> {
let mut conflicts = Vec::new();
let mut install_paths: HashMap<String, Vec<String>> = HashMap::new();
for (key, dep) in dependencies {
let install_path = format!("{}/{}", key.0, key.1); install_paths.entry(install_path.clone()).or_default().push(format!(
"{}/{} (from {})",
key.0,
key.1,
dep.source.as_deref().unwrap_or("local")
));
}
for (path, deps) in install_paths {
if deps.len() > 1 {
conflicts.push((path, deps));
}
}
conflicts
}
pub fn has_conflict(
&mut self,
dependencies: &HashMap<DependencyKey, DetailedDependency>,
new_dep: &ResourceDependency,
new_key: &DependencyKey,
) -> bool {
let (new_path, new_source, new_tool) = match new_dep {
ResourceDependency::Simple(path) => (path, None, None),
ResourceDependency::Detailed(details) => {
(&details.path, details.source.as_deref(), details.tool.as_deref())
}
};
for (key, dep) in dependencies {
if key.0 == new_key.0 && key.1 != new_key.1 && dep.path == *new_path
&& dep.source == new_source.map(String::from)
&& dep.tool == new_tool.map(String::from)
{
return true;
}
}
false
}
}
impl Default for ConflictService {
fn default() -> Self {
Self::new()
}
}