mirror of
https://github.com/Luzifer/sii.git
synced 2024-10-18 05:14:19 +00:00
Add writing of unit files
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
426e54f372
commit
ccb5be1dbe
11 changed files with 457 additions and 179 deletions
|
@ -11,7 +11,7 @@ type Company struct {
|
||||||
JobOffer []Ptr `sii:"job_offer"`
|
JobOffer []Ptr `sii:"job_offer"`
|
||||||
CargoOfferSeeds []int64 `sii:"cargo_offer_seeds"`
|
CargoOfferSeeds []int64 `sii:"cargo_offer_seeds"`
|
||||||
Discovered bool `sii:"discovered"`
|
Discovered bool `sii:"discovered"`
|
||||||
ReservedTrailerSlot int64 `sii:"reserved_trailer_slot"` // TODO: Maybe wrong type, haven't seen other than "nil"
|
ReservedTrailerSlot Ptr `sii:"reserved_trailer_slot"` // TODO: Maybe wrong type, haven't seen other than "nil"
|
||||||
|
|
||||||
blockName string
|
blockName string
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ func init() {
|
||||||
|
|
||||||
type JobOfferData struct {
|
type JobOfferData struct {
|
||||||
Target string `sii:"target"`
|
Target string `sii:"target"`
|
||||||
ExpirationTime int64 `sii:"expiration_time"`
|
ExpirationTime *int64 `sii:"expiration_time"`
|
||||||
Urgency int64 `sii:"urgency"`
|
Urgency *int64 `sii:"urgency"`
|
||||||
ShortestDistanceKM int64 `sii:"shortest_distance_km"`
|
ShortestDistanceKM int64 `sii:"shortest_distance_km"`
|
||||||
FerryTime int64 `sii:"ferry_time"`
|
FerryTime int64 `sii:"ferry_time"`
|
||||||
FerryPrice int64 `sii:"ferry_price"`
|
FerryPrice int64 `sii:"ferry_price"`
|
||||||
|
@ -17,7 +17,7 @@ type JobOfferData struct {
|
||||||
TrailerDefinition Ptr `sii:"trailer_definition"` // External pointer
|
TrailerDefinition Ptr `sii:"trailer_definition"` // External pointer
|
||||||
UnitsCount int64 `sii:"units_count"`
|
UnitsCount int64 `sii:"units_count"`
|
||||||
FillRatio float32 `sii:"fill_ratio"`
|
FillRatio float32 `sii:"fill_ratio"`
|
||||||
TrailerPlace int64 `sii:"trailer_place"` // TODO: What's this? Seems to be "0" in all jobs
|
TrailerPlace []Placement `sii:"trailer_place"`
|
||||||
|
|
||||||
blockName string
|
blockName string
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Player struct {
|
type Player struct {
|
||||||
HQCity string `sii:"hq_city"`
|
HQCity Ptr `sii:"hq_city"`
|
||||||
Trailers []Ptr `sii:"trailers"`
|
Trailers []Ptr `sii:"trailers"`
|
||||||
TrailerUtilizationLogs []Ptr `sii:"trailer_utilization_logs"`
|
TrailerUtilizationLogs []Ptr `sii:"trailer_utilization_logs"`
|
||||||
TrailerDefs []Ptr `sii:"trailer_defs"`
|
TrailerDefs []Ptr `sii:"trailer_defs"`
|
||||||
|
|
10
datatypes.go
10
datatypes.go
|
@ -21,24 +21,24 @@ var placementRegexp = regexp.MustCompile(`^\(([0-9.]+|&[0-9a-f]+), ([0-9.]+|&[0-
|
||||||
type Placement [7]float32
|
type Placement [7]float32
|
||||||
|
|
||||||
func (p Placement) MarshalSII() ([]byte, error) {
|
func (p Placement) MarshalSII() ([]byte, error) {
|
||||||
var siiFloats = [][]byte{}
|
var siiFloats = make([][]byte, 7)
|
||||||
|
|
||||||
for _, f := range p {
|
for i, f := range p {
|
||||||
b, err := float2sii(f)
|
b, err := float2sii(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "Unable to encode float")
|
return nil, errors.Wrap(err, "Unable to encode float")
|
||||||
}
|
}
|
||||||
siiFloats = append(siiFloats, b)
|
siiFloats[i] = b
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf = new(bytes.Buffer)
|
var buf = new(bytes.Buffer)
|
||||||
|
|
||||||
buf.Write([]byte("("))
|
buf.Write([]byte("("))
|
||||||
bytes.Join(siiFloats[0:3], []byte(", "))
|
buf.Write(bytes.Join(siiFloats[0:3], []byte(", ")))
|
||||||
buf.Write([]byte(") ("))
|
buf.Write([]byte(") ("))
|
||||||
buf.Write(siiFloats[3])
|
buf.Write(siiFloats[3])
|
||||||
buf.Write([]byte("; "))
|
buf.Write([]byte("; "))
|
||||||
bytes.Join(siiFloats[4:7], []byte(", "))
|
buf.Write(bytes.Join(siiFloats[4:7], []byte(", ")))
|
||||||
buf.Write([]byte(")"))
|
buf.Write([]byte(")"))
|
||||||
|
|
||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
|
|
59
generator.go
Normal file
59
generator.go
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package sii
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeSIIPlainFile(unit *Unit, w io.Writer) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Write file header
|
||||||
|
if _, err = fmt.Fprintln(w, "SiiNunit\n{"); err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to write header")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, block := range unit.Entries {
|
||||||
|
// Write block header
|
||||||
|
if _, err = fmt.Fprintf(w, "%s : %s {\n", block.Class(), block.Name()); err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to write block header")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain and write block content
|
||||||
|
var raw []byte
|
||||||
|
|
||||||
|
if reflect.TypeOf(block).Elem().Implements(reflect.TypeOf((*Marshaler)(nil)).Elem()) {
|
||||||
|
raw, err = block.(Marshaler).MarshalSII()
|
||||||
|
} else {
|
||||||
|
raw, err = genericMarshal(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to marshal block")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(raw) > 0 {
|
||||||
|
raw = append(bytes.TrimRight(raw, "\n"), '\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = w.Write(raw); err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to write block data")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close block
|
||||||
|
if _, err = fmt.Fprintf(w, "}\n\n"); err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to close block")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write file footer
|
||||||
|
if _, err = fmt.Fprintln(w, "}"); err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to write footer")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,36 +1,26 @@
|
||||||
package sii
|
package sii
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
singleLineValue = `^\s*%s\s?:\s?(.+)$`
|
|
||||||
arrayLineValue = `^\s*%s(\[([0-9]*)\])?\s?:\s?(.+)$`
|
|
||||||
)
|
|
||||||
|
|
||||||
func genericMarshal(in interface{}) ([]byte, error) {
|
func genericMarshal(in interface{}) ([]byte, error) {
|
||||||
return nil, errors.New("Not implemented")
|
if reflect.TypeOf(in).Kind() == reflect.Ptr {
|
||||||
|
in = reflect.ValueOf(in).Elem().Interface()
|
||||||
}
|
}
|
||||||
|
|
||||||
func genericUnmarshal(in []byte, out interface{}, unit *Unit) error {
|
if reflect.TypeOf(in).Kind() != reflect.Struct {
|
||||||
if reflect.TypeOf(out).Kind() != reflect.Ptr {
|
return nil, errors.New("Calling marshaller with non-struct")
|
||||||
return errors.New("Calling parser with non-pointer")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if reflect.ValueOf(out).Elem().Kind() != reflect.Struct {
|
var buf = new(bytes.Buffer)
|
||||||
return errors.New("Calling parser with pointer to non-struct")
|
|
||||||
}
|
|
||||||
|
|
||||||
st := reflect.ValueOf(out).Elem()
|
st := reflect.ValueOf(in)
|
||||||
for i := 0; i < st.NumField(); i++ {
|
for i := 0; i < st.NumField(); i++ {
|
||||||
valField := st.Field(i)
|
valField := st.Field(i)
|
||||||
typeField := st.Type().Field(i)
|
typeField := st.Type().Field(i)
|
||||||
|
@ -44,208 +34,146 @@ func genericUnmarshal(in []byte, out interface{}, unit *Unit) error {
|
||||||
switch typeField.Type {
|
switch typeField.Type {
|
||||||
|
|
||||||
case reflect.TypeOf(Ptr{}):
|
case reflect.TypeOf(Ptr{}):
|
||||||
data := getSingleValue(in, attributeName)
|
v := valField.Interface().(Ptr).MarshalSII()
|
||||||
v := Ptr{unit: unit}
|
buf.WriteString(fmt.Sprintf(" %s: %s\n", attributeName, v))
|
||||||
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
|
continue
|
||||||
|
|
||||||
case reflect.TypeOf(Placement{}):
|
case reflect.TypeOf(Placement{}):
|
||||||
data := getSingleValue(in, attributeName)
|
v, err := valField.Interface().(Placement).MarshalSII()
|
||||||
v := Placement{}
|
if err != nil {
|
||||||
if err := v.UnmarshalSII(data); err != nil {
|
return nil, errors.Wrap(err, "Unable to encode Placement")
|
||||||
return errors.Wrapf(err, "Unable to parse Placement for attribute %q", attributeName)
|
|
||||||
}
|
}
|
||||||
valField.Set(reflect.ValueOf(v))
|
buf.WriteString(fmt.Sprintf(" %s: %s\n", attributeName, v))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch typeField.Type.Kind() {
|
switch typeField.Type.Kind() {
|
||||||
|
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
v, err := strconv.ParseBool(string(getSingleValue(in, attributeName)))
|
v := valField.Bool()
|
||||||
if err != nil {
|
buf.WriteString(fmt.Sprintf(" %s: %s\n", attributeName, strconv.FormatBool(v)))
|
||||||
return errors.Wrapf(err, "Unable to parse boolean for attribute %q", attributeName)
|
|
||||||
}
|
|
||||||
valField.SetBool(v)
|
|
||||||
|
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
v, err := sii2float(getSingleValue(in, attributeName))
|
v, err := float2sii(float32(valField.Float()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Unable to parse float for attribute %q", attributeName)
|
return nil, errors.Wrap(err, "Unable to encode float32")
|
||||||
}
|
}
|
||||||
valField.Set(reflect.ValueOf(v))
|
buf.WriteString(fmt.Sprintf(" %s: %s\n", attributeName, v))
|
||||||
|
|
||||||
case reflect.Int, reflect.Int64:
|
case reflect.Int, reflect.Int64:
|
||||||
bv := getSingleValue(in, attributeName)
|
buf.WriteString(fmt.Sprintf(" %s: %d\n", attributeName, valField.Int()))
|
||||||
if isNilValue(bv) {
|
|
||||||
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:
|
case reflect.String:
|
||||||
v := strings.Trim(string(getSingleValue(in, attributeName)), `"`)
|
buf.WriteString(fmt.Sprintf(" %s: %q\n", attributeName, valField.String()))
|
||||||
valField.SetString(v)
|
|
||||||
|
|
||||||
case reflect.Uint64:
|
case reflect.Uint64:
|
||||||
v, err := strconv.ParseUint(string(getSingleValue(in, attributeName)), 10, 64)
|
v := strconv.FormatUint(valField.Uint(), 10)
|
||||||
if err != nil {
|
buf.WriteString(fmt.Sprintf(" %s: %s\n", attributeName, v))
|
||||||
return errors.Wrapf(err, "Unable to parse uint for attribute %q", attributeName)
|
|
||||||
|
case reflect.Ptr:
|
||||||
|
|
||||||
|
switch typeField.Type.Elem().Kind() {
|
||||||
|
|
||||||
|
case reflect.Int64:
|
||||||
|
var v string
|
||||||
|
if valField.IsNil() {
|
||||||
|
v = "nil"
|
||||||
|
} else {
|
||||||
|
v = strconv.FormatInt(valField.Elem().Int(), 10)
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf(" %s: %s\n", attributeName, v))
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, errors.Errorf("Unsupported type: *%s", typeField.Type.Elem().Kind())
|
||||||
|
|
||||||
}
|
}
|
||||||
valField.SetUint(v)
|
|
||||||
|
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
ba, err := getArrayValues(in, attributeName)
|
var values []string
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "Unable to fetch array values for attribute %q", attributeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch typeField.Type.Elem() {
|
switch typeField.Type.Elem() {
|
||||||
|
|
||||||
case reflect.TypeOf(Ptr{}):
|
case reflect.TypeOf(Ptr{}):
|
||||||
var v []Ptr
|
for _, val := range valField.Interface().([]Ptr) {
|
||||||
for _, bv := range ba {
|
values = append(values, string(val.MarshalSII()))
|
||||||
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)
|
buf.Write(encodeSliceValue(attributeName, values))
|
||||||
}
|
|
||||||
valField.Set(reflect.ValueOf(v))
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case reflect.TypeOf(Placement{}):
|
case reflect.TypeOf(Placement{}):
|
||||||
var v []Placement
|
for _, val := range valField.Interface().([]Placement) {
|
||||||
for _, bv := range ba {
|
ev, err := val.MarshalSII()
|
||||||
e := Placement{}
|
if err != nil {
|
||||||
if err := e.UnmarshalSII(bv); err != nil {
|
return nil, errors.Wrap(err, "Unable to encode Placement")
|
||||||
return errors.Wrapf(err, "Unable to parse Placement for attribute %q", attributeName)
|
|
||||||
}
|
}
|
||||||
v = append(v, e)
|
values = append(values, string(ev))
|
||||||
}
|
}
|
||||||
valField.Set(reflect.ValueOf(v))
|
buf.Write(encodeSliceValue(attributeName, values))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case reflect.TypeOf(RawValue{}):
|
case reflect.TypeOf(RawValue{}):
|
||||||
var v []RawValue
|
for _, val := range valField.Interface().([]RawValue) {
|
||||||
for _, bv := range ba {
|
ev, err := val.MarshalSII()
|
||||||
e := RawValue{}
|
if err != nil {
|
||||||
if err := e.UnmarshalSII(bv); err != nil {
|
return nil, errors.Wrap(err, "Unable to encode RawValue")
|
||||||
return errors.Wrapf(err, "Unable to parse RawValue for attribute %q", attributeName)
|
|
||||||
}
|
}
|
||||||
v = append(v, e)
|
values = append(values, string(ev))
|
||||||
}
|
}
|
||||||
valField.Set(reflect.ValueOf(v))
|
buf.Write(encodeSliceValue(attributeName, values))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch typeField.Type.Elem().Kind() {
|
switch typeField.Type.Elem().Kind() {
|
||||||
|
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
var v []bool
|
for _, val := range valField.Interface().([]bool) {
|
||||||
for _, bv := range ba {
|
values = append(values, strconv.FormatBool(val))
|
||||||
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)
|
buf.Write(encodeSliceValue(attributeName, values))
|
||||||
}
|
|
||||||
valField.Set(reflect.ValueOf(v))
|
|
||||||
|
|
||||||
case reflect.Int:
|
case reflect.Int:
|
||||||
var v []int
|
for _, val := range valField.Interface().([]int) {
|
||||||
for _, bv := range ba {
|
values = append(values, strconv.FormatInt(int64(val), 10))
|
||||||
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)
|
buf.Write(encodeSliceValue(attributeName, values))
|
||||||
}
|
|
||||||
valField.Set(reflect.ValueOf(v))
|
|
||||||
|
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
var v []int64
|
for _, val := range valField.Interface().([]int64) {
|
||||||
for _, bv := range ba {
|
values = append(values, strconv.FormatInt(val, 10))
|
||||||
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)
|
buf.Write(encodeSliceValue(attributeName, values))
|
||||||
}
|
|
||||||
valField.Set(reflect.ValueOf(v))
|
|
||||||
|
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
var v []string
|
for _, val := range valField.Interface().([]string) {
|
||||||
for _, bv := range ba {
|
values = append(values, val)
|
||||||
v = append(v, strings.Trim(string(bv), `"`))
|
|
||||||
}
|
}
|
||||||
valField.Set(reflect.ValueOf(v))
|
buf.Write(encodeSliceValue(attributeName, values))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("Unsupported type: []%s", typeField.Type.Elem().Kind())
|
return nil, errors.Errorf("Unsupported type: []%s", typeField.Type.Elem().Kind())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("Unsupported type: %s", typeField.Type.Kind())
|
return nil, errors.Errorf("Unsupported type: %s", typeField.Type.Kind())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSingleValue(in []byte, name string) []byte {
|
return buf.Bytes(), nil
|
||||||
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) {
|
func encodeSliceValue(attributeName string, values []string) []byte {
|
||||||
rex := regexp.MustCompile(fmt.Sprintf(arrayLineValue, name))
|
var buf = new(bytes.Buffer)
|
||||||
var out [][]byte
|
|
||||||
|
|
||||||
var scanner = bufio.NewScanner(bytes.NewReader(in))
|
fmt.Fprintf(buf, " %s: %d\n", attributeName, len(values))
|
||||||
for scanner.Scan() {
|
|
||||||
if rex.Match(scanner.Bytes()) {
|
for i, v := range values {
|
||||||
grp := rex.FindSubmatch(scanner.Bytes())
|
fmt.Fprintf(buf, " %s[%d]: %s\n", attributeName, i, v)
|
||||||
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 {
|
return buf.Bytes()
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNilValue(in []byte) bool {
|
|
||||||
return reflect.DeepEqual(in, []byte("nil"))
|
|
||||||
}
|
}
|
||||||
|
|
270
genericUnmarshaller.go
Normal file
270
genericUnmarshaller.go
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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.Ptr:
|
||||||
|
|
||||||
|
switch typeField.Type.Elem().Kind() {
|
||||||
|
|
||||||
|
case reflect.Int64:
|
||||||
|
bv := getSingleValue(in, attributeName)
|
||||||
|
if !isNilValue(bv) {
|
||||||
|
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.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] = make([]byte, len(grp[3]))
|
||||||
|
for i, b := range grp[3] {
|
||||||
|
out[idx][i] = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, errors.Wrap(scanner.Err(), "Unable to parse array lines")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNilValue(in []byte) bool {
|
||||||
|
return reflect.DeepEqual(in, []byte("nil"))
|
||||||
|
}
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -15,6 +17,10 @@ func float2sii(f float32) ([]byte, error) {
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if math.Floor(float64(f)) == float64(f) {
|
||||||
|
return []byte(fmt.Sprintf("%.0f", f)), nil
|
||||||
|
}
|
||||||
|
|
||||||
err = binary.Write(buf, binary.BigEndian, f)
|
err = binary.Write(buf, binary.BigEndian, f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "Unable to encode float")
|
return nil, errors.Wrap(err, "Unable to encode float")
|
||||||
|
|
|
@ -49,7 +49,7 @@ func parseSIIPlainFile(r io.Reader) (*Unit, error) {
|
||||||
|
|
||||||
case line == "}":
|
case line == "}":
|
||||||
if inBlock {
|
if inBlock {
|
||||||
if err := processBlock(unit, blockClass, blockName, blockContent); err != nil {
|
if err := processBlock(unit, blockClass, blockName, bytes.Trim(blockContent, "\n")); err != nil {
|
||||||
return nil, errors.Wrap(err, "Unable to process block")
|
return nil, errors.Wrap(err, "Unable to process block")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
19
sii.go
19
sii.go
|
@ -117,8 +117,23 @@ func WriteUnitFile(filename string, encrypt bool, data *Unit) error {
|
||||||
return ErrNoEncryptionKeySet
|
return ErrNoEncryptionKeySet
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Implement this
|
var buf = new(bytes.Buffer)
|
||||||
return errors.New("Not implemented")
|
|
||||||
|
if err := writeSIIPlainFile(data, buf); err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to create SII file content")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Implement encryption
|
||||||
|
|
||||||
|
// Create output file
|
||||||
|
f, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to open output file")
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
_, err = buf.WriteTo(f)
|
||||||
|
return errors.Wrap(err, "Unable to write buffer")
|
||||||
}
|
}
|
||||||
|
|
||||||
func readFTHeader(f io.ReaderAt) ([]byte, error) {
|
func readFTHeader(f io.ReaderAt) ([]byte, error) {
|
||||||
|
|
2
unit.go
2
unit.go
|
@ -25,7 +25,7 @@ func (r *RawBlock) Init(class, name string) {
|
||||||
r.blockClass = class
|
r.blockClass = class
|
||||||
r.blockName = name
|
r.blockName = name
|
||||||
}
|
}
|
||||||
func (r RawBlock) MarshalSII() []byte { return r.Data }
|
func (r RawBlock) MarshalSII() ([]byte, error) { return r.Data, nil }
|
||||||
func (r RawBlock) Name() string { return r.blockName }
|
func (r RawBlock) Name() string { return r.blockName }
|
||||||
func (r RawBlock) Class() string { return r.blockClass }
|
func (r RawBlock) Class() string { return r.blockClass }
|
||||||
func (r *RawBlock) UnmarshalSII(in []byte) error {
|
func (r *RawBlock) UnmarshalSII(in []byte) error {
|
||||||
|
|
Loading…
Reference in a new issue