This commit is contained in:
Alexander Neumann 2018-05-03 20:47:38 +02:00
parent e4c0d77bdd
commit 722517c480
6 changed files with 183 additions and 0 deletions

View File

@ -0,0 +1,76 @@
package config
import (
"fmt"
"reflect"
"github.com/davecgh/go-spew/spew"
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/hcl/hcl/token"
"github.com/restic/restic/internal/errors"
)
// Repo is a configured repository
type Repo struct {
Backend string
Path string
}
// Config contains configuration items read from a file.
type Config struct {
Quiet bool `hcl:"quiet"`
Repos map[string]Repo `hcl:"repo"`
}
// listTags returns the all the top-level tags with the name tagname of obj.
func listTags(obj interface{}, tagname string) map[string]struct{} {
list := make(map[string]struct{})
// resolve indirection if obj is a pointer
v := reflect.Indirect(reflect.ValueOf(obj))
for i := 0; i < v.NumField(); i++ {
f := v.Type().Field(i)
val := f.Tag.Get(tagname)
list[val] = struct{}{}
}
return list
}
// Parse parses a config file from buf.
func Parse(buf []byte) (cfg Config, err error) {
parsed, err := hcl.ParseBytes(buf)
if err != nil {
return Config{}, err
}
err = hcl.DecodeObject(&cfg, parsed)
if err != nil {
return Config{}, err
}
// check for additional top-level items
validNames := listTags(cfg, "hcl")
for _, item := range parsed.Node.(*ast.ObjectList).Items {
fmt.Printf("-----------\n")
spew.Dump(item)
var ident string
for _, key := range item.Keys {
if key.Token.Type == token.IDENT {
ident = key.Token.Text
}
}
fmt.Printf("ident is %q\n", ident)
if _, ok := validNames[ident]; !ok {
return Config{}, errors.Errorf("unknown option %q found at line %v, column %v: %v",
ident, item.Pos().Line, item.Pos().Column)
}
}
// spew.Dump(cfg)
return cfg, nil
}

View File

@ -0,0 +1,83 @@
package config
import (
"encoding/json"
"flag"
"io/ioutil"
"path/filepath"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
)
var updateGoldenFiles = flag.Bool("update", false, "update golden files in testdata/")
func readTestFile(t testing.TB, filename string) []byte {
data, err := ioutil.ReadFile(filepath.Join("testdata", filename))
if err != nil {
t.Fatal(err)
}
return data
}
func saveGoldenFile(t testing.TB, base string, cfg Config) {
buf, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
t.Fatalf("error marshaling result: %v", err)
}
buf = append(buf, '\n')
if err = ioutil.WriteFile(filepath.Join("testdata", base+".golden"), buf, 0644); err != nil {
t.Fatalf("unable to update golden file: %v", err)
}
}
func loadGoldenFile(t testing.TB, base string) Config {
buf, err := ioutil.ReadFile(filepath.Join("testdata", base+".golden"))
if err != nil {
t.Fatal(err)
}
var cfg Config
err = json.Unmarshal(buf, &cfg)
if err != nil {
t.Fatal(err)
}
return cfg
}
func TestRead(t *testing.T) {
entries, err := ioutil.ReadDir("testdata")
if err != nil {
t.Fatal(err)
}
for _, entry := range entries {
filename := entry.Name()
if filepath.Ext(filename) != ".conf" {
continue
}
base := strings.TrimSuffix(filename, ".conf")
t.Run(base, func(t *testing.T) {
buf := readTestFile(t, filename)
cfg, err := Parse(buf)
if err != nil {
t.Fatal(err)
}
if *updateGoldenFiles {
saveGoldenFile(t, base, cfg)
}
want := loadGoldenFile(t, base)
if !cmp.Equal(want, cfg) {
t.Errorf("wrong config: %v", cmp.Diff(want, cfg))
}
})
}
}

View File

@ -0,0 +1 @@
quiet = true

View File

@ -0,0 +1,4 @@
{
"Quiet": true,
"Repos": null
}

View File

@ -0,0 +1,10 @@
repo "test" {
backend = "local"
path = "/foo/bar/baz"
}
foobar "test" {
x = "y"
}
quiet = false

View File

@ -0,0 +1,9 @@
{
"Quiet": false,
"Repos": {
"test": {
"Backend": "local",
"Path": "/foo/bar/baz"
}
}
}