2023-09-18 10:38:25 +02:00
|
|
|
package bfcodegen
|
|
|
|
|
|
|
|
import (
|
2023-10-31 22:58:28 +01:00
|
|
|
"bytes"
|
|
|
|
_ "embed"
|
2023-09-18 10:38:25 +02:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2023-11-01 04:20:08 +01:00
|
|
|
"go/format"
|
2023-09-18 10:38:25 +02:00
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext"
|
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/cryptext"
|
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/rext"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
2023-10-31 22:58:28 +01:00
|
|
|
"text/template"
|
2023-09-18 10:38:25 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
type IDDef struct {
|
|
|
|
File string
|
|
|
|
FileRelative string
|
|
|
|
Name string
|
|
|
|
}
|
|
|
|
|
2024-03-10 12:49:31 +01:00
|
|
|
type IDGenOptions struct {
|
|
|
|
DebugOutput *bool
|
|
|
|
}
|
|
|
|
|
2023-09-18 10:38:25 +02:00
|
|
|
var rexIDPackage = rext.W(regexp.MustCompile(`^package\s+(?P<name>[A-Za-z0-9_]+)\s*$`))
|
|
|
|
|
|
|
|
var rexIDDef = rext.W(regexp.MustCompile(`^\s*type\s+(?P<name>[A-Za-z0-9_]+)\s+string\s*//\s*(@id:type).*$`))
|
|
|
|
|
|
|
|
var rexIDChecksumConst = rext.W(regexp.MustCompile(`const ChecksumIDGenerator = "(?P<cs>[A-Za-z0-9_]*)"`))
|
|
|
|
|
2023-10-31 22:58:28 +01:00
|
|
|
//go:embed id-generate.template
|
|
|
|
var templateIDGenerateText string
|
|
|
|
|
2024-03-11 20:58:06 +01:00
|
|
|
func GenerateIDSpecs(sourceDir string, destFile string, opt IDGenOptions) error {
|
2024-03-10 12:49:31 +01:00
|
|
|
|
|
|
|
debugOutput := langext.Coalesce(opt.DebugOutput, false)
|
2023-09-18 10:38:25 +02:00
|
|
|
|
|
|
|
files, err := os.ReadDir(sourceDir)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
oldChecksum := "N/A"
|
|
|
|
if _, err := os.Stat(destFile); !os.IsNotExist(err) {
|
|
|
|
content, err := os.ReadFile(destFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if m, ok := rexIDChecksumConst.MatchFirst(string(content)); ok {
|
|
|
|
oldChecksum = m.GroupByName("cs").Value()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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") })
|
2023-09-18 10:42:43 +02:00
|
|
|
files = langext.ArrFilter(files, func(v os.DirEntry) bool { return !strings.HasSuffix(v.Name(), "_gen.go") })
|
2023-09-18 10:38:25 +02:00
|
|
|
langext.SortBy(files, func(v os.DirEntry) string { return v.Name() })
|
|
|
|
|
|
|
|
newChecksumStr := goext.GoextVersion
|
|
|
|
for _, f := range files {
|
|
|
|
content, err := os.ReadFile(path.Join(sourceDir, f.Name()))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
newChecksumStr += "\n" + f.Name() + "\t" + cryptext.BytesSha256(content)
|
|
|
|
}
|
|
|
|
|
|
|
|
newChecksum := cryptext.BytesSha256([]byte(newChecksumStr))
|
|
|
|
|
|
|
|
if newChecksum != oldChecksum {
|
|
|
|
fmt.Printf("[IDGenerate] Checksum has changed ( %s -> %s ), will generate new file\n\n", oldChecksum, newChecksum)
|
|
|
|
} else {
|
|
|
|
fmt.Printf("[IDGenerate] Checksum unchanged ( %s ), nothing to do\n", oldChecksum)
|
2023-09-18 10:43:29 +02:00
|
|
|
return nil
|
2023-09-18 10:38:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
allIDs := make([]IDDef, 0)
|
|
|
|
|
|
|
|
pkgname := ""
|
|
|
|
|
|
|
|
for _, f := range files {
|
2024-03-10 12:49:31 +01:00
|
|
|
if debugOutput {
|
|
|
|
fmt.Printf("========= %s =========\n\n", f.Name())
|
|
|
|
}
|
|
|
|
|
|
|
|
fileIDs, pn, err := processIDFile(sourceDir, path.Join(sourceDir, f.Name()), debugOutput)
|
2023-09-18 10:38:25 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-03-10 12:49:31 +01:00
|
|
|
if debugOutput {
|
|
|
|
fmt.Printf("\n")
|
|
|
|
}
|
2023-09-18 10:38:25 +02:00
|
|
|
|
|
|
|
allIDs = append(allIDs, fileIDs...)
|
|
|
|
|
|
|
|
if pn != "" {
|
|
|
|
pkgname = pn
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if pkgname == "" {
|
|
|
|
return errors.New("no package name found in any file")
|
|
|
|
}
|
|
|
|
|
2023-11-01 04:20:08 +01:00
|
|
|
fdata, err := format.Source([]byte(fmtIDOutput(newChecksum, allIDs, pkgname)))
|
2023-09-18 10:38:25 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-11-01 04:20:08 +01:00
|
|
|
err = os.WriteFile(destFile, fdata, 0o755)
|
2023-09-18 10:38:25 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-03-10 12:49:31 +01:00
|
|
|
func processIDFile(basedir string, fn string, debugOutput bool) ([]IDDef, string, error) {
|
2023-09-18 10:38:25 +02:00
|
|
|
file, err := os.Open(fn)
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer func() { _ = file.Close() }()
|
|
|
|
|
|
|
|
bin, err := io.ReadAll(file)
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
lines := strings.Split(string(bin), "\n")
|
|
|
|
|
|
|
|
ids := make([]IDDef, 0)
|
|
|
|
|
|
|
|
pkgname := ""
|
|
|
|
|
|
|
|
for i, line := range lines {
|
|
|
|
if i == 0 && strings.HasPrefix(line, "// Code generated by") {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if match, ok := rexIDPackage.MatchFirst(line); i == 0 && ok {
|
|
|
|
pkgname = match.GroupByName("name").Value()
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if match, ok := rexIDDef.MatchFirst(line); ok {
|
|
|
|
|
|
|
|
rfp, err := filepath.Rel(basedir, fn)
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
def := IDDef{
|
|
|
|
File: fn,
|
|
|
|
FileRelative: rfp,
|
|
|
|
Name: match.GroupByName("name").Value(),
|
|
|
|
}
|
2024-03-10 12:49:31 +01:00
|
|
|
|
|
|
|
if debugOutput {
|
|
|
|
fmt.Printf("Found ID definition { '%s' }\n", def.Name)
|
|
|
|
}
|
|
|
|
|
2023-09-18 10:38:25 +02:00
|
|
|
ids = append(ids, def)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ids, pkgname, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func fmtIDOutput(cs string, ids []IDDef, pkgname string) string {
|
2023-10-31 22:58:28 +01:00
|
|
|
templ := template.Must(template.New("id-generate").Parse(templateIDGenerateText))
|
2023-09-18 10:38:25 +02:00
|
|
|
|
2023-10-31 22:58:28 +01:00
|
|
|
buffer := bytes.Buffer{}
|
2023-09-18 10:38:25 +02:00
|
|
|
|
|
|
|
anyDef := langext.ArrFirstOrNil(ids, func(def IDDef) bool { return def.Name == "AnyID" || def.Name == "AnyId" })
|
|
|
|
|
2023-10-31 22:58:28 +01:00
|
|
|
err := templ.Execute(&buffer, langext.H{
|
|
|
|
"PkgName": pkgname,
|
|
|
|
"Checksum": cs,
|
|
|
|
"GoextVersion": goext.GoextVersion,
|
|
|
|
"IDs": ids,
|
|
|
|
"AnyDef": anyDef,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
2023-09-18 10:38:25 +02:00
|
|
|
}
|
|
|
|
|
2023-10-31 22:58:28 +01:00
|
|
|
return buffer.String()
|
2023-09-18 10:38:25 +02:00
|
|
|
}
|