resolve.go (1875B)
1 package main 2 3 // Graph of Nodes that resolves dependencies. 4 type Graph struct { 5 Nodes []Node 6 unresolved *NodeMap 7 resolved *NodeMap 8 } 9 10 // Append a node to the graph. 11 func (g *Graph) Append(n Node) { 12 g.Nodes = append(g.Nodes, n) 13 } 14 15 // Resolve the graph. 16 func (g *Graph) Resolve() []Node { 17 g.unresolved = &NodeMap{m: map[string]int{}} 18 g.resolved = &NodeMap{m: map[string]int{}} 19 for _, n := range g.Nodes { 20 g.resolve(n) 21 } 22 return g.resolved.List() 23 } 24 25 func (g *Graph) resolve(n Node) { 26 g.unresolved.Append(n) 27 defer g.resolved.Append(n) 28 defer g.unresolved.Remove(n) 29 for _, edge := range n.Requires() { 30 if _, resolved := g.resolved.Lookup(edge.ID()); !resolved { 31 if _, seen := g.unresolved.Lookup(edge.ID()); seen { 32 panic("Circular reference") 33 } else { 34 g.resolve(edge) 35 } 36 } 37 } 38 } 39 40 // Node on a graph. 41 type Node interface { 42 ID() string 43 Requires() []Node 44 } 45 46 // NodeMap is a map of nodes. 47 type NodeMap struct { 48 l []Node 49 m map[string]int 50 } 51 52 // List returns an ordered list of nodes. 53 func (m *NodeMap) List() []Node { 54 return m.l 55 } 56 57 // Append a node. 58 func (m *NodeMap) Append(n Node) { 59 m.l = append(m.l, n) 60 m.m[n.ID()] = len(m.l) - 1 61 } 62 63 // Remove a node. 64 func (m *NodeMap) Remove(n Node) { 65 ii := m.m[n.ID()] 66 m.l = append(m.l[:ii], m.l[ii+1:]...) 67 delete(m.m, n.ID()) 68 } 69 70 // Lookup a node for the given id. 71 func (m *NodeMap) Lookup(id string) (Node, bool) { 72 ii, ok := m.m[id] 73 if !ok { 74 return nil, false 75 } 76 return m.l[ii], true 77 } 78 79 // taskNode wraps a Task to implement the Node interface. 80 // Index allows us to return a Task object for a given Task name. 81 type taskNode struct { 82 Task Task 83 Index map[string]Task 84 } 85 86 func (n taskNode) ID() string { 87 return n.Task.Name 88 } 89 90 func (n taskNode) Requires() (list []Node) { 91 for _, r := range n.Task.Requires { 92 list = append(list, taskNode{Task: n.Index[r], Index: n.Index}) 93 } 94 return list 95 }