252 lines
6.0 KiB
Go
252 lines
6.0 KiB
Go
// 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"
|
||
}
|