diff --git a/terraform/transform.go b/terraform/transform.go index e1f7828c6..ca5736940 100644 --- a/terraform/transform.go +++ b/terraform/transform.go @@ -1,7 +1,21 @@ package terraform +import ( + "github.com/hashicorp/terraform/dag" +) + // GraphTransformer is the interface that transformers implement. This // interface is only for transforms that need entire graph visibility. type GraphTransformer interface { Transform(*Graph) error } + +// GraphVertexTransformer is an interface that transforms a single +// Vertex within with graph. This is a specialization of GraphTransformer +// that makes it easy to do vertex replacement. +// +// The GraphTransformer that runs through the GraphVertexTransformers is +// VertexTransformer. +type GraphVertexTransformer interface { + Transform(dag.Vertex) (dag.Vertex, error) +} diff --git a/terraform/transform_vertex.go b/terraform/transform_vertex.go new file mode 100644 index 000000000..6b1293fc2 --- /dev/null +++ b/terraform/transform_vertex.go @@ -0,0 +1,44 @@ +package terraform + +import ( + "fmt" + + "github.com/hashicorp/terraform/dag" +) + +// VertexTransformer is a GraphTransformer that transforms vertices +// using the GraphVertexTransformers. The Transforms are run in sequential +// order. If a transform replaces a vertex then the next transform will see +// the new vertex. +type VertexTransformer struct { + Transforms []GraphVertexTransformer +} + +func (t *VertexTransformer) Transform(g *Graph) error { + for _, v := range g.Vertices() { + for _, vt := range t.Transforms { + newV, err := vt.Transform(v) + if err != nil { + return err + } + + // If the vertex didn't change, then don't do anything more + if newV == v { + continue + } + + // Vertex changed, replace it within the graph + if ok := g.Replace(v, newV); !ok { + // This should never happen, big problem + return fmt.Errorf( + "Failed to replace %s with %s!\n\nSource: %#v\n\nTarget: %#v", + dag.VertexName(v), dag.VertexName(newV), v, newV) + } + + // Replace v so that future transforms use the proper vertex + v = newV + } + } + + return nil +} diff --git a/terraform/transform_vertex_test.go b/terraform/transform_vertex_test.go new file mode 100644 index 000000000..05d117689 --- /dev/null +++ b/terraform/transform_vertex_test.go @@ -0,0 +1,58 @@ +package terraform + +import ( + "strings" + "testing" + + "github.com/hashicorp/terraform/dag" +) + +func TestVertexTransformer_impl(t *testing.T) { + var _ GraphTransformer = new(VertexTransformer) +} + +func TestVertexTransformer(t *testing.T) { + var g Graph + g.Add(1) + g.Add(2) + g.Add(3) + g.Connect(dag.BasicEdge(1, 2)) + g.Connect(dag.BasicEdge(2, 3)) + + { + tf := &VertexTransformer{ + Transforms: []GraphVertexTransformer{ + &testVertexTransform{Source: 2, Target: 42}, + }, + } + if err := tf.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + actual := strings.TrimSpace(g.String()) + expected := strings.TrimSpace(testVertexTransformerStr) + if actual != expected { + t.Fatalf("bad: %s", actual) + } +} + +type testVertexTransform struct { + Source, Target dag.Vertex +} + +func (t *testVertexTransform) Transform(v dag.Vertex) (dag.Vertex, error) { + if t.Source == v { + v = t.Target + } + + return v, nil +} + +const testVertexTransformerStr = ` +1 + 42 +3 +42 + 3 +`