diff --git a/pkg/client/client.go b/pkg/client/client.go index 86ed0a99e..73f75db60 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -1194,13 +1194,6 @@ func (c *KpmClient) downloadDeps(deps pkg.Dependencies, lockDeps pkg.Dependencie depGraph := graph.New(graph.StringHash, graph.Directed()) - for _, d := range newDeps.Deps { - err := depGraph.AddVertex(fmt.Sprintf("%s@%s", d.Name, d.Version)) - if err != nil { - return nil, nil, err - } - } - // Recursively download the dependencies of the new dependencies. for _, d := range newDeps.Deps { // Load kcl.mod file of the new downloaded dependencies. @@ -1223,12 +1216,19 @@ func (c *KpmClient) downloadDeps(deps pkg.Dependencies, lockDeps pkg.Dependencie } source := fmt.Sprintf("%s@%s", d.Name, d.Version) + err = depGraph.AddVertex(source) + if err != nil { + if err != graph.ErrVertexAlreadyExists { + return nil, nil, err + } + } + sourcesOfNestedDepGraph, err := FindSource(nestedDepGraph) if err != nil { return nil, nil, err } - depGraph, err = graph.Union(depGraph, nestedDepGraph) + depGraph, err = Union(depGraph, nestedDepGraph) if err != nil { return nil, nil, err } @@ -1236,7 +1236,7 @@ func (c *KpmClient) downloadDeps(deps pkg.Dependencies, lockDeps pkg.Dependencie // make an edge between the source of all nested dep graph and main dep graph for _, sourceOfNestedDepGraph := range sourcesOfNestedDepGraph { err = depGraph.AddEdge(source, sourceOfNestedDepGraph) - if err != nil { + if err != nil && err != graph.ErrEdgeAlreadyExists { return nil, nil, err } } @@ -1341,3 +1341,61 @@ func FindSource[K comparable, T any](g graph.Graph[K, T]) ([]K, error) { } return sources, nil } + +// Union combines two given graphs into a new graph. The vertex hashes in both +// graphs are expected to be unique. The two input graphs will remain unchanged. +// +// Both graphs should be either directed or undirected. All traits for the new +// graph will be derived from g. +// +// If the same vertex happens to be in both g and h, then an error will not be +// thrown as happens in original Union function and successful operation takes place. +func Union[K comparable, T any](g, h graph.Graph[K, T]) (graph.Graph[K, T], error) { + union, err := g.Clone() + if err != nil { + return union, fmt.Errorf("failed to clone g: %w", err) + } + + adjacencyMap, err := h.AdjacencyMap() + if err != nil { + return union, fmt.Errorf("failed to get adjacency map: %w", err) + } + + addedEdges := make(map[K]map[K]struct{}) + + for currentHash := range adjacencyMap { + vertex, err := h.Vertex(currentHash) + if err != nil { + return union, fmt.Errorf("failed to get vertex %v: %w", currentHash, err) + } + + err = union.AddVertex(vertex) + if err != nil && err != graph.ErrVertexAlreadyExists { + return union, fmt.Errorf("failed to add vertex %v: %w", currentHash, err) + } + } + + for _, adjacencies := range adjacencyMap { + for _, edge := range adjacencies { + if _, sourceOK := addedEdges[edge.Source]; sourceOK { + if _, targetOK := addedEdges[edge.Source][edge.Target]; targetOK { + // If the edge addedEdges[source][target] exists, the edge + // has already been created and thus can be skipped here. + continue + } + } + + err = union.AddEdge(edge.Source, edge.Target) + if err != nil { + return union, fmt.Errorf("failed to add edge (%v, %v): %w", edge.Source, edge.Target, err) + } + + if _, ok := addedEdges[edge.Source]; !ok { + addedEdges[edge.Source] = make(map[K]struct{}) + } + addedEdges[edge.Source][edge.Target] = struct{}{} + } + } + + return union, nil +}