internal/encoding: don't clobber files on crash
Change-Id: I5b5449192c53e6fb21e4046202eb9ef2c9e280fa
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7861
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/internal/encoding/encoder.go b/internal/encoding/encoder.go
index b6e09ae..f803d95 100644
--- a/internal/encoding/encoder.go
+++ b/internal/encoding/encoder.go
@@ -15,9 +15,11 @@
package encoding
import (
+ "bytes"
"encoding/json"
"fmt"
"io"
+ "io/ioutil"
"os"
"path/filepath"
@@ -37,7 +39,7 @@
// An Encoder allows
type Encoder struct {
cfg *Config
- closer io.Closer
+ close func() error
interpret func(*cue.Instance) (*ast.File, error)
encFile func(*ast.File) error
encValue func(cue.Value) error
@@ -55,21 +57,21 @@
}
func (e Encoder) Close() error {
- if e.closer == nil {
+ if e.close == nil {
return nil
}
- return e.closer.Close()
+ return e.close()
}
// NewEncoder writes content to the file with the given specification.
func NewEncoder(f *build.File, cfg *Config) (*Encoder, error) {
- w, closer, err := writer(f, cfg)
+ w, close, err := writer(f, cfg)
if err != nil {
return nil, err
}
e := &Encoder{
- cfg: cfg,
- closer: closer,
+ cfg: cfg,
+ close: close,
}
switch f.Interpretation {
@@ -241,7 +243,7 @@
return e.encValue(inst.Value())
}
-func writer(f *build.File, cfg *Config) (io.Writer, io.Closer, error) {
+func writer(f *build.File, cfg *Config) (_ io.Writer, close func() error, err error) {
if cfg.Out != nil {
return cfg.Out, nil, nil
}
@@ -258,6 +260,11 @@
"error writing %q", path)
}
}
- w, err := os.Create(path)
- return w, w, err
+ // Delay opening the file until we can write it to completion. This will
+ // prevent clobbering the file in case of a crash.
+ b := &bytes.Buffer{}
+ fn := func() error {
+ return ioutil.WriteFile(path, b.Bytes(), 0644)
+ }
+ return b, fn, nil
}