Add io.Reader/io.Writer support to Format (#96)
diff --git a/tfexec/fmt.go b/tfexec/fmt.go
index d80a39b..de30890 100644
--- a/tfexec/fmt.go
+++ b/tfexec/fmt.go
@@ -4,6 +4,7 @@
"bytes"
"context"
"fmt"
+ "io"
"os/exec"
"path/filepath"
"strings"
@@ -42,22 +43,27 @@
// FormatString formats a passed string.
func (tf *Terraform) FormatString(ctx context.Context, content string) (string, error) {
+ in := strings.NewReader(content)
+ var outBuf bytes.Buffer
+ err := tf.Format(ctx, in, &outBuf)
+ if err != nil {
+ return "", err
+ }
+ return outBuf.String(), nil
+}
+
+// Format performs formatting on the unformatted io.Reader (as stdin to the CLI) and returns
+// the formatted result on the formatted io.Writer.
+func (tf *Terraform) Format(ctx context.Context, unformatted io.Reader, formatted io.Writer) error {
cmd, err := tf.formatCmd(ctx, nil, Dir("-"))
if err != nil {
- return "", err
+ return err
}
- cmd.Stdin = strings.NewReader(content)
+ cmd.Stdin = unformatted
+ cmd.Stdout = mergeWriters(cmd.Stdout, formatted)
- var outBuf bytes.Buffer
- cmd.Stdout = mergeWriters(cmd.Stdout, &outBuf)
-
- err = tf.runTerraformCmd(cmd)
- if err != nil {
- return "", err
- }
-
- return outBuf.String(), nil
+ return tf.runTerraformCmd(cmd)
}
// FormatWrite attempts to format and modify all config files in the working or selected (via DirOption) directory.
diff --git a/tfexec/internal/e2etest/fmt_test.go b/tfexec/internal/e2etest/fmt_test.go
index f081b00..c1622de 100644
--- a/tfexec/internal/e2etest/fmt_test.go
+++ b/tfexec/internal/e2etest/fmt_test.go
@@ -1,11 +1,15 @@
package e2etest
import (
+ "bytes"
"context"
+ "io"
+ "io/ioutil"
"path/filepath"
"reflect"
"strings"
"testing"
+ "time"
"github.com/hashicorp/go-version"
@@ -84,3 +88,89 @@
}
})
}
+
+func TestFormat(t *testing.T) {
+ runTest(t, "", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) {
+ unformatted := strings.TrimSpace(`
+resource "foo" "bar" {
+ baz = 1
+ qux = 2
+}
+`)
+
+ expected := strings.TrimSpace(`
+resource "foo" "bar" {
+ baz = 1
+ qux = 2
+}
+`)
+
+ start := time.Now()
+ var actual bytes.Buffer
+ err := tf.Format(context.Background(), strings.NewReader(unformatted), &actual)
+ if err != nil {
+ t.Fatal(err)
+ }
+ duration := time.Since(start)
+ t.Logf("formatting took %dms", duration.Milliseconds())
+
+ actualString := strings.TrimSpace(actual.String())
+ if actualString != expected {
+ t.Fatalf("expected:\n%s\ngot:\n%s\n", expected, actualString)
+ }
+ })
+}
+
+func TestFormat_warmFormatter(t *testing.T) {
+ runTest(t, "", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) {
+ unformatted := strings.TrimSpace(`
+resource "foo" "bar" {
+ baz = 1
+ qux = 2
+}
+`)
+
+ expected := strings.TrimSpace(`
+resource "foo" "bar" {
+ baz = 1
+ qux = 2
+}
+`)
+
+ inR, inW := io.Pipe()
+ outR, outW := io.Pipe()
+
+ go func() {
+ err := tf.Format(context.Background(), inR, outW)
+ if err != nil {
+ outW.CloseWithError(err)
+ }
+ _ = outW.Close()
+ }()
+
+ t.Log("Sleeping while CLI is warmed...")
+ time.Sleep(5 * time.Second)
+ t.Log("Sending unformatted data...")
+ start := time.Now()
+ _, err := inW.Write([]byte(unformatted))
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = inW.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ actual, err := ioutil.ReadAll(outR)
+ if err != nil {
+ t.Fatal(err)
+ }
+ duration := time.Since(start)
+ t.Logf("formatting took %dms", duration.Milliseconds())
+
+ actualString := strings.TrimSpace(string(actual))
+ if actualString != expected {
+ t.Fatalf("expected:\n%s\ngot:\n%s\n", expected, actualString)
+ }
+ })
+}