fix parsing overrides
This commit is contained in:
		
							
								
								
									
										1
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
									
									
									
									
								
							| @@ -21,6 +21,7 @@ build: check-no-root $(BIN) | |||||||
|  |  | ||||||
| export CGO_ENABLED := 0 | export CGO_ENABLED := 0 | ||||||
| $(BIN): | $(BIN): | ||||||
|  | 	go generate ./... | ||||||
| 	go build -ldflags="-X 'gitea.plemya-x.ru/Plemya-x/ALR/internal/config.Version=$(GIT_VERSION)'" -o $@ | 	go build -ldflags="-X 'gitea.plemya-x.ru/Plemya-x/ALR/internal/config.Version=$(GIT_VERSION)'" -o $@ | ||||||
|  |  | ||||||
| check-no-root: | check-no-root: | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> |     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> | ||||||
|         <text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text> |         <text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text> | ||||||
|         <text x="33.5" y="14">coverage</text> |         <text x="33.5" y="14">coverage</text> | ||||||
|         <text x="86" y="15" fill="#010101" fill-opacity=".3">20.1%</text> |         <text x="86" y="15" fill="#010101" fill-opacity=".3">19.7%</text> | ||||||
|         <text x="86" y="14">20.1%</text> |         <text x="86" y="14">19.7%</text> | ||||||
|     </g> |     </g> | ||||||
| </svg> | </svg> | ||||||
|   | |||||||
| Before Width: | Height: | Size: 926 B After Width: | Height: | Size: 926 B | 
							
								
								
									
										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" | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								go.mod
									
									
									
									
									
								
							| @@ -18,6 +18,7 @@ require ( | |||||||
| 	github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0 | 	github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0 | ||||||
| 	github.com/go-git/go-billy/v5 v5.6.0 | 	github.com/go-git/go-billy/v5 v5.6.0 | ||||||
| 	github.com/go-git/go-git/v5 v5.13.0 | 	github.com/go-git/go-git/v5 v5.13.0 | ||||||
|  | 	github.com/goccy/go-yaml v1.18.0 | ||||||
| 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 | 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 | ||||||
| 	github.com/goreleaser/nfpm/v2 v2.41.0 | 	github.com/goreleaser/nfpm/v2 v2.41.0 | ||||||
| 	github.com/hashicorp/go-hclog v0.14.1 | 	github.com/hashicorp/go-hclog v0.14.1 | ||||||
| @@ -38,7 +39,6 @@ require ( | |||||||
| 	golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 | 	golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 | ||||||
| 	golang.org/x/sys v0.31.0 | 	golang.org/x/sys v0.31.0 | ||||||
| 	golang.org/x/text v0.23.0 | 	golang.org/x/text v0.23.0 | ||||||
| 	gopkg.in/yaml.v3 v3.0.1 |  | ||||||
| 	modernc.org/sqlite v1.25.0 | 	modernc.org/sqlite v1.25.0 | ||||||
| 	mvdan.cc/sh/v3 v3.10.0 | 	mvdan.cc/sh/v3 v3.10.0 | ||||||
| 	xorm.io/xorm v1.3.9 | 	xorm.io/xorm v1.3.9 | ||||||
| @@ -139,6 +139,7 @@ require ( | |||||||
| 	google.golang.org/grpc v1.58.3 // indirect | 	google.golang.org/grpc v1.58.3 // indirect | ||||||
| 	google.golang.org/protobuf v1.36.1 // indirect | 	google.golang.org/protobuf v1.36.1 // indirect | ||||||
| 	gopkg.in/warnings.v0 v0.1.2 // indirect | 	gopkg.in/warnings.v0 v0.1.2 // indirect | ||||||
|  | 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||||
| 	lukechampine.com/uint128 v1.2.0 // indirect | 	lukechampine.com/uint128 v1.2.0 // indirect | ||||||
| 	modernc.org/cc/v3 v3.40.0 // indirect | 	modernc.org/cc/v3 v3.40.0 // indirect | ||||||
| 	modernc.org/ccgo/v3 v3.16.13 // indirect | 	modernc.org/ccgo/v3 v3.16.13 // indirect | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							| @@ -164,6 +164,8 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= | |||||||
| github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= | github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= | ||||||
| github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI= | github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI= | ||||||
| github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= | github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= | ||||||
|  | github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= | ||||||
|  | github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= | ||||||
| github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= | ||||||
| github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||||
| github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||||
|   | |||||||
							
								
								
									
										44
									
								
								info.go
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								info.go
									
									
									
									
									
								
							| @@ -23,10 +23,10 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
|  |  | ||||||
|  | 	"github.com/goccy/go-yaml" | ||||||
| 	"github.com/jeandeaual/go-locale" | 	"github.com/jeandeaual/go-locale" | ||||||
| 	"github.com/leonelquinteros/gotext" | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| 	"gopkg.in/yaml.v3" |  | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
| 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||||
| @@ -121,35 +121,27 @@ func InfoCmd() *cli.Command { | |||||||
| 				systemLang = "en" | 				systemLang = "en" | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if !all { | 			info, err := distro.ParseOSRelease(ctx) | ||||||
| 				info, err := distro.ParseOSRelease(ctx) | 			if err != nil { | ||||||
| 				if err != nil { | 				return cliutils.FormatCliExit(gotext.Get("Error parsing os-release file"), err) | ||||||
| 					return cliutils.FormatCliExit(gotext.Get("Error parsing os-release file"), err) | 			} | ||||||
| 				} | 			names, err = overrides.Resolve( | ||||||
| 				names, err = overrides.Resolve( | 				info, | ||||||
| 					info, | 				overrides.DefaultOpts. | ||||||
| 					overrides.DefaultOpts. | 					WithLanguages([]string{systemLang}), | ||||||
| 						WithLanguages([]string{systemLang}), | 			) | ||||||
| 				) | 			if err != nil { | ||||||
| 				if err != nil { | 				return cliutils.FormatCliExit(gotext.Get("Error resolving overrides"), err) | ||||||
| 					return cliutils.FormatCliExit(gotext.Get("Error resolving overrides"), err) |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			for _, pkg := range pkgs { | 			for _, pkg := range pkgs { | ||||||
| 				if !all { | 				alrsh.ResolvePackage(&pkg, names) | ||||||
| 					alrsh.ResolvePackage(&pkg, names) | 				view := alrsh.NewPackageView(pkg) | ||||||
| 					err = yaml.NewEncoder(os.Stdout).Encode(pkg) | 				view.Resolved = !all | ||||||
| 					if err != nil { | 				err = yaml.NewEncoder(os.Stdout, yaml.UseJSONMarshaler(), yaml.OmitEmpty()).Encode(view) | ||||||
| 						return cliutils.FormatCliExit(gotext.Get("Error encoding script variables"), err) | 				if err != nil { | ||||||
| 					} | 					return cliutils.FormatCliExit(gotext.Get("Error encoding script variables"), err) | ||||||
| 				} else { |  | ||||||
| 					err = yaml.NewEncoder(os.Stdout).Encode(pkg) |  | ||||||
| 					if err != nil { |  | ||||||
| 						return cliutils.FormatCliExit(gotext.Get("Error encoding script variables"), err) |  | ||||||
| 					} |  | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				fmt.Println("---") | 				fmt.Println("---") | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -367,7 +367,7 @@ func (b *Builder) BuildPackage( | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	slog.Debug("ViewScript") | 	slog.Debug("ViewScript") | ||||||
| 	slog.Debug("", "varsOfPackages", varsOfPackages) | 	slog.Debug("", "varsOfPackages", varsOfPackages[0]) | ||||||
| 	err = b.scriptViewerExecutor.ViewScript(ctx, input, sf, basePkg) | 	err = b.scriptViewerExecutor.ViewScript(ctx, input, sf, basePkg) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
|   | |||||||
| @@ -23,7 +23,6 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log/slog" |  | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| @@ -75,75 +74,99 @@ func New(info *distro.OSRelease, runner *interp.Runner) *Decoder { | |||||||
| // DecodeVar decodes a variable to val using reflection. | // DecodeVar decodes a variable to val using reflection. | ||||||
| // Structs should use the "sh" struct tag. | // Structs should use the "sh" struct tag. | ||||||
| func (d *Decoder) DecodeVar(name string, val any) error { | func (d *Decoder) DecodeVar(name string, val any) error { | ||||||
| 	variable := d.getVar(name) | 	origType := reflect.TypeOf(val).Elem() | ||||||
| 	if variable == nil { | 	isOverridableField := strings.Contains(origType.String(), "OverridableField[") | ||||||
| 		return VarNotFoundError{name} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ | 	if !isOverridableField { | ||||||
| 		WeaklyTypedInput: true, | 		variable := d.getVarNoOverrides(name) | ||||||
| 		DecodeHook: mapstructure.DecodeHookFuncValue(func(from, to reflect.Value) (interface{}, error) { | 		if variable == nil { | ||||||
| 			if strings.Contains(to.Type().String(), "alrsh.OverridableField") { | 			return VarNotFoundError{name} | ||||||
| 				if to.Kind() != reflect.Ptr && to.CanAddr() { | 		} | ||||||
| 					to = to.Addr() |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				names, err := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name)) | 		dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ | ||||||
| 				if err != nil { | 			WeaklyTypedInput: true, | ||||||
| 					return nil, err | 			Result:           val, // передаем указатель на новое значение | ||||||
| 				} | 			TagName:          "sh", | ||||||
|  | 			DecodeHook: mapstructure.DecodeHookFuncValue(func(from, to reflect.Value) (interface{}, error) { | ||||||
| 				isNotSet := true | 				if from.Kind() == reflect.Slice && to.Kind() == reflect.String { | ||||||
|  | 					s, ok := from.Interface().([]string) | ||||||
| 				setMethod := to.MethodByName("Set") | 					if ok && len(s) == 1 { | ||||||
| 				setResolvedMethod := to.MethodByName("SetResolved") | 						return s[0], nil | ||||||
|  |  | ||||||
| 				for _, varName := range names { |  | ||||||
| 					val := d.getVarNoOverrides(varName) |  | ||||||
| 					if val == nil { |  | ||||||
| 						continue |  | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
| 					t := setMethod.Type().In(1) |  | ||||||
|  |  | ||||||
| 					newVal := from |  | ||||||
|  |  | ||||||
| 					if !newVal.Type().AssignableTo(t) { |  | ||||||
| 						newVal = reflect.New(t) |  | ||||||
| 						err = d.DecodeVar(name, newVal.Interface()) |  | ||||||
| 						if err != nil { |  | ||||||
| 							return nil, err |  | ||||||
| 						} |  | ||||||
| 						newVal = newVal.Elem() |  | ||||||
| 					} |  | ||||||
|  |  | ||||||
| 					if isNotSet { |  | ||||||
| 						setResolvedMethod.Call([]reflect.Value{newVal}) |  | ||||||
| 					} |  | ||||||
|  |  | ||||||
| 					override := strings.TrimPrefix(strings.TrimPrefix(varName, name), "_") |  | ||||||
| 					setMethod.Call([]reflect.Value{reflect.ValueOf(override), newVal}) |  | ||||||
| 				} | 				} | ||||||
|  | 				return from.Interface(), nil | ||||||
|  | 			}), | ||||||
|  | 		}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 				return to, nil | 		switch variable.Kind { | ||||||
|  | 		case expand.Indexed: | ||||||
|  | 			return dec.Decode(variable.List) | ||||||
|  | 		case expand.Associative: | ||||||
|  | 			return dec.Decode(variable.Map) | ||||||
|  | 		default: | ||||||
|  | 			return dec.Decode(variable.Str) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		vars := d.getVarsByPrefix(name) | ||||||
|  |  | ||||||
|  | 		if len(vars) == 0 { | ||||||
|  | 			return VarNotFoundError{name} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		reflectVal := reflect.ValueOf(val) | ||||||
|  | 		overridableVal := reflect.ValueOf(val).Elem() | ||||||
|  |  | ||||||
|  | 		dataField := overridableVal.FieldByName("data") | ||||||
|  | 		if !dataField.IsValid() { | ||||||
|  | 			return fmt.Errorf("data field not found in OverridableField") | ||||||
|  | 		} | ||||||
|  | 		mapType := dataField.Type() // map[string]T | ||||||
|  | 		elemType := mapType.Elem()  // T | ||||||
|  |  | ||||||
|  | 		var overridablePtr reflect.Value | ||||||
|  | 		if reflectVal.Kind() == reflect.Ptr { | ||||||
|  | 			overridablePtr = reflectVal | ||||||
|  | 		} else { | ||||||
|  | 			if !reflectVal.CanAddr() { | ||||||
|  | 				return fmt.Errorf("OverridableField value is not addressable") | ||||||
| 			} | 			} | ||||||
| 			return from.Interface(), nil | 			overridablePtr = reflectVal.Addr() | ||||||
| 		}), | 		} | ||||||
| 		Result:  val, |  | ||||||
| 		TagName: "sh", |  | ||||||
| 	}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		slog.Warn("err", "err", err) |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	switch variable.Kind { | 		setValue := overridablePtr.MethodByName("Set") | ||||||
| 	case expand.Indexed: | 		if !setValue.IsValid() { | ||||||
| 		return dec.Decode(variable.List) | 			return fmt.Errorf("method Set not found on OverridableField") | ||||||
| 	case expand.Associative: | 		} | ||||||
| 		return dec.Decode(variable.Map) |  | ||||||
| 	default: | 		for _, v := range vars { | ||||||
| 		return dec.Decode(variable.Str) | 			varName := v.Name | ||||||
|  |  | ||||||
|  | 			key := strings.TrimPrefix(strings.TrimPrefix(varName, name), "_") | ||||||
|  | 			newVal := reflect.New(elemType) | ||||||
|  |  | ||||||
|  | 			if err := d.DecodeVar(varName, newVal.Interface()); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			keyValue := reflect.ValueOf(key) | ||||||
|  | 			setValue.Call([]reflect.Value{keyValue, newVal.Elem()}) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		resolveValue := overridablePtr.MethodByName("Resolve") | ||||||
|  | 		if !resolveValue.IsValid() { | ||||||
|  | 			return fmt.Errorf("method Resolve not found on OverridableField") | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		names, err := overrides.Resolve(d.info, overrides.DefaultOpts) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		resolveValue.Call([]reflect.Value{reflect.ValueOf(names)}) | ||||||
|  | 		return nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -284,23 +307,6 @@ func (d *Decoder) getFunc(name string) *syntax.Stmt { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // getVar gets a variable based on its name, taking into account |  | ||||||
| // override variables and nameref variables. |  | ||||||
| func (d *Decoder) getVar(name string) *expand.Variable { |  | ||||||
| 	names, err := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, varName := range names { |  | ||||||
| 		res := d.getVarNoOverrides(varName) |  | ||||||
| 		if res != nil { |  | ||||||
| 			return res |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (d *Decoder) getVarNoOverrides(name string) *expand.Variable { | func (d *Decoder) getVarNoOverrides(name string) *expand.Variable { | ||||||
| 	val, ok := d.Runner.Vars[name] | 	val, ok := d.Runner.Vars[name] | ||||||
| 	if ok { | 	if ok { | ||||||
| @@ -318,6 +324,32 @@ func (d *Decoder) getVarNoOverrides(name string) *expand.Variable { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type vars struct { | ||||||
|  | 	Name  string | ||||||
|  | 	Value *expand.Variable | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d *Decoder) getVarsByPrefix(prefix string) []*vars { | ||||||
|  | 	result := make([]*vars, 0) | ||||||
|  | 	for name, val := range d.Runner.Vars { | ||||||
|  | 		if !strings.HasPrefix(name, prefix) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		switch prefix { | ||||||
|  | 		case "auto_req": | ||||||
|  | 			if strings.HasPrefix(name, "auto_req_skiplist") { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		case "auto_prov": | ||||||
|  | 			if strings.HasPrefix(name, "auto_prov_skiplist") { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		result = append(result, &vars{name, &val}) | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  |  | ||||||
| func IsTruthy(value string) bool { | func IsTruthy(value string) bool { | ||||||
| 	value = strings.ToLower(strings.TrimSpace(value)) | 	value = strings.ToLower(strings.TrimSpace(value)) | ||||||
| 	return value == "true" || value == "yes" || value == "1" | 	return value == "true" || value == "yes" || value == "1" | ||||||
|   | |||||||
| @@ -32,24 +32,25 @@ import ( | |||||||
| 	"mvdan.cc/sh/v3/syntax" | 	"mvdan.cc/sh/v3/syntax" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/decoder" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/decoder" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type BuildVars struct { | type BuildVars struct { | ||||||
| 	Name          string   `sh:"name,required"` | 	Name          string                           `sh:"name,required"` | ||||||
| 	Version       string   `sh:"version,required"` | 	Version       string                           `sh:"version,required"` | ||||||
| 	Release       int      `sh:"release,required"` | 	Release       int                              `sh:"release,required"` | ||||||
| 	Epoch         uint     `sh:"epoch"` | 	Epoch         uint                             `sh:"epoch"` | ||||||
| 	Description   string   `sh:"desc"` | 	Description   alrsh.OverridableField[string]   `sh:"desc"` | ||||||
| 	Homepage      string   `sh:"homepage"` | 	Homepage      string                           `sh:"homepage"` | ||||||
| 	Maintainer    string   `sh:"maintainer"` | 	Maintainer    string                           `sh:"maintainer"` | ||||||
| 	Architectures []string `sh:"architectures"` | 	Architectures []string                         `sh:"architectures"` | ||||||
| 	Licenses      []string `sh:"license"` | 	Licenses      []string                         `sh:"license"` | ||||||
| 	Provides      []string `sh:"provides"` | 	Provides      []string                         `sh:"provides"` | ||||||
| 	Conflicts     []string `sh:"conflicts"` | 	Conflicts     []string                         `sh:"conflicts"` | ||||||
| 	Depends       []string `sh:"deps"` | 	Depends       []string                         `sh:"deps"` | ||||||
| 	BuildDepends  []string `sh:"build_deps"` | 	BuildDepends  alrsh.OverridableField[[]string] `sh:"build_deps"` | ||||||
| 	Replaces      []string `sh:"replaces"` | 	Replaces      alrsh.OverridableField[[]string] `sh:"replaces"` | ||||||
| } | } | ||||||
|  |  | ||||||
| const testScript = ` | const testScript = ` | ||||||
| @@ -113,22 +114,34 @@ func TestDecodeVars(t *testing.T) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	expected := BuildVars{ | 	expected := BuildVars{ | ||||||
| 		Name:          "test", | 		Name:    "test", | ||||||
| 		Version:       "0.0.1", | 		Version: "0.0.1", | ||||||
| 		Release:       1, | 		Release: 1, | ||||||
| 		Epoch:         2, | 		Epoch:   2, | ||||||
| 		Description:   "Test package", | 		Description: alrsh.OverridableFromMap(map[string]string{ | ||||||
|  | 			"": "Test package", | ||||||
|  | 		}), | ||||||
| 		Homepage:      "https://gitea.plemya-x.ru/xpamych/ALR", | 		Homepage:      "https://gitea.plemya-x.ru/xpamych/ALR", | ||||||
| 		Maintainer:    "Евгений Храмов <xpamych@yandex.ru>", | 		Maintainer:    "Евгений Храмов <xpamych@yandex.ru>", | ||||||
| 		Architectures: []string{"arm64", "amd64"}, | 		Architectures: []string{"arm64", "amd64"}, | ||||||
| 		Licenses:      []string{"GPL-3.0-or-later"}, | 		Licenses:      []string{"GPL-3.0-or-later"}, | ||||||
| 		Provides:      []string{"test"}, | 		Provides:      []string{"test"}, | ||||||
| 		Conflicts:     []string{"test"}, | 		Conflicts:     []string{"test"}, | ||||||
| 		Replaces:      []string{"test-legacy"}, | 		Replaces: alrsh.OverridableFromMap(map[string][]string{ | ||||||
| 		Depends:       []string{"sudo"}, | 			"":        {"test-old"}, | ||||||
| 		BuildDepends:  []string{"go"}, | 			"test_os": {"test-legacy"}, | ||||||
|  | 		}), | ||||||
|  | 		Depends: []string{"sudo"}, | ||||||
|  | 		BuildDepends: alrsh.OverridableFromMap(map[string][]string{ | ||||||
|  | 			"":     {"golang"}, | ||||||
|  | 			"arch": {"go"}, | ||||||
|  | 		}), | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	expected.Description.SetResolved("Test package") | ||||||
|  | 	expected.Replaces.SetResolved([]string{"test-legacy"}) | ||||||
|  | 	expected.BuildDepends.SetResolved([]string{"go"}) | ||||||
|  |  | ||||||
| 	if !reflect.DeepEqual(bv, expected) { | 	if !reflect.DeepEqual(bv, expected) { | ||||||
| 		t.Errorf("Expected %v, got %v", expected, bv) | 		t.Errorf("Expected %v, got %v", expected, bv) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -138,11 +138,11 @@ msgstr "" | |||||||
| msgid "Can't detect system language" | msgid "Can't detect system language" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: info.go:135 | #: info.go:134 | ||||||
| msgid "Error resolving overrides" | msgid "Error resolving overrides" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: info.go:144 info.go:149 | #: info.go:143 | ||||||
| msgid "Error encoding script variables" | msgid "Error encoding script variables" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -145,11 +145,11 @@ msgstr "Ошибка при поиске пакетов" | |||||||
| msgid "Can't detect system language" | msgid "Can't detect system language" | ||||||
| msgstr "Ошибка при определении языка системы" | msgstr "Ошибка при определении языка системы" | ||||||
|  |  | ||||||
| #: info.go:135 | #: info.go:134 | ||||||
| msgid "Error resolving overrides" | msgid "Error resolving overrides" | ||||||
| msgstr "Ошибка устранения переорпеделений" | msgstr "Ошибка устранения переорпеделений" | ||||||
|  |  | ||||||
| #: info.go:144 info.go:149 | #: info.go:143 | ||||||
| msgid "Error encoding script variables" | msgid "Error encoding script variables" | ||||||
| msgstr "Ошибка кодирования переменных скрита" | msgstr "Ошибка кодирования переменных скрита" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -68,10 +68,12 @@ func (o *OverridableField[T]) Resolve(overrides []string) { | |||||||
| 	for _, override := range overrides { | 	for _, override := range overrides { | ||||||
| 		if v, ok := o.Has(override); ok { | 		if v, ok := o.Has(override); ok { | ||||||
| 			o.SetResolved(v) | 			o.SetResolved(v) | ||||||
|  | 			return | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Database serialization (JSON) | ||||||
| func (f *OverridableField[T]) ToDB() ([]byte, error) { | func (f *OverridableField[T]) ToDB() ([]byte, error) { | ||||||
| 	var data map[string]T | 	var data map[string]T | ||||||
|  |  | ||||||
| @@ -103,6 +105,7 @@ func (f *OverridableField[T]) FromDB(data []byte) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Gob serialization | ||||||
| type overridableFieldGobPayload[T any] struct { | type overridableFieldGobPayload[T any] struct { | ||||||
| 	Data     map[string]T | 	Data     map[string]T | ||||||
| 	Resolved T | 	Resolved T | ||||||
| @@ -136,6 +139,48 @@ func (f *OverridableField[T]) GobDecode(data []byte) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type overridableFieldJSONPayload[T any] struct { | ||||||
|  | 	Resolved *T           `json:"resolved,omitempty,omitzero"` | ||||||
|  | 	Data     map[string]T `json:"overrides,omitempty,omitzero"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f OverridableField[T]) MarshalJSON() ([]byte, error) { | ||||||
|  | 	data := make(map[string]T) | ||||||
|  |  | ||||||
|  | 	for k, v := range f.data { | ||||||
|  | 		if k == "" { | ||||||
|  | 			data["default"] = v | ||||||
|  | 		} else { | ||||||
|  | 			data[k] = v | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	payload := overridableFieldJSONPayload[T]{ | ||||||
|  | 		Data:     data, | ||||||
|  | 		Resolved: &f.resolved, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return json.Marshal(payload) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *OverridableField[T]) UnmarshalJSON(data []byte) error { | ||||||
|  | 	var payload overridableFieldJSONPayload[T] | ||||||
|  | 	if err := json.Unmarshal(data, &payload); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if payload.Data == nil { | ||||||
|  | 		payload.Data = make(map[string]T) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	f.data = payload.Data | ||||||
|  | 	if payload.Resolved != nil { | ||||||
|  | 		f.resolved = *payload.Resolved | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| func OverridableFromMap[T any](data map[string]T) OverridableField[T] { | func OverridableFromMap[T any](data map[string]T) OverridableField[T] { | ||||||
| 	if data == nil { | 	if data == nil { | ||||||
| 		data = make(map[string]T) | 		data = make(map[string]T) | ||||||
|   | |||||||
| @@ -14,9 +14,12 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  | ||||||
|  | //go:generate go run ../../generators/alrsh-package | ||||||
|  |  | ||||||
| package alrsh | package alrsh | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"strings" | 	"strings" | ||||||
| @@ -39,38 +42,38 @@ func ParseNames(dec *decoder.Decoder) (*PackageNames, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| type Package struct { | type Package struct { | ||||||
| 	Repository  string `xorm:"pk 'repository'"` | 	Repository  string `xorm:"pk 'repository'" json:"repository"` | ||||||
| 	Name        string `xorm:"pk 'name'"` | 	Name        string `xorm:"pk 'name'" json:"name"` | ||||||
| 	BasePkgName string `xorm:"notnull 'basepkg_name'"` | 	BasePkgName string `xorm:"notnull 'basepkg_name'" json:"basepkg_name"` | ||||||
|  |  | ||||||
| 	Version       string   `sh:"version" xorm:"notnull 'version'"` | 	Version       string   `sh:"version" xorm:"notnull 'version'" json:"version"` | ||||||
| 	Release       int      `sh:"release" xorm:"notnull 'release'"` | 	Release       int      `sh:"release" xorm:"notnull 'release'" json:"release"` | ||||||
| 	Epoch         uint     `sh:"epoch" xorm:"'epoch'"` | 	Epoch         uint     `sh:"epoch" xorm:"'epoch'" json:"epoch"` | ||||||
| 	Architectures []string `sh:"architectures" xorm:"json 'architectures'"` | 	Architectures []string `sh:"architectures" xorm:"json 'architectures'" json:"architectures"` | ||||||
| 	Licenses      []string `sh:"license" xorm:"json 'licenses'"` | 	Licenses      []string `sh:"license" xorm:"json 'licenses'" json:"license"` | ||||||
| 	Provides      []string `sh:"provides" xorm:"json 'provides'"` | 	Provides      []string `sh:"provides" xorm:"json 'provides'" json:"provides"` | ||||||
| 	Conflicts     []string `sh:"conflicts" xorm:"json 'conflicts'"` | 	Conflicts     []string `sh:"conflicts" xorm:"json 'conflicts'" json:"conflicts"` | ||||||
| 	Replaces      []string `sh:"replaces" xorm:"json 'replaces'"` | 	Replaces      []string `sh:"replaces" xorm:"json 'replaces'" json:"replaces"` | ||||||
|  |  | ||||||
| 	Summary          OverridableField[string]   `sh:"summary" xorm:"'summary'"` | 	Summary          OverridableField[string]   `sh:"summary" xorm:"'summary'" json:"summary"` | ||||||
| 	Description      OverridableField[string]   `sh:"desc" xorm:"'description'"` | 	Description      OverridableField[string]   `sh:"desc" xorm:"'description'" json:"description"` | ||||||
| 	Group            OverridableField[string]   `sh:"group" xorm:"'group_name'"` | 	Group            OverridableField[string]   `sh:"group" xorm:"'group_name'" json:"group"` | ||||||
| 	Homepage         OverridableField[string]   `sh:"homepage" xorm:"'homepage'"` | 	Homepage         OverridableField[string]   `sh:"homepage" xorm:"'homepage'" json:"homepage"` | ||||||
| 	Maintainer       OverridableField[string]   `sh:"maintainer" xorm:"'maintainer'"` | 	Maintainer       OverridableField[string]   `sh:"maintainer" xorm:"'maintainer'" json:"maintainer"` | ||||||
| 	Depends          OverridableField[[]string] `sh:"deps" xorm:"'depends'"` | 	Depends          OverridableField[[]string] `sh:"deps" xorm:"'depends'" json:"deps"` | ||||||
| 	BuildDepends     OverridableField[[]string] `sh:"build_deps" xorm:"'builddepends'"` | 	BuildDepends     OverridableField[[]string] `sh:"build_deps" xorm:"'builddepends'" json:"build_deps"` | ||||||
| 	OptDepends       OverridableField[[]string] `sh:"opt_deps" xorm:"'optdepends'"` | 	OptDepends       OverridableField[[]string] `sh:"opt_deps" xorm:"'optdepends'" json:"opt_deps,omitempty"` | ||||||
| 	Sources          OverridableField[[]string] `sh:"sources" xorm:"-"` | 	Sources          OverridableField[[]string] `sh:"sources" xorm:"-" json:"sources"` | ||||||
| 	Checksums        OverridableField[[]string] `sh:"checksums" xorm:"-"` | 	Checksums        OverridableField[[]string] `sh:"checksums" xorm:"-" json:"checksums,omitempty"` | ||||||
| 	Backup           OverridableField[[]string] `sh:"backup" xorm:"-"` | 	Backup           OverridableField[[]string] `sh:"backup" xorm:"-" json:"backup"` | ||||||
| 	Scripts          OverridableField[Scripts]  `sh:"scripts" xorm:"-"` | 	Scripts          OverridableField[Scripts]  `sh:"scripts" xorm:"-" json:"scripts,omitempty"` | ||||||
| 	AutoReq          OverridableField[[]string] `sh:"auto_req" xorm:"-"` | 	AutoReq          OverridableField[[]string] `sh:"auto_req" xorm:"-" json:"auto_req"` | ||||||
| 	AutoProv         OverridableField[[]string] `sh:"auto_prov" xorm:"-"` | 	AutoProv         OverridableField[[]string] `sh:"auto_prov" xorm:"-" json:"auto_prov"` | ||||||
| 	AutoReqSkipList  OverridableField[[]string] `sh:"auto_req_skiplist" xorm:"-"` | 	AutoReqSkipList  OverridableField[[]string] `sh:"auto_req_skiplist" xorm:"-" json:"auto_req_skiplist,omitempty"` | ||||||
| 	AutoProvSkipList OverridableField[[]string] `sh:"auto_prov_skiplist" xorm:"-"` | 	AutoProvSkipList OverridableField[[]string] `sh:"auto_prov_skiplist" xorm:"-" json:"auto_prov_skiplist,omitempty"` | ||||||
|  |  | ||||||
| 	FireJailed       OverridableField[bool]              `sh:"firejailed" xorm:"-"` | 	FireJailed       OverridableField[bool]              `sh:"firejailed" xorm:"-" json:"firejailed"` | ||||||
| 	FireJailProfiles OverridableField[map[string]string] `sh:"firejail_profiles" xorm:"-"` | 	FireJailProfiles OverridableField[map[string]string] `sh:"firejail_profiles" xorm:"-" json:"firejail_profiles,omitempty"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type Scripts struct { | type Scripts struct { | ||||||
| @@ -84,25 +87,70 @@ type Scripts struct { | |||||||
| 	PostTrans   string `sh:"posttrans"` | 	PostTrans   string `sh:"posttrans"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func ResolvePackage(p *Package, overrides []string) { | func (p Package) MarshalJSONWithOptions(includeOverrides bool) ([]byte, error) { | ||||||
| 	val := reflect.ValueOf(p).Elem() | 	// Сначала сериализуем обычным способом для получения базовой структуры | ||||||
| 	typ := val.Type() | 	type PackageAlias Package | ||||||
|  | 	baseData, err := json.Marshal(PackageAlias(p)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	for i := range val.NumField() { | 	// Десериализуем в map для модификации | ||||||
| 		field := val.Field(i) | 	var result map[string]json.RawMessage | ||||||
| 		fieldType := typ.Field(i) | 	if err := json.Unmarshal(baseData, &result); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 		if !field.CanInterface() { | 	// Теперь заменяем OverridableField поля | ||||||
|  | 	v := reflect.ValueOf(p) | ||||||
|  | 	t := reflect.TypeOf(p) | ||||||
|  |  | ||||||
|  | 	for i := 0; i < v.NumField(); i++ { | ||||||
|  | 		field := v.Field(i) | ||||||
|  | 		fieldType := t.Field(i) | ||||||
|  |  | ||||||
|  | 		jsonTag := fieldType.Tag.Get("json") | ||||||
|  | 		if jsonTag == "" || jsonTag == "-" { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if field.Kind() == reflect.Struct && strings.HasPrefix(fieldType.Type.String(), "alrsh.OverridableField") { | 		fieldName := jsonTag | ||||||
| 			of := field.Addr().Interface() | 		if commaIdx := strings.Index(jsonTag, ","); commaIdx != -1 { | ||||||
| 			if res, ok := of.(interface { | 			fieldName = jsonTag[:commaIdx] | ||||||
| 				Resolve([]string) | 		} | ||||||
| 			}); ok { |  | ||||||
| 				res.Resolve(overrides) | 		if field.Type().Name() == "OverridableField" || | ||||||
|  | 			(field.Type().Kind() == reflect.Struct && | ||||||
|  | 				strings.Contains(field.Type().String(), "OverridableField")) { | ||||||
|  |  | ||||||
|  | 			fieldPtr := field.Addr() | ||||||
|  |  | ||||||
|  | 			resolvedMethod := fieldPtr.MethodByName("Resolved") | ||||||
|  | 			if resolvedMethod.IsValid() { | ||||||
|  | 				resolved := resolvedMethod.Call(nil)[0] | ||||||
|  |  | ||||||
|  | 				fieldData := map[string]interface{}{ | ||||||
|  | 					"resolved": resolved.Interface(), | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				if includeOverrides { | ||||||
|  | 					allMethod := field.MethodByName("All") | ||||||
|  | 					if allMethod.IsValid() { | ||||||
|  | 						overrides := allMethod.Call(nil)[0] | ||||||
|  | 						if !overrides.IsNil() && overrides.Len() > 0 { | ||||||
|  | 							fieldData["overrides"] = overrides.Interface() | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				fieldJSON, err := json.Marshal(fieldData) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return nil, err | ||||||
|  | 				} | ||||||
|  | 				result[fieldName] = json.RawMessage(fieldJSON) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	return json.Marshal(result) | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										105
									
								
								pkg/alrsh/package_gen.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								pkg/alrsh/package_gen.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | |||||||
|  | // 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/>. | ||||||
|  |  | ||||||
|  | // DO NOT EDIT MANUALLY. This file is generated. | ||||||
|  | package alrsh | ||||||
|  |  | ||||||
|  | type packageResolved struct { | ||||||
|  | 	Repository       string            `json:"repository"` | ||||||
|  | 	Name             string            `json:"name"` | ||||||
|  | 	BasePkgName      string            `json:"basepkg_name"` | ||||||
|  | 	Version          string            `json:"version"` | ||||||
|  | 	Release          int               `json:"release"` | ||||||
|  | 	Epoch            uint              `json:"epoch"` | ||||||
|  | 	Architectures    []string          `json:"architectures"` | ||||||
|  | 	Licenses         []string          `json:"license"` | ||||||
|  | 	Provides         []string          `json:"provides"` | ||||||
|  | 	Conflicts        []string          `json:"conflicts"` | ||||||
|  | 	Replaces         []string          `json:"replaces"` | ||||||
|  | 	Summary          string            `json:"summary"` | ||||||
|  | 	Description      string            `json:"description"` | ||||||
|  | 	Group            string            `json:"group"` | ||||||
|  | 	Homepage         string            `json:"homepage"` | ||||||
|  | 	Maintainer       string            `json:"maintainer"` | ||||||
|  | 	Depends          []string          `json:"deps"` | ||||||
|  | 	BuildDepends     []string          `json:"build_deps"` | ||||||
|  | 	OptDepends       []string          `json:"opt_deps,omitempty"` | ||||||
|  | 	Sources          []string          `json:"sources"` | ||||||
|  | 	Checksums        []string          `json:"checksums,omitempty"` | ||||||
|  | 	Backup           []string          `json:"backup"` | ||||||
|  | 	Scripts          Scripts           `json:"scripts,omitempty"` | ||||||
|  | 	AutoReq          []string          `json:"auto_req"` | ||||||
|  | 	AutoProv         []string          `json:"auto_prov"` | ||||||
|  | 	AutoReqSkipList  []string          `json:"auto_req_skiplist,omitempty"` | ||||||
|  | 	AutoProvSkipList []string          `json:"auto_prov_skiplist,omitempty"` | ||||||
|  | 	FireJailed       bool              `json:"firejailed"` | ||||||
|  | 	FireJailProfiles map[string]string `json:"firejail_profiles,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func PackageToResolved(src *Package) packageResolved { | ||||||
|  | 	return packageResolved{ | ||||||
|  | 		Repository:       src.Repository, | ||||||
|  | 		Name:             src.Name, | ||||||
|  | 		BasePkgName:      src.BasePkgName, | ||||||
|  | 		Version:          src.Version, | ||||||
|  | 		Release:          src.Release, | ||||||
|  | 		Epoch:            src.Epoch, | ||||||
|  | 		Architectures:    src.Architectures, | ||||||
|  | 		Licenses:         src.Licenses, | ||||||
|  | 		Provides:         src.Provides, | ||||||
|  | 		Conflicts:        src.Conflicts, | ||||||
|  | 		Replaces:         src.Replaces, | ||||||
|  | 		Summary:          src.Summary.Resolved(), | ||||||
|  | 		Description:      src.Description.Resolved(), | ||||||
|  | 		Group:            src.Group.Resolved(), | ||||||
|  | 		Homepage:         src.Homepage.Resolved(), | ||||||
|  | 		Maintainer:       src.Maintainer.Resolved(), | ||||||
|  | 		Depends:          src.Depends.Resolved(), | ||||||
|  | 		BuildDepends:     src.BuildDepends.Resolved(), | ||||||
|  | 		OptDepends:       src.OptDepends.Resolved(), | ||||||
|  | 		Sources:          src.Sources.Resolved(), | ||||||
|  | 		Checksums:        src.Checksums.Resolved(), | ||||||
|  | 		Backup:           src.Backup.Resolved(), | ||||||
|  | 		Scripts:          src.Scripts.Resolved(), | ||||||
|  | 		AutoReq:          src.AutoReq.Resolved(), | ||||||
|  | 		AutoProv:         src.AutoProv.Resolved(), | ||||||
|  | 		AutoReqSkipList:  src.AutoReqSkipList.Resolved(), | ||||||
|  | 		AutoProvSkipList: src.AutoProvSkipList.Resolved(), | ||||||
|  | 		FireJailed:       src.FireJailed.Resolved(), | ||||||
|  | 		FireJailProfiles: src.FireJailProfiles.Resolved(), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func ResolvePackage(pkg *Package, overrides []string) { | ||||||
|  | 	pkg.Summary.Resolve(overrides) | ||||||
|  | 	pkg.Description.Resolve(overrides) | ||||||
|  | 	pkg.Group.Resolve(overrides) | ||||||
|  | 	pkg.Homepage.Resolve(overrides) | ||||||
|  | 	pkg.Maintainer.Resolve(overrides) | ||||||
|  | 	pkg.Depends.Resolve(overrides) | ||||||
|  | 	pkg.BuildDepends.Resolve(overrides) | ||||||
|  | 	pkg.OptDepends.Resolve(overrides) | ||||||
|  | 	pkg.Sources.Resolve(overrides) | ||||||
|  | 	pkg.Checksums.Resolve(overrides) | ||||||
|  | 	pkg.Backup.Resolve(overrides) | ||||||
|  | 	pkg.Scripts.Resolve(overrides) | ||||||
|  | 	pkg.AutoReq.Resolve(overrides) | ||||||
|  | 	pkg.AutoProv.Resolve(overrides) | ||||||
|  | 	pkg.AutoReqSkipList.Resolve(overrides) | ||||||
|  | 	pkg.AutoProvSkipList.Resolve(overrides) | ||||||
|  | 	pkg.FireJailed.Resolve(overrides) | ||||||
|  | 	pkg.FireJailProfiles.Resolve(overrides) | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								pkg/alrsh/view.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								pkg/alrsh/view.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | // 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 alrsh | ||||||
|  |  | ||||||
|  | import "encoding/json" | ||||||
|  |  | ||||||
|  | type PackageView struct { | ||||||
|  | 	pkg Package | ||||||
|  |  | ||||||
|  | 	Resolved bool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewPackageView(v Package) PackageView { | ||||||
|  | 	return PackageView{pkg: v} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p PackageView) MarshalJSON() ([]byte, error) { | ||||||
|  | 	if p.Resolved { | ||||||
|  | 		return json.Marshal(PackageToResolved(&p.pkg)) | ||||||
|  | 	} else { | ||||||
|  | 		return json.Marshal(p.pkg) | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user