fix parsing overrides
This commit is contained in:
251
generators/alrsh-package/main.go
Normal file
251
generators/alrsh-package/main.go
Normal file
@ -0,0 +1,251 @@
|
||||
// ALR - Any Linux Repository
|
||||
// Copyright (C) 2025 The ALR Authors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func resolvedStructGenerator(buf *bytes.Buffer, fields []*ast.Field) {
|
||||
contentTemplate := template.Must(template.New("").Parse(`
|
||||
type {{ .EntityNameLower }}Resolved struct {
|
||||
{{ .StructFields }}
|
||||
}
|
||||
`))
|
||||
|
||||
var structFieldsBuilder strings.Builder
|
||||
|
||||
for _, field := range fields {
|
||||
for _, name := range field.Names {
|
||||
// Поле с типом
|
||||
fieldTypeStr := exprToString(field.Type)
|
||||
|
||||
// Структура поля
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("\t")
|
||||
buf.WriteString(name.Name)
|
||||
buf.WriteString(" ")
|
||||
buf.WriteString(fieldTypeStr)
|
||||
|
||||
// Обработка json-тега
|
||||
jsonTag := ""
|
||||
if field.Tag != nil {
|
||||
raw := strings.Trim(field.Tag.Value, "`")
|
||||
tag := reflect.StructTag(raw)
|
||||
if val := tag.Get("json"); val != "" {
|
||||
jsonTag = val
|
||||
}
|
||||
}
|
||||
if jsonTag == "" {
|
||||
jsonTag = strings.ToLower(name.Name)
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf(" `json:\"%s\"`", jsonTag))
|
||||
buf.WriteString("\n")
|
||||
structFieldsBuilder.Write(buf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
params := struct {
|
||||
EntityNameLower string
|
||||
StructFields string
|
||||
}{
|
||||
EntityNameLower: "package",
|
||||
StructFields: structFieldsBuilder.String(),
|
||||
}
|
||||
|
||||
err := contentTemplate.Execute(buf, params)
|
||||
if err != nil {
|
||||
log.Fatalf("execute template: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func toResolvedFuncGenerator(buf *bytes.Buffer, fields []*ast.Field) {
|
||||
contentTemplate := template.Must(template.New("").Parse(`
|
||||
func {{ .EntityName }}ToResolved(src *{{ .EntityName }}) {{ .EntityNameLower }}Resolved {
|
||||
return {{ .EntityNameLower }}Resolved{
|
||||
{{ .Assignments }}
|
||||
}
|
||||
}
|
||||
`))
|
||||
|
||||
var assignmentsBuilder strings.Builder
|
||||
|
||||
for _, field := range fields {
|
||||
for _, name := range field.Names {
|
||||
var assignBuf bytes.Buffer
|
||||
assignBuf.WriteString("\t\t")
|
||||
assignBuf.WriteString(name.Name)
|
||||
assignBuf.WriteString(": ")
|
||||
if isOverridableField(field.Type) {
|
||||
assignBuf.WriteString(fmt.Sprintf("src.%s.Resolved()", name.Name))
|
||||
} else {
|
||||
assignBuf.WriteString(fmt.Sprintf("src.%s", name.Name))
|
||||
}
|
||||
assignBuf.WriteString(",\n")
|
||||
assignmentsBuilder.Write(assignBuf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
params := struct {
|
||||
EntityName string
|
||||
EntityNameLower string
|
||||
Assignments string
|
||||
}{
|
||||
EntityName: "Package",
|
||||
EntityNameLower: "package",
|
||||
Assignments: assignmentsBuilder.String(),
|
||||
}
|
||||
|
||||
err := contentTemplate.Execute(buf, params)
|
||||
if err != nil {
|
||||
log.Fatalf("execute template: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func resolveFuncGenerator(buf *bytes.Buffer, fields []*ast.Field) {
|
||||
contentTemplate := template.Must(template.New("").Parse(`
|
||||
func Resolve{{ .EntityName }}(pkg *{{ .EntityName }}, overrides []string) {
|
||||
{{.Code}}}
|
||||
`))
|
||||
|
||||
var codeBuilder strings.Builder
|
||||
|
||||
for _, field := range fields {
|
||||
for _, name := range field.Names {
|
||||
if isOverridableField(field.Type) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(fmt.Sprintf("\t\tpkg.%s.Resolve(overrides)\n", name.Name))
|
||||
codeBuilder.Write(buf.Bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params := struct {
|
||||
EntityName string
|
||||
Code string
|
||||
}{
|
||||
EntityName: "Package",
|
||||
Code: codeBuilder.String(),
|
||||
}
|
||||
|
||||
err := contentTemplate.Execute(buf, params)
|
||||
if err != nil {
|
||||
log.Fatalf("execute template: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
path := os.Getenv("GOFILE")
|
||||
if path == "" {
|
||||
log.Fatal("GOFILE must be set")
|
||||
}
|
||||
|
||||
fset := token.NewFileSet()
|
||||
node, err := parser.ParseFile(fset, path, nil, parser.AllErrors)
|
||||
if err != nil {
|
||||
log.Fatalf("parsing file: %v", err)
|
||||
}
|
||||
|
||||
entityName := "Package" // имя структуры, которую анализируем
|
||||
|
||||
found := false
|
||||
|
||||
fields := make([]*ast.Field, 0)
|
||||
|
||||
// Ищем структуру с нужным именем
|
||||
for _, decl := range node.Decls {
|
||||
genDecl, ok := decl.(*ast.GenDecl)
|
||||
if !ok || genDecl.Tok != token.TYPE {
|
||||
continue
|
||||
}
|
||||
for _, spec := range genDecl.Specs {
|
||||
typeSpec := spec.(*ast.TypeSpec)
|
||||
if typeSpec.Name.Name != entityName {
|
||||
continue
|
||||
}
|
||||
structType, ok := typeSpec.Type.(*ast.StructType)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
fields = structType.Fields.List
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
log.Fatalf("struct %s not found", entityName)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
buf.WriteString("// DO NOT EDIT MANUALLY. This file is generated.\n")
|
||||
buf.WriteString("package alrsh")
|
||||
|
||||
resolvedStructGenerator(&buf, fields)
|
||||
toResolvedFuncGenerator(&buf, fields)
|
||||
resolveFuncGenerator(&buf, fields)
|
||||
|
||||
// Форматируем вывод
|
||||
formatted, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
log.Fatalf("formatting: %v", err)
|
||||
}
|
||||
|
||||
outPath := strings.TrimSuffix(path, ".go") + "_gen.go"
|
||||
outFile, err := os.Create(outPath)
|
||||
if err != nil {
|
||||
log.Fatalf("create file: %v", err)
|
||||
}
|
||||
_, err = outFile.Write(formatted)
|
||||
if err != nil {
|
||||
log.Fatalf("writing output: %v", err)
|
||||
}
|
||||
outFile.Close()
|
||||
}
|
||||
|
||||
func exprToString(expr ast.Expr) string {
|
||||
if t, ok := expr.(*ast.IndexExpr); ok {
|
||||
if ident, ok := t.X.(*ast.Ident); ok && ident.Name == "OverridableField" {
|
||||
return exprToString(t.Index) // T
|
||||
}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := format.Node(&buf, token.NewFileSet(), expr); err != nil {
|
||||
return "<invalid>"
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func isOverridableField(expr ast.Expr) bool {
|
||||
indexExpr, ok := expr.(*ast.IndexExpr)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
ident, ok := indexExpr.X.(*ast.Ident)
|
||||
return ok && ident.Name == "OverridableField"
|
||||
}
|
Reference in New Issue
Block a user