package df import ( "bytes" "encoding/json" "fmt" "go/format" "io/ioutil" "os" "strings" "text/template" "github.com/iancoleman/strcase" "github.com/robertjanetzko/LegendsBrowser2/backend/util" ) var backendTemplate = template.Must(template.New("").Parse(`// Code generated by legendsbrowser; DO NOT EDIT. package model import ( "encoding/xml" "strconv" "fmt" ) {{- range $name, $obj := $.Objects }} type {{ $obj.Name }} struct { {{- range $fname, $field := $obj.Fields }} {{- if not (and (eq $fname "type") (not (not $obj.SubTypes))) }} {{ $field.TypeLine }} {{- end }} {{- end }} {{- if not (not $obj.SubTypes) }} Details {{ $obj.Name }}Details {{- 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 }} func (x *{{ $obj.Name }}) RelatedToHf(id int) bool { return {{ $obj.Related "hfid,hf_id,_hf,hist_figure_id,Hfid,histfig_id,histfig" }} } {{- end }} // Parser func n(d []byte) int { v, _ := strconv.Atoi(string(d)) return v } {{- 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) { var ( {{- if not $plus }} obj = &{{ $obj.Name }}{} {{- end }} data []byte ) {{- if $plus }} if obj == nil { obj = &{{ $obj.Name }}{} } {{- end }} {{- range $fname, $field := $obj.Fields }} {{ $field.Init $plus }} {{- end }} 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 }} {{- if $field.Active $plus }} case "{{ $fname }}": {{ $field.StartAction $plus }} {{- end }} {{- 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 { return obj, nil } switch t.Name.Local { {{- range $fname, $field := $obj.Fields }}{{- if $field.Active $plus }} case "{{ $fname }}": {{- if and (eq $fname "type") (not (not $obj.SubTypes)) }} var err error 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 }} {{- end }} default: d.Skip() } if err != nil { return nil, err } return obj, nil {{- else }} {{ $field.EndAction }} {{- end }} {{- end }}{{- end }} default: // fmt.Println("unknown field", t.Name.Local) } } } } {{- end }} {{- end }} `)) func generateBackendCode(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 = backendTemplate.Execute(&buf, struct { Objects *Metadata Modes []bool }{ Objects: objects, Modes: []bool{false, true}, }) if err != nil { return err } p, err := format.Source(buf.Bytes()) if err != nil { fmt.Println("WARN: could not format source", err) p = buf.Bytes() } _, err = f.Write(p) return err } func (f Field) TypeLine() string { n := f.Name if n == "Id" || n == "Name" { n = n + "_" } m := "" if f.Multiple { m = "[]" } t := f.Type if f.Type == "array" { t = "[]*" + *f.ElementType } if f.Type == "map" { t = "map[int]*" + *f.ElementType } if f.Type == "object" { t = "*" + *f.ElementType } j := fmt.Sprintf("`json:\"%s\" legend:\"%s\"`", strcase.ToLowerCamel(f.Name), f.Legend) return fmt.Sprintf("%s %s%s %s", n, m, t, j) } 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 { n := f.Name if n == "Id" || n == "Name" { n = n + "_" } if f.Type == "object" { var p string if !plus { p = fmt.Sprintf("v, _ := parse%s(d, &t)", *f.ElementType) } else { p = fmt.Sprintf("v, _ := parse%sPlus(d, &t, &%s{})", *f.ElementType, *f.ElementType) } 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" { gen := fmt.Sprintf("parse%s", *f.ElementType) if f.Type == "array" { return fmt.Sprintf("parseArray(d, &obj.%s, %s)", f.Name, gen) } if f.Type == "map" { 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) } } } if f.Type == "int" || f.Type == "string" || f.Type == "bool" { 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 == "bool" { return fmt.Sprintf("obj.%s = true", 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 "" } func (obj Object) Related(fields string) string { var list []string fs := strings.Split(fields, ",") for n, f := range obj.Fields { if f.Type == "int" && !f.Multiple && util.ContainsAny(n, fs...) { list = append(list, fmt.Sprintf("x.%s == id", f.Name)) } } if len(list) > 0 { return strings.Join(list, " || ") } return "false" }