json.go 1.8 KB
Newer Older
1
package jsonutil
protolambda's avatar
protolambda committed
2 3 4

import (
	"encoding/json"
5
	"errors"
protolambda's avatar
protolambda committed
6 7 8
	"fmt"
	"io"
	"os"
9 10

	"github.com/ethereum-optimism/optimism/op-service/ioutil"
protolambda's avatar
protolambda committed
11 12
)

13
func LoadJSON[X any](inputPath string) (*X, error) {
14 15 16
	if inputPath == "" {
		return nil, errors.New("no path specified")
	}
17
	var f io.ReadCloser
18
	f, err := ioutil.OpenDecompressed(inputPath)
protolambda's avatar
protolambda committed
19 20 21 22 23
	if err != nil {
		return nil, fmt.Errorf("failed to open file %q: %w", inputPath, err)
	}
	defer f.Close()
	var state X
24 25
	decoder := json.NewDecoder(f)
	if err := decoder.Decode(&state); err != nil {
protolambda's avatar
protolambda committed
26 27
		return nil, fmt.Errorf("failed to decode file %q: %w", inputPath, err)
	}
28 29 30 31
	// We are only expecting 1 JSON object - confirm there is no trailing data
	if _, err := decoder.Token(); err != io.EOF {
		return nil, fmt.Errorf("unexpected trailing data in file %q", inputPath)
	}
protolambda's avatar
protolambda committed
32 33 34
	return &state, nil
}

35
func WriteJSON[X any](outputPath string, value X, perm os.FileMode) error {
36 37 38
	if outputPath == "" {
		return nil
	}
protolambda's avatar
protolambda committed
39
	var out io.Writer
40 41
	finish := func() error { return nil }
	if outputPath != "-" {
42
		f, err := ioutil.NewAtomicWriterCompressed(outputPath, perm)
protolambda's avatar
protolambda committed
43 44 45
		if err != nil {
			return fmt.Errorf("failed to open output file: %w", err)
		}
46 47 48 49
		// Ensure we close the stream without renaming even if failures occur.
		defer func() {
			_ = f.Abort()
		}()
protolambda's avatar
protolambda committed
50
		out = f
51 52 53
		// Closing the file causes it to be renamed to the final destination
		// so make sure we handle any errors it returns
		finish = f.Close
protolambda's avatar
protolambda committed
54
	} else {
55
		out = os.Stdout
protolambda's avatar
protolambda committed
56 57
	}
	enc := json.NewEncoder(out)
58
	enc.SetIndent("", "  ")
protolambda's avatar
protolambda committed
59 60 61 62 63 64 65
	if err := enc.Encode(value); err != nil {
		return fmt.Errorf("failed to encode to JSON: %w", err)
	}
	_, err := out.Write([]byte{'\n'})
	if err != nil {
		return fmt.Errorf("failed to append new-line: %w", err)
	}
66 67 68
	if err := finish(); err != nil {
		return fmt.Errorf("failed to finish write: %w", err)
	}
protolambda's avatar
protolambda committed
69 70
	return nil
}