package resolver import ( "fmt" "sort" "sync" ) // DependencyGraph represents a dependency graph for packages type DependencyGraph struct { nodes map[string]*GraphNode mu sync.RWMutex } // GraphNode represents a node in the dependency graph type GraphNode struct { Package *SourcePackage Dependencies []string Dependents []string Level int // -1 means not calculated yet Built bool } // NewDependencyGraph creates a new dependency graph func NewDependencyGraph() *DependencyGraph { return &DependencyGraph{ nodes: make(map[string]*GraphNode), } } // AddPackage adds a package to the graph func (g *DependencyGraph) AddPackage(pkg *SourcePackage) { g.mu.Lock() defer g.mu.Unlock() if _, exists := g.nodes[pkg.Name]; !exists { g.nodes[pkg.Name] = &GraphNode{ Package: pkg, Dependencies: []string{}, Dependents: []string{}, Level: -1, } } } // AddDependency adds a dependency relationship: pkg depends on dep func (g *DependencyGraph) AddDependency(pkg, dep string) { g.mu.Lock() defer g.mu.Unlock() // Ensure both nodes exist if _, exists := g.nodes[pkg]; !exists { g.nodes[pkg] = &GraphNode{ Package: &SourcePackage{Name: pkg}, Dependencies: []string{}, Dependents: []string{}, Level: -1, } } if _, exists := g.nodes[dep]; !exists { g.nodes[dep] = &GraphNode{ Package: &SourcePackage{Name: dep}, Dependencies: []string{}, Dependents: []string{}, Level: -1, } } // Add dependency relationship pkgNode := g.nodes[pkg] depNode := g.nodes[dep] // Add dep to pkg's dependencies if not already there found := false for _, d := range pkgNode.Dependencies { if d == dep { found = true break } } if !found { pkgNode.Dependencies = append(pkgNode.Dependencies, dep) } // Add pkg to dep's dependents if not already there found = false for _, d := range depNode.Dependents { if d == pkg { found = true break } } if !found { depNode.Dependents = append(depNode.Dependents, pkg) } } // CalculateBuildOrder performs topological sort to determine build order func (g *DependencyGraph) CalculateBuildOrder() error { g.mu.Lock() defer g.mu.Unlock() // Reset all levels for _, node := range g.nodes { node.Level = -1 } // Find nodes with no dependencies (level 0) queue := make([]*GraphNode, 0) for _, node := range g.nodes { if len(node.Dependencies) == 0 { node.Level = 0 queue = append(queue, node) } } // BFS to calculate levels for len(queue) > 0 { current := queue[0] queue = queue[1:] for _, depName := range current.Dependents { depNode := g.nodes[depName] // Calculate the maximum level of all dependencies maxDepLevel := -1 for _, dep := range depNode.Dependencies { if depNode, exists := g.nodes[dep]; exists { if depNode.Level > maxDepLevel { maxDepLevel = depNode.Level } } } newLevel := maxDepLevel + 1 if depNode.Level == -1 || depNode.Level < newLevel { depNode.Level = newLevel queue = append(queue, depNode) } } } // Check for cycles (nodes that still have level -1) for _, node := range g.nodes { if node.Level == -1 { return fmt.Errorf("dependency cycle detected involving package %s", node.Package.Name) } } return nil } // GetBuildOrder returns packages in build order (dependencies first) func (g *DependencyGraph) GetBuildOrder() []*SourcePackage { g.mu.RLock() defer g.mu.RUnlock() // Group nodes by level levels := make(map[int][]*GraphNode) for _, node := range g.nodes { levels[node.Level] = append(levels[node.Level], node) } // Sort levels var sortedLevels []int for level := range levels { sortedLevels = append(sortedLevels, level) } sort.Ints(sortedLevels) // Build result result := make([]*SourcePackage, 0) for _, level := range sortedLevels { nodes := levels[level] // Sort nodes within level for deterministic order sort.Slice(nodes, func(i, j int) bool { return nodes[i].Package.Name < nodes[j].Package.Name }) for _, node := range nodes { result = append(result, node.Package) } } return result } // GetStats returns graph statistics func (g *DependencyGraph) GetStats() (nodeCount int, maxLevel int) { g.mu.RLock() defer g.mu.RUnlock() nodeCount = len(g.nodes) maxLevel = 0 for _, node := range g.nodes { if node.Level > maxLevel { maxLevel = node.Level } } return nodeCount, maxLevel } // GetDependencies returns all dependencies for a package func (g *DependencyGraph) GetDependencies(pkgName string) []string { g.mu.RLock() defer g.mu.RUnlock() if node, exists := g.nodes[pkgName]; exists { return append([]string{}, node.Dependencies...) } return nil } // GetDependents returns all packages that depend on the given package func (g *DependencyGraph) GetDependents(pkgName string) []string { g.mu.RLock() defer g.mu.RUnlock() if node, exists := g.nodes[pkgName]; exists { return append([]string{}, node.Dependents...) } return nil }