implement dag.Subgrapher interface

This allows the dag package to detect subgraphs, even when impelemnted
by types from other packages
This commit is contained in:
James Bardin 2016-11-09 16:52:22 -05:00
parent 28d406c040
commit 7b774f771b
10 changed files with 45 additions and 24 deletions

View File

@ -24,6 +24,10 @@ type WalkFunc func(Vertex) error
// walk as an argument
type DepthWalkFunc func(Vertex, int) error
func (g *AcyclicGraph) DirectedGraph() Grapher {
return g
}
// Returns a Set that includes every Vertex yielded by walking down from the
// provided starting Vertex v.
func (g *AcyclicGraph) Ancestors(v Vertex) (*Set, error) {

View File

@ -17,6 +17,18 @@ type Graph struct {
once sync.Once
}
// Subgrapher allows a Vertex to be a Graph itself, by returning a Grapher.
type Subgrapher interface {
Subgraph() Grapher
}
// A Grapher is any type that returns a Grapher, mainly used to identify
// dag.Graph and dag.AcyclicGraph. In the case of Graph and AcyclicGraph, they
// return themselves.
type Grapher interface {
DirectedGraph() Grapher
}
// Vertex of the graph.
type Vertex interface{}
@ -27,6 +39,10 @@ type NamedVertex interface {
Name() string
}
func (g *Graph) DirectedGraph() Grapher {
return g
}
// Vertices returns the list of all the vertices in the graph.
func (g *Graph) Vertices() []Vertex {
list := g.vertices.List()

View File

@ -60,7 +60,7 @@ func newMarshalGraph(name string, g *Graph) *marshalGraph {
for _, v := range g.Vertices() {
id := marshalVertexID(v)
if sg, ok := marshalSubgraph(v); ok {
if sg, ok := marshalSubgrapher(v); ok {
sdg := newMarshalGraph(VertexName(v), sg)
sdg.ID = id
@ -129,22 +129,19 @@ func marshalVertexID(v Vertex) string {
panic("unhashable value in graph")
}
func debugSubgraph(v Vertex) (*Graph, bool) {
val := reflect.ValueOf(v)
m, ok := val.Type().MethodByName("Subgraph")
// check for a Subgrapher, and return the underlying *Graph.
func marshalSubgrapher(v Vertex) (*Graph, bool) {
sg, ok := v.(Subgrapher)
if !ok {
return nil, false
}
if m.Type.NumOut() != 1 {
return nil, false
switch g := sg.Subgraph().DirectedGraph().(type) {
case *Graph:
return g, true
case *AcyclicGraph:
return &g.Graph, true
}
// can't check for the subgraph type, because we can't import terraform, so
// we assume this is the correct method.
// TODO: create a dag interface type that we can satisfy
sg := val.MethodByName("Subgraph").Call(nil)[0]
ag := sg.Elem().FieldByName("AcyclicGraph").Interface().(AcyclicGraph)
return &ag.Graph, true
return nil, false
}

View File

@ -43,6 +43,10 @@ type Graph struct {
once sync.Once
}
func (g *Graph) DirectedGraph() dag.Grapher {
return &g.AcyclicGraph
}
// Annotations returns the annotations that are configured for the
// given vertex. The map is guaranteed to be non-nil but may be empty.
//
@ -317,7 +321,7 @@ func (g *Graph) walk(walker GraphWalker) error {
walker.Debug().Printf(
"[DEBUG] vertex %T(%s.%s): subgraph\n", v, path, dag.VertexName(v))
if rerr = sn.Subgraph().walk(walker); rerr != nil {
if rerr = sn.Subgraph().(*Graph).walk(walker); rerr != nil {
return
}
}

View File

@ -156,7 +156,7 @@ func (n *graphNodeModuleExpanded) EvalTree() EvalNode {
// GraphNodeFlattenable impl.
func (n *graphNodeModuleExpanded) FlattenGraph() *Graph {
graph := n.Subgraph()
graph := n.Subgraph().(*Graph)
input := n.Original.Module.RawConfig
// Go over each vertex and do some modifications to the graph for
@ -189,7 +189,7 @@ func (n *graphNodeModuleExpanded) FlattenGraph() *Graph {
}
// GraphNodeSubgraph impl.
func (n *graphNodeModuleExpanded) Subgraph() *Graph {
func (n *graphNodeModuleExpanded) Subgraph() dag.Grapher {
return n.Graph
}

View File

@ -33,7 +33,7 @@ func TestGraphNodeConfigModuleExpand(t *testing.T) {
t.Fatalf("err: %s", err)
}
actual := strings.TrimSpace(g.Subgraph().String())
actual := strings.TrimSpace(g.Subgraph().(*Graph).String())
expected := strings.TrimSpace(testGraphNodeModuleExpandStr)
if actual != expected {
t.Fatalf("bad:\n\n%s", actual)

View File

@ -184,7 +184,7 @@ func (dg *DebugGraph) buildSubgraph(modName string, g *Graph, modDepth int) erro
for _, v := range g.Vertices() {
if sn, ok := v.(GraphNodeSubgraph); ok {
subgraphVertices[v] = sn.Subgraph()
subgraphVertices[v] = sn.Subgraph().(*Graph)
}
}
@ -200,7 +200,7 @@ func (dg *DebugGraph) buildSubgraph(modName string, g *Graph, modDepth int) erro
toDraw = append(toDraw, v)
if sn, ok := v.(GraphNodeSubgraph); ok {
subgraphVertices[v] = sn.Subgraph()
subgraphVertices[v] = sn.Subgraph().(*Graph)
}
return nil
}

View File

@ -299,7 +299,7 @@ type testDrawableSubgraph struct {
func (node *testDrawableSubgraph) Name() string {
return node.VertexName
}
func (node *testDrawableSubgraph) Subgraph() *Graph {
func (node *testDrawableSubgraph) Subgraph() dag.Grapher {
return node.SubgraphMock
}
func (node *testDrawableSubgraph) DotNode(n string, opts *dag.DotOpts) *dot.Node {

View File

@ -24,7 +24,7 @@ type GraphNodeDynamicExpandable interface {
// GraphNodeSubgraph is an interface a node can implement if it has
// a larger subgraph that should be walked.
type GraphNodeSubgraph interface {
Subgraph() *Graph
Subgraph() dag.Grapher
}
// ExpandTransform is a transformer that does a subgraph expansion
@ -56,7 +56,7 @@ func (n *GraphNodeBasicSubgraph) Name() string {
return n.NameValue
}
func (n *GraphNodeBasicSubgraph) Subgraph() *Graph {
func (n *GraphNodeBasicSubgraph) Subgraph() dag.Grapher {
return n.Graph
}

View File

@ -30,7 +30,7 @@ func TestExpandTransform(t *testing.T) {
t.Fatalf("not subgraph: %#v", out)
}
actual := strings.TrimSpace(sn.Subgraph().String())
actual := strings.TrimSpace(sn.Subgraph().(*Graph).String())
expected := strings.TrimSpace(testExpandTransformStr)
if actual != expected {
t.Fatalf("bad: %s", actual)
@ -66,7 +66,7 @@ type testSubgraph struct {
Graph *Graph
}
func (n *testSubgraph) Subgraph() *Graph {
func (n *testSubgraph) Subgraph() dag.Grapher {
return n.Graph
}