package loader import "fmt" func ModuleDependencyOrder(mods map[string]*ModuleInfo, primaryModule string) ([]string, error) { if _, ok := mods[primaryModule]; !ok { return nil, fmt.Errorf("primary package %q not found", primaryModule) } visited := map[string]VisitedStatusKind{} order := []string{} var visit func(string) error visit = func(name string) error { switch visited[name] { case VisitedStatusVisiting: return fmt.Errorf("circular import detected at %q", name) case VisitedStatusDone: return nil } info, ok := mods[name] if !ok { return fmt.Errorf("package imports unknown package %q", name) } visited[name] = VisitedStatusVisiting for _, dependency := range info.Imports { if _, ok := mods[dependency]; !ok { return fmt.Errorf("package %q imports unknown package %q", name, dependency) } if err := visit(dependency); err != nil { return err } } visited[name] = VisitedStatusDone order = append(order, name) return nil } if err := visit(primaryModule); err != nil { return nil, err } return order, nil } func BuildDependencyGraph(mods map[string]*ModuleInfo) (map[string][]string, error) { graph := map[string][]string{} for name, info := range mods { graph[name] = info.Imports for _, dep := range info.Imports { if _, ok := mods[dep]; !ok { return nil, fmt.Errorf("package %q imports unknown package %q", name, dep) } } } return graph, nil } type VisitedStatusKind int const ( VisitedStatusUnseen VisitedStatusKind = iota VisitedStatusVisiting VisitedStatusDone ) func TopoSort(graph map[string][]string) ([]string, error) { visited := map[string]VisitedStatusKind{} order := []string{} var visit func(string) error visit = func(n string) error { switch visited[n] { case VisitedStatusVisiting: return fmt.Errorf("circular import detected at %q", n) case VisitedStatusDone: return nil } visited[n] = VisitedStatusVisiting for _, dep := range graph[n] { if err := visit(dep); err != nil { return err } } visited[n] = VisitedStatusDone order = append(order, n) return nil } for node := range graph { if visited[node] == VisitedStatusUnseen { if err := visit(node); err != nil { return nil, err } } } return order, nil }