Skip to content

Commit

Permalink
Add KDL parser (#266)
Browse files Browse the repository at this point in the history
  • Loading branch information
dezren39 authored Dec 31, 2023
1 parent 09d28ae commit d2028fd
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ go get -u github.com/knadh/koanf/parsers/toml
- [Reading raw bytes](#reading-raw-bytes)
- [Reading from maps and structs](#reading-from-nested-maps)
- [Unmarshalling and marshalling](#unmarshalling-and-marshalling)
- [Order of merge and key case senstivity](#order-of-merge-and-key-case-sensitivity)
- [Order of merge and key case sensitivity](#order-of-merge-and-key-case-sensitivity)
- [Custom Providers and Parsers](#custom-providers-and-parsers)
- [Custom merge strategies](#custom-merge-strategies)
- [List of installable Providers and Parsers](#api)
Expand Down
13 changes: 13 additions & 0 deletions parsers/kdl/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module github.com/knadh/koanf/parsers/kdl

go 1.18

require (
github.com/pmezard/go-difflib v1.0.0 // i github.com/sblinch/kdl-go v0.0.0-20231112203113-9fa9a505b79a // indirect
github.com/stretchr/testify v1.8.1
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/sblinch/kdl-go v0.0.0-20231112203113-9fa9a505b79a // indirect
)
19 changes: 19 additions & 0 deletions parsers/kdl/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sblinch/kdl-go v0.0.0-20231112203113-9fa9a505b79a h1:q2ywS3Q4C8vJKm0gBmUmub0yyLs6VAYivOKAvfhurJ4=
github.com/sblinch/kdl-go v0.0.0-20231112203113-9fa9a505b79a/go.mod h1:b3oNGuAKOQzhsCKmuLc/urEOPzgHj6fB8vl8bwTBh28=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
26 changes: 26 additions & 0 deletions parsers/kdl/kdl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Package kdl implements a koanf.Parser that parses KDL bytes as conf maps.
package kdl

import (
kdl "github.com/sblinch/kdl-go"
)

// KDL implements a KDL parser.
type KDL struct{}

// Parser returns a KDL Parser.
func Parser() *KDL {
return &KDL{}
}

// Unmarshal parses the given KDL bytes.
func (p *KDL) Unmarshal(b []byte) (map[string]interface{}, error) {
var o map[string]interface{}
err := kdl.Unmarshal(b, &o)
return o, err
}

// Marshal marshals the given config map to KDL bytes.
func (p *KDL) Marshal(o map[string]interface{}) ([]byte, error) {
return kdl.Marshal(o)
}
189 changes: 189 additions & 0 deletions parsers/kdl/kdl_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package kdl

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestKDL_Unmarshal(t *testing.T) {
testCases := []struct {
name string
input []byte
keys []string
values []interface{}
isErr bool
}{
{
name: "Empty KDL",
input: []byte(``),
},
{
name: "Valid KDL",
input: []byte(`key "val" ; name "test" ; number 2.0`),
keys: []string{"key", "name", "number"},
values: []interface{}{"val", "test", 2.0},
},
{
name: "Invalid KDL - syntax error",
input: []byte(`node1 key="val`),
isErr: true,
},
{
name: "Complex KDL - Different types",
input: []byte(`
array 1.0 2.0 3.0
boolean true
color "gold"
"null" null
number 123
object a="b" c="d" e=2.7 f=true
string "Hello World"
`),
keys: []string{"array", "boolean", "color", "null", "number", "object", "string"},
values: []interface{}{[]interface{}{1.0, 2.0, 3.0},
true,
"gold",
nil,
int64(123),
map[string]interface{}{"a": "b", "c": "d", "e": 2.7, "f": true},
"Hello World"},
},
{
name: "Invalid KDL - missing value",
input: []byte(`node1 boolean=`),
isErr: true,
},
{
name: "Complex KDL - Nested map",
input: []byte(`key "value"
"1" "skipped"
map key="skipped" key="value"
nested_map {
map key="value" 17 {
list "item1" "item2" "item3"
mixup "y"=1 2 3 4
first "first"=1 2 3 4
child "test"=1 2 3 4 { "y" 5 ; "d" 6 ; }
}
}
`),
keys: []string{"key", "1", "map", "nested_map"},
values: []interface{}{
"value",
"skipped",
map[string]interface{}{
"key": "value",
},
map[string]interface{}{
"map": map[string]interface{}{
"0": int64(17),
"key": "value",
"list": []interface{}{
"item1",
"item2",
"item3",
},
"mixup": map[string]interface{}{
"y": int64(1),
"0": int64(2),
"1": int64(3),
"2": int64(4),
},
"first": map[string]interface{}{
"first": int64(1),
"0": int64(2),
"1": int64(3),
"2": int64(4),
},
"child": map[string]interface{}{
"test": int64(1),
"0": int64(2),
"1": int64(3),
"2": int64(4),
"y": int64(5),
"d": int64(6),
},
},
},
},
},
}

k := Parser() // Assuming Parser() is implemented for KDL

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
out, err := k.Unmarshal(tc.input)
if tc.isErr {
assert.NotNil(t, err)
} else {
assert.Nil(t, err)
for i, k := range tc.keys {
v := out[k]
assert.Equal(t, tc.values[i], v)
}
}
})
}
}

func TestKDL_Marshal(t *testing.T) {
testCases := []struct {
name string
input map[string]interface{}
stringifiedOutput string
isErr bool
}{
{
name: "Empty KDL",
input: map[string]interface{}{},
stringifiedOutput: ``,
},
{
name: "Valid KDL",
input: map[string]interface{}{
"key": "val",
"name": "test",
"number": 2.0,
},
stringifiedOutput: `key "val"
name "test"
number 2.0
`,
},
{
name: "Complex KDL - Different types",
input: map[string]interface{}{
"null": nil,
"boolean": true,
"color": "gold",
"number": int64(123),
"string": "Hello World",
// "array": []interface{}{1, 2, 3, 4, 5}, // https://github.com/sblinch/kdl-go/issues/3
"object": map[string]interface{}{"a": "b", "c": "d"},
},
stringifiedOutput: `boolean true
color "gold"
number 123
string "Hello World"
object a="b" c="d"
null null
`,
},
}

k := Parser() // Assuming Parser() is implemented for KDL

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
out, err := k.Marshal(tc.input)
if tc.isErr {
assert.NotNil(t, err)
} else {
assert.Nil(t, err)
assert.Equal(t, tc.stringifiedOutput, string(out))
}
})
}
}

0 comments on commit d2028fd

Please sign in to comment.