2022-04-14 18:39:18 +03:00
|
|
|
package df
|
|
|
|
|
|
|
|
import (
|
2022-04-14 20:06:52 +03:00
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
2022-04-14 18:39:18 +03:00
|
|
|
"fmt"
|
2022-04-14 20:06:52 +03:00
|
|
|
"go/format"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
2022-04-15 16:27:51 +03:00
|
|
|
"sort"
|
2022-04-14 18:39:18 +03:00
|
|
|
"text/template"
|
|
|
|
|
|
|
|
"github.com/iancoleman/strcase"
|
2022-04-15 16:27:51 +03:00
|
|
|
"github.com/robertjanetzko/LegendsBrowser2/backend/util"
|
2022-04-14 18:39:18 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
type Object struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
Id bool `json:"id,omitempty"`
|
|
|
|
Named bool `json:"named,omitempty"`
|
|
|
|
Typed bool `json:"typed,omitempty"`
|
2022-04-15 16:27:51 +03:00
|
|
|
SubTypes *[]Subtype `json:"subtypes,omitempty"`
|
2022-04-14 18:39:18 +03:00
|
|
|
SubTypeOf *string `json:"subtypeof,omitempty"`
|
|
|
|
Fields map[string]Field `json:"fields"`
|
|
|
|
}
|
|
|
|
|
2022-04-15 16:27:51 +03:00
|
|
|
type Subtype struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
BaseType string `json:"base"`
|
|
|
|
PlusType string `json:"plus"`
|
|
|
|
}
|
|
|
|
|
2022-04-14 18:39:18 +03:00
|
|
|
type Field struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
Type string `json:"type"`
|
|
|
|
Multiple bool `json:"multiple,omitempty"`
|
|
|
|
ElementType *string `json:"elements,omitempty"`
|
|
|
|
Legend string `json:"legend"`
|
2022-04-15 16:27:51 +03:00
|
|
|
Base bool
|
|
|
|
Plus bool
|
2022-04-14 18:39:18 +03:00
|
|
|
}
|
|
|
|
|
2022-04-15 11:03:11 +03:00
|
|
|
func (f Field) TypeLine() string {
|
2022-04-14 18:39:18 +03:00
|
|
|
n := f.Name
|
|
|
|
|
|
|
|
if n == "Id" || n == "Name" {
|
|
|
|
n = n + "_"
|
|
|
|
}
|
|
|
|
|
|
|
|
m := ""
|
|
|
|
if f.Multiple {
|
|
|
|
m = "[]"
|
|
|
|
}
|
|
|
|
t := f.Type
|
|
|
|
if f.Type == "array" {
|
2022-04-15 11:03:11 +03:00
|
|
|
t = "[]*" + *f.ElementType
|
2022-04-14 18:39:18 +03:00
|
|
|
}
|
|
|
|
if f.Type == "map" {
|
2022-04-15 11:03:11 +03:00
|
|
|
t = "map[int]*" + *f.ElementType
|
2022-04-14 18:39:18 +03:00
|
|
|
}
|
|
|
|
if f.Type == "object" {
|
2022-04-15 11:03:11 +03:00
|
|
|
t = "*" + *f.ElementType
|
2022-04-14 18:39:18 +03:00
|
|
|
}
|
|
|
|
j := fmt.Sprintf("`json:\"%s\" legend:\"%s\"`", strcase.ToLowerCamel(f.Name), f.Legend)
|
|
|
|
return fmt.Sprintf("%s %s%s %s", n, m, t, j)
|
|
|
|
}
|
|
|
|
|
2022-04-15 16:27:51 +03:00
|
|
|
func (f Field) Init(plus bool) string {
|
|
|
|
if !plus && f.Type == "map" {
|
|
|
|
return fmt.Sprintf("obj.%s = make(map[int]*%s)", f.Name, *f.ElementType)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f Field) StartAction(plus bool) string {
|
2022-04-14 18:39:18 +03:00
|
|
|
n := f.Name
|
|
|
|
|
|
|
|
if n == "Id" || n == "Name" {
|
|
|
|
n = n + "_"
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.Type == "object" {
|
2022-04-15 16:27:51 +03:00
|
|
|
p := fmt.Sprintf("v, _ := parse%s(d, &t)", *f.ElementType)
|
2022-04-14 18:39:18 +03:00
|
|
|
if !f.Multiple {
|
|
|
|
return fmt.Sprintf("%s\nobj.%s = v", p, n)
|
|
|
|
} else {
|
|
|
|
return fmt.Sprintf("%s\nobj.%s = append(obj.%s, v)", p, n, n)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.Type == "array" || f.Type == "map" {
|
2022-04-15 11:03:11 +03:00
|
|
|
gen := fmt.Sprintf("parse%s", *f.ElementType)
|
2022-04-14 18:39:18 +03:00
|
|
|
|
|
|
|
if f.Type == "array" {
|
|
|
|
return fmt.Sprintf("parseArray(d, &obj.%s, %s)", f.Name, gen)
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.Type == "map" {
|
2022-04-15 16:27:51 +03:00
|
|
|
if !plus {
|
|
|
|
return fmt.Sprintf("parseMap(d, &obj.%s, %s)", f.Name, gen)
|
|
|
|
} else {
|
|
|
|
gen = fmt.Sprintf("parse%sPlus", *f.ElementType)
|
|
|
|
return fmt.Sprintf("parseMapPlus(d, &obj.%s, %s)", f.Name, gen)
|
|
|
|
}
|
2022-04-14 18:39:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.Type == "int" || f.Type == "string" {
|
|
|
|
return "data = nil"
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f Field) EndAction() string {
|
|
|
|
n := f.Name
|
|
|
|
|
|
|
|
if n == "Id" || n == "Name" {
|
|
|
|
n = n + "_"
|
|
|
|
}
|
|
|
|
|
|
|
|
if !f.Multiple {
|
|
|
|
if f.Type == "int" {
|
|
|
|
return fmt.Sprintf("obj.%s = n(data)", n)
|
|
|
|
} else if f.Type == "string" {
|
|
|
|
return fmt.Sprintf("obj.%s = string(data)", n)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if f.Type == "int" {
|
|
|
|
return fmt.Sprintf("obj.%s = append(obj.%s, n(data))", n, n)
|
|
|
|
} else if f.Type == "string" {
|
|
|
|
return fmt.Sprintf("obj.%s = append(obj.%s, string(data))", n, n)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2022-04-15 16:27:51 +03:00
|
|
|
func (f Field) Active(plus bool) bool {
|
|
|
|
if plus && f.Plus {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if !plus && f.Base {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
type ActiveSubType struct {
|
|
|
|
Case string
|
|
|
|
Name string
|
|
|
|
Options []string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f Object) ActiveSubTypes(plus bool) []*ActiveSubType {
|
|
|
|
subs := make(map[string]*ActiveSubType)
|
|
|
|
|
|
|
|
for _, s := range *f.SubTypes {
|
|
|
|
if !plus && s.BaseType == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if plus && s.PlusType == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
a := ActiveSubType{}
|
|
|
|
if plus {
|
|
|
|
a.Case = s.PlusType
|
|
|
|
} else {
|
|
|
|
a.Case = s.BaseType
|
|
|
|
}
|
|
|
|
a.Name = f.Name + s.Name
|
|
|
|
a.Options = append(a.Options, a.Name)
|
|
|
|
|
|
|
|
if sub, ok := subs[a.Case]; ok {
|
|
|
|
sub.Options = append(sub.Options, a.Name)
|
|
|
|
} else {
|
|
|
|
subs[a.Case] = &a
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
list := util.Values(subs)
|
|
|
|
sort.SliceStable(list, func(i, j int) bool {
|
|
|
|
return list[i].Case < list[j].Case
|
|
|
|
})
|
|
|
|
|
|
|
|
return list
|
|
|
|
}
|
|
|
|
|
2022-04-14 18:39:18 +03:00
|
|
|
var packageTemplate = template.Must(template.New("").Parse(`// Code generated by legendsbrowser; DO NOT EDIT.
|
|
|
|
package model
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/xml"
|
|
|
|
"strconv"
|
2022-04-15 16:27:51 +03:00
|
|
|
"fmt"
|
2022-04-14 18:39:18 +03:00
|
|
|
)
|
|
|
|
|
2022-04-15 16:27:51 +03:00
|
|
|
{{- range $name, $obj := $.Objects }}
|
2022-04-14 18:39:18 +03:00
|
|
|
type {{ $obj.Name }} struct {
|
|
|
|
{{- range $fname, $field := $obj.Fields }}
|
|
|
|
{{- if not (and (eq $fname "type") (not (not $obj.SubTypes))) }}
|
2022-04-15 11:03:11 +03:00
|
|
|
{{ $field.TypeLine }}
|
2022-04-14 18:39:18 +03:00
|
|
|
{{- end }}
|
|
|
|
{{- end }}
|
|
|
|
{{- if not (not $obj.SubTypes) }}
|
|
|
|
Details any
|
|
|
|
{{- end }}
|
|
|
|
}
|
|
|
|
|
|
|
|
{{- if $obj.Id }}
|
|
|
|
func (x *{{ $obj.Name }}) Id() int { return x.Id_ }
|
|
|
|
{{- end }}
|
|
|
|
{{- if $obj.Named }}
|
|
|
|
func (x *{{ $obj.Name }}) Name() string { return x.Name_ }
|
|
|
|
{{- end }}
|
|
|
|
{{- end }}
|
|
|
|
|
|
|
|
// Parser
|
|
|
|
|
|
|
|
func n(d []byte) int {
|
|
|
|
v, _ := strconv.Atoi(string(d))
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2022-04-15 16:27:51 +03:00
|
|
|
{{- range $name, $obj := $.Objects }}
|
|
|
|
{{- range $plus := $.Modes }}
|
|
|
|
func parse{{ $obj.Name }}{{ if $plus }}Plus{{ end }}(d *xml.Decoder, start *xml.StartElement{{ if $plus }}, obj *{{ $obj.Name }}{{ end }}) (*{{ $obj.Name }}, error) {
|
2022-04-14 18:39:18 +03:00
|
|
|
var (
|
2022-04-15 16:27:51 +03:00
|
|
|
{{- if not $plus }}
|
|
|
|
obj = &{{ $obj.Name }}{}
|
|
|
|
{{- end }}
|
2022-04-14 18:39:18 +03:00
|
|
|
data []byte
|
|
|
|
)
|
2022-04-15 16:27:51 +03:00
|
|
|
{{- if $plus }}
|
|
|
|
if obj == nil {
|
|
|
|
obj = &{{ $obj.Name }}{}
|
|
|
|
}
|
|
|
|
{{- end }}
|
|
|
|
|
|
|
|
{{- range $fname, $field := $obj.Fields }}
|
|
|
|
{{ $field.Init $plus }}
|
|
|
|
{{- end }}
|
|
|
|
|
2022-04-14 18:39:18 +03:00
|
|
|
for {
|
|
|
|
tok, err := d.Token()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
switch t := tok.(type) {
|
|
|
|
case xml.StartElement:
|
|
|
|
switch t.Name.Local {
|
|
|
|
{{- range $fname, $field := $obj.Fields }}
|
2022-04-15 16:27:51 +03:00
|
|
|
{{- if $field.Active $plus }}
|
2022-04-14 18:39:18 +03:00
|
|
|
case "{{ $fname }}":
|
2022-04-15 16:27:51 +03:00
|
|
|
{{ $field.StartAction $plus }}
|
|
|
|
{{- end }}
|
2022-04-14 18:39:18 +03:00
|
|
|
{{- end }}
|
|
|
|
default:
|
|
|
|
// fmt.Println("unknown field", t.Name.Local)
|
|
|
|
d.Skip()
|
|
|
|
}
|
|
|
|
|
|
|
|
case xml.CharData:
|
|
|
|
data = append(data, t...)
|
|
|
|
|
|
|
|
case xml.EndElement:
|
|
|
|
if t.Name.Local == start.Name.Local {
|
2022-04-15 16:27:51 +03:00
|
|
|
return obj, nil
|
2022-04-14 18:39:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
switch t.Name.Local {
|
2022-04-15 16:27:51 +03:00
|
|
|
{{- range $fname, $field := $obj.Fields }}{{- if $field.Active $plus }}
|
2022-04-14 18:39:18 +03:00
|
|
|
case "{{ $fname }}":
|
|
|
|
{{- if and (eq $fname "type") (not (not $obj.SubTypes)) }}
|
2022-04-15 16:27:51 +03:00
|
|
|
|
2022-04-14 18:39:18 +03:00
|
|
|
var err error
|
2022-04-15 16:27:51 +03:00
|
|
|
switch string(data) {
|
|
|
|
{{- range $sub := ($obj.ActiveSubTypes $plus) }}
|
|
|
|
case "{{ $sub.Case }}":
|
|
|
|
{{- if eq 1 (len $sub.Options) }}
|
|
|
|
{{- if not $plus }}
|
|
|
|
obj.Details, err = parse{{ $sub.Name }}(d, start)
|
|
|
|
{{- else }}
|
|
|
|
obj.Details, err = parse{{ $sub.Name }}Plus(d, start, obj.Details.(*{{ $sub.Name }}))
|
|
|
|
{{- end }}
|
|
|
|
{{- else }}
|
|
|
|
switch details := obj.Details.(type) {
|
|
|
|
{{- range $opt := $sub.Options }}
|
|
|
|
case *{{ $opt}}:
|
|
|
|
obj.Details, err = parse{{ $opt }}Plus(d, start, details)
|
|
|
|
{{- end }}
|
|
|
|
default:
|
|
|
|
fmt.Println("unknown subtype option", obj.Details)
|
|
|
|
d.Skip()
|
|
|
|
}
|
|
|
|
{{- end }}
|
2022-04-14 18:39:18 +03:00
|
|
|
{{- end }}
|
|
|
|
default:
|
|
|
|
d.Skip()
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-04-15 16:27:51 +03:00
|
|
|
return obj, nil
|
|
|
|
|
2022-04-14 18:39:18 +03:00
|
|
|
{{- else }}
|
|
|
|
{{ $field.EndAction }}
|
|
|
|
{{- end }}
|
2022-04-15 16:27:51 +03:00
|
|
|
{{- end }}{{- end }}
|
2022-04-14 18:39:18 +03:00
|
|
|
default:
|
|
|
|
// fmt.Println("unknown field", t.Name.Local)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{{- end }}
|
2022-04-15 16:27:51 +03:00
|
|
|
{{- end }}
|
2022-04-14 18:39:18 +03:00
|
|
|
`))
|
2022-04-14 20:06:52 +03:00
|
|
|
|
|
|
|
func generateCode(objects *Metadata) error {
|
|
|
|
file, _ := json.MarshalIndent(objects, "", " ")
|
|
|
|
_ = ioutil.WriteFile("model.json", file, 0644)
|
|
|
|
|
|
|
|
f, err := os.Create("../backend/model/model.go")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
err = packageTemplate.Execute(&buf, struct {
|
|
|
|
Objects *Metadata
|
2022-04-15 16:27:51 +03:00
|
|
|
Modes []bool
|
2022-04-14 20:06:52 +03:00
|
|
|
}{
|
|
|
|
Objects: objects,
|
2022-04-15 16:27:51 +03:00
|
|
|
Modes: []bool{false, true},
|
2022-04-14 20:06:52 +03:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
p, err := format.Source(buf.Bytes())
|
|
|
|
if err != nil {
|
2022-04-15 16:27:51 +03:00
|
|
|
fmt.Println("WARN: could not format source", err)
|
|
|
|
p = buf.Bytes()
|
2022-04-14 20:06:52 +03:00
|
|
|
}
|
|
|
|
_, err = f.Write(p)
|
|
|
|
return err
|
|
|
|
}
|