~alazarte/cfgreader

52a1f1900e8a800e610e70c24f4e70d683d47578 — Alejandro Lazarte 3 months ago
Initial commit

Unmarshall of simple config file, just to practice Go's reflect lib
3 files changed, 112 insertions(+), 0 deletions(-)

A test
A unmarshaler.go
A unmarshaler_test.go
A  => test +5 -0
@@ 1,5 @@
Pem=fullchainfile.pem
Sk=privkeyfile.pem
Handler.Name=someName
Handler.Path=/tmp
Handler.Type=type

A  => unmarshaler.go +71 -0
@@ 1,71 @@
package cfgreader

import (
	"bufio"
	"io"
	"os"
	"reflect"
	"regexp"
)

type unmarshalError string

func (e unmarshalError) Error() string {
	return string(e)
}

func unmarshalKeyValue(m map[string]interface{}, v *reflect.Value) {
	for key, ival := range m {
		if reflect.ValueOf(ival).Kind() == reflect.Map {
			v2 := v.FieldByName(key)
			unmarshalKeyValue(ival.(map[string]interface{}), &v2)
		}
		switch v.FieldByName(key).Type().Kind() {
		case reflect.String:
			v.FieldByName(key).SetString(ival.(string))
		}
	}
}

func unmarshalCfg(m map[string]interface{}, v interface{}) error {
	vt := reflect.ValueOf(v).Elem()
	if !vt.CanSet() {
		return unmarshalError("cannot set interface")
	}
	unmarshalKeyValue(m, &vt)
	return nil
}

func FromFile(filepath string, v interface{}) error {
	f, err := os.Open(filepath)
	if err != nil {
		return err
	}
	buf := bufio.NewReader(f)
	group := regexp.MustCompile(`(?:([A-Z][a-z]+)\.)?([A-Z][a-z]+)=([A-Za-z0-9\./]+)\n`)
	m := make(map[string]interface{})
	for {
		l, err := buf.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			continue
		}
		ss := group.FindAllStringSubmatch(l, -1)
		if len(ss) == 0 {
			continue
		}
		if ss[0][1] == "" {
			// "" "Key" "Value"
			m[ss[0][2]] = ss[0][3]
			continue
		}
		if m[ss[0][1]] == nil {
			m[ss[0][1]] = make(map[string]interface{})
		}
		// "Group" "Key" "Value"
		m[ss[0][1]].(map[string]interface{})[ss[0][2]] = ss[0][3]
	}
	return unmarshalCfg(m, v)
}

A  => unmarshaler_test.go +36 -0
@@ 1,36 @@
package cfgreader

import (
	"fmt"
	"os"
	"testing"
)

func TestInit(t *testing.T) {
	contents := `Pem=fullchainfile.pem
Sk=privkeyfile.pem
Handler.Name=someName
Handler.Path=/tmp
Handler.Type=type
`
	if err := os.WriteFile("test", []byte(contents), 0644); err != nil {
		t.Fatal(err)
	}
}
func TestHelloWorld(t *testing.T) {
	c := struct {
		Pem     string
		Sk      string
		Handler struct {
			Name string
			Path string
			Type string
		}
	}{}
	if err := FromFile("test", &c); err != nil {
		t.Fatal(err)
	}
	if fmt.Sprintf("%q", &c) != "&{\"fullchainfile.pem\" \"privkeyfile.pem\" {\"someName\" \"/tmp\" \"type\"}}" {
		t.Fatal("Not what I expected")
	}
}