Skip to content

Commit

Permalink
Add Decoder struct and DecodeFS()
Browse files Browse the repository at this point in the history
Refactor things a wee bit to add a decoder struct. This doesn't really
*do* anything right now, but will be needed to add options, such as
"strict mode" in #197 or "extended errors" in #299.

Also add DecodeFS() as a companion to DecodeFile() to read from a fs.FS,
and since using a reader is the default with NewDecoder() (and was
already easy to do before with strings.NewReader()) I marked
DecodeReader() as deprecated.
  • Loading branch information
arp242 committed Jun 20, 2021
1 parent 8b03fa5 commit bd6589b
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 17 deletions.
48 changes: 32 additions & 16 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"io/ioutil"
"math"
"os"
"reflect"
"strings"
"time"
Expand Down Expand Up @@ -57,8 +58,7 @@ func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
return md.unify(primValue.undecoded, rvalue(v))
}

// Decode will decode the contents of `data` in TOML format into a pointer
// `v`.
// Decode TOML data.
//
// TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be
// used interchangeably.)
Expand Down Expand Up @@ -93,15 +93,33 @@ func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
//
// This decoder will not handle cyclic types. If a cyclic type is passed,
// `Decode` will not terminate.
func Decode(data string, v interface{}) (MetaData, error) {
type Decoder struct {
r io.Reader
}

// NewDecoder creates a new Decoder.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: r}
}

// Decode TOML data in to the pointer `v`.
func (dec *Decoder) Decode(v interface{}) (MetaData, error) {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr {
return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v))
}
if rv.IsNil() {
return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v))
}
p, err := parse(data)

// TODO: have parser should read from io.Reader? Or at the very least, make
// it read from []byte rather than string
data, err := ioutil.ReadAll(dec.r)
if err != nil {
return MetaData{}, err
}

p, err := parse(string(data))
if err != nil {
return MetaData{}, err
}
Expand All @@ -112,24 +130,22 @@ func Decode(data string, v interface{}) (MetaData, error) {
return md, md.unify(p.mapping, indirect(rv))
}

// Decode the TOML data in to the pointer v.
//
// See Decoder for the full documentation.
func Decode(data string, v interface{}) (MetaData, error) {
return NewDecoder(strings.NewReader(data)).Decode(v)
}

// DecodeFile is just like Decode, except it will automatically read the
// contents of the file at `fpath` and decode it for you.
func DecodeFile(fpath string, v interface{}) (MetaData, error) {
bs, err := ioutil.ReadFile(fpath)
if err != nil {
return MetaData{}, err
}
return Decode(string(bs), v)
}

// DecodeReader is just like Decode, except it will consume all bytes
// from the reader and decode it for you.
func DecodeReader(r io.Reader, v interface{}) (MetaData, error) {
bs, err := ioutil.ReadAll(r)
fp, err := os.Open(fpath)
if err != nil {
return MetaData{}, err
}
return Decode(string(bs), v)
defer fp.Close()
return NewDecoder(fp).Decode(v)
}

// unify performs a sort of type unification based on the structure of `rv`,
Expand Down
18 changes: 18 additions & 0 deletions decode_go116.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// +build go1.16

package toml

import (
"io/fs"
)

// DecodeFS is just like Decode, except it will automatically read the contents
// of the file at `fpath` from a fs.FS instance.
func DecodeFS(fsys fs.FS, fpath string, v interface{}) (MetaData, error) {
fp, err := fsys.Open(fpath)
if err != nil {
return MetaData{}, err
}
defer fp.Close()
return NewDecoder(fp).Decode(v)
}
28 changes: 28 additions & 0 deletions decode_go116_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// +build go1.16

package toml

import (
"fmt"
"testing"
"testing/fstest"
)

func TestDecodeFS(t *testing.T) {
fsys := fstest.MapFS{
"test.toml": &fstest.MapFile{
Data: []byte("a = 42"),
},
}

var i struct{ A int }
meta, err := DecodeFS(fsys, "test.toml", &i)
if err != nil {
t.Fatal(err)
}
have := fmt.Sprintf("%v %v %v", i, meta.Keys(), meta.Type("a"))
want := "{42} [a] Integer"
if have != want {
t.Errorf("\nhave: %s\nwant: %s", have, want)
}
}
12 changes: 11 additions & 1 deletion deprecated.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package toml

import "encoding"
import (
"encoding"
"io"
)

// DEPRECATED!
//
Expand All @@ -21,3 +24,10 @@ func PrimitiveDecode(primValue Primitive, v interface{}) error {
md := MetaData{decoded: make(map[string]bool)}
return md.unify(primValue.undecoded, rvalue(v))
}

// DEPRECATED!
//
// Use NewDecoder(reader).Decode(&v) instead.
func DecodeReader(r io.Reader, v interface{}) (MetaData, error) {
return NewDecoder(r).Decode(v)
}

0 comments on commit bd6589b

Please sign in to comment.