Skip to content

Instantly share code, notes, and snippets.

@kevinburke
Created July 26, 2016 16:52
Show Gist options
  • Save kevinburke/477ca3a49d67f886cd2ade96a2abaf2a to your computer and use it in GitHub Desktop.
Save kevinburke/477ca3a49d67f886cd2ade96a2abaf2a to your computer and use it in GitHub Desktop.
package configparser
import (
"encoding/json"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/hcl/hcl/token"
)
func NewMap(l *ast.ObjectList) *Map {
return &Map{
ol: l,
}
}
type Map struct {
ol *ast.ObjectList
}
func (m *Map) Keys() []string {
var keys []string
for _, item := range m.ol.Items {
if len(item.Keys) != 1 {
continue
}
if item.Keys[0].Token.Text == "" {
continue
}
keys = append(keys, item.Keys[0].Token.Text)
}
return keys
}
func (m *Map) Get(key string) (ast.Node, bool) {
for _, item := range m.ol.Items {
if len(item.Keys) != 1 {
continue
}
if item.Keys[0].Token.Text == key {
return item.Val, true
}
}
return nil, false
}
func (m *Map) GetAll(key string) []ast.Node {
nodes := make([]ast.Node, 0)
for _, item := range m.ol.Items {
if len(item.Keys) != 1 {
continue
}
if item.Keys[0].Token.Text == key {
nodes = append(nodes, item.Val)
}
}
return nodes
}
func newval(val interface{}, line int) *ast.LiteralType {
// this should hopefully be a simple value like int, string. TODO actual
// switch(type) command
bits, err := json.Marshal(val)
if err != nil {
panic(err)
}
return &ast.LiteralType{
Token: token.Token{
Type: token.STRING,
Pos: token.Pos{
Line: line,
},
Text: string(bits),
},
}
}
func (m *Map) Set(key string, val interface{}) {
set := false
for _, item := range m.ol.Items {
if len(item.Keys) != 1 {
continue
}
if item.Keys[0].Token.Text == key {
item.Val = newval(val, item.Assign.Line)
set = true
break
}
}
if !set {
lastItem := m.ol.Items[len(m.ol.Items)-1]
m.ol.Items = append(m.ol.Items, &ast.ObjectItem{
Keys: []*ast.ObjectKey{
{token.Token{
Type: token.STRING,
Pos: token.Pos{
Line: lastItem.Assign.Line + 1,
},
Text: key,
}},
},
Assign: token.Pos{
Line: lastItem.Assign.Line + 1,
},
Val: newval(val, lastItem.Assign.Line+1),
})
}
return
}
package configparser
import (
"testing"
"github.com/letsencrypt/boulder/test"
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
)
var configfile = `
handler {
# this is a route
route = "/v1/hello_world"
/*
* this si a multiline comment
* about this handler */
function_name = "Functionname"
foo = 3
bar = 7
blah = 8
}
handler {
# this is a route
route = "/v1/hello_world"
/*
* this si a multiline comment
* about this handler */
function_name = "Functionname"
}`
func TestGet(t *testing.T) {
f, err := hcl.ParseBytes([]byte(configfile))
if err != nil {
t.Fatal(err)
}
// "An HCL file is an object list" so this should work
m := NewMap(f.Node.(*ast.ObjectList))
node, found := m.Get("handler")
test.AssertEquals(t, found, true)
_, found = m.Get("unknown_key")
test.AssertEquals(t, found, false)
innerm := NewMap(node.(*ast.ObjectType).List)
node, found = innerm.Get("function_name")
test.AssertEquals(t, found, true)
lt, ok := node.(*ast.LiteralType)
test.Assert(t, ok, "converting to literal type")
test.AssertEquals(t, lt.Token.Text, "\"Functionname\"")
}
func TestGetAll(t *testing.T) {
f, err := hcl.ParseBytes([]byte(configfile))
if err != nil {
t.Fatal(err)
}
// "An HCL file is an object list" so this should work
m := NewMap(f.Node.(*ast.ObjectList))
nodes := m.GetAll("handler")
test.AssertEquals(t, len(nodes), 2)
nodes = m.GetAll("unknown_key")
test.AssertEquals(t, len(nodes), 0)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment