v0.0.342 support json data in enum comment
Some checks failed
Build Docker and Deploy / Run goext test-suite (push) Failing after 1m29s

This commit is contained in:
Mike Schwörer 2023-12-07 17:57:06 +01:00
parent b0be93a7a0
commit f042183433
Signed by: Mikescher
GPG Key ID: D3C7172E0A70F8CF
9 changed files with 227 additions and 34 deletions

Binary file not shown.

View File

@ -12,8 +12,8 @@ import (
"time" "time"
) )
//go:embed _test_example.tgz //go:embed _test_example_1.tgz
var CSIDExampleModels []byte var CSIDExampleModels1 []byte
func TestGenerateCSIDSpecs(t *testing.T) { func TestGenerateCSIDSpecs(t *testing.T) {
@ -21,7 +21,7 @@ func TestGenerateCSIDSpecs(t *testing.T) {
tmpDir := filepath.Join(t.TempDir(), langext.MustHexUUID()) tmpDir := filepath.Join(t.TempDir(), langext.MustHexUUID())
err := os.WriteFile(tmpFile, CSIDExampleModels, 0o777) err := os.WriteFile(tmpFile, CSIDExampleModels1, 0o777)
tst.AssertNoErr(t, err) tst.AssertNoErr(t, err)
t.Cleanup(func() { _ = os.Remove(tmpFile) }) t.Cleanup(func() { _ = os.Remove(tmpFile) })

View File

@ -3,6 +3,7 @@ package bfcodegen
import ( import (
"bytes" "bytes"
_ "embed" _ "embed"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"go/format" "go/format"
@ -14,6 +15,7 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"reflect"
"regexp" "regexp"
"strings" "strings"
"text/template" "text/template"
@ -23,6 +25,8 @@ type EnumDefVal struct {
VarName string VarName string
Value string Value string
Description *string Description *string
Data *map[string]any
RawComment *string
} }
type EnumDef struct { type EnumDef struct {
@ -37,7 +41,7 @@ var rexEnumPackage = rext.W(regexp.MustCompile(`^package\s+(?P<name>[A-Za-z0-9_]
var rexEnumDef = rext.W(regexp.MustCompile(`^\s*type\s+(?P<name>[A-Za-z0-9_]+)\s+(?P<type>[A-Za-z0-9_]+)\s*//\s*(@enum:type).*$`)) var rexEnumDef = rext.W(regexp.MustCompile(`^\s*type\s+(?P<name>[A-Za-z0-9_]+)\s+(?P<type>[A-Za-z0-9_]+)\s*//\s*(@enum:type).*$`))
var rexEnumValueDef = rext.W(regexp.MustCompile(`^\s*(?P<name>[A-Za-z0-9_]+)\s+(?P<type>[A-Za-z0-9_]+)\s*=\s*(?P<value>("[A-Za-z0-9_:\s\-.]+"|[0-9]+))\s*(//(?P<descr>.*))?.*$`)) var rexEnumValueDef = rext.W(regexp.MustCompile(`^\s*(?P<name>[A-Za-z0-9_]+)\s+(?P<type>[A-Za-z0-9_]+)\s*=\s*(?P<value>("[A-Za-z0-9_:\s\-.]+"|[0-9]+))\s*(//(?P<comm>.*))?.*$`))
var rexEnumChecksumConst = rext.W(regexp.MustCompile(`const ChecksumEnumGenerator = "(?P<cs>[A-Za-z0-9_]*)"`)) var rexEnumChecksumConst = rext.W(regexp.MustCompile(`const ChecksumEnumGenerator = "(?P<cs>[A-Za-z0-9_]*)"`))
@ -46,11 +50,6 @@ var templateEnumGenerateText string
func GenerateEnumSpecs(sourceDir string, destFile string) error { func GenerateEnumSpecs(sourceDir string, destFile string) error {
files, err := os.ReadDir(sourceDir)
if err != nil {
return err
}
oldChecksum := "N/A" oldChecksum := "N/A"
if _, err := os.Stat(destFile); !os.IsNotExist(err) { if _, err := os.Stat(destFile); !os.IsNotExist(err) {
content, err := os.ReadFile(destFile) content, err := os.ReadFile(destFile)
@ -62,6 +61,26 @@ func GenerateEnumSpecs(sourceDir string, destFile string) error {
} }
} }
gocode, _, err := _generateEnumSpecs(sourceDir, oldChecksum, destFile, true)
if err != nil {
return err
}
err = os.WriteFile(destFile, []byte(gocode), 0o755)
if err != nil {
return err
}
return nil
}
func _generateEnumSpecs(sourceDir string, destFile string, oldChecksum string, gofmt bool) (string, string, error) {
files, err := os.ReadDir(sourceDir)
if err != nil {
return "", "", err
}
files = langext.ArrFilter(files, func(v os.DirEntry) bool { return v.Name() != path.Base(destFile) }) files = langext.ArrFilter(files, func(v os.DirEntry) bool { return v.Name() != path.Base(destFile) })
files = langext.ArrFilter(files, func(v os.DirEntry) bool { return strings.HasSuffix(v.Name(), ".go") }) files = langext.ArrFilter(files, func(v os.DirEntry) bool { return strings.HasSuffix(v.Name(), ".go") })
files = langext.ArrFilter(files, func(v os.DirEntry) bool { return !strings.HasSuffix(v.Name(), "_gen.go") }) files = langext.ArrFilter(files, func(v os.DirEntry) bool { return !strings.HasSuffix(v.Name(), "_gen.go") })
@ -71,7 +90,7 @@ func GenerateEnumSpecs(sourceDir string, destFile string) error {
for _, f := range files { for _, f := range files {
content, err := os.ReadFile(path.Join(sourceDir, f.Name())) content, err := os.ReadFile(path.Join(sourceDir, f.Name()))
if err != nil { if err != nil {
return err return "", "", err
} }
newChecksumStr += "\n" + f.Name() + "\t" + cryptext.BytesSha256(content) newChecksumStr += "\n" + f.Name() + "\t" + cryptext.BytesSha256(content)
} }
@ -82,7 +101,7 @@ func GenerateEnumSpecs(sourceDir string, destFile string) error {
fmt.Printf("[EnumGenerate] Checksum has changed ( %s -> %s ), will generate new file\n\n", oldChecksum, newChecksum) fmt.Printf("[EnumGenerate] Checksum has changed ( %s -> %s ), will generate new file\n\n", oldChecksum, newChecksum)
} else { } else {
fmt.Printf("[EnumGenerate] Checksum unchanged ( %s ), nothing to do\n", oldChecksum) fmt.Printf("[EnumGenerate] Checksum unchanged ( %s ), nothing to do\n", oldChecksum)
return nil return "", oldChecksum, nil
} }
allEnums := make([]EnumDef, 0) allEnums := make([]EnumDef, 0)
@ -93,7 +112,7 @@ func GenerateEnumSpecs(sourceDir string, destFile string) error {
fmt.Printf("========= %s =========\n\n", f.Name()) fmt.Printf("========= %s =========\n\n", f.Name())
fileEnums, pn, err := processEnumFile(sourceDir, path.Join(sourceDir, f.Name())) fileEnums, pn, err := processEnumFile(sourceDir, path.Join(sourceDir, f.Name()))
if err != nil { if err != nil {
return err return "", "", err
} }
fmt.Printf("\n") fmt.Printf("\n")
@ -106,20 +125,21 @@ func GenerateEnumSpecs(sourceDir string, destFile string) error {
} }
if pkgname == "" { if pkgname == "" {
return errors.New("no package name found in any file") return "", "", errors.New("no package name found in any file")
} }
fdata, err := format.Source([]byte(fmtEnumOutput(newChecksum, allEnums, pkgname))) rdata := fmtEnumOutput(newChecksum, allEnums, pkgname)
if !gofmt {
return rdata, newChecksum, nil
}
fdata, err := format.Source([]byte(rdata))
if err != nil { if err != nil {
return err return "", "", err
} }
err = os.WriteFile(destFile, fdata, 0o755) return string(fdata), newChecksum, nil
if err != nil {
return err
}
return nil
} }
func processEnumFile(basedir string, fn string) ([]EnumDef, string, error) { func processEnumFile(basedir string, fn string) ([]EnumDef, string, error) {
@ -171,10 +191,34 @@ func processEnumFile(basedir string, fn string) ([]EnumDef, string, error) {
if match, ok := rexEnumValueDef.MatchFirst(line); ok { if match, ok := rexEnumValueDef.MatchFirst(line); ok {
typename := match.GroupByName("type").Value() typename := match.GroupByName("type").Value()
comment := match.GroupByNameOrEmpty("comm").ValueOrNil()
var descr *string = nil
var data *map[string]any = nil
if comment != nil {
comment = langext.Ptr(strings.TrimSpace(*comment))
if strings.HasPrefix(*comment, "{") {
if v, ok := tryParseDataComment(*comment); ok {
data = &v
if anyDataDescr, ok := v["description"]; ok {
if dataDescr, ok := anyDataDescr.(string); ok {
descr = &dataDescr
}
}
} else {
descr = comment
}
} else {
descr = comment
}
}
def := EnumDefVal{ def := EnumDefVal{
VarName: match.GroupByName("name").Value(), VarName: match.GroupByName("name").Value(),
Value: match.GroupByName("value").Value(), Value: match.GroupByName("value").Value(),
Description: match.GroupByNameOrEmpty("descr").ValueOrNil(), RawComment: comment,
Description: descr,
Data: data,
} }
found := false found := false
@ -199,6 +243,41 @@ func processEnumFile(basedir string, fn string) ([]EnumDef, string, error) {
return enums, pkgname, nil return enums, pkgname, nil
} }
func tryParseDataComment(s string) (map[string]any, bool) {
r := make(map[string]any)
err := json.Unmarshal([]byte(s), &r)
if err != nil {
return nil, false
}
for _, v := range r {
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Ptr && rv.IsNil() {
continue
}
if rv.Kind() == reflect.Bool {
continue
}
if rv.Kind() == reflect.String {
continue
}
if rv.Kind() == reflect.Int64 {
continue
}
if rv.Kind() == reflect.Float64 {
continue
}
return nil, false
}
return r, true
}
func fmtEnumOutput(cs string, enums []EnumDef, pkgname string) string { func fmtEnumOutput(cs string, enums []EnumDef, pkgname string) string {
templ := template.New("enum-generate") templ := template.New("enum-generate")
@ -211,6 +290,47 @@ func fmtEnumOutput(cs string, enums []EnumDef, pkgname string) string {
"hasDescr": func(v EnumDef) bool { "hasDescr": func(v EnumDef) bool {
return langext.ArrAll(v.Values, func(val EnumDefVal) bool { return val.Description != nil }) return langext.ArrAll(v.Values, func(val EnumDefVal) bool { return val.Description != nil })
}, },
"hasData": func(v EnumDef) bool {
return len(v.Values) > 0 && langext.ArrAll(v.Values, func(val EnumDefVal) bool { return val.Data != nil })
},
"gostr": func(v any) string {
return fmt.Sprintf("%#+v", v)
},
"goobj": func(name string, v any) string {
return fmt.Sprintf("%#+v", v)
},
"godatakey": func(v string) string {
return strings.ToUpper(v[0:1]) + v[1:]
},
"godatavalue": func(v any) string {
return fmt.Sprintf("%#+v", v)
},
"godatatype": func(v any) string {
return fmt.Sprintf("%T", v)
},
"mapindex": func(v map[string]any, k string) any {
return v[k]
},
"generalDataKeys": func(v EnumDef) map[string]string {
r0 := make(map[string]int)
for _, eval := range v.Values {
for k := range *eval.Data {
if ctr, ok := r0[k]; ok {
r0[k] = ctr + 1
} else {
r0[k] = 1
}
}
}
r1 := langext.MapToArr(r0)
r2 := langext.ArrFilter(r1, func(p langext.MapEntry[string, int]) bool { return p.Value == len(v.Values) })
r3 := langext.ArrMap(r2, func(p langext.MapEntry[string, int]) string { return p.Key })
r4 := langext.ArrToKVMap(r3, func(p string) string { return p }, func(p string) string { return fmt.Sprintf("%T", (*v.Values[0].Data)[p]) })
return r4
},
}) })
templ = template.Must(templ.Parse(templateEnumGenerateText)) templ = template.Must(templ.Parse(templateEnumGenerateText))

View File

@ -11,21 +11,38 @@ const ChecksumEnumGenerator = "{{.Checksum}}" // GoExtVersion: {{.GoextVersion}}
{{ $hasStr := ( . | hasStr ) }} {{ $hasStr := ( . | hasStr ) }}
{{ $hasDescr := ( . | hasDescr ) }} {{ $hasDescr := ( . | hasDescr ) }}
{{ $hasData := ( . | hasData ) }}
// ================================ {{.EnumTypeName}} ================================ // ================================ {{.EnumTypeName}} ================================
// //
// File: {{.FileRelative}} // File: {{.FileRelative}}
// StringEnum: {{$hasStr | boolToStr}} // StringEnum: {{$hasStr | boolToStr}}
// DescrEnum: {{$hasDescr | boolToStr}} // DescrEnum: {{$hasDescr | boolToStr}}
// DataEnum: {{$hasData | boolToStr}}
// //
{{ $typename := .EnumTypeName }}
{{ $enumdef := . }}
var __{{.EnumTypeName}}Values = []{{.EnumTypeName}}{ {{range .Values}} var __{{.EnumTypeName}}Values = []{{.EnumTypeName}}{ {{range .Values}}
{{.VarName}}, {{end}} {{.VarName}}, {{end}}
} }
{{if $hasDescr}} {{if $hasDescr}}
var __{{.EnumTypeName}}Descriptions = map[{{.EnumTypeName}}]string{ {{range .Values}} var __{{.EnumTypeName}}Descriptions = map[{{.EnumTypeName}}]string{ {{range .Values}}
{{.VarName}}: "{{.Description | deref | trimSpace}}", {{end}} {{.VarName}}: {{.Description | deref | trimSpace | gostr}}, {{end}}
}
{{end}}
{{if $hasData}}
type {{ .EnumTypeName }}Data struct { {{ range $datakey, $datatype := ($enumdef | generalDataKeys) }}
{{ $datakey | godatakey }} {{ $datatype }} `json:"{{ $datakey }}"` {{ end }}
}
var __{{.EnumTypeName}}Data = map[{{.EnumTypeName}}]{{.EnumTypeName}}Data{ {{range .Values}} {{ $enumvalue := . }}
{{.VarName}}: {{ $typename }}Data{ {{ range $datakey, $datatype := $enumdef | generalDataKeys }}
{{ $datakey | godatakey }}: {{ (mapindex $enumvalue.Data $datakey) | godatavalue }}, {{ end }}
}, {{end}}
} }
{{end}} {{end}}
@ -64,6 +81,15 @@ func (e {{.EnumTypeName}}) Description() string {
} }
{{end}} {{end}}
{{if $hasData}}
func (e {{.EnumTypeName}}) Data() {{.EnumTypeName}}Data {
if d, ok := __{{.EnumTypeName}}Data[e]; ok {
return d
}
return {{.EnumTypeName}}Data{}
}
{{end}}
func (e {{.EnumTypeName}}) VarName() string { func (e {{.EnumTypeName}}) VarName() string {
if d, ok := __{{.EnumTypeName}}Varnames[e]; ok { if d, ok := __{{.EnumTypeName}}Varnames[e]; ok {
return d return d

View File

@ -12,8 +12,11 @@ import (
"time" "time"
) )
//go:embed _test_example.tgz //go:embed _test_example_1.tgz
var EnumExampleModels []byte var EnumExampleModels1 []byte
//go:embed _test_example_2.tgz
var EnumExampleModels2 []byte
func TestGenerateEnumSpecs(t *testing.T) { func TestGenerateEnumSpecs(t *testing.T) {
@ -21,7 +24,7 @@ func TestGenerateEnumSpecs(t *testing.T) {
tmpDir := filepath.Join(t.TempDir(), langext.MustHexUUID()) tmpDir := filepath.Join(t.TempDir(), langext.MustHexUUID())
err := os.WriteFile(tmpFile, EnumExampleModels, 0o777) err := os.WriteFile(tmpFile, EnumExampleModels1, 0o777)
tst.AssertNoErr(t, err) tst.AssertNoErr(t, err)
t.Cleanup(func() { _ = os.Remove(tmpFile) }) t.Cleanup(func() { _ = os.Remove(tmpFile) })
@ -34,17 +37,53 @@ func TestGenerateEnumSpecs(t *testing.T) {
_, err = cmdext.Runner("tar").Arg("-xvzf").Arg(tmpFile).Arg("-C").Arg(tmpDir).FailOnExitCode().FailOnTimeout().Timeout(time.Minute).Run() _, err = cmdext.Runner("tar").Arg("-xvzf").Arg(tmpFile).Arg("-C").Arg(tmpDir).FailOnExitCode().FailOnTimeout().Timeout(time.Minute).Run()
tst.AssertNoErr(t, err) tst.AssertNoErr(t, err)
err = GenerateEnumSpecs(tmpDir, tmpDir+"/enums_gen.go") s1, cs1, err := _generateEnumSpecs(tmpDir, "", "N/A", true)
tst.AssertNoErr(t, err) tst.AssertNoErr(t, err)
err = GenerateEnumSpecs(tmpDir, tmpDir+"/enums_gen.go") s2, cs2, err := _generateEnumSpecs(tmpDir, "", "N/A", true)
tst.AssertNoErr(t, err) tst.AssertNoErr(t, err)
tst.AssertEqual(t, cs1, cs2)
tst.AssertEqual(t, s1, s2)
fmt.Println() fmt.Println()
fmt.Println() fmt.Println()
fmt.Println() fmt.Println()
fmt.Println("=====================================================================================================") fmt.Println("=====================================================================================================")
fmt.Println(string(tst.Must(os.ReadFile(tmpDir + "/enums_gen.go"))(t))) fmt.Println(s1)
fmt.Println("=====================================================================================================")
fmt.Println()
fmt.Println()
fmt.Println()
}
func TestGenerateEnumSpecsData(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), langext.MustHexUUID()+".tgz")
tmpDir := filepath.Join(t.TempDir(), langext.MustHexUUID())
err := os.WriteFile(tmpFile, EnumExampleModels2, 0o777)
tst.AssertNoErr(t, err)
t.Cleanup(func() { _ = os.Remove(tmpFile) })
err = os.Mkdir(tmpDir, 0o777)
tst.AssertNoErr(t, err)
t.Cleanup(func() { _ = os.RemoveAll(tmpFile) })
_, err = cmdext.Runner("tar").Arg("-xvzf").Arg(tmpFile).Arg("-C").Arg(tmpDir).FailOnExitCode().FailOnTimeout().Timeout(time.Minute).Run()
tst.AssertNoErr(t, err)
s1, _, err := _generateEnumSpecs(tmpDir, "", "", true)
tst.AssertNoErr(t, err)
fmt.Println()
fmt.Println()
fmt.Println()
fmt.Println("=====================================================================================================")
fmt.Println(s1)
fmt.Println("=====================================================================================================") fmt.Println("=====================================================================================================")
fmt.Println() fmt.Println()
fmt.Println() fmt.Println()

View File

@ -12,8 +12,8 @@ import (
"time" "time"
) )
//go:embed _test_example.tgz //go:embed _test_example_1.tgz
var IDExampleModels []byte var IDExampleModels1 []byte
func TestGenerateIDSpecs(t *testing.T) { func TestGenerateIDSpecs(t *testing.T) {
@ -21,7 +21,7 @@ func TestGenerateIDSpecs(t *testing.T) {
tmpDir := filepath.Join(t.TempDir(), langext.MustHexUUID()) tmpDir := filepath.Join(t.TempDir(), langext.MustHexUUID())
err := os.WriteFile(tmpFile, IDExampleModels, 0o777) err := os.WriteFile(tmpFile, IDExampleModels1, 0o777)
tst.AssertNoErr(t, err) tst.AssertNoErr(t, err)
t.Cleanup(func() { _ = os.Remove(tmpFile) }) t.Cleanup(func() { _ = os.Remove(tmpFile) })

View File

@ -1,5 +1,5 @@
package goext package goext
const GoextVersion = "0.0.341" const GoextVersion = "0.0.342"
const GoextVersionTimestamp = "2023-12-07T14:43:12+0100" const GoextVersionTimestamp = "2023-12-07T17:57:06+0100"

View File

@ -29,6 +29,14 @@ func ArrToMap[T comparable, V any](a []V, keyfunc func(V) T) map[T]V {
return result return result
} }
func ArrToKVMap[T any, K comparable, V any](a []T, keyfunc func(T) K, valfunc func(T) V) map[K]V {
result := make(map[K]V, len(a))
for _, v := range a {
result[keyfunc(v)] = valfunc(v)
}
return result
}
func ArrToSet[T comparable](a []T) map[T]bool { func ArrToSet[T comparable](a []T) map[T]bool {
result := make(map[T]bool, len(a)) result := make(map[T]bool, len(a))
for _, v := range a { for _, v := range a {