in dev/tools/controllerbuilder/pkg/io/replace.go [69:281]
func rebuild(destFilePath string, src string) error {
destFset := token.NewFileSet()
destFile, err := parser.ParseFile(destFset, destFilePath, nil, parser.ParseComments)
if err != nil {
return fmt.Errorf("parsing destination file: %w", err)
}
// Make sure src package exists otherwise the parser will fail
_, _, found := strings.Cut(src, "package")
if !found {
src = "package " + destFile.Name.Name + "\n" + src
}
newFset := token.NewFileSet()
newFile, err := parser.ParseFile(newFset, "", src, parser.ParseComments)
if err != nil {
return fmt.Errorf("parsing src file: %w", err)
}
// TODO: Parse the entire package (rather than a single `file`) to avoid redeclaration errors for structs
// defined in different files within the same package.
fwriter, err := os.Create(destFilePath)
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
defer fwriter.Close()
newDecls := []ast.Decl{}
oldComments := MapGoComments(destFile)
newComments := MapGoComments(newFile)
mergedComments := MergeStructComments(oldComments, newComments)
newStructs := mapStruct(newFile)
oldFileComments := destFile.Comments
if oldFileComments == nil {
oldFileComments = []*ast.CommentGroup{}
}
destFile.Comments = []*ast.CommentGroup{}
offset := token.Pos(0)
if oldFileComments != nil {
if strings.Contains(oldFileComments[0].Text(), "Copyright") {
destFile.Comments = append(destFile.Comments, oldFileComments[0])
offset += oldFileComments[0].End() + 1
}
}
offset += destFile.Package + 1
var errs error
visited := map[string]struct{}{}
// TODO: add comments for VAR, CONST, FUNC and IMPORT.
ast.Inspect(destFile, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.GenDecl:
switch x.Tok {
case token.VAR, token.CONST:
/*
newVarDecl := &ast.GenDecl{
Tok: x.Tok,
Specs: []ast.Spec{},
}
for _, spec := range x.Specs {
valueSpec, ok := spec.(*ast.ValueSpec)
if !ok {
return true
}
newSpec := &ast.ValueSpec{
Names: valueSpec.Names,
Values: valueSpec.Values,
Type: valueSpec.Type,
}
newVarDecl.Specs = append(newVarDecl.Specs, newSpec)
}*/
delta := x.Specs[len(x.Specs)-1].End() - x.Specs[0].Pos()
offset += delta + 1
newDecls = append(newDecls, x)
case token.IMPORT:
/*
newImportDecl := &ast.GenDecl{
Tok: token.IMPORT,
Specs: []ast.Spec{},
}
for _, spec := range x.Specs {
importSpec, ok := spec.(*ast.ImportSpec)
if !ok {
return true
}
newSpec := &ast.ImportSpec{
Name: importSpec.Name,
Path: importSpec.Path,
}
newImportDecl.Specs = append(newImportDecl.Specs, newSpec)
}*/
delta := x.Specs[len(x.Specs)-1].End() - x.Specs[0].Pos()
offset += delta + 1
newDecls = append(newDecls, x)
case token.TYPE:
for _, spec := range x.Specs {
typeSpec, ok := spec.(*ast.TypeSpec)
if !ok {
return true
}
var newStructDecl *ast.GenDecl
newStruct, ok := newStructs[typeSpec.Name.Name]
comments := mergedComments[typeSpec.Name.Name]
if !ok {
newStructDecl, err = copyStruct(x, comments)
if err != nil {
errors.Join(errs, err)
return false
}
} else {
newStructDecl, err = mergeStruct(x, newStruct, comments)
if err != nil {
errors.Join(errs, err)
return false
}
}
offset = comments.SetCommentPos(offset)
if comments.Comment != nil {
newStructDecl.Doc = comments.Comment
}
typeStruct := newStructDecl.Specs[0].(*ast.TypeSpec)
for _, field := range typeStruct.Type.(*ast.StructType).Fields.List {
// Add field comment on top of the field.
newCommentList := []*ast.Comment{}
if field.Doc != nil {
for _, c := range field.Doc.List {
c.Slash = offset
offset += c.End() + 1
newCommentList = append(newCommentList, c)
}
field.Doc = &ast.CommentGroup{List: newCommentList}
}
delta := field.End() - field.Pos()
offset += delta + 1
}
newDecls = append(newDecls, newStructDecl)
visited[typeSpec.Name.Name] = struct{}{}
}
}
case *ast.FuncDecl:
newFunDecl := &ast.FuncDecl{
Name: x.Name,
Type: x.Type,
Body: x.Body,
}
newFields := []*ast.Field{}
if x.Recv != nil {
for _, r := range x.Recv.List {
newField := &ast.Field{
Names: r.Names,
Type: r.Type,
Tag: r.Tag,
}
newFields = append(newFields, newField)
}
}
newFunDecl.Recv = &ast.FieldList{
List: newFields,
}
// TODO: Add comments to funcDecl
newDecls = append(newDecls, newFunDecl)
delta := x.Body.End() - x.Body.Pos()
offset += delta + 1
case *ast.BadDecl:
newBadDecl := &ast.BadDecl{
From: x.From,
To: x.To,
}
newDecls = append(newDecls, newBadDecl)
}
return true
})
if errs != nil {
return err
}
// TODO Add non struct type from newFile
ast.Inspect(newFile, func(n ast.Node) bool {
x, ok := n.(*ast.GenDecl)
if !ok || x.Tok != token.TYPE {
return true
}
for _, spec := range x.Specs {
typeSpec, ok := spec.(*ast.TypeSpec)
if !ok {
continue
}
if _, ok := visited[typeSpec.Name.Name]; ok {
continue
}
newDecls = append(newDecls, x)
}
return true
})
destFile.Decls = newDecls
/*
if err := RebuildCommentPos(fwriter, destFset, destFile, mergedComments, offset); err != nil {
return err
}*/
err = format.Node(fwriter, destFset, destFile)
if err != nil {
return fmt.Errorf("error formatting code: %w", err)
}
return nil
}