mirror of
https://github.com/Luzifer/sii.git
synced 2024-12-21 00:21:15 +00:00
406 lines
10 KiB
Go
406 lines
10 KiB
Go
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 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
|
|
|
|
case reflect.TypeOf(RawValue{}):
|
|
data := getSingleValue(in, attributeName)
|
|
v := RawValue{}
|
|
if err := v.UnmarshalSII(data); err != nil {
|
|
return errors.Wrapf(err, "Unable to parse RawValue 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:
|
|
bv := getSingleValue(in, attributeName)
|
|
if isNilValue(bv) || len(bv) == 0 {
|
|
continue
|
|
}
|
|
v, err := strconv.ParseInt(string(bv), 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.Array:
|
|
|
|
switch typeField.Type.Elem().Kind() {
|
|
|
|
case reflect.Float32:
|
|
switch typeField.Type.Len() {
|
|
|
|
case 3:
|
|
grps := regexp.MustCompile(`^\(([0-9.-]+|&[0-9a-f]+), ([0-9.-]+|&[0-9a-f]+), ([0-9.-]+|&[0-9a-f]+)\)$`).
|
|
FindSubmatch(getSingleValue(in, attributeName))
|
|
var v [3]float32
|
|
|
|
for i := range v {
|
|
val, err := sii2float(grps[i+1][:])
|
|
if err != nil {
|
|
return errors.Wrapf(err, "Unable to parse float32 for attribute %q", attributeName)
|
|
}
|
|
v[i] = val
|
|
}
|
|
valField.Set(reflect.ValueOf(v))
|
|
|
|
default:
|
|
return errors.Errorf("Unsupported type: [%d]%s", typeField.Type.Len(), typeField.Type.Elem().Kind())
|
|
|
|
}
|
|
|
|
case reflect.Int64:
|
|
switch typeField.Type.Len() {
|
|
|
|
case 3:
|
|
grps := regexp.MustCompile(`^\(([0-9.-]+), ([0-9.-]+), ([0-9.-]+)\)$`).
|
|
FindSubmatch(getSingleValue(in, attributeName))
|
|
var v [3]int64
|
|
|
|
for i := range v {
|
|
val, err := strconv.ParseInt(string(grps[i+1][:]), 10, 64)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "Unable to parse int64 for attribute %q", attributeName)
|
|
}
|
|
v[i] = val
|
|
}
|
|
valField.Set(reflect.ValueOf(v))
|
|
|
|
default:
|
|
return errors.Errorf("Unsupported type: [%d]%s", typeField.Type.Len(), typeField.Type.Elem().Kind())
|
|
|
|
}
|
|
|
|
default:
|
|
return errors.Errorf("Unsupported type: [%d]%s", typeField.Type.Len(), typeField.Type.Elem().Kind())
|
|
|
|
}
|
|
|
|
case reflect.Ptr:
|
|
|
|
switch typeField.Type.Elem().Kind() {
|
|
|
|
case reflect.Int64:
|
|
bv := getSingleValue(in, attributeName)
|
|
if !isNilValue(bv) && len(bv) > 0 {
|
|
v, err := strconv.ParseInt(string(bv), 10, 64)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "Unable to parse int for attribute %q", attributeName)
|
|
}
|
|
valField.Set(reflect.ValueOf(&v))
|
|
}
|
|
|
|
default:
|
|
return errors.Errorf("Unsupported type: *%s", typeField.Type.Elem().Kind())
|
|
|
|
}
|
|
|
|
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 Placement for attribute %q", attributeName)
|
|
}
|
|
v = append(v, e)
|
|
}
|
|
valField.Set(reflect.ValueOf(v))
|
|
continue
|
|
|
|
case reflect.TypeOf(RawValue{}):
|
|
var v []RawValue
|
|
for _, bv := range ba {
|
|
e := RawValue{}
|
|
if err := e.UnmarshalSII(bv); err != nil {
|
|
return errors.Wrapf(err, "Unable to parse RawValue 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.Float32:
|
|
var v []float32
|
|
for _, bv := range ba {
|
|
pbv, err := sii2float(bv)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "Unable to parse float32 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 {
|
|
if len(bv) == 0 {
|
|
v = append(v, 0)
|
|
continue
|
|
}
|
|
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))
|
|
|
|
case reflect.Array:
|
|
|
|
switch typeField.Type.Elem().Elem().Kind() {
|
|
|
|
case reflect.Float32:
|
|
switch typeField.Type.Elem().Len() {
|
|
|
|
case 3:
|
|
var v [][3]float32
|
|
for _, bv := range ba {
|
|
grps := regexp.MustCompile(`^\(([0-9.-]+|&[0-9a-f]+), ([0-9.-]+|&[0-9a-f]+), ([0-9.-]+|&[0-9a-f]+)\)$`).
|
|
FindSubmatch(bv)
|
|
var sv [3]float32
|
|
|
|
for i := range sv {
|
|
val, err := sii2float(grps[i+1][:])
|
|
if err != nil {
|
|
return errors.Wrapf(err, "Unable to parse float32 for attribute %q", attributeName)
|
|
}
|
|
sv[i] = val
|
|
}
|
|
v = append(v, sv)
|
|
}
|
|
valField.Set(reflect.ValueOf(v))
|
|
|
|
case 4:
|
|
var v [][4]float32
|
|
for _, bv := range ba {
|
|
grps := regexp.MustCompile(`^\(([0-9.-]+|&[0-9a-f]+); ([0-9.-]+|&[0-9a-f]+), ([0-9.-]+|&[0-9a-f]+), ([0-9.-]+|&[0-9a-f]+)\)$`).
|
|
FindSubmatch(bv)
|
|
var sv [4]float32
|
|
|
|
for i := range sv {
|
|
val, err := sii2float(grps[i+1][:])
|
|
if err != nil {
|
|
return errors.Wrapf(err, "Unable to parse float32 for attribute %q", attributeName)
|
|
}
|
|
sv[i] = val
|
|
}
|
|
v = append(v, sv)
|
|
}
|
|
valField.Set(reflect.ValueOf(v))
|
|
|
|
default:
|
|
return errors.Errorf("Unsupported len of type: [][%d]%s", typeField.Type.Elem().Len(), typeField.Type.Elem().Elem().Kind())
|
|
|
|
}
|
|
|
|
default:
|
|
return errors.Errorf("Unsupported type: [][%d]%s", typeField.Type.Elem().Len(), typeField.Type.Elem().Elem().Kind())
|
|
|
|
}
|
|
|
|
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 {
|
|
tb := make([]byte, len(grp[3]))
|
|
for i, b := range grp[3] {
|
|
tb[i] = b
|
|
}
|
|
out = append(out, bytes.TrimSpace(tb))
|
|
continue
|
|
}
|
|
|
|
idx, err := strconv.Atoi(string(grp[2]))
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "Unable to parse array index")
|
|
}
|
|
|
|
out[idx] = make([]byte, len(grp[3]))
|
|
tb := make([]byte, len(grp[3]))
|
|
for i, b := range grp[3] {
|
|
tb[i] = b
|
|
}
|
|
out[idx] = bytes.TrimSpace(tb)
|
|
}
|
|
}
|
|
|
|
return out, errors.Wrap(scanner.Err(), "Unable to parse array lines")
|
|
}
|
|
|
|
func isNilValue(in []byte) bool {
|
|
return reflect.DeepEqual(in, []byte("nil"))
|
|
}
|