mirror of https://github.com/helm/helm
Merge pull request #25 from technosophos/feat/toml-parser
feat(chart): add values parserpull/613/head
commit
d3830753b4
@ -0,0 +1,11 @@
|
||||
poet = "Coleridge"
|
||||
title = "Rime of the Ancient Mariner"
|
||||
stanza = ["at", "length", "did", "cross", "an", "Albatross"]
|
||||
|
||||
[mariner]
|
||||
with = "crossbow"
|
||||
shot = "ALBATROSS"
|
||||
|
||||
[water.water]
|
||||
where = "everywhere"
|
||||
nor = "any drop to drink"
|
@ -0,0 +1,67 @@
|
||||
package chart
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
var ErrNoTable = errors.New("no table")
|
||||
|
||||
type Values map[string]interface{}
|
||||
|
||||
// Table gets a table (TOML subsection) from a Values object.
|
||||
//
|
||||
// The table is returned as a Values.
|
||||
//
|
||||
// Compound table names may be specified with dots:
|
||||
//
|
||||
// foo.bar
|
||||
//
|
||||
// The above will be evaluated as "The table bar inside the table
|
||||
// foo".
|
||||
//
|
||||
// An ErrNoTable is returned if the table does not exist.
|
||||
func (v Values) Table(name string) (Values, error) {
|
||||
names := strings.Split(name, ".")
|
||||
table := v
|
||||
var err error
|
||||
|
||||
for _, n := range names {
|
||||
table, err = tableLookup(table, n)
|
||||
if err != nil {
|
||||
return table, err
|
||||
}
|
||||
}
|
||||
return table, err
|
||||
}
|
||||
|
||||
func tableLookup(v Values, simple string) (Values, error) {
|
||||
v2, ok := v[simple]
|
||||
if !ok {
|
||||
return v, ErrNoTable
|
||||
}
|
||||
vv, ok := v2.(map[string]interface{})
|
||||
if !ok {
|
||||
return vv, ErrNoTable
|
||||
}
|
||||
return vv, nil
|
||||
}
|
||||
|
||||
// ReadValues will parse TOML byte data into a Values.
|
||||
func ReadValues(data []byte) (Values, error) {
|
||||
out := map[string]interface{}{}
|
||||
err := toml.Unmarshal(data, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// ReadValuesFile will parse a TOML file into a Values.
|
||||
func ReadValuesFile(filename string) (Values, error) {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return map[string]interface{}{}, err
|
||||
}
|
||||
return ReadValues(data)
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
package chart
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func TestReadValues(t *testing.T) {
|
||||
doc := `# Test TOML parse
|
||||
poet = "Coleridge"
|
||||
title = "Rime of the Ancient Mariner"
|
||||
stanza = ["at", "length", "did", "cross", "an", "Albatross"]
|
||||
|
||||
[mariner]
|
||||
with = "crossbow"
|
||||
shot = "ALBATROSS"
|
||||
|
||||
[water.water]
|
||||
where = "everywhere"
|
||||
nor = "any drop to drink"
|
||||
`
|
||||
|
||||
data, err := ReadValues([]byte(doc))
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing bytes: %s", err)
|
||||
}
|
||||
matchValues(t, data)
|
||||
}
|
||||
|
||||
func TestReadValuesFile(t *testing.T) {
|
||||
data, err := ReadValuesFile("./testdata/coleridge.toml")
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading TOML file: %s", err)
|
||||
}
|
||||
matchValues(t, data)
|
||||
}
|
||||
|
||||
func ExampleValues() {
|
||||
doc := `title="Moby Dick"
|
||||
[chapter.one]
|
||||
title = "Loomings"
|
||||
|
||||
[chapter.two]
|
||||
title = "The Carpet-Bag"
|
||||
|
||||
[chapter.three]
|
||||
title = "The Spouter Inn"
|
||||
`
|
||||
d, err := ReadValues([]byte(doc))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ch1, err := d.Table("chapter.one")
|
||||
if err != nil {
|
||||
panic("could not find chapter one")
|
||||
}
|
||||
fmt.Print(ch1["title"])
|
||||
// Output:
|
||||
// Loomings
|
||||
}
|
||||
|
||||
func TestTable(t *testing.T) {
|
||||
doc := `title="Moby Dick"
|
||||
[chapter.one]
|
||||
title = "Loomings"
|
||||
|
||||
[chapter.two]
|
||||
title = "The Carpet-Bag"
|
||||
|
||||
[chapter.three]
|
||||
title = "The Spouter Inn"
|
||||
`
|
||||
d, err := ReadValues([]byte(doc))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse the White Whale: %s", err)
|
||||
}
|
||||
|
||||
if _, err := d.Table("title"); err == nil {
|
||||
t.Fatalf("Title is not a table.")
|
||||
}
|
||||
|
||||
if _, err := d.Table("chapter"); err != nil {
|
||||
t.Fatalf("Failed to get the chapter table: %s\n%v", err, d)
|
||||
}
|
||||
|
||||
if v, err := d.Table("chapter.one"); err != nil {
|
||||
t.Errorf("Failed to get chapter.one: %s", err)
|
||||
} else if v["title"] != "Loomings" {
|
||||
t.Errorf("Unexpected title: %s", v["title"])
|
||||
}
|
||||
|
||||
if _, err := d.Table("chapter.three"); err != nil {
|
||||
t.Errorf("Chapter three is missing: %s\n%v", err, d)
|
||||
}
|
||||
|
||||
if _, err := d.Table("chapter.OneHundredThirtySix"); err == nil {
|
||||
t.Errorf("I think you mean 'Epilogue'")
|
||||
}
|
||||
}
|
||||
|
||||
func matchValues(t *testing.T, data map[string]interface{}) {
|
||||
if data["poet"] != "Coleridge" {
|
||||
t.Errorf("Unexpected poet: %s", data["poet"])
|
||||
}
|
||||
|
||||
if o, err := ttpl("{{len .stanza}}", data); err != nil {
|
||||
t.Errorf("len stanza: %s", err)
|
||||
} else if o != "6" {
|
||||
t.Errorf("Expected 6, got %s", o)
|
||||
}
|
||||
|
||||
if o, err := ttpl("{{.mariner.shot}}", data); err != nil {
|
||||
t.Errorf(".mariner.shot: %s", err)
|
||||
} else if o != "ALBATROSS" {
|
||||
t.Errorf("Expected that mariner shot ALBATROSS")
|
||||
}
|
||||
|
||||
if o, err := ttpl("{{.water.water.where}}", data); err != nil {
|
||||
t.Errorf(".water.water.where: %s", err)
|
||||
} else if o != "everywhere" {
|
||||
t.Errorf("Expected water water everywhere")
|
||||
}
|
||||
}
|
||||
|
||||
func ttpl(tpl string, v map[string]interface{}) (string, error) {
|
||||
var b bytes.Buffer
|
||||
tt := template.Must(template.New("t").Parse(tpl))
|
||||
if err := tt.Execute(&b, v); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return b.String(), nil
|
||||
}
|
Loading…
Reference in new issue