update cfg/common interface to add Parse()->Read() and Write() methods | add 'ini' format + tests
This commit is contained in:
parent
ea4dfef5e9
commit
cafd4063a0
|
@ -6,8 +6,11 @@ import (
|
|||
|
||||
// ConfigurationFormat is the common interface for all configuration parser
|
||||
type ConfigurationFormat interface {
|
||||
// Parse the given file
|
||||
Parse(io.Reader) error
|
||||
// Read the given file
|
||||
Read(io.Reader) error
|
||||
|
||||
// Write the given update to the file
|
||||
Write(io.Writer) error
|
||||
|
||||
// Get the value of a field if it exists
|
||||
Get(string) (string, bool)
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package cnf
|
||||
|
||||
import (
|
||||
"github.com/go-ini/ini"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Ini struct {
|
||||
sections []string
|
||||
data *ini.File
|
||||
parsed bool
|
||||
}
|
||||
|
||||
// Read extract a reader as its JSON representation
|
||||
func (d *Ini) Read(_reader io.Reader) error {
|
||||
|
||||
// 1. get json decoder
|
||||
file, err := ini.Load(ioutil.NopCloser(_reader))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.data = file
|
||||
d.sections = file.SectionStrings()
|
||||
d.parsed = true
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// Write the INI representation to a writer
|
||||
func (d *Ini) Write(_writer io.Writer) error {
|
||||
_, err := d.data.WriteTo(_writer)
|
||||
return err
|
||||
}
|
||||
|
||||
// Get returns the value of a dot-separated path, and if it exists
|
||||
// the maximum depth is 2 : Section.Field
|
||||
func (d *Ini) Get(_path string) (string, bool) {
|
||||
|
||||
// 1. split path
|
||||
path := strings.Split(_path, ".")
|
||||
|
||||
// 2. fail if too long
|
||||
if len(path) > 2 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// 3. direct field (no section)
|
||||
if len(path) == 1 {
|
||||
// unknown field
|
||||
if !d.data.Section("").Haskey(path[0]) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// found
|
||||
return d.data.Section("").Key(path[0]).String(), true
|
||||
}
|
||||
|
||||
// 4. fail on missing section
|
||||
found := false
|
||||
for _, sec := range d.sections {
|
||||
if sec == path[0] {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// 5. Fail on unknown field
|
||||
if !d.data.Section(path[0]).HasKey(path[1]) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// 6. return value
|
||||
return d.data.Section(path[0]).Key(path[1]).String(), true
|
||||
|
||||
}
|
||||
|
||||
// Set the value of a dot-separated path, and creates it if not found
|
||||
func (d *Ini) Set(_path, _value string) bool {
|
||||
|
||||
// 1. split path
|
||||
path := strings.Split(_path, ".")
|
||||
|
||||
// 2. fail if too long
|
||||
if len(path) > 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 3. direct field (no section)
|
||||
section, field := "", path[0]
|
||||
if len(path) > 1 {
|
||||
section, field = path[0], path[1]
|
||||
}
|
||||
|
||||
// 4. Set field value
|
||||
d.data.Section(section).Key(field).SetValue(_value)
|
||||
return true
|
||||
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
package cnf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIniGet(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
raw string
|
||||
key string
|
||||
}{
|
||||
{"key = value", "key"},
|
||||
{"[section]\nkey = value", "section.key"},
|
||||
{"[ignoresec]\nignore = xxx\n[section]\nkey = value", "section.key"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
parser := new(Ini)
|
||||
reader := bytes.NewBufferString(test.raw)
|
||||
|
||||
// try to extract value
|
||||
err := parser.Read(reader)
|
||||
if err != nil {
|
||||
t.Errorf("parse error: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// extract value
|
||||
value, found := parser.Get(test.key)
|
||||
if !found {
|
||||
t.Errorf("expected a result, got none")
|
||||
continue
|
||||
}
|
||||
|
||||
// check value
|
||||
if value != "value" {
|
||||
t.Errorf("expected 'value' got '%s'", value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestIniSetPathExists(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
raw string
|
||||
key string
|
||||
value string
|
||||
}{
|
||||
{"key = value", "key", "newvalue"},
|
||||
{"[section]\nkey = value", "section.key", "newvalue"},
|
||||
{"[ignoresec]\nignore = xxx\n[section]\nkey = value", "section.key", "newvalue"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
parser := new(Ini)
|
||||
reader := bytes.NewBufferString(test.raw)
|
||||
|
||||
// try to extract value
|
||||
err := parser.Read(reader)
|
||||
if err != nil {
|
||||
t.Errorf("parse error: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// update value
|
||||
if !parser.Set(test.key, test.value) {
|
||||
t.Errorf("cannot set '%s' to '%s'", test.key, test.value)
|
||||
continue
|
||||
}
|
||||
|
||||
// check new value
|
||||
value, found := parser.Get(test.key)
|
||||
if !found {
|
||||
t.Errorf("expected a result, got none")
|
||||
continue
|
||||
}
|
||||
|
||||
// check value
|
||||
if value != test.value {
|
||||
t.Errorf("expected '%s' got '%s'", test.value, value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestIniSetCreatePath(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
raw string
|
||||
key string
|
||||
ignore string // path to field that must be present after transformation
|
||||
value string
|
||||
}{
|
||||
{"ignore = xxx", "key", "ignore", "newvalue"},
|
||||
{"ignore = xxx\n[section]\nkey = value", "section.key", "ignore", "newvalue"},
|
||||
{"[section]\nkey = value\nignore = xxx", "section.key", "section.ignore", "newvalue"},
|
||||
{"[ignoresec]\nignore = xxx\n[section]\nkey = value", "section.key", "ignoresec.ignore", "newvalue"},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
|
||||
parser := new(Ini)
|
||||
reader := bytes.NewBufferString(test.raw)
|
||||
|
||||
// try to extract value
|
||||
err := parser.Read(reader)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] parse error: %s", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// update value
|
||||
if !parser.Set(test.key, test.value) {
|
||||
t.Errorf("[%d] cannot set '%s' to '%s'", i, test.key, test.value)
|
||||
continue
|
||||
}
|
||||
|
||||
// check new value
|
||||
value, found := parser.Get(test.key)
|
||||
if !found {
|
||||
t.Errorf("[%d] expected a result, got none", i)
|
||||
continue
|
||||
}
|
||||
|
||||
// check value
|
||||
if value != test.value {
|
||||
t.Errorf("[%d] expected '%s' got '%s'", i, test.value, value)
|
||||
continue
|
||||
}
|
||||
|
||||
// check that ignore field is still there
|
||||
value, found = parser.Get(test.ignore)
|
||||
if !found {
|
||||
t.Errorf("[%d] expected ignore field, got none", i)
|
||||
continue
|
||||
}
|
||||
|
||||
// check value
|
||||
if value != "xxx" {
|
||||
t.Errorf("[%d] expected ignore value to be '%s' got '%s'", i, "xxx", value)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -11,8 +11,8 @@ type Json struct {
|
|||
parsed bool
|
||||
}
|
||||
|
||||
// Parse extract a reader as its JSON representation
|
||||
func (d *Json) Parse(_reader io.Reader) error {
|
||||
// Read extract a reader as its JSON representation
|
||||
func (d *Json) Read(_reader io.Reader) error {
|
||||
|
||||
// 1. get json decoder
|
||||
decoder := json.NewDecoder(_reader)
|
||||
|
@ -26,6 +26,12 @@ func (d *Json) Parse(_reader io.Reader) error {
|
|||
|
||||
}
|
||||
|
||||
// Write the JSON representation to a writer
|
||||
func (d *Json) Write(_writer io.Writer) error {
|
||||
encoder := json.NewEncoder(_writer)
|
||||
return encoder.Encode(&d.data)
|
||||
}
|
||||
|
||||
// browse returns the target of a dot-separated path (as an interface{} chain where the last is the target if found)
|
||||
func (d *Json) browse(_path string) ([]interface{}, bool) {
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
func TestJsonGet(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
raw string
|
||||
|
@ -23,7 +23,7 @@ func TestGet(t *testing.T) {
|
|||
reader := bytes.NewBufferString(test.raw)
|
||||
|
||||
// try to extract value
|
||||
err := parser.Parse(reader)
|
||||
err := parser.Read(reader)
|
||||
if err != nil {
|
||||
t.Errorf("parse error: %s", err)
|
||||
continue
|
||||
|
@ -45,7 +45,7 @@ func TestGet(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestGetNotString(t *testing.T) {
|
||||
func TestJsonGetNotString(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
raw string
|
||||
|
@ -63,7 +63,7 @@ func TestGetNotString(t *testing.T) {
|
|||
reader := bytes.NewBufferString(test.raw)
|
||||
|
||||
// try to extract value
|
||||
err := parser.Parse(reader)
|
||||
err := parser.Read(reader)
|
||||
if err != nil {
|
||||
t.Errorf("parse error: %s", err)
|
||||
continue
|
||||
|
@ -80,7 +80,7 @@ func TestGetNotString(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestSetPathExistsAndIsString(t *testing.T) {
|
||||
func TestJsonSetPathExistsAndIsString(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
raw string
|
||||
|
@ -99,7 +99,7 @@ func TestSetPathExistsAndIsString(t *testing.T) {
|
|||
reader := bytes.NewBufferString(test.raw)
|
||||
|
||||
// try to extract value
|
||||
err := parser.Parse(reader)
|
||||
err := parser.Read(reader)
|
||||
if err != nil {
|
||||
t.Errorf("parse error: %s", err)
|
||||
continue
|
||||
|
@ -127,7 +127,7 @@ func TestSetPathExistsAndIsString(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestSetCreatePath(t *testing.T) {
|
||||
func TestJsonSetCreatePath(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
raw string
|
||||
|
@ -150,7 +150,7 @@ func TestSetCreatePath(t *testing.T) {
|
|||
reader := bytes.NewBufferString(test.raw)
|
||||
|
||||
// try to extract value
|
||||
err := parser.Parse(reader)
|
||||
err := parser.Read(reader)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] parse error: %s", i, err)
|
||||
continue
|
||||
|
|
|
@ -37,7 +37,7 @@ func Load(path string) (ConfigurationFormat, error) {
|
|||
defer file.Close()
|
||||
|
||||
// parse
|
||||
err = confFormat.Parse(file)
|
||||
err = confFormat.Read(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse file as '%s' | %s", extension, err)
|
||||
}
|
||||
|
@ -68,6 +68,8 @@ func loadFromExtension(ext string) ConfigurationFormat {
|
|||
switch ext {
|
||||
case "json":
|
||||
return new(Json)
|
||||
case "ini":
|
||||
return new(Ini)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -52,6 +52,8 @@ func (d *config) Build(_args string) error {
|
|||
|
||||
func (d config) Exec(ctx ExecutionContext) ([]byte, error) {
|
||||
|
||||
fmt.Printf("path is '%v'\n", d.Path)
|
||||
|
||||
return nil, nil
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue