package preview import ( "fmt" "github.com/porter-dev/switchboard/pkg/types" ) type dependencyResolver struct { resources []*types.Resource graph map[string][]string resolved map[string]bool unresolved map[string]bool } func newDependencyResolver(resources []*types.Resource) *dependencyResolver { return &dependencyResolver{ resources: resources, graph: make(map[string][]string), resolved: make(map[string]bool), unresolved: make(map[string]bool), } } func (r *dependencyResolver) Resolve() error { if len(r.resources) > 0 { // construct dependency graph for _, resource := range r.resources { // check for duplicate resource if _, ok := r.graph[resource.Name]; ok { return fmt.Errorf("duplicate resource detected: '%s'", resource.Name) } r.graph[resource.Name] = append(r.graph[resource.Name], resource.DependsOn...) } for _, resource := range r.resources { err := r.depResolve(resource.Name) if err != nil { return err } } } return nil } func (r *dependencyResolver) depResolve(name string) error { r.unresolved[name] = true for _, dep := range r.graph[name] { if _, ok := r.graph[dep]; !ok { return fmt.Errorf("for resource '%s': invalid dependency '%s'", name, dep) } if _, ok := r.resolved[dep]; !ok { if _, ok = r.unresolved[dep]; ok { return fmt.Errorf("circular depedency detected: '%s' -> '%s'", name, dep) } err := r.depResolve(dep) if err != nil { return err } } } r.resolved[name] = true delete(r.unresolved, name) return nil }