Skip to content

Instantly share code, notes, and snippets.

@kevinburke
Created August 2, 2016 15:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kevinburke/07a21855df700b0acdea9e91c6fde7cf to your computer and use it in GitHub Desktop.
Save kevinburke/07a21855df700b0acdea9e91c6fde7cf to your computer and use it in GitHub Desktop.
package configparser
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"github.com/hashicorp/hcl"
"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 NewMapValue(lt *ast.LiteralType) *MapValue {
return &MapValue{
lt: lt,
}
}
type MapValue struct {
lt *ast.LiteralType
}
func (mv *MapValue) Text() string {
return mv.lt.Token.Text
}
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
}
// Get a ast.Node from the map
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
}
// Get a ast.LiteralType from the map
func (m *Map) GetValue(key string) (*MapValue, bool) {
node, ok := m.Get(key)
if !ok {
return nil, ok
}
lt, ok := node.(*ast.LiteralType)
if !ok {
return nil, false
}
return NewMapValue(lt), true
}
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
}
func ParseFile(filename string) (*ast.File, error) {
bits, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return hcl.ParseBytes(bits)
}
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