1
0
Fork 0
mirror of https://github.com/Luzifer/sii.git synced 2024-10-18 05:14:19 +00:00
sii/genericMarshaller.go

232 lines
5.8 KiB
Go
Raw Normal View History

package sii
import (
"bufio"
"bytes"
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
"github.com/pkg/errors"
)
var (
singleLineValue = `^\s*%s\s?:\s?(.+)$`
arrayLineValue = `^\s*%s(\[([0-9]*)\])?\s?:\s?(.+)$`
)
func genericMarshal(in interface{}) ([]byte, error) {
return nil, errors.New("Not implemented")
}
func genericUnmarshal(in []byte, out interface{}, unit *Unit) error {
if reflect.TypeOf(out).Kind() != reflect.Ptr {
return errors.New("Calling parser with non-pointer")
}
if reflect.ValueOf(out).Elem().Kind() != reflect.Struct {
return errors.New("Calling parser with pointer to non-struct")
}
st := reflect.ValueOf(out).Elem()
for i := 0; i < st.NumField(); i++ {
valField := st.Field(i)
typeField := st.Type().Field(i)
attributeName := typeField.Tag.Get("sii")
if attributeName == "" {
// Names must be explicitly defined, library does not support name guessing
continue
}
switch typeField.Type {
case reflect.TypeOf(Ptr{}):
data := getSingleValue(in, attributeName)
v := Ptr{unit: unit}
if err := v.UnmarshalSII(data); err != nil {
return errors.Wrapf(err, "Unable to parse Ptr for attribute %q", attributeName)
}
valField.Set(reflect.ValueOf(v))
continue
case reflect.TypeOf(Placement{}):
data := getSingleValue(in, attributeName)
v := Placement{}
if err := v.UnmarshalSII(data); err != nil {
return errors.Wrapf(err, "Unable to parse Placement for attribute %q", attributeName)
}
valField.Set(reflect.ValueOf(v))
continue
}
switch typeField.Type.Kind() {
case reflect.Bool:
v, err := strconv.ParseBool(string(getSingleValue(in, attributeName)))
if err != nil {
return errors.Wrapf(err, "Unable to parse boolean for attribute %q", attributeName)
}
valField.SetBool(v)
case reflect.Float32:
v, err := sii2float(getSingleValue(in, attributeName))
if err != nil {
return errors.Wrapf(err, "Unable to parse float for attribute %q", attributeName)
}
valField.Set(reflect.ValueOf(v))
case reflect.Int, reflect.Int64:
v, err := strconv.ParseInt(string(getSingleValue(in, attributeName)), 10, 64)
if err != nil {
return errors.Wrapf(err, "Unable to parse int for attribute %q", attributeName)
}
valField.SetInt(v)
case reflect.String:
v := strings.Trim(string(getSingleValue(in, attributeName)), `"`)
valField.SetString(v)
case reflect.Uint64:
v, err := strconv.ParseUint(string(getSingleValue(in, attributeName)), 10, 64)
if err != nil {
return errors.Wrapf(err, "Unable to parse uint for attribute %q", attributeName)
}
valField.SetUint(v)
case reflect.Slice:
ba, err := getArrayValues(in, attributeName)
if err != nil {
return errors.Wrapf(err, "Unable to fetch array values for attribute %q", attributeName)
}
switch typeField.Type.Elem() {
case reflect.TypeOf(Ptr{}):
var v []Ptr
for _, bv := range ba {
e := Ptr{unit: unit}
if err := e.UnmarshalSII(bv); err != nil {
return errors.Wrapf(err, "Unable to parse Ptr for attribute %q", attributeName)
}
v = append(v, e)
}
valField.Set(reflect.ValueOf(v))
continue
case reflect.TypeOf(Placement{}):
var v []Placement
for _, bv := range ba {
e := Placement{}
if err := e.UnmarshalSII(bv); err != nil {
return errors.Wrapf(err, "Unable to parse Ptr for attribute %q", attributeName)
}
v = append(v, e)
}
valField.Set(reflect.ValueOf(v))
continue
}
switch typeField.Type.Elem().Kind() {
case reflect.Bool:
var v []bool
for _, bv := range ba {
pbv, err := strconv.ParseBool(string(bv))
if err != nil {
return errors.Wrapf(err, "Unable to parse boolean for attribute %q", attributeName)
}
v = append(v, pbv)
}
valField.Set(reflect.ValueOf(v))
case reflect.Int:
var v []int
for _, bv := range ba {
pbv, err := strconv.Atoi(string(bv))
if err != nil {
return errors.Wrapf(err, "Unable to parse int for attribute %q", attributeName)
}
v = append(v, pbv)
}
valField.Set(reflect.ValueOf(v))
case reflect.Int64:
var v []int64
for _, bv := range ba {
pbv, err := strconv.ParseInt(string(bv), 10, 64)
if err != nil {
return errors.Wrapf(err, "Unable to parse int for attribute %q", attributeName)
}
v = append(v, pbv)
}
valField.Set(reflect.ValueOf(v))
case reflect.String:
var v []string
for _, bv := range ba {
v = append(v, strings.Trim(string(bv), `"`))
}
valField.Set(reflect.ValueOf(v))
default:
return errors.Errorf("Unsupported type: []%s", typeField.Type.Elem().Kind())
}
default:
return errors.Errorf("Unsupported type: %s", typeField.Type.Kind())
}
}
return nil
}
func getSingleValue(in []byte, name string) []byte {
rex := regexp.MustCompile(fmt.Sprintf(singleLineValue, name))
var scanner = bufio.NewScanner(bytes.NewReader(in))
for scanner.Scan() {
if rex.Match(scanner.Bytes()) {
grp := rex.FindSubmatch(scanner.Bytes())
return grp[1]
}
}
return nil
}
func getArrayValues(in []byte, name string) ([][]byte, error) {
rex := regexp.MustCompile(fmt.Sprintf(arrayLineValue, name))
var out [][]byte
var scanner = bufio.NewScanner(bytes.NewReader(in))
for scanner.Scan() {
if rex.Match(scanner.Bytes()) {
grp := rex.FindSubmatch(scanner.Bytes())
if len(grp[1]) == 0 {
arrayLen, err := strconv.Atoi(string(grp[3]))
if err != nil {
return nil, errors.Wrap(err, "Unable to parse array capacity")
}
out = make([][]byte, arrayLen)
continue
}
if len(grp[2]) == 0 {
out = append(out, grp[3])
continue
}
idx, err := strconv.Atoi(string(grp[2]))
if err != nil {
return nil, errors.Wrap(err, "Unable to parse array index")
}
out[idx] = grp[3]
}
}
return out, nil
}