forked from Plemya-x/ALR
		
	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