use std::collections::HashSet;
use crate::core::ResourceType;
use crate::lockfile::{LockFile, LockedResource};
use crate::manifest::Manifest;
use super::DependencyResolver;
impl DependencyResolver {
pub(super) fn create_filtered_manifest(
&self,
deps_to_update: &[(String, ResourceType)],
) -> Manifest {
let mut filtered = Manifest {
sources: self.core.manifest.sources.clone(),
tools: self.core.manifest.tools.clone(),
patches: self.core.manifest.patches.clone(),
project_patches: self.core.manifest.project_patches.clone(),
private_patches: self.core.manifest.private_patches.clone(),
manifest_dir: self.core.manifest.manifest_dir.clone(),
..Default::default()
};
for (dep_name, resource_type) in deps_to_update {
let source_map = match resource_type {
ResourceType::Agent => &self.core.manifest.agents,
ResourceType::Snippet => &self.core.manifest.snippets,
ResourceType::Command => &self.core.manifest.commands,
ResourceType::Script => &self.core.manifest.scripts,
ResourceType::Hook => &self.core.manifest.hooks,
ResourceType::McpServer => &self.core.manifest.mcp_servers,
ResourceType::Skill => &self.core.manifest.skills,
};
if let Some(dep_spec) = source_map.get(dep_name) {
let target_map = match resource_type {
ResourceType::Agent => &mut filtered.agents,
ResourceType::Snippet => &mut filtered.snippets,
ResourceType::Command => &mut filtered.commands,
ResourceType::Script => &mut filtered.scripts,
ResourceType::Hook => &mut filtered.hooks,
ResourceType::McpServer => &mut filtered.mcp_servers,
ResourceType::Skill => &mut filtered.skills,
};
target_map.insert(dep_name.clone(), dep_spec.clone());
}
}
filtered
}
pub(super) fn filter_lockfile_entries(
existing: &LockFile,
deps_to_update: &[String],
) -> (LockFile, Vec<(String, ResourceType)>) {
let update_set: HashSet<&String> = deps_to_update.iter().collect();
let mut unchanged = LockFile::new();
let mut deps_requiring_resolution = Vec::new();
let should_update = |resource: &LockedResource| {
if let Some(alias) = &resource.manifest_alias {
if update_set.contains(alias) {
return true;
}
}
if update_set.contains(&resource.name) {
return true;
}
false
};
for resource_type in [
ResourceType::Agent,
ResourceType::Snippet,
ResourceType::Command,
ResourceType::Script,
ResourceType::Hook,
ResourceType::McpServer,
] {
let resources = existing.get_resources(&resource_type);
let unchanged_resources = unchanged.get_resources_mut(&resource_type);
for resource in resources {
if should_update(resource) {
let name = resource.manifest_alias.as_ref().unwrap_or(&resource.name);
deps_requiring_resolution.push((name.clone(), resource_type));
} else {
unchanged_resources.push(resource.clone());
}
}
}
unchanged.sources = existing.sources.clone();
(unchanged, deps_requiring_resolution)
}
pub(super) fn merge_lockfiles(mut unchanged: LockFile, updated: LockFile) -> LockFile {
let identity_key = |resource: &LockedResource| -> String {
format!(
"{}::{}::{}::{}",
resource.name,
resource.source.as_deref().unwrap_or("local"),
resource.tool.as_deref().unwrap_or(""),
resource.variant_inputs.hash()
)
};
for resource_type in [
ResourceType::Agent,
ResourceType::Snippet,
ResourceType::Command,
ResourceType::Script,
ResourceType::Hook,
ResourceType::McpServer,
] {
let updated_resources = updated.get_resources(&resource_type);
let unchanged_resources = unchanged.get_resources_mut(&resource_type);
let updated_identities: HashSet<String> =
updated_resources.iter().map(&identity_key).collect();
unchanged_resources.retain(|resource| {
let key = identity_key(resource);
!updated_identities.contains(&key)
});
unchanged_resources.extend(updated_resources.iter().cloned());
}
let updated_source_names: HashSet<&str> =
updated.sources.iter().map(|s| s.name.as_str()).collect();
unchanged.sources.retain(|source| !updated_source_names.contains(source.name.as_str()));
unchanged.sources.extend(updated.sources);
unchanged
}
}