| 
 | 1 | +package main  | 
 | 2 | + | 
 | 3 | +import (  | 
 | 4 | +	"bytes"  | 
 | 5 | +	"fmt"  | 
 | 6 | +	"go/ast"  | 
 | 7 | +	"go/parser"  | 
 | 8 | +	"go/printer"  | 
 | 9 | +	"go/token"  | 
 | 10 | +	"log"  | 
 | 11 | +	"os"  | 
 | 12 | +	"path/filepath"  | 
 | 13 | +	"reflect"  | 
 | 14 | +	"strings"  | 
 | 15 | + | 
 | 16 | +	"golang.org/x/tools/imports"  | 
 | 17 | +)  | 
 | 18 | + | 
 | 19 | +const newPkgName = "versiontwo"  | 
 | 20 | + | 
 | 21 | +const (  | 
 | 22 | +	srcDir = "./pkg/config"  | 
 | 23 | +	dstDir = "./pkg/commands/internal/migrate/versiontwo"  | 
 | 24 | +)  | 
 | 25 | + | 
 | 26 | +func main() {  | 
 | 27 | +	stat, err := os.Stat(srcDir)  | 
 | 28 | +	if err != nil {  | 
 | 29 | +		log.Fatal(err)  | 
 | 30 | +	}  | 
 | 31 | + | 
 | 32 | +	if !stat.IsDir() {  | 
 | 33 | +		log.Fatalf("%s is not a directory", srcDir)  | 
 | 34 | +	}  | 
 | 35 | + | 
 | 36 | +	_ = os.RemoveAll(dstDir)  | 
 | 37 | + | 
 | 38 | +	err = processPackage(srcDir, dstDir)  | 
 | 39 | +	if err != nil {  | 
 | 40 | +		log.Fatalf("Processing package error: %v", err)  | 
 | 41 | +	}  | 
 | 42 | +}  | 
 | 43 | + | 
 | 44 | +func processPackage(srcDir, dstDir string) error {  | 
 | 45 | +	return filepath.Walk(srcDir, func(srcPath string, _ os.FileInfo, err error) error {  | 
 | 46 | +		if err != nil {  | 
 | 47 | +			return err  | 
 | 48 | +		}  | 
 | 49 | + | 
 | 50 | +		if skipFile(srcPath) {  | 
 | 51 | +			return nil  | 
 | 52 | +		}  | 
 | 53 | + | 
 | 54 | +		fset := token.NewFileSet()  | 
 | 55 | + | 
 | 56 | +		file, err := parser.ParseFile(fset, srcPath, nil, parser.AllErrors)  | 
 | 57 | +		if err != nil {  | 
 | 58 | +			return fmt.Errorf("parsing %s: %w", srcPath, err)  | 
 | 59 | +		}  | 
 | 60 | + | 
 | 61 | +		processFile(file)  | 
 | 62 | + | 
 | 63 | +		return writeNewFile(fset, file, srcPath, dstDir)  | 
 | 64 | +	})  | 
 | 65 | +}  | 
 | 66 | + | 
 | 67 | +func skipFile(path string) bool {  | 
 | 68 | +	if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {  | 
 | 69 | +		return true  | 
 | 70 | +	}  | 
 | 71 | + | 
 | 72 | +	switch filepath.Base(path) {  | 
 | 73 | +	case "base_loader.go", "loader.go":  | 
 | 74 | +		return true  | 
 | 75 | +	default:  | 
 | 76 | +		return false  | 
 | 77 | +	}  | 
 | 78 | +}  | 
 | 79 | + | 
 | 80 | +func processFile(file *ast.File) {  | 
 | 81 | +	file.Name.Name = newPkgName  | 
 | 82 | + | 
 | 83 | +	var newDecls []ast.Decl  | 
 | 84 | +	for _, decl := range file.Decls {  | 
 | 85 | +		d, ok := decl.(*ast.GenDecl)  | 
 | 86 | +		if !ok {  | 
 | 87 | +			continue  | 
 | 88 | +		}  | 
 | 89 | + | 
 | 90 | +		switch d.Tok {  | 
 | 91 | +		case token.CONST, token.VAR:  | 
 | 92 | +			continue  | 
 | 93 | +		case token.TYPE:  | 
 | 94 | +			for _, spec := range d.Specs {  | 
 | 95 | +				typeSpec, ok := spec.(*ast.TypeSpec)  | 
 | 96 | +				if !ok {  | 
 | 97 | +					continue  | 
 | 98 | +				}  | 
 | 99 | + | 
 | 100 | +				structType, ok := typeSpec.Type.(*ast.StructType)  | 
 | 101 | +				if !ok {  | 
 | 102 | +					continue  | 
 | 103 | +				}  | 
 | 104 | + | 
 | 105 | +				processStructFields(structType)  | 
 | 106 | +			}  | 
 | 107 | +		default:  | 
 | 108 | +			// noop  | 
 | 109 | +		}  | 
 | 110 | + | 
 | 111 | +		newDecls = append(newDecls, decl)  | 
 | 112 | +	}  | 
 | 113 | + | 
 | 114 | +	file.Decls = newDecls  | 
 | 115 | +}  | 
 | 116 | + | 
 | 117 | +func processStructFields(structType *ast.StructType) {  | 
 | 118 | +	var newFields []*ast.Field  | 
 | 119 | + | 
 | 120 | +	for _, field := range structType.Fields.List {  | 
 | 121 | +		if len(field.Names) > 0 && !field.Names[0].IsExported() {  | 
 | 122 | +			continue  | 
 | 123 | +		}  | 
 | 124 | + | 
 | 125 | +		if field.Tag == nil {  | 
 | 126 | +			continue  | 
 | 127 | +		}  | 
 | 128 | + | 
 | 129 | +		field.Type = convertType(field.Type)  | 
 | 130 | +		field.Tag.Value = convertStructTag(field.Tag.Value)  | 
 | 131 | + | 
 | 132 | +		newFields = append(newFields, field)  | 
 | 133 | +	}  | 
 | 134 | + | 
 | 135 | +	structType.Fields.List = newFields  | 
 | 136 | +}  | 
 | 137 | + | 
 | 138 | +func convertType(expr ast.Expr) ast.Expr {  | 
 | 139 | +	ident, ok := expr.(*ast.Ident)  | 
 | 140 | +	if !ok {  | 
 | 141 | +		return expr  | 
 | 142 | +	}  | 
 | 143 | + | 
 | 144 | +	switch ident.Name {  | 
 | 145 | +	case "bool", "string", "int", "int8", "int16", "int32", "int64", "float32", "float64":  | 
 | 146 | +		return &ast.StarExpr{X: ident}  | 
 | 147 | + | 
 | 148 | +	default:  | 
 | 149 | +		return expr  | 
 | 150 | +	}  | 
 | 151 | +}  | 
 | 152 | + | 
 | 153 | +func convertStructTag(value string) string {  | 
 | 154 | +	structTag := reflect.StructTag(strings.Trim(value, "`"))  | 
 | 155 | + | 
 | 156 | +	key := structTag.Get("mapstructure")  | 
 | 157 | + | 
 | 158 | +	if key == ",squash" {  | 
 | 159 | +		return wrapStructTag(`yaml:",inline"`)  | 
 | 160 | +	}  | 
 | 161 | + | 
 | 162 | +	return wrapStructTag(fmt.Sprintf(`yaml:"%[1]s,omitempty" toml:"%[1]s,multiline,omitempty"`, key))  | 
 | 163 | +}  | 
 | 164 | + | 
 | 165 | +func wrapStructTag(s string) string {  | 
 | 166 | +	return "`" + s + "`"  | 
 | 167 | +}  | 
 | 168 | + | 
 | 169 | +func writeNewFile(fset *token.FileSet, file *ast.File, srcPath, dstDir string) error {  | 
 | 170 | +	var buf bytes.Buffer  | 
 | 171 | + | 
 | 172 | +	buf.WriteString("// Code generated by pkg/commands/internal/migrate/cloner/cloner.go. DO NOT EDIT.\n\n")  | 
 | 173 | + | 
 | 174 | +	err := printer.Fprint(&buf, fset, file)  | 
 | 175 | +	if err != nil {  | 
 | 176 | +		return fmt.Errorf("printing %s: %w", srcPath, err)  | 
 | 177 | +	}  | 
 | 178 | + | 
 | 179 | +	dstPath := filepath.Join(dstDir, filepath.Base(srcPath))  | 
 | 180 | + | 
 | 181 | +	_ = os.MkdirAll(filepath.Dir(dstPath), os.ModePerm)  | 
 | 182 | + | 
 | 183 | +	formatted, err := imports.Process(dstPath, buf.Bytes(), nil)  | 
 | 184 | +	if err != nil {  | 
 | 185 | +		return fmt.Errorf("formatting %s: %w", dstPath, err)  | 
 | 186 | +	}  | 
 | 187 | + | 
 | 188 | +	//nolint:gosec,mnd // The permission is right.  | 
 | 189 | +	err = os.WriteFile(dstPath, formatted, 0o644)  | 
 | 190 | +	if err != nil {  | 
 | 191 | +		return fmt.Errorf("writing file %s: %w", dstPath, err)  | 
 | 192 | +	}  | 
 | 193 | + | 
 | 194 | +	return nil  | 
 | 195 | +}  | 
0 commit comments