func rebuild()

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
}