github.com/cdmixer/woolloomooloo@v0.1.0/pkg/graph/topsort.go (about)

     1  // Copyright 2016-2018, Pulumi Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package graph
    16  
    17  import (
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  // Topsort topologically sorts the graph, yielding an array of nodes that are in dependency order, using a simple
    22  // DFS-based algorithm.  The graph must be acyclic, otherwise this function will return an error.
    23  func Topsort(g Graph) ([]Vertex, error) {
    24  	var sorted []Vertex               // will hold the sorted vertices.
    25  	visiting := make(map[Vertex]bool) // temporary entries to detect cycles.
    26  	visited := make(map[Vertex]bool)  // entries to avoid visiting the same node twice.
    27  
    28  	// Now enumerate the roots, topologically sorting their dependencies.
    29  	roots := g.Roots()
    30  	for _, r := range roots {
    31  		if err := topvisit(r.To(), &sorted, visiting, visited); err != nil {
    32  			return sorted, err
    33  		}
    34  	}
    35  	return sorted, nil
    36  }
    37  
    38  func topvisit(n Vertex, sorted *[]Vertex, visiting map[Vertex]bool, visited map[Vertex]bool) error {
    39  	if visiting[n] {
    40  		// This is not a DAG!  Stop sorting right away, and issue an error.
    41  		// IDEA: return diagnostic information about why this isn't a DAG (e.g., full cycle path).
    42  		return errors.New("Graph is not a DAG")
    43  	}
    44  	if !visited[n] {
    45  		visiting[n] = true
    46  		for _, m := range n.Outs() {
    47  			if err := topvisit(m.To(), sorted, visiting, visited); err != nil {
    48  				return err
    49  			}
    50  		}
    51  		visited[n] = true
    52  		visiting[n] = false
    53  		*sorted = append(*sorted, n)
    54  	}
    55  	return nil
    56  }