json.go 3.13 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
	"fmt"
	"io"
8

9 10
	"github.com/BurntSushi/toml"

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

14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
type Decoder interface {
	Decode(v interface{}) error
}

type DecoderFactory func(r io.Reader) Decoder

type Encoder interface {
	Encode(v interface{}) error
}

type EncoderFactory func(w io.Writer) Encoder

type jsonDecoder struct {
	d *json.Decoder
}

func newJSONDecoder(r io.Reader) Decoder {
	return &jsonDecoder{
		d: json.NewDecoder(r),
	}
}

func (d *jsonDecoder) Decode(v interface{}) error {
	if err := d.d.Decode(v); err != nil {
		return fmt.Errorf("failed to decode JSON: %w", err)
	}
	if _, err := d.d.Token(); err != io.EOF {
		return errors.New("unexpected trailing data")
	}
	return nil
}

type tomlDecoder struct {
	r io.Reader
}

func newTOMLDecoder(r io.Reader) Decoder {
	return &tomlDecoder{
		r: r,
	}
}

func (d *tomlDecoder) Decode(v interface{}) error {
	if _, err := toml.NewDecoder(d.r).Decode(v); err != nil {
		return fmt.Errorf("failed to decode TOML: %w", err)
	}
	return nil
}

type jsonEncoder struct {
	e *json.Encoder
}

func newJSONEncoder(w io.Writer) Encoder {
	e := json.NewEncoder(w)
	e.SetIndent("", "  ")
	return &jsonEncoder{
		e: e,
	}
}

func (e *jsonEncoder) Encode(v interface{}) error {
	if err := e.e.Encode(v); err != nil {
		return fmt.Errorf("failed to encode JSON: %w", err)
	}
	return nil
}

type tomlEncoder struct {
	w io.Writer
}

func newTOMLEncoder(w io.Writer) Encoder {
	return &tomlEncoder{
		w: w,
	}
}

func (e *tomlEncoder) Encode(v interface{}) error {
	if err := toml.NewEncoder(e.w).Encode(v); err != nil {
		return fmt.Errorf("failed to encode TOML: %w", err)
	}
	return nil
}

99
func LoadJSON[X any](inputPath string) (*X, error) {
100 101 102 103 104 105 106 107
	return load[X](inputPath, newJSONDecoder)
}

func LoadTOML[X any](inputPath string) (*X, error) {
	return load[X](inputPath, newTOMLDecoder)
}

func load[X any](inputPath string, dec DecoderFactory) (*X, error) {
108 109 110
	if inputPath == "" {
		return nil, errors.New("no path specified")
	}
111
	var f io.ReadCloser
112
	f, err := ioutil.OpenDecompressed(inputPath)
protolambda's avatar
protolambda committed
113 114 115 116 117
	if err != nil {
		return nil, fmt.Errorf("failed to open file %q: %w", inputPath, err)
	}
	defer f.Close()
	var state X
118
	if err := dec(f).Decode(&state); err != nil {
protolambda's avatar
protolambda committed
119 120 121 122 123
		return nil, fmt.Errorf("failed to decode file %q: %w", inputPath, err)
	}
	return &state, nil
}

124
func WriteJSON[X any](value X, target ioutil.OutputTarget) error {
125 126 127 128 129 130 131 132
	return write(value, target, newJSONEncoder)
}

func WriteTOML[X any](value X, target ioutil.OutputTarget) error {
	return write(value, target, newTOMLEncoder)
}

func write[X any](value X, target ioutil.OutputTarget, enc EncoderFactory) error {
133 134 135
	out, closer, abort, err := target()
	if err != nil {
		return err
136
	}
137 138
	if out == nil {
		return nil // No output stream selected so skip generating the content entirely
protolambda's avatar
protolambda committed
139
	}
140
	defer abort()
141 142
	if err := enc(out).Encode(value); err != nil {
		return fmt.Errorf("failed to encode: %w", err)
protolambda's avatar
protolambda committed
143
	}
144
	_, err = out.Write([]byte{'\n'})
protolambda's avatar
protolambda committed
145 146 147
	if err != nil {
		return fmt.Errorf("failed to append new-line: %w", err)
	}
148
	if err := closer.Close(); err != nil {
149 150
		return fmt.Errorf("failed to finish write: %w", err)
	}
protolambda's avatar
protolambda committed
151 152
	return nil
}