|  | package tfexec | 
|  |  | 
|  | import ( | 
|  | "context" | 
|  | "fmt" | 
|  | "io" | 
|  | "os/exec" | 
|  | "path/filepath" | 
|  | "strings" | 
|  | ) | 
|  |  | 
|  | type formatConfig struct { | 
|  | recursive bool | 
|  | dir       string | 
|  | } | 
|  |  | 
|  | var defaultFormatConfig = formatConfig{ | 
|  | recursive: false, | 
|  | } | 
|  |  | 
|  | type FormatOption interface { | 
|  | configureFormat(*formatConfig) | 
|  | } | 
|  |  | 
|  | func (opt *RecursiveOption) configureFormat(conf *formatConfig) { | 
|  | conf.recursive = opt.recursive | 
|  | } | 
|  |  | 
|  | func (opt *DirOption) configureFormat(conf *formatConfig) { | 
|  | conf.dir = opt.path | 
|  | } | 
|  |  | 
|  | // FormatString formats a passed string, given a path to Terraform. | 
|  | func FormatString(ctx context.Context, execPath string, content string) (string, error) { | 
|  | tf, err := NewTerraform(filepath.Dir(execPath), execPath) | 
|  | if err != nil { | 
|  | return "", err | 
|  | } | 
|  |  | 
|  | return tf.FormatString(ctx, content) | 
|  | } | 
|  |  | 
|  | // FormatString formats a passed string. | 
|  | func (tf *Terraform) FormatString(ctx context.Context, content string) (string, error) { | 
|  | in := strings.NewReader(content) | 
|  | var outBuf strings.Builder | 
|  | 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 | 
|  | } | 
|  |  | 
|  | cmd.Stdin = unformatted | 
|  | cmd.Stdout = mergeWriters(cmd.Stdout, formatted) | 
|  |  | 
|  | return tf.runTerraformCmd(ctx, cmd) | 
|  | } | 
|  |  | 
|  | // FormatWrite attempts to format and modify all config files in the working or selected (via DirOption) directory. | 
|  | func (tf *Terraform) FormatWrite(ctx context.Context, opts ...FormatOption) error { | 
|  | for _, o := range opts { | 
|  | switch o := o.(type) { | 
|  | case *DirOption: | 
|  | if o.path == "-" { | 
|  | return fmt.Errorf("a path of \"-\" is not supported for this method, please use FormatString") | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | cmd, err := tf.formatCmd(ctx, []string{"-write=true", "-list=false", "-diff=false"}, opts...) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | return tf.runTerraformCmd(ctx, cmd) | 
|  | } | 
|  |  | 
|  | // FormatCheck returns true if the config files in the working or selected (via DirOption) directory are already formatted. | 
|  | func (tf *Terraform) FormatCheck(ctx context.Context, opts ...FormatOption) (bool, []string, error) { | 
|  | for _, o := range opts { | 
|  | switch o := o.(type) { | 
|  | case *DirOption: | 
|  | if o.path == "-" { | 
|  | return false, nil, fmt.Errorf("a path of \"-\" is not supported for this method, please use FormatString") | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | cmd, err := tf.formatCmd(ctx, []string{"-write=false", "-list=true", "-diff=false", "-check=true"}, opts...) | 
|  | if err != nil { | 
|  | return false, nil, err | 
|  | } | 
|  |  | 
|  | var outBuf strings.Builder | 
|  | cmd.Stdout = mergeWriters(cmd.Stdout, &outBuf) | 
|  |  | 
|  | err = tf.runTerraformCmd(ctx, cmd) | 
|  | if err == nil { | 
|  | return true, nil, nil | 
|  | } | 
|  | if cmd.ProcessState.ExitCode() == 3 { | 
|  | // unformatted, parse the file list | 
|  |  | 
|  | files := []string{} | 
|  | lines := strings.Split(strings.Replace(outBuf.String(), "\r\n", "\n", -1), "\n") | 
|  | for _, l := range lines { | 
|  | l = strings.TrimSpace(l) | 
|  | if l == "" { | 
|  | continue | 
|  | } | 
|  | files = append(files, l) | 
|  | } | 
|  |  | 
|  | return false, files, nil | 
|  | } | 
|  | return false, nil, err | 
|  | } | 
|  |  | 
|  | func (tf *Terraform) formatCmd(ctx context.Context, args []string, opts ...FormatOption) (*exec.Cmd, error) { | 
|  | err := tf.compatible(ctx, tf0_7_7, nil) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("fmt was first introduced in Terraform 0.7.7: %w", err) | 
|  | } | 
|  |  | 
|  | c := defaultFormatConfig | 
|  |  | 
|  | for _, o := range opts { | 
|  | switch o.(type) { | 
|  | case *RecursiveOption: | 
|  | err := tf.compatible(ctx, tf0_12_0, nil) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("-recursive was added to fmt in Terraform 0.12: %w", err) | 
|  | } | 
|  | } | 
|  |  | 
|  | o.configureFormat(&c) | 
|  | } | 
|  |  | 
|  | args = append([]string{"fmt", "-no-color"}, args...) | 
|  |  | 
|  | if c.recursive { | 
|  | args = append(args, "-recursive") | 
|  | } | 
|  |  | 
|  | if c.dir != "" { | 
|  | args = append(args, c.dir) | 
|  | } | 
|  |  | 
|  | return tf.buildTerraformCmd(ctx, nil, args...), nil | 
|  | } |