github.com/whiteboxio/flow@v0.0.3-0.20190918184116-508d75d68a2c/pkg/util/data/topology.go (about)

     1  package data
     2  
     3  import (
     4  	"fmt"
     5  )
     6  
     7  type TopologyNode interface{}
     8  
     9  type TopologyEdge struct {
    10  	From TopologyNode
    11  	To   TopologyNode
    12  }
    13  
    14  type Topology struct {
    15  	Edges map[TopologyEdge]struct{}
    16  	Nodes map[TopologyNode]struct{}
    17  }
    18  
    19  func NewTopology(nodes ...TopologyNode) *Topology {
    20  	nodeset := make(map[TopologyNode]struct{})
    21  	for _, node := range nodes {
    22  		nodeset[node] = struct{}{}
    23  	}
    24  	return &Topology{
    25  		Edges: make(map[TopologyEdge]struct{}),
    26  		Nodes: nodeset,
    27  	}
    28  }
    29  
    30  func (top *Topology) AddNode(node TopologyNode) {
    31  	top.Nodes[node] = struct{}{}
    32  }
    33  
    34  // ConnectTo creates a directed edge between node "from" to node "to".
    35  // If a topology represents a dependency graph, this notation should be
    36  // interpreted as: node "from" depends on node "to", i.e. in a case of a
    37  // topological sort node "to" would be visited before node "from".
    38  //
    39  // Example
    40  //
    41  // top := NewTopology(A, B)
    42  // top.Connect(A, B) // A -> B
    43  // top.Sort() // returns {B, A}: B has been visited, which satisfies A
    44  // dependencies.
    45  func (top *Topology) Connect(from, to TopologyNode) error {
    46  	if _, ok := top.Nodes[from]; !ok {
    47  		return fmt.Errorf("Can not connect from unknown node: %#v", from)
    48  	}
    49  	if _, ok := top.Nodes[to]; !ok {
    50  		return fmt.Errorf("Can not connect to unknown node: %#v", to)
    51  	}
    52  	top.Edges[TopologyEdge{From: from, To: to}] = struct{}{}
    53  
    54  	return nil
    55  }
    56  
    57  func (top *Topology) Sort() ([]TopologyNode, error) {
    58  	temp := make(map[TopologyNode]bool)
    59  	perm := make(map[TopologyNode]bool)
    60  	outs := make(map[TopologyNode][]TopologyNode)
    61  	for edge := range top.Edges {
    62  		if _, ok := outs[edge.From]; !ok {
    63  			outs[edge.From] = make([]TopologyNode, 0, 1)
    64  		}
    65  		outs[edge.From] = append(outs[edge.From], edge.To)
    66  	}
    67  
    68  	var visitAll func([]TopologyNode) ([]TopologyNode, error)
    69  	visitAll = func(nodes []TopologyNode) ([]TopologyNode, error) {
    70  		res := make([]TopologyNode, 0)
    71  		for _, node := range nodes {
    72  			if perm[node] {
    73  				continue
    74  			}
    75  			if temp[node] {
    76  				return nil, fmt.Errorf("Detected graph cycle on node %#v", node)
    77  			}
    78  			temp[node] = true
    79  			if subs, ok := outs[node]; ok {
    80  				subsorted, err := visitAll(subs)
    81  				if err != nil {
    82  					return nil, err
    83  				}
    84  				res = append(res, subsorted...)
    85  			}
    86  			perm[node] = true
    87  			res = append(res, node)
    88  		}
    89  		return res, nil
    90  	}
    91  	nodes := make([]TopologyNode, 0, len(top.Nodes))
    92  	for node := range top.Nodes {
    93  		nodes = append(nodes, node)
    94  	}
    95  
    96  	if res, err := visitAll(nodes); err != nil {
    97  		return nil, err
    98  	} else {
    99  		return res, nil
   100  	}
   101  }