"""Multi-provider resource merging with conflict resolution. Merges resources from multiple scan profiles into a unified inventory, resolving naming conflicts by prefixing with the provider identifier. """ from dataclasses import replace from collections import defaultdict from iac_reverse.models import DiscoveredResource, ScanResult class ResourceMerger: """Merges resources from multiple ScanResult objects into a unified list. When resources from different providers share the same name, the merger resolves the conflict by prefixing each conflicting resource's name with its provider identifier (e.g., "kubernetes_nginx", "docker_swarm_nginx"). Provider-specific attributes are preserved unchanged. """ def merge(self, scan_results: list[ScanResult]) -> list[DiscoveredResource]: """Merge resources from multiple scan results into a unified list. Args: scan_results: List of ScanResult objects, one per provider/scan profile. Returns: A unified list of DiscoveredResource with naming conflicts resolved by prefixing conflicting names with the provider identifier. """ # Collect all resources from all scan results all_resources: list[DiscoveredResource] = [] for result in scan_results: all_resources.extend(result.resources) # Group resources by name to detect conflicts resources_by_name: dict[str, list[DiscoveredResource]] = defaultdict(list) for resource in all_resources: resources_by_name[resource.name].append(resource) # Identify conflicting names: same name from different providers conflicting_names: set[str] = set() for name, resources in resources_by_name.items(): providers = {r.provider for r in resources} if len(providers) > 1: conflicting_names.add(name) # Build the merged list, resolving conflicts merged: list[DiscoveredResource] = [] for resource in all_resources: if resource.name in conflicting_names: prefixed_name = f"{resource.provider.value}_{resource.name}" merged.append(replace(resource, name=prefixed_name)) else: merged.append(resource) return merged