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:
parent
28d406c040
commit
7b774f771b
|
@ -24,6 +24,10 @@ type WalkFunc func(Vertex) error
|
||||||
// walk as an argument
|
// walk as an argument
|
||||||
type DepthWalkFunc func(Vertex, int) error
|
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
|
// Returns a Set that includes every Vertex yielded by walking down from the
|
||||||
// provided starting Vertex v.
|
// provided starting Vertex v.
|
||||||
func (g *AcyclicGraph) Ancestors(v Vertex) (*Set, error) {
|
func (g *AcyclicGraph) Ancestors(v Vertex) (*Set, error) {
|
||||||
|
|
16
dag/graph.go
16
dag/graph.go
|
@ -17,6 +17,18 @@ type Graph struct {
|
||||||
once sync.Once
|
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.
|
// Vertex of the graph.
|
||||||
type Vertex interface{}
|
type Vertex interface{}
|
||||||
|
|
||||||
|
@ -27,6 +39,10 @@ type NamedVertex interface {
|
||||||
Name() string
|
Name() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Graph) DirectedGraph() Grapher {
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
// Vertices returns the list of all the vertices in the graph.
|
// Vertices returns the list of all the vertices in the graph.
|
||||||
func (g *Graph) Vertices() []Vertex {
|
func (g *Graph) Vertices() []Vertex {
|
||||||
list := g.vertices.List()
|
list := g.vertices.List()
|
||||||
|
|
|
@ -60,7 +60,7 @@ func newMarshalGraph(name string, g *Graph) *marshalGraph {
|
||||||
|
|
||||||
for _, v := range g.Vertices() {
|
for _, v := range g.Vertices() {
|
||||||
id := marshalVertexID(v)
|
id := marshalVertexID(v)
|
||||||
if sg, ok := marshalSubgraph(v); ok {
|
if sg, ok := marshalSubgrapher(v); ok {
|
||||||
|
|
||||||
sdg := newMarshalGraph(VertexName(v), sg)
|
sdg := newMarshalGraph(VertexName(v), sg)
|
||||||
sdg.ID = id
|
sdg.ID = id
|
||||||
|
@ -129,22 +129,19 @@ func marshalVertexID(v Vertex) string {
|
||||||
panic("unhashable value in graph")
|
panic("unhashable value in graph")
|
||||||
}
|
}
|
||||||
|
|
||||||
func debugSubgraph(v Vertex) (*Graph, bool) {
|
// check for a Subgrapher, and return the underlying *Graph.
|
||||||
val := reflect.ValueOf(v)
|
func marshalSubgrapher(v Vertex) (*Graph, bool) {
|
||||||
m, ok := val.Type().MethodByName("Subgraph")
|
sg, ok := v.(Subgrapher)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.Type.NumOut() != 1 {
|
switch g := sg.Subgraph().DirectedGraph().(type) {
|
||||||
return nil, false
|
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
|
return nil, false
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,10 @@ type Graph struct {
|
||||||
once sync.Once
|
once sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Graph) DirectedGraph() dag.Grapher {
|
||||||
|
return &g.AcyclicGraph
|
||||||
|
}
|
||||||
|
|
||||||
// Annotations returns the annotations that are configured for the
|
// Annotations returns the annotations that are configured for the
|
||||||
// given vertex. The map is guaranteed to be non-nil but may be empty.
|
// 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(
|
walker.Debug().Printf(
|
||||||
"[DEBUG] vertex %T(%s.%s): subgraph\n", v, path, dag.VertexName(v))
|
"[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
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,7 @@ func (n *graphNodeModuleExpanded) EvalTree() EvalNode {
|
||||||
|
|
||||||
// GraphNodeFlattenable impl.
|
// GraphNodeFlattenable impl.
|
||||||
func (n *graphNodeModuleExpanded) FlattenGraph() *Graph {
|
func (n *graphNodeModuleExpanded) FlattenGraph() *Graph {
|
||||||
graph := n.Subgraph()
|
graph := n.Subgraph().(*Graph)
|
||||||
input := n.Original.Module.RawConfig
|
input := n.Original.Module.RawConfig
|
||||||
|
|
||||||
// Go over each vertex and do some modifications to the graph for
|
// Go over each vertex and do some modifications to the graph for
|
||||||
|
@ -189,7 +189,7 @@ func (n *graphNodeModuleExpanded) FlattenGraph() *Graph {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeSubgraph impl.
|
// GraphNodeSubgraph impl.
|
||||||
func (n *graphNodeModuleExpanded) Subgraph() *Graph {
|
func (n *graphNodeModuleExpanded) Subgraph() dag.Grapher {
|
||||||
return n.Graph
|
return n.Graph
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ func TestGraphNodeConfigModuleExpand(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := strings.TrimSpace(g.Subgraph().String())
|
actual := strings.TrimSpace(g.Subgraph().(*Graph).String())
|
||||||
expected := strings.TrimSpace(testGraphNodeModuleExpandStr)
|
expected := strings.TrimSpace(testGraphNodeModuleExpandStr)
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Fatalf("bad:\n\n%s", actual)
|
t.Fatalf("bad:\n\n%s", actual)
|
||||||
|
|
|
@ -184,7 +184,7 @@ func (dg *DebugGraph) buildSubgraph(modName string, g *Graph, modDepth int) erro
|
||||||
|
|
||||||
for _, v := range g.Vertices() {
|
for _, v := range g.Vertices() {
|
||||||
if sn, ok := v.(GraphNodeSubgraph); ok {
|
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)
|
toDraw = append(toDraw, v)
|
||||||
|
|
||||||
if sn, ok := v.(GraphNodeSubgraph); ok {
|
if sn, ok := v.(GraphNodeSubgraph); ok {
|
||||||
subgraphVertices[v] = sn.Subgraph()
|
subgraphVertices[v] = sn.Subgraph().(*Graph)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -299,7 +299,7 @@ type testDrawableSubgraph struct {
|
||||||
func (node *testDrawableSubgraph) Name() string {
|
func (node *testDrawableSubgraph) Name() string {
|
||||||
return node.VertexName
|
return node.VertexName
|
||||||
}
|
}
|
||||||
func (node *testDrawableSubgraph) Subgraph() *Graph {
|
func (node *testDrawableSubgraph) Subgraph() dag.Grapher {
|
||||||
return node.SubgraphMock
|
return node.SubgraphMock
|
||||||
}
|
}
|
||||||
func (node *testDrawableSubgraph) DotNode(n string, opts *dag.DotOpts) *dot.Node {
|
func (node *testDrawableSubgraph) DotNode(n string, opts *dag.DotOpts) *dot.Node {
|
||||||
|
|
|
@ -24,7 +24,7 @@ type GraphNodeDynamicExpandable interface {
|
||||||
// GraphNodeSubgraph is an interface a node can implement if it has
|
// GraphNodeSubgraph is an interface a node can implement if it has
|
||||||
// a larger subgraph that should be walked.
|
// a larger subgraph that should be walked.
|
||||||
type GraphNodeSubgraph interface {
|
type GraphNodeSubgraph interface {
|
||||||
Subgraph() *Graph
|
Subgraph() dag.Grapher
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExpandTransform is a transformer that does a subgraph expansion
|
// ExpandTransform is a transformer that does a subgraph expansion
|
||||||
|
@ -56,7 +56,7 @@ func (n *GraphNodeBasicSubgraph) Name() string {
|
||||||
return n.NameValue
|
return n.NameValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *GraphNodeBasicSubgraph) Subgraph() *Graph {
|
func (n *GraphNodeBasicSubgraph) Subgraph() dag.Grapher {
|
||||||
return n.Graph
|
return n.Graph
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ func TestExpandTransform(t *testing.T) {
|
||||||
t.Fatalf("not subgraph: %#v", out)
|
t.Fatalf("not subgraph: %#v", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := strings.TrimSpace(sn.Subgraph().String())
|
actual := strings.TrimSpace(sn.Subgraph().(*Graph).String())
|
||||||
expected := strings.TrimSpace(testExpandTransformStr)
|
expected := strings.TrimSpace(testExpandTransformStr)
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Fatalf("bad: %s", actual)
|
t.Fatalf("bad: %s", actual)
|
||||||
|
@ -66,7 +66,7 @@ type testSubgraph struct {
|
||||||
Graph *Graph
|
Graph *Graph
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *testSubgraph) Subgraph() *Graph {
|
func (n *testSubgraph) Subgraph() dag.Grapher {
|
||||||
return n.Graph
|
return n.Graph
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue