diff --git a/cmd/hertz_migrate/internal/cli/cli.go b/cmd/hertz_migrate/internal/cli/cli.go index 6e0b140..d6c357c 100644 --- a/cmd/hertz_migrate/internal/cli/cli.go +++ b/cmd/hertz_migrate/internal/cli/cli.go @@ -22,9 +22,15 @@ import ( "go/token" "log" "os" - "path/filepath" + "strings" "sync" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/logic" + + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/logic/gin" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/logs" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/types" + mapset "github.com/deckarep/golang-set/v2" "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal" "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/logic/chi" @@ -37,12 +43,20 @@ import ( func init() { globalMap = make(map[string]interface{}) - funcSet = mapset.NewSet[string]() + webCtxSet = mapset.NewSet[string]() } var ( globalArgs = &Args{} globalMap map[string]any + webCtxSet mapset.Set[string] + fset *token.FileSet + wg sync.WaitGroup + + printerConfig = printer.Config{ + Mode: printer.UseSpaces, + Tabwidth: 4, + } ) type Args struct { @@ -51,6 +65,9 @@ type Args struct { HzRepo string IgnoreDirs []string Debug bool + UseGin bool + UseNetHTTP bool + UseChi bool } const ignoreDirsText = ` @@ -68,8 +85,8 @@ func Init() *cli.App { &cli.StringFlag{ Name: "hz-repo", Aliases: []string{"r"}, + Value: "github.com/cloudwego/hertz", Usage: "Specify the url of the hertz repository you want to bring in.", - DefaultText: "github.com/cloudwego/hertz", Destination: &globalArgs.HzRepo, }, &cli.StringFlag{ @@ -80,25 +97,60 @@ func Init() *cli.App { }, &cli.StringSliceFlag{ Name: "ignore-dirs", - Aliases: []string{"D"}, + Aliases: []string{"I"}, Usage: ignoreDirsText, }, + &cli.BoolFlag{ + Name: "debug", + Aliases: []string{"D"}, + Destination: &globalArgs.Debug, + Value: false, + }, + &cli.BoolFlag{ + Name: "use-gin", + Aliases: []string{"g"}, + Usage: "migrate to hertz with gin as the web framework", + Destination: &globalArgs.UseGin, + }, + &cli.BoolFlag{ + Name: "use-net-http", + Aliases: []string{"n"}, + Usage: "migrate to hertz with net/http as the web framework", + Destination: &globalArgs.UseNetHTTP, + }, + &cli.BoolFlag{ + Name: "use-chi", + Aliases: []string{"c"}, + Usage: "migrate to hertz with chi as the web framework", + Destination: &globalArgs.UseNetHTTP, + }, } app.Action = Run return app } func Run(c *cli.Context) error { + fset = token.NewFileSet() globalArgs.IgnoreDirs = c.StringSlice("ignore-dirs") + if globalArgs.UseChi { + globalArgs.UseNetHTTP = true + } + + if globalArgs.Debug { + logs.SetLevel(logs.LevelDebug) + } + if globalArgs.TargetDir != "" { gofiles, err := utils.CollectGoFiles(globalArgs.TargetDir, globalArgs.IgnoreDirs) - for _, f := range gofiles { - log.Println(f) + if err != nil { + return err } + + goModDirs, err := utils.SearchAllDirHasGoMod(globalArgs.TargetDir) if err != nil { - log.Fatal("Error collecting go files:", err) + return err } - goModDirs = utils.SearchAllDirHasGoMod(globalArgs.TargetDir) + for _, dir := range goModDirs { wg.Add(1) dir := dir @@ -109,147 +161,207 @@ func Run(c *cli.Context) error { } wg.Wait() - beforeProcessFiles(gofiles) - processFiles(gofiles, globalArgs.Debug) + for _, path := range gofiles { + file, err := parser.ParseFile(fset, path, nil, 0) + if err != nil { + logs.Debugf("Parse file fail, error: %v", err) + return internal.ErrParseFile + } + + astutil.Apply(file, func(c *astutil.Cursor) bool { + logic.GetHttpServerProps(c) + if globalArgs.UseGin { + gin.GetFuncNameHasGinCtx(c) + } + if globalArgs.UseNetHTTP { + nethttp.FindHandlerFuncName(c, webCtxSet) + } + return true + }, nil) + } + + if err = processFiles(gofiles); err != nil { + return err + } for _, dir := range goModDirs { utils.RunGoImports(dir) + utils.RunGoModTidy(dir) } } + logs.Info("everything are ok!") return nil } -var ( - funcSet mapset.Set[string] - goModDirs []string - wg sync.WaitGroup -) - -func processFiles(gofiles []string, debug bool) { +func processFiles(gofiles []string) error { for _, path := range gofiles { - processFile(path, debug) - } -} + var containsGin bool -func beforeProcessFiles(gofiles []string) { - for _, path := range gofiles { - beforeProcessFile(path) - } -} + file, err := parser.ParseFile(fset, path, nil, parser.ParseComments) + if err != nil { + logs.Debugf("Parse file fail, error: %v", err) + return internal.ErrParseFile + } -func beforeProcessFile(path string) { - fset := token.NewFileSet() - file, err := parser.ParseFile(fset, path, nil, 0) - if err != nil { - log.Fatal(err) - } + astutil.AddNamedImport(fset, file, "hzserver", globalArgs.HzRepo+"/pkg/app/server") + astutil.AddNamedImport(fset, file, "hzapp", globalArgs.HzRepo+"/pkg/app") + astutil.AddNamedImport(fset, file, "hzroute", globalArgs.HzRepo+"/pkg/route") + astutil.AddNamedImport(fset, file, "hzerrors", globalArgs.HzRepo+"/pkg/common/errors") + astutil.AddNamedImport(fset, file, "hzutils", globalArgs.HzRepo+"/pkg/common/utils") - astutil.Apply(file, func(c *astutil.Cursor) bool { - nethttp.CollectHandlerFuncName(c, funcSet) - return true - }, nil) -} + for _, importSpec := range file.Imports { + importStr := importSpec.Path.Value -func processFile(path string, debug bool) { - var mode parser.Mode - fset := token.NewFileSet() - path, err := filepath.Abs(path) - if err != nil { - log.Fatal(err) - } - if debug { - mode = 0 - } else { - mode = parser.ParseComments - } + if globalArgs.UseGin { + if strings.Contains(importStr, `github.com/gin-gonic/gin`) { + containsGin = true + } - file, err := parser.ParseFile(fset, path, nil, mode) - if err != nil { - log.Fatal(err) - } + if strings.Contains(importStr, `"github.com/gin-contrib/cors"`) { + importSpec.Path.Value = `"github.com/hertz-contrib/cors"` + containsGin = true + } - processAST(file, fset) + if strings.Contains(importStr, `github.com/swaggo/gin-swagger`) { + importSpec.Path.Value = `"github.com/hertz-contrib/swagger"` + containsGin = true + } + } + } - var buf bytes.Buffer - if err := printer.Fprint(&buf, fset, file); err != nil { - log.Println(err) - } + if !containsGin && globalArgs.UseGin { + continue + } - replace := formatCodeAfterReplace(fset, buf) - outputPath := path + astutil.Apply(file, func(c *astutil.Cursor) bool { + switch node := c.Node().(type) { + case *ast.StarExpr: + if globalArgs.UseChi { + if utils.CheckPtrPkgAndStructName(node, "chi", "Mux") { + c.Replace(types.StarServerHertz) + } + } + if globalArgs.UseGin { + if sel, ok := node.X.(*ast.SelectorExpr); ok { + if utils.CheckSelPkgAndStruct(sel, "gin", "Engine") { + c.Replace(types.StarServerHertz) + } - if err := os.WriteFile(outputPath, replace.Bytes(), os.ModePerm); err == nil { - log.Println("File updated:", outputPath) - } else { - log.Println(err) - } -} + if utils.CheckSelPkgAndStruct(sel, "gin", "RouterGroup") { + c.Replace(types.StarRouteGroup) + } + } + } + case *ast.FieldList: + if globalArgs.UseGin { + gin.ReplaceGinCtx(node) + } + case *ast.SelectorExpr: + if globalArgs.UseGin { + if utils.CheckSelPkgAndStruct(node, "route", "IRoutes") { + c.Replace(types.SelIRoutes) + } + } + } + if globalArgs.UseNetHTTP { + nethttp.GetOptionsFromHttpServer(c, globalMap) + nethttp.PackServerHertz(c, globalMap) + nethttp.ReplaceNetHttpHandler(c) + } + return true + }, nil) -func processAST(file *ast.File, fset *token.FileSet) { - astutil.AddNamedImport(fset, file, "hzserver", globalArgs.HzRepo+"/pkg/app/server") - astutil.AddNamedImport(fset, file, "hzapp", globalArgs.HzRepo+"/pkg/app") - - astutil.Apply(file, func(c *astutil.Cursor) bool { - nethttp.GetOptionsFromHttpServer(c, globalMap) - nethttp.PackServerHertz(c, globalMap) - chi.PackChiMux(c) - nethttp.ReplaceNetHttpHandler(c) - nethttp.PackSetStatusCode(c) - return true - }, nil) - - astutil.Apply(file, func(c *astutil.Cursor) bool { - chiGroup(c) - netHttpGroup(c, funcSet) - return true - }, nil) -} + astutil.Apply(file, func(c *astutil.Cursor) bool { + netHttpGroup(c, webCtxSet) + switch node := c.Node().(type) { + case *ast.SelectorExpr: + if globalArgs.UseNetHTTP { + if utils.CheckSelObj(node, "http", "ResponseWriter") { + switch node.Sel.Name { + case "WriteHeader": + c.Replace(types.SelSetStatusCode) + case "Write": + c.Replace(types.SelWrite) + case "Header": + c.Replace(types.SelRespHeader) + } + } -func chiGroup(c *astutil.Cursor) { - chi.PackChiRouterMethod(c) - chi.PackChiNewRouter(c, globalMap) - chi.PackChiMux(c) -} + if node.Sel.Name == "HandleFunc" { + node.Sel.Name = "Any" + } + nethttp.ReplaceRequestOp(node, c) + } -func netHttpGroup(c *astutil.Cursor, funcSet mapset.Set[string]) { - nethttp.PackHandleFunc(c) - nethttp.PackFprintf(c) - nethttp.ReplaceHttpError(c) - nethttp.ReplaceHttpRedirect(c) - nethttp.ReplaceRequestURI(c) - nethttp.ReplaceReqMethod(c) - nethttp.ReplaceReqHost(c) - nethttp.ReplaceReqHeader(c) - nethttp.ReplaceReqHeaderOperation(c) - nethttp.ReplaceRespHeader(c) - nethttp.ReplaceRespWrite(c) - nethttp.ReplaceRespNotFound(c) - nethttp.ReplaceReqURLQuery(c) - nethttp.ReplaceReqURLString(c) - nethttp.ReplaceReqURLPath(c) - nethttp.ReplaceReqCookie(c) - nethttp.ReplaceReqFormFile(c) - nethttp.ReplaceReqFormGet(c) - nethttp.ReplaceReqFormValue(c) - nethttp.ReplaceReqMultipartForm(c) - nethttp.PackType2AppHandlerFunc(c) - nethttp.ReplaceReqMultipartFormOperation(c, globalMap) - nethttp.ReplaceFuncBodyHttpHandlerParam(c, funcSet) + if globalArgs.UseGin { + if utils.CheckSelPkgAndStruct(node, "gin", "HandlerFunc") { + c.Replace(types.SelAppHandlerFunc) + } -} + if utils.CheckSelPkgAndStruct(node, "gin", "H") { + node.X.(*ast.Ident).Name = "hzutils" + } -func formatCodeAfterReplace(fset *token.FileSet, buf bytes.Buffer) *bytes.Buffer { - file, _ := parser.ParseFile(fset, "", buf.String(), parser.ParseComments) + gin.ReplaceBinding(node, c) + gin.ReplaceRequestOp(node, c) + gin.ReplaceRespOp(node, c) + gin.ReplaceErrorType(node) + } + case *ast.CallExpr: + if globalArgs.UseChi { + chi.PackChiRouterMethod(node) + } + if globalArgs.UseNetHTTP { + nethttp.ReplaceHttpOp(node, c) + nethttp.ReplaceReqOrRespOp(node, c) + nethttp.ReplaceReqURLQuery(node) + if utils.CheckCallPkgAndMethodName(node, "http", "NotFound") { + c.Replace(types.CallNotFound) + } + } - var output bytes.Buffer - cfg := printer.Config{ - Mode: printer.UseSpaces, - Tabwidth: 4, + if globalArgs.UseGin { + gin.ReplaceGinNew(node, c) + gin.ReplaceGinRun(node) + gin.ReplaceGinCtxOp(node, c) + gin.ReplaceCallReqOrResp(node, c) + gin.ReplaceStatisFS(node) + } + } + if globalArgs.UseGin { + gin.ReplaceCtxParamList(c) + } + if globalArgs.UseChi { + chi.PackChiNewRouter(c, globalMap) + } + return true + }, nil) + + var buf bytes.Buffer + + if err = printerConfig.Fprint(&buf, fset, file); err != nil { + logs.Debugf("Fprint fail, error: %v", err) + return internal.ErrSaveChanges + } + + if err := os.WriteFile(path, buf.Bytes(), os.ModePerm); err == nil { + log.Println("File updated:", path) + } } - err := cfg.Fprint(&output, fset, file) - if err != nil { - log.Println(err) - return nil + return nil +} + +func netHttpGroup(c *astutil.Cursor, funcSet mapset.Set[string]) { + if globalArgs.UseNetHTTP { + nethttp.PackFprintf(c) + nethttp.ReplaceReqHeader(c) + nethttp.ReplaceReqHeaderOperation(c) + nethttp.ReplaceRespWrite(c) + nethttp.ReplaceReqFormGet(c) + nethttp.ReplaceReqFormValue(c) + nethttp.ReplaceReqMultipartForm(c) + nethttp.PackType2AppHandlerFunc(c) + nethttp.ReplaceReqMultipartFormOperation(c, globalMap) + nethttp.ReplaceFuncBodyHttpHandlerParam(c, funcSet) } - return &output } diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceRespNotFound.go b/cmd/hertz_migrate/internal/errors.go similarity index 55% rename from cmd/hertz_migrate/internal/logic/netHttp/replaceRespNotFound.go rename to cmd/hertz_migrate/internal/errors.go index 2f5fcef..5daaa3d 100644 --- a/cmd/hertz_migrate/internal/logic/netHttp/replaceRespNotFound.go +++ b/cmd/hertz_migrate/internal/errors.go @@ -12,33 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -package netHttp +package internal -import ( - . "go/ast" +import "errors" - "golang.org/x/tools/go/ast/astutil" +var ( + ErrCollectGoFiles = errors.New("collect go files error") + ErrSearchGoMod = errors.New("search go.mod dir fail") + ErrParseFile = errors.New("parse go file fail") + ErrSaveChanges = errors.New("change go file fail") + ErrGetAbsPath = errors.New("get absolute path fail") + ErrRunCommand = errors.New("run command fail") + ErrChangeDir = errors.New("change directory fail") ) - -func ReplaceRespNotFound(cur *astutil.Cursor) { - callExpr, ok := cur.Node().(*CallExpr) - if !ok { - return - } - - selExpr, ok := callExpr.Fun.(*SelectorExpr) - if !ok { - return - } - - ident, ok := selExpr.X.(*Ident) - if !ok || ident.Name != "http" || selExpr.Sel.Name != "NotFound" { - return - } - - callExpr.Fun = &SelectorExpr{ - X: NewIdent("c"), - Sel: NewIdent("NotFound"), - } - callExpr.Args = []Expr{} -} diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqURLString.go b/cmd/hertz_migrate/internal/global.go similarity index 59% rename from cmd/hertz_migrate/internal/logic/netHttp/replaceReqURLString.go rename to cmd/hertz_migrate/internal/global.go index 576ae70..4d1f59e 100644 --- a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqURLString.go +++ b/cmd/hertz_migrate/internal/global.go @@ -12,27 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -package netHttp +package internal import ( - . "go/ast" + "go/ast" - "golang.org/x/tools/go/ast/astutil" + mapset "github.com/deckarep/golang-set/v2" ) -func ReplaceReqURLString(cur *astutil.Cursor) { - selExpr, ok := cur.Node().(*SelectorExpr) - if !ok || selExpr.Sel == nil || selExpr.Sel.Name != "String" { - return - } - ce, ok := selExpr.X.(*SelectorExpr) - if !ok || ce.Sel.Name != "URL" { - return - } - selExpr.X = &CallExpr{ - Fun: &SelectorExpr{ - X: NewIdent("c"), - Sel: NewIdent("URI"), - }, - } +var ( + CtxSet mapset.Set[string] + WebCtxSet mapset.Set[string] + Options []ast.Expr + ServerName string +) + +func init() { + CtxSet = mapset.NewSet[string]() + WebCtxSet = mapset.NewSet[string]() } diff --git a/cmd/hertz_migrate/internal/logic/chi/packChiMux.go b/cmd/hertz_migrate/internal/logic/chi/packChiMux.go index 87cf0f2..0734f92 100644 --- a/cmd/hertz_migrate/internal/logic/chi/packChiMux.go +++ b/cmd/hertz_migrate/internal/logic/chi/packChiMux.go @@ -17,27 +17,14 @@ package chi import ( . "go/ast" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/types" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" + "golang.org/x/tools/go/ast/astutil" ) -func PackChiMux(cur *astutil.Cursor) { - funcType, ok := cur.Node().(*FuncType) - if !ok || funcType.Results == nil { - return - } - - if len(funcType.Results.List) == 1 { - starExpr, ok := funcType.Results.List[0].Type.(*StarExpr) - if !ok { - return - } - selExpr, ok := starExpr.X.(*SelectorExpr) - if !ok { - return - } - if selExpr.Sel.Name == "Mux" && selExpr.X.(*Ident).Name == "chi" { - selExpr.X.(*Ident).Name = "hzserver" - selExpr.Sel.Name = "Hertz" - } +func PackChiMux(c *astutil.Cursor, node *StarExpr) { + if utils.CheckPtrPkgAndStructName(node, "chi", "Mux") { + c.Replace(types.StarServerHertz) } } diff --git a/cmd/hertz_migrate/internal/logic/chi/packChiRouterMethod.go b/cmd/hertz_migrate/internal/logic/chi/packChiRouterMethod.go index 12863f4..b775ffa 100644 --- a/cmd/hertz_migrate/internal/logic/chi/packChiRouterMethod.go +++ b/cmd/hertz_migrate/internal/logic/chi/packChiRouterMethod.go @@ -19,12 +19,10 @@ import ( "go/token" "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" - "golang.org/x/tools/go/ast/astutil" ) -func PackChiRouterMethod(cur *astutil.Cursor) { - callExpr, ok := cur.Node().(*CallExpr) - if !ok || len(callExpr.Args) < 2 { +func PackChiRouterMethod(callExpr *CallExpr) { + if len(callExpr.Args) < 2 { return } selExpr, ok := callExpr.Fun.(*SelectorExpr) @@ -32,6 +30,16 @@ func PackChiRouterMethod(cur *astutil.Cursor) { return } + if ident, ok := selExpr.X.(*Ident); ok { + if !utils.CheckObjStarExpr(ident.Obj, "chi", "Mux") { + return + } + } + + if _, ok := selExpr.X.(*SelectorExpr); ok { + return + } + if len(callExpr.Args) == 2 { switch selExpr.Sel.Name { case "Get": diff --git a/cmd/hertz_migrate/internal/logic/gin/findCtxInFieldList.go b/cmd/hertz_migrate/internal/logic/gin/findCtxInFieldList.go new file mode 100644 index 0000000..fda2d0c --- /dev/null +++ b/cmd/hertz_migrate/internal/logic/gin/findCtxInFieldList.go @@ -0,0 +1,136 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gin + +import ( + . "go/ast" + + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" + + "golang.org/x/tools/go/ast/astutil" +) + +func GetFuncNameHasGinCtx(c *astutil.Cursor) { + findInnerFuncName(c) + findFuncName(c) + findTypePropName(c) + findExprStmtFuncName(c) +} + +func findTypePropName(cur *astutil.Cursor) { + var paramList []*Field + if field, ok := cur.Node().(*Field); ok { + if funcType, ok := field.Type.(*FuncType); ok { + paramList = funcType.Params.List + for _, _field := range paramList { + switch t := _field.Type.(type) { + case *StarExpr: + if selExpr, ok := t.X.(*SelectorExpr); ok { + if selExpr.Sel.Name == "Context" { + internal.WebCtxSet.Add(field.Names[0].Name) + } + } + case *SelectorExpr: + if utils.CheckSelPkgAndStruct(t, "context", "Context") { + internal.CtxSet.Add(field.Names[0].Name) + } + } + } + } + } +} + +func findExprStmtFuncName(cur *astutil.Cursor) { + if exprStmt, ok := cur.Node().(*ExprStmt); ok { + if callExpr, ok := exprStmt.X.(*CallExpr); ok { + if ident, ok := callExpr.Fun.(*Ident); ok { + funcName := ident.Name + for _, arg := range callExpr.Args { + switch node := arg.(type) { + case *Ident: + if utils.CheckObjStarExpr(node.Obj, "gin", "Context") { + internal.WebCtxSet.Add(funcName) + } + case *SelectorExpr: + if utils.CheckSelPkgAndStruct(node, "gin", "Context") { + internal.CtxSet.Add(funcName) + } + } + } + } + } + } +} + +func findInnerFuncName(cur *astutil.Cursor) { + var ( + funcName string + paramList []*Field + ) + if blockStmt, ok := cur.Node().(*BlockStmt); ok { + for _, stmt := range blockStmt.List { + if as, ok := stmt.(*AssignStmt); ok { + if len(as.Lhs) == 1 { + if ident, ok := as.Lhs[0].(*Ident); ok { + funcName = ident.Name + } + } + if len(as.Rhs) == 1 { + if funcLit, ok := as.Rhs[0].(*FuncLit); ok { + paramList = funcLit.Type.Params.List + for _, field := range paramList { + switch t := field.Type.(type) { + case *StarExpr: + if selExpr, ok := t.X.(*SelectorExpr); ok { + if utils.CheckSelPkgAndStruct(selExpr, "gin", "Context") { + internal.WebCtxSet.Add(funcName) + } + } + case *SelectorExpr: + if utils.CheckSelPkgAndStruct(t, "context", "Context") { + internal.CtxSet.Add(funcName) + } + } + } + } + } + } + } + } +} + +func findFuncName(cur *astutil.Cursor) { + var paramList []*Field + if funcDecl, ok := cur.Node().(*FuncDecl); ok { + funcType := funcDecl.Type + paramList = funcType.Params.List + + for _, field := range paramList { + switch t := field.Type.(type) { + case *StarExpr: + if selExpr, ok := t.X.(*SelectorExpr); ok { + if selExpr.Sel.Name == "Context" { + internal.WebCtxSet.Add(funcDecl.Name.Name) + } + } + case *SelectorExpr: + if utils.CheckSelPkgAndStruct(t, "context", "Context") { + internal.CtxSet.Add(funcDecl.Name.Name) + } + } + } + } +} diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceRespHeader.go b/cmd/hertz_migrate/internal/logic/gin/replaceBinding.go similarity index 57% rename from cmd/hertz_migrate/internal/logic/netHttp/replaceRespHeader.go rename to cmd/hertz_migrate/internal/logic/gin/replaceBinding.go index 4d4f815..fd1f048 100644 --- a/cmd/hertz_migrate/internal/logic/netHttp/replaceRespHeader.go +++ b/cmd/hertz_migrate/internal/logic/gin/replaceBinding.go @@ -12,38 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -package netHttp +package gin import ( - . "go/ast" - + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/types" "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" + "go/ast" "golang.org/x/tools/go/ast/astutil" ) -func ReplaceRespHeader(cur *astutil.Cursor) { - callExpr, ok := cur.Node().(*CallExpr) - if !ok { - return - } - - selExpr, ok := callExpr.Fun.(*SelectorExpr) - if !ok || selExpr.Sel == nil { - return - } - - if selExpr.Sel.Name == "Header" { - if utils.CheckStructName(selExpr, "ResponseWriter") { - callExpr := &SelectorExpr{ - X: &SelectorExpr{ - X: NewIdent("c"), - Sel: NewIdent("Response"), - }, - Sel: NewIdent("Header"), +func ReplaceBinding(se *ast.SelectorExpr, cur *astutil.Cursor) { + if ident, ok := se.X.(*ast.Ident); ok { + if utils.CheckObjStarExpr(ident.Obj, "hzapp", "RequestContext") { + switch se.Sel.Name { + case "Bind": + cur.Replace(types.ExportCtxOp(ident.Name, "Bind")) + case "ShouldBind", "ShouldBindJSON", "ShouldBindQuery", "ShouldBindHeader": + cur.Replace(types.ExportCtxOp(ident.Name, "BindAndValidate")) } - // Replace the right-hand side of the assignment statement - cur.Replace(callExpr) } } } diff --git a/cmd/hertz_migrate/internal/logic/gin/replaceCallReqOrResp.go b/cmd/hertz_migrate/internal/logic/gin/replaceCallReqOrResp.go new file mode 100644 index 0000000..f635c48 --- /dev/null +++ b/cmd/hertz_migrate/internal/logic/gin/replaceCallReqOrResp.go @@ -0,0 +1,69 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gin + +import ( + . "go/ast" + + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/types" + + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" + "golang.org/x/tools/go/ast/astutil" +) + +func ReplaceCallReqOrResp(call *CallExpr, cur *astutil.Cursor) { + if se, ok := call.Fun.(*SelectorExpr); ok { + if _se, ok := se.X.(*SelectorExpr); ok { + if ident, ok := _se.X.(*Ident); ok { + if utils.CheckObjStarExpr(ident.Obj, "hzapp", "RequestContext") { + switch _se.Sel.Name { + case "Request": + switch se.Sel.Name { + case "FormValue": + cur.Replace(&CallExpr{ + Fun: &Ident{Name: "string"}, + Args: []Expr{ + &CallExpr{ + Fun: types.ExportCtxOp(ident.Name, "FormValue"), + Args: call.Args, + }, + }, + }) + case "FormFile": + if as, ok := cur.Parent().(*AssignStmt); ok { + as.Lhs = as.Lhs[1:] + } + case "UserAgent": + cur.Replace(types.ExportUserAgent(ident.Name)) + } + case "Writer": + switch se.Sel.Name { + case "Header": + cur.Replace(types.ExportRespHeader(ident.Name)) + } + case "FormFile": + if as, ok := cur.Parent().(*AssignStmt); ok { + as.Lhs = as.Lhs[1:] + } + case "GetRawData": + if as, ok := cur.Parent().(*AssignStmt); ok { + as.Lhs = as.Lhs[:1] + } + } + } + } + } + } +} diff --git a/cmd/hertz_migrate/internal/logic/gin/replaceCtxParam.go b/cmd/hertz_migrate/internal/logic/gin/replaceCtxParam.go new file mode 100644 index 0000000..eee6b39 --- /dev/null +++ b/cmd/hertz_migrate/internal/logic/gin/replaceCtxParam.go @@ -0,0 +1,186 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gin + +import ( + . "go/ast" + + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" + + "golang.org/x/tools/go/ast/astutil" +) + +func ReplaceCtxParamList(cur *astutil.Cursor) { + ifStmtReplaceLogic := func(st *IfStmt) { + if assignStmt, ok := st.Init.(*AssignStmt); ok { + if len(assignStmt.Rhs) == 1 { + if callExpr, ok := assignStmt.Rhs[0].(*CallExpr); ok { + replaceParamsWithFuncName(callExpr) + return + } + } + } + + switch node := st.Cond.(type) { + case *CallExpr: + replaceParamsWithFuncName(node) + case *BinaryExpr: + inspectBinary(node) + } + } + + assignStmtReplaceLogic := func(as *AssignStmt) { + for _, rhs := range as.Rhs { + switch node := rhs.(type) { + case *CallExpr: + replaceParamsWithFuncName(node) + continue + case *TypeAssertExpr: + if callExpr, ok := node.X.(*CallExpr); ok { + replaceParamsWithFuncName(callExpr) + continue + } + } + } + } + + // Remove the function name from the set if it is found in the current file + internal.CtxSet.Each(func(s string) bool { + if internal.WebCtxSet.Contains(s) { + internal.WebCtxSet.Remove(s) + } + return true + }) + + if blockStmt, ok := cur.Node().(*BlockStmt); ok { + for _, stmt := range blockStmt.List { + switch _stmt := stmt.(type) { + case *ExprStmt: + switch t := _stmt.X.(type) { + case *CallExpr: + replaceParamsWithFuncName(t) + continue + } + case *IfStmt: + ifStmtReplaceLogic(_stmt) + continue + case *SwitchStmt: + for _, s := range _stmt.Body.List { + if caseClause, ok := s.(*CaseClause); ok { + for _, _case := range caseClause.Body { + switch node := _case.(type) { + case *ExprStmt: + if callExpr, ok := node.X.(*CallExpr); ok { + replaceParamsWithFuncName(callExpr) + continue + } + case *IfStmt: + ifStmtReplaceLogic(node) + continue + case *AssignStmt: + assignStmtReplaceLogic(node) + continue + case *ReturnStmt: + for _, field := range node.Results { + if ce, ok := field.(*CallExpr); ok { + replaceParamsWithFuncName(ce) + continue + } + } + } + } + } + } + case *AssignStmt: + assignStmtReplaceLogic(_stmt) + continue + case *ReturnStmt: + for _, field := range _stmt.Results { + if ce, ok := field.(*CallExpr); ok { + replaceParamsWithFuncName(ce) + continue + } + } + } + } + } + + if call, ok := cur.Node().(*CallExpr); ok { + for _, elt := range call.Args { + if elt, ok := elt.(*CallExpr); ok { + replaceParamsWithFuncName(elt) + } + } + } +} + +func replaceParamsWithFuncName(callExpr *CallExpr) { + switch node := callExpr.Fun.(type) { + case *Ident: + replaceCallExprParams(callExpr, node.Name) + case *SelectorExpr: + replaceCallExprParams(callExpr, node.Sel.Name) + } +} + +func replaceCallExprParams(callExpr *CallExpr, funcName string) { + if internal.WebCtxSet.Contains(funcName) { + for index, arg := range callExpr.Args { + if ident, ok := arg.(*Ident); ok { + if utils.CheckObjStarExpr(ident.Obj, "hzapp", "RequestContext") { + var fieldList []Expr + fieldList = append(fieldList, callExpr.Args[:index]...) + fieldList = append(fieldList, NewIdent("_ctx")) + fieldList = append(fieldList, callExpr.Args[index:]...) + callExpr.Args = fieldList + return + } + } + } + } + + if internal.CtxSet.Contains(funcName) { + for index, arg := range callExpr.Args { + if ident, ok := arg.(*Ident); ok { + if utils.CheckObjSelExpr(ident.Obj, "context", "Context") || + utils.CheckObjStarExpr(ident.Obj, "hzapp", "RequestContext") { + callExpr.Args[index] = NewIdent("_ctx") + break + } + } + } + } +} + +func inspectBinary(binary *BinaryExpr) { + switch node := binary.X.(type) { + case *CallExpr: + replaceParamsWithFuncName(node) + case *BinaryExpr: + inspectBinary(node) + } + + switch node := binary.Y.(type) { + case *CallExpr: + replaceParamsWithFuncName(node) + case *BinaryExpr: + inspectBinary(node) + case *UnaryExpr: + if callExpr, ok := node.X.(*CallExpr); ok { + replaceParamsWithFuncName(callExpr) + } + } +} diff --git a/cmd/hertz_migrate/internal/logic/netHttp/packListenAndServe.go b/cmd/hertz_migrate/internal/logic/gin/replaceErrorType.go similarity index 61% rename from cmd/hertz_migrate/internal/logic/netHttp/packListenAndServe.go rename to cmd/hertz_migrate/internal/logic/gin/replaceErrorType.go index df54515..cc6254b 100644 --- a/cmd/hertz_migrate/internal/logic/netHttp/packListenAndServe.go +++ b/cmd/hertz_migrate/internal/logic/gin/replaceErrorType.go @@ -12,25 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package netHttp +package gin import ( - . "go/ast" - - "golang.org/x/tools/go/ast/astutil" + "go/ast" ) -func PackListenAndServe(cur *astutil.Cursor, globalMap map[string]any) { - selExpr, ok := cur.Node().(*SelectorExpr) - if ok { - if selExpr.Sel == nil { - return - } - if selExpr.Sel.Name == "ListenAndServe" { - v, ok := globalMap["serverName"] - if ok { - selExpr.X.(*Ident).Name = v.(string) - selExpr.Sel.Name = "Spin" +func ReplaceErrorType(se *ast.SelectorExpr) { + if ident, ok := se.X.(*ast.Ident); ok { + switch se.Sel.Name { + case "ErrorTypeBind", "ErrorTypeRender", "ErrorTypePrivate", "ErrorTypePublic", "ErrorTypeAny", "ErrorTypeNu": + if ident.Name == "gin" { + ident.Name = "hzerrors" } } } diff --git a/cmd/hertz_migrate/internal/logic/gin/replaceGinCtx.go b/cmd/hertz_migrate/internal/logic/gin/replaceGinCtx.go new file mode 100644 index 0000000..ba8dbf3 --- /dev/null +++ b/cmd/hertz_migrate/internal/logic/gin/replaceGinCtx.go @@ -0,0 +1,64 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gin + +import ( + "go/ast" + + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/types" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" +) + +func ReplaceGinCtx(fields *ast.FieldList) { + ctxIndex := -1 + if fields.List == nil { + return + } + + for index, field := range fields.List { + if star, ok := field.Type.(*ast.StarExpr); ok { + if sel, ok := star.X.(*ast.SelectorExpr); ok { + if utils.CheckSelPkgAndStruct(sel, "gin", "Context") { + ctxIndex = index + field.Type = types.StarCtx + break + } + } + } + } + + if ctxIndex >= 0 { + slice := make([]*ast.Field, len(fields.List)+1) + slice = append(slice, fields.List[:ctxIndex]...) + slice = append(slice, &ast.Field{ + Names: []*ast.Ident{ + ast.NewIdent("_ctx"), + }, + Type: &ast.SelectorExpr{ + X: ast.NewIdent("context"), + Sel: ast.NewIdent("Context"), + }, + }) + + slice = append(slice, fields.List[ctxIndex:]...) + var fieldList []*ast.Field + for _, field := range slice { + if field != nil { + fieldList = append(fieldList, field) + } + } + fields.List = fieldList + } +} diff --git a/cmd/hertz_migrate/internal/logic/gin/replaceGinCtxOp.go b/cmd/hertz_migrate/internal/logic/gin/replaceGinCtxOp.go new file mode 100644 index 0000000..058d5a6 --- /dev/null +++ b/cmd/hertz_migrate/internal/logic/gin/replaceGinCtxOp.go @@ -0,0 +1,62 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gin + +import ( + . "go/ast" + "go/token" + + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/types" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" + "golang.org/x/tools/go/ast/astutil" +) + +func ReplaceGinCtxOp(call *CallExpr, cur *astutil.Cursor) { + if se, ok := call.Fun.(*SelectorExpr); ok { + if ident, ok := se.X.(*Ident); ok { + if utils.CheckObjStarExpr(ident.Obj, "hzapp", "RequestContext") { + switch se.Sel.Name { + case "Next": + cur.Replace(types.ExportCtxNext(ident.Name)) + case "GetHeader": + cur.Replace(types.ExportCtxGetHeader(ident.Name, call.Args)) + case "Cookie": + as := cur.Parent().(*AssignStmt) + // Remove the second assignment + as.Lhs = as.Lhs[:1] + cur.Replace(types.ExportCtxCookie(ident.Name, call.Args)) + case "SetCookie": + var callArgs []Expr + for index, elt := range call.Args { + if index == 5 { + callArgs = append(callArgs, &BasicLit{Value: "0", Kind: token.INT}) + callArgs = append(callArgs, elt) + continue + } + callArgs = append(callArgs, elt) + } + call.Args = callArgs + case "Redirect": + redirectURIString := call.Args[1] + cur.Replace(types.ExportCallRedirect(ident.Name, call.Args[0], redirectURIString)) + case "GetRawData": + if as, ok := cur.Parent().(*AssignStmt); ok { + as.Lhs = as.Lhs[:1] + } + } + } + } + } +} diff --git a/cmd/hertz_migrate/internal/logic/gin/replaceGinNew.go b/cmd/hertz_migrate/internal/logic/gin/replaceGinNew.go new file mode 100644 index 0000000..0a1c511 --- /dev/null +++ b/cmd/hertz_migrate/internal/logic/gin/replaceGinNew.go @@ -0,0 +1,53 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gin + +import ( + . "go/ast" + + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" + + "golang.org/x/tools/go/ast/astutil" +) + +func ReplaceGinNew(call *CallExpr, c *astutil.Cursor) { + if sel, ok := call.Fun.(*SelectorExpr); ok { + if utils.CheckSelPkgAndStruct(sel, "gin", "New") || + utils.CheckSelPkgAndStruct(sel, "gin", "Default") { + args := internal.Options + if as, ok := c.Parent().(*AssignStmt); ok { + if ident, ok := as.Lhs[0].(*Ident); ok { + internal.ServerName = ident.Name + } + } + + var initTypeIdent *Ident + if utils.CheckSelPkgAndStruct(sel, "gin", "New") { + initTypeIdent = NewIdent("New") + } else { + initTypeIdent = NewIdent("Default") + } + + c.Replace(&CallExpr{ + Fun: &SelectorExpr{ + X: NewIdent("hzserver"), + Sel: initTypeIdent, + }, + Args: args, + }) + } + } +} diff --git a/cmd/hertz_migrate/internal/logic/gin/replaceGinRun.go b/cmd/hertz_migrate/internal/logic/gin/replaceGinRun.go new file mode 100644 index 0000000..ac0a0d0 --- /dev/null +++ b/cmd/hertz_migrate/internal/logic/gin/replaceGinRun.go @@ -0,0 +1,42 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gin + +import ( + "go/ast" + + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" +) + +func ReplaceGinRun(node *ast.CallExpr) { + if sel, ok := node.Fun.(*ast.SelectorExpr); ok { + if ident, ok := sel.X.(*ast.Ident); ok { + if utils.CheckObjSelExpr(ident.Obj, "hzserver", "Default") || + utils.CheckObjStarExpr(ident.Obj, "hzserver", "Hertz") { + if sel.Sel.Name == "Run" { + sel.Sel.Name = "Spin" + node.Args = []ast.Expr{} + } + } + if utils.CheckObjSelExpr(ident.Obj, "http", "Server") { + if sel.Sel.Name == "ListenAndServe" { + ident.Name = internal.ServerName + sel.Sel.Name = "Run" + } + } + } + } +} diff --git a/cmd/hertz_migrate/internal/logic/gin/replaceRequestOp.go b/cmd/hertz_migrate/internal/logic/gin/replaceRequestOp.go new file mode 100644 index 0000000..b488ac8 --- /dev/null +++ b/cmd/hertz_migrate/internal/logic/gin/replaceRequestOp.go @@ -0,0 +1,70 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gin + +import ( + . "go/ast" + + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/types" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" + "golang.org/x/tools/go/ast/astutil" +) + +func ReplaceRequestOp(se *SelectorExpr, cur *astutil.Cursor) { + if _se, ok := se.X.(*SelectorExpr); ok { + if ident, ok := _se.X.(*Ident); ok { + if utils.CheckObjStarExpr(ident.Obj, "hzapp", "RequestContext") { + if _se.Sel.Name == "Request" { + switch se.Sel.Name { + case "RequestURI": + cur.Replace(types.ExportRequestURI(ident.Name)) + case "Method": + cur.Replace(types.ExportReqMethod(ident.Name)) + case "Host": + cur.Replace(types.ExportReqHost(ident.Name)) + } + } + } + } + + if __se, ok := _se.X.(*SelectorExpr); ok { + if ident, ok := __se.X.(*Ident); ok { + if utils.CheckObjStarExpr(ident.Obj, "hzapp", "RequestContext") { + if __se.Sel.Name == "Request" { + switch _se.Sel.Name { + case "URL": + switch se.Sel.Name { + case "Path": + cur.Replace(types.ExportURIPath(ident.Name)) + case "String": + cur.Replace(types.ExportURIString(ident.Name)) + case "RawQuery": + cur.Replace(types.ExportURIQueryString(ident.Name)) + } + case "Form": + if se.Sel.Name == "Get" { + cur.Replace(types.ExportCtxOp(ident.Name, "FormValue")) + } + case "Header": + if se.Sel.Name == "Values" { + cur.Replace(types.ExportReqHeaderGetAll(ident.Name)) + } + } + } + } + } + } + } +} diff --git a/cmd/hertz_migrate/internal/logic/gin/replaceRespOp.go b/cmd/hertz_migrate/internal/logic/gin/replaceRespOp.go new file mode 100644 index 0000000..ef148c6 --- /dev/null +++ b/cmd/hertz_migrate/internal/logic/gin/replaceRespOp.go @@ -0,0 +1,77 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gin + +import ( + . "go/ast" + + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/types" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" + "golang.org/x/tools/go/ast/astutil" +) + +func ReplaceRespOp(se *SelectorExpr, cur *astutil.Cursor) { + if ident, ok := se.X.(*Ident); ok { + if utils.CheckObjStarExpr(ident.Obj, "hzapp", "RequestContext") { + switch se.Sel.Name { + case "Writer": + cur.Replace(&CallExpr{ + Fun: &SelectorExpr{ + X: &SelectorExpr{ + X: NewIdent(ident.Name), + Sel: NewIdent("Response"), + }, + Sel: NewIdent("BodyWriter"), + }, + }) + } + } + } + + if _se, ok := se.X.(*SelectorExpr); ok { + if ident, ok := _se.X.(*Ident); ok { + if utils.CheckObjStarExpr(ident.Obj, "hzapp", "RequestContext") { + if _se.Sel.Name == "Writer" { + switch se.Sel.Name { + case "Write": + cur.Replace(types.ExportCtxOp(ident.Name, "Write")) + return + case "WriteString": + cur.Replace(types.ExportCtxOp(ident.Name, "SetBodyString")) + case "WriteHeader": + cur.Replace(types.ExportCtxOp(ident.Name, "SetStatusCode")) + return + case "Status": + cur.Replace(types.ExportStatusCode(ident.Name)) + } + } + } + } + } + + if call, ok := se.X.(*CallExpr); ok { + if _se, ok := call.Fun.(*SelectorExpr); ok { + if __se, ok := _se.X.(*SelectorExpr); ok { + if ident, ok := __se.X.(*Ident); ok { + if utils.CheckObjStarExpr(ident.Obj, "hzapp", "RequestContext") { + if __se.Sel.Name == "Writer" && se.Sel.Name == "Values" { + se.Sel.Name = "GetAll" + } + } + } + } + } + } +} diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqFormFile.go b/cmd/hertz_migrate/internal/logic/gin/replaceRouteOp.go similarity index 53% rename from cmd/hertz_migrate/internal/logic/netHttp/replaceReqFormFile.go rename to cmd/hertz_migrate/internal/logic/gin/replaceRouteOp.go index 4c73f2a..59a0757 100644 --- a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqFormFile.go +++ b/cmd/hertz_migrate/internal/logic/gin/replaceRouteOp.go @@ -12,41 +12,29 @@ // See the License for the specific language governing permissions and // limitations under the License. -package netHttp +package gin import ( . "go/ast" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/types" "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" - - "golang.org/x/tools/go/ast/astutil" ) -func ReplaceReqFormFile(cur *astutil.Cursor) { - stmt, ok := cur.Node().(*AssignStmt) - if !ok || len(stmt.Lhs) != 3 || len(stmt.Rhs) != 1 { - return - } - - ce, ok := stmt.Rhs[0].(*CallExpr) - if !ok || len(ce.Args) != 1 { - return - } - - selExpr, ok := ce.Fun.(*SelectorExpr) - if !ok || selExpr.Sel.Name != "FormFile" { - return - } - - if utils.CheckPtrStructName(selExpr, "Request") { - se := &SelectorExpr{ - X: &SelectorExpr{ - X: NewIdent("c"), - Sel: NewIdent("Request"), - }, - Sel: NewIdent("FormFile"), +func ReplaceStatisFS(call *CallExpr) { + if se, ok := call.Fun.(*SelectorExpr); ok { + switch se.Sel.Name { + case "StaticFS": + httpSystemExpr := call.Args[1] + if _call, ok := httpSystemExpr.(*CallExpr); ok { + if _se, ok := _call.Fun.(*SelectorExpr); ok { + if utils.CheckSelPkgAndStruct(_se, "gin", "Dir") { + root := _call.Args[0] + listDir := _call.Args[1] + call.Args[1] = types.ExportedAppFSPtr(root, listDir) + } + } + } } - ce.Fun = se - stmt.Lhs = stmt.Lhs[1:] } } diff --git a/cmd/hertz_migrate/internal/logic/netHttp/collectHandlerFuncName.go b/cmd/hertz_migrate/internal/logic/netHttp/collectHandlerFuncName.go index 7c16bd4..c2319b1 100644 --- a/cmd/hertz_migrate/internal/logic/netHttp/collectHandlerFuncName.go +++ b/cmd/hertz_migrate/internal/logic/netHttp/collectHandlerFuncName.go @@ -22,7 +22,7 @@ import ( "golang.org/x/tools/go/ast/astutil" ) -func CollectHandlerFuncName(cur *astutil.Cursor, funcSet mapset.Set[string]) { +func FindHandlerFuncName(cur *astutil.Cursor, funcSet mapset.Set[string]) { collectTmpFuncName(cur, funcSet) collectCommonFuncName(cur, funcSet) collectExprStmtName(cur, funcSet) diff --git a/cmd/hertz_migrate/internal/logic/netHttp/packHandlerFunc.go b/cmd/hertz_migrate/internal/logic/netHttp/packHandlerFunc.go index a3478a2..b6fe0cb 100644 --- a/cmd/hertz_migrate/internal/logic/netHttp/packHandlerFunc.go +++ b/cmd/hertz_migrate/internal/logic/netHttp/packHandlerFunc.go @@ -25,8 +25,6 @@ func PackHandleFunc(cur *astutil.Cursor) { if selExpr.Sel == nil { return } - if selExpr.Sel.Name == "HandleFunc" { - selExpr.Sel.Name = "Any" - } + } } diff --git a/cmd/hertz_migrate/internal/logic/netHttp/packSetStatusCode.go b/cmd/hertz_migrate/internal/logic/netHttp/packSetStatusCode.go deleted file mode 100644 index 16968a3..0000000 --- a/cmd/hertz_migrate/internal/logic/netHttp/packSetStatusCode.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2024 CloudWeGo Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package netHttp - -import ( - . "go/ast" - - "golang.org/x/tools/go/ast/astutil" -) - -func PackSetStatusCode(cur *astutil.Cursor) { - callExpr, ok := cur.Node().(*CallExpr) - if !ok { - return - } - selExpr, ok := callExpr.Fun.(*SelectorExpr) - if !ok { - return - } - if selExpr.Sel == nil { - return - } - if selExpr.Sel.Name == "WriteHeader" { - if ident, ok := selExpr.X.(*Ident); ok { - if field, ok := ident.Obj.Decl.(*Field); ok { - expr, ok := field.Type.(*SelectorExpr) - if ok { - if expr.Sel.Name == "ResponseWriter" { - se := &SelectorExpr{ - X: NewIdent("c"), - Sel: NewIdent("SetStatusCode"), - } - callExpr.Fun = se - } - } - } - } - } -} diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceHttpError.go b/cmd/hertz_migrate/internal/logic/netHttp/replaceHttpError.go deleted file mode 100644 index e79ef2b..0000000 --- a/cmd/hertz_migrate/internal/logic/netHttp/replaceHttpError.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2024 CloudWeGo Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package netHttp - -import ( - . "go/ast" - "go/token" - - "golang.org/x/tools/go/ast/astutil" -) - -func ReplaceHttpError(cur *astutil.Cursor) { - callExpr, ok := cur.Node().(*CallExpr) - if !ok { - return - } - - selExpr, ok := callExpr.Fun.(*SelectorExpr) - if !ok { - return - } - - ident, ok := selExpr.X.(*Ident) - if !ok || ident.Name != "http" || selExpr.Sel.Name != "Error" { - return - } - - callExpr.Args = callExpr.Args[1:] - lit, ok := callExpr.Args[0].(*BasicLit) - if ok { - if lit.Kind == token.STRING { - if lit.Value == "\"\"" { - callExpr.Args = callExpr.Args[1:] - callExpr.Fun = &SelectorExpr{ - X: NewIdent("c"), - Sel: NewIdent("AbortWithStatus"), - } - return - } - } - } - - callExpr.Fun = &SelectorExpr{ - X: NewIdent("c"), - Sel: NewIdent("AbortWithMsg"), - } -} diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceHttpOp.go b/cmd/hertz_migrate/internal/logic/netHttp/replaceHttpOp.go new file mode 100644 index 0000000..062f579 --- /dev/null +++ b/cmd/hertz_migrate/internal/logic/netHttp/replaceHttpOp.go @@ -0,0 +1,42 @@ +package netHttp + +import ( + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/types" + . "go/ast" + "go/token" + "golang.org/x/tools/go/ast/astutil" +) + +func ReplaceHttpOp(call *CallExpr, cur *astutil.Cursor) { + if sel, ok := call.Fun.(*SelectorExpr); ok { + if ident, ok := sel.X.(*Ident); ok { + if ident.Name == "http" { + switch sel.Sel.Name { + case "NotFound": + cur.Replace(types.CallNotFound) + case "Redirect": + // http.Redirect(w http.ResponseWriter, r *http.Request, url string, code int) + redirectURIString := call.Args[2] + cur.Replace(types.ExportCallRedirect("c", call.Args[3], redirectURIString)) + case "Error": + // remove w http.ResponseWriter + callArgs := call.Args[1:] + lit, ok := callArgs[0].(*BasicLit) + if ok && lit.Kind == token.STRING { + if lit.Value == "\"\"" { + cur.Replace(&CallExpr{ + Fun: types.ExportCtxOp("c", "AbortWithStatus"), + Args: callArgs[1:], + }) + } else { + cur.Replace(&CallExpr{ + Fun: types.ExportCtxOp("c", "AbortWithMsg"), + Args: callArgs, + }) + } + } + } + } + } + } +} diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceHttpRedirect.go b/cmd/hertz_migrate/internal/logic/netHttp/replaceHttpRedirect.go deleted file mode 100644 index bfd70be..0000000 --- a/cmd/hertz_migrate/internal/logic/netHttp/replaceHttpRedirect.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2024 CloudWeGo Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package netHttp - -import ( - . "go/ast" - - "golang.org/x/tools/go/ast/astutil" -) - -func ReplaceHttpRedirect(cur *astutil.Cursor) { - blockStmt, ok := cur.Node().(*BlockStmt) - if !ok { - return - } - for _, stmt := range blockStmt.List { - estmt, ok := stmt.(*ExprStmt) - if !ok { - continue - } - callExpr, ok := estmt.X.(*CallExpr) - if !ok { - continue - } - selExpr, ok := callExpr.Fun.(*SelectorExpr) - if ok && selExpr.Sel.Name == "Redirect" && selExpr.X.(*Ident).Name == "http" { - uriExpr := callExpr.Args[2] - statusExpr := callExpr.Args[3] - estmt.X = &CallExpr{ - Fun: &SelectorExpr{ - X: NewIdent("c"), - Sel: NewIdent("Redirect"), - }, - Args: []Expr{ - statusExpr, - &CallExpr{ - Fun: &ArrayType{ - Elt: NewIdent("byte"), - }, - Args: []Expr{uriExpr}, - }, - }, - } - } - } -} diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqCookie.go b/cmd/hertz_migrate/internal/logic/netHttp/replaceReqCookie.go deleted file mode 100644 index acfe53b..0000000 --- a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqCookie.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2024 CloudWeGo Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package netHttp - -import ( - . "go/ast" - - "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" - "golang.org/x/tools/go/ast/astutil" -) - -func ReplaceReqCookie(cur *astutil.Cursor) { - var cookieName string - blockStmt, ok := cur.Node().(*BlockStmt) - if !ok { - return - } - for _, stmt := range blockStmt.List { - if cookieName != "" { - switch ss := stmt.(type) { - case *IfStmt: - binaryExpr, ok := ss.Cond.(*BinaryExpr) - if !ok { - continue - } - selExpr, ok := binaryExpr.X.(*SelectorExpr) - if ok { - if selExpr.X.(*Ident).Name == cookieName && selExpr.Sel.Name == "Value" { - binaryExpr.X = &CallExpr{ - Fun: NewIdent("string"), - Args: []Expr{NewIdent(cookieName)}, - } - } - continue - } - case *AssignStmt: - if len(ss.Lhs) != 1 || len(ss.Rhs) != 1 { - continue - } - selExpr, ok := ss.Rhs[0].(*SelectorExpr) - if ok { - if selExpr.X.(*Ident).Name == cookieName && selExpr.Sel.Name == "Value" { - ss.Rhs[0] = &CallExpr{ - Fun: NewIdent("string"), - Args: []Expr{NewIdent(cookieName)}, - } - } - continue - } - } - } else { - assignStmt, ok := stmt.(*AssignStmt) - if ok { - if len(assignStmt.Lhs) != 2 || len(assignStmt.Rhs) != 1 { - continue - } - callExpr, ok := assignStmt.Rhs[0].(*CallExpr) - if !ok { - continue - } - selExpr, ok := callExpr.Fun.(*SelectorExpr) - if !ok { - continue - } - if utils.CheckPtrStructName(selExpr, "Request") && selExpr.Sel.Name == "Cookie" { - assignStmt.Lhs = assignStmt.Lhs[:len(assignStmt.Lhs)-1] - cookieName = assignStmt.Lhs[0].(*Ident).Name - selExpr.X = NewIdent("c") - } - } - } - } -} diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqHost.go b/cmd/hertz_migrate/internal/logic/netHttp/replaceReqHost.go deleted file mode 100644 index 8c9972a..0000000 --- a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqHost.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2024 CloudWeGo Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package netHttp - -import ( - . "go/ast" - - "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" - - "golang.org/x/tools/go/ast/astutil" -) - -// ReplaceReqHost replaces r.Host with string(c.Host) -func ReplaceReqHost(cur *astutil.Cursor) { - replaceAssignStmtReqHost(cur) - replaceIfStmtReqHost(cur) -} - -// replaceAssignStmtReqHost replaces r.Host with string(c.Host) in AssignStmt -func replaceAssignStmtReqHost(cur *astutil.Cursor) { - assignStmt, ok := cur.Node().(*AssignStmt) - if !ok || len(assignStmt.Rhs) != 1 { - return - } - - selExpr, ok := assignStmt.Rhs[0].(*SelectorExpr) - if !ok || selExpr.Sel.Name != "Host" { - return - } - - if utils.CheckPtrStructName(selExpr, "Request") { - // Create a new expression - newExpr := &CallExpr{ - Fun: &Ident{Name: "string"}, - Args: []Expr{ - &CallExpr{ - Fun: &SelectorExpr{ - X: &SelectorExpr{X: &Ident{Name: "c"}, Sel: &Ident{Name: "Request"}}, - Sel: &Ident{Name: "Host"}, - }, - }, - }, - } - - // Replace the right-hand side of the assignment statement - assignStmt.Rhs = []Expr{newExpr} - } -} - -// replaceIfStmtReqHost replaces r.Host with string(c.Host) in IfStmt -func replaceIfStmtReqHost(cur *astutil.Cursor) { - ifStmt, ok := cur.Node().(*IfStmt) - if !ok { - return - } - - binaryExpr, ok := ifStmt.Cond.(*BinaryExpr) - if !ok { - return - } - - se, ok := binaryExpr.X.(*SelectorExpr) - if !ok { - return - } - - if utils.CheckPtrStructName(se, "Request") && se.Sel.Name == "Host" { - be := &BinaryExpr{ - X: &CallExpr{ - Fun: &Ident{Name: "string"}, - Args: []Expr{ - &CallExpr{ - Fun: &SelectorExpr{ - X: &SelectorExpr{X: &Ident{Name: "c"}, Sel: &Ident{Name: "Request"}}, - Sel: &Ident{Name: "Host"}, - }, - }, - }, - }, - Op: binaryExpr.Op, - Y: binaryExpr.Y, - } - - ifStmt.Cond = be - } -} diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqMethod.go b/cmd/hertz_migrate/internal/logic/netHttp/replaceReqMethod.go deleted file mode 100644 index 5740336..0000000 --- a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqMethod.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2024 CloudWeGo Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package netHttp - -import ( - . "go/ast" - "sync" - - "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" - - "golang.org/x/tools/go/ast/astutil" -) - -var stringMethodExpr *CallExpr - -func ReplaceReqMethod(cur *astutil.Cursor) { - var once sync.Once - once.Do(func() { - stringMethodExpr = &CallExpr{ - Fun: &Ident{Name: "string"}, - Args: []Expr{ - &CallExpr{ - Fun: &SelectorExpr{ - X: &Ident{Name: "c"}, - Sel: &Ident{Name: "Method"}, - }, - }, - }, - } - }) - replaceAssignStmtReqMethod(cur) - replaceIfStmtReqMethod(cur) - replaceSwitchStmtReqMethod(cur) -} - -func replaceSwitchStmtReqMethod(cur *astutil.Cursor) { - switchStmt, ok := cur.Node().(*SwitchStmt) - if !ok { - return - } - selExpr, ok := switchStmt.Tag.(*SelectorExpr) - if !ok { - return - } - if utils.CheckPtrStructName(selExpr, "Request") && selExpr.Sel.Name == "Method" { - switchStmt.Tag = stringMethodExpr - } -} - -func replaceAssignStmtReqMethod(cur *astutil.Cursor) { - assignStmt, ok := cur.Node().(*AssignStmt) - if !ok { - return - } - if len(assignStmt.Rhs) == 1 { - selExpr, ok := assignStmt.Rhs[0].(*SelectorExpr) - if !ok { - return - } - if selExpr.Sel.Name == "Method" { - if utils.CheckPtrStructName(selExpr, "Request") { - assignStmt.Rhs[0] = stringMethodExpr - } - } - } -} - -func replaceIfStmtReqMethod(cur *astutil.Cursor) { - ifStmt, ok := cur.Node().(*IfStmt) - if !ok { - return - } - binaryExpr, ok := ifStmt.Cond.(*BinaryExpr) - if !ok { - return - } - se, ok := binaryExpr.X.(*SelectorExpr) - if !ok { - return - } - if utils.CheckPtrStructName(se, "Request") { - if se.Sel.Name == "Method" { - be := &BinaryExpr{ - X: stringMethodExpr, - Y: binaryExpr.Y, - Op: binaryExpr.Op, - } - ifStmt.Cond = be - } - } -} diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqOrRespOp.go b/cmd/hertz_migrate/internal/logic/netHttp/replaceReqOrRespOp.go new file mode 100644 index 0000000..ec81545 --- /dev/null +++ b/cmd/hertz_migrate/internal/logic/netHttp/replaceReqOrRespOp.go @@ -0,0 +1,30 @@ +package netHttp + +import ( + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/types" + . "go/ast" + + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" + "golang.org/x/tools/go/ast/astutil" +) + +func ReplaceReqOrRespOp(call *CallExpr, cur *astutil.Cursor) { + if sel, ok := call.Fun.(*SelectorExpr); ok { + if ident, ok := sel.X.(*Ident); ok { + if utils.CheckObjStarExpr(ident.Obj, "http", "Request") { + ident.Name = "c" + switch sel.Sel.Name { + case "FormFile": + if as, ok := cur.Parent().(*AssignStmt); ok { + as.Lhs = as.Lhs[1:] + } + case "Cookie": + if as, ok := cur.Parent().(*AssignStmt); ok { + as.Lhs = as.Lhs[:1] + } + cur.Replace(types.ExportCtxCookie("c", call.Args)) + } + } + } + } +} diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqURLPath.go b/cmd/hertz_migrate/internal/logic/netHttp/replaceReqURLPath.go deleted file mode 100644 index 06ea9a7..0000000 --- a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqURLPath.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2024 CloudWeGo Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package netHttp - -import ( - . "go/ast" - - "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" - "golang.org/x/tools/go/ast/astutil" -) - -func ReplaceReqURLPath(cur *astutil.Cursor) { - replaceBlockStmtReqURLPath(cur) - replaceExprStmtReqURLPath(cur) -} - -func replaceExprStmtReqURLPath(cur *astutil.Cursor) { - callExpr, ok := cur.Node().(*CallExpr) - if !ok { - return - } - for i, arg := range callExpr.Args { - arg, ok := arg.(*SelectorExpr) - if !ok || arg.Sel.Name != "Path" { - continue - } - selExpr, ok := arg.X.(*SelectorExpr) - if !ok { - continue - } - if utils.CheckPtrStructName(selExpr, "Request") { - callExpr.Args[i] = &CallExpr{ - Fun: &Ident{Name: "string"}, - Args: []Expr{ - &CallExpr{ - Fun: &SelectorExpr{ - X: &CallExpr{ - Fun: &SelectorExpr{ - X: &Ident{Name: "c"}, - Sel: &Ident{Name: "URI"}, - }, - }, - Sel: &Ident{Name: "Path"}, - }, - }, - }, - } - } - } -} - -func replaceBlockStmtReqURLPath(cur *astutil.Cursor) { - blockStmt, ok := cur.Node().(*BlockStmt) - if !ok { - return - } - for i, stmt := range blockStmt.List { - assignStmt, ok := stmt.(*AssignStmt) - if !ok { - continue - } - if len(assignStmt.Rhs) == 1 { - selExpr, ok := assignStmt.Rhs[0].(*SelectorExpr) - if !ok { - continue - } - if selExpr.Sel.Name == "Path" { - assignStmt.Rhs[0] = &CallExpr{ - Fun: &Ident{Name: "string"}, - Args: []Expr{ - &CallExpr{ - Fun: &SelectorExpr{ - X: &CallExpr{ - Fun: &SelectorExpr{ - X: &Ident{Name: "c"}, - Sel: &Ident{Name: "URI"}, - }, - }, - Sel: &Ident{Name: "Path"}, - }, - }, - }, - } - blockStmt.List[i] = assignStmt - } - } - } -} diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqURLQueryGet.go b/cmd/hertz_migrate/internal/logic/netHttp/replaceReqURLQueryGet.go index e9c8458..ab9602b 100644 --- a/cmd/hertz_migrate/internal/logic/netHttp/replaceReqURLQueryGet.go +++ b/cmd/hertz_migrate/internal/logic/netHttp/replaceReqURLQueryGet.go @@ -16,16 +16,9 @@ package netHttp import ( . "go/ast" - - "golang.org/x/tools/go/ast/astutil" ) -func ReplaceReqURLQuery(cur *astutil.Cursor) { - callExpr, ok := cur.Node().(*CallExpr) - if !ok || len(callExpr.Args) != 1 { - return - } - +func ReplaceReqURLQuery(callExpr *CallExpr) { _selExpr, ok := callExpr.Fun.(*SelectorExpr) if !ok { return diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceRequestOp.go b/cmd/hertz_migrate/internal/logic/netHttp/replaceRequestOp.go new file mode 100644 index 0000000..2483c25 --- /dev/null +++ b/cmd/hertz_migrate/internal/logic/netHttp/replaceRequestOp.go @@ -0,0 +1,43 @@ +package netHttp + +import ( + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/types" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" + . "go/ast" + "golang.org/x/tools/go/ast/astutil" +) + +func ReplaceRequestOp(sel *SelectorExpr, cur *astutil.Cursor) { + if ident, ok := sel.X.(*Ident); ok { + if utils.CheckObjStarExpr(ident.Obj, "http", "Request") { + switch sel.Sel.Name { + case "RequestURI": + cur.Replace(types.ExportRequestURI("c")) + case "Method": + cur.Replace(types.ExportReqMethod("c")) + case "Host": + cur.Replace(types.ExportReqHost("c")) + case "ContentLength": + cur.Replace(types.CallContentLength) + case "RemoteAddr": + cur.Replace(types.CallRemoteAddr) + } + } + } + + if _sel, ok := sel.X.(*SelectorExpr); ok { + if ident, ok := _sel.X.(*Ident); ok { + if utils.CheckObjStarExpr(ident.Obj, "http", "Request") { + if _sel.Sel.Name == "URL" { + _sel.Sel.Name = "URI" + switch sel.Sel.Name { + case "Path": + cur.Replace(types.ExportURIPath("c")) + case "String": + cur.Replace(types.ExportURIString("c")) + } + } + } + } + } +} diff --git a/cmd/hertz_migrate/internal/logic/netHttp/replaceRequestURI.go b/cmd/hertz_migrate/internal/logic/netHttp/replaceRequestURI.go deleted file mode 100644 index 9cf56e2..0000000 --- a/cmd/hertz_migrate/internal/logic/netHttp/replaceRequestURI.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2024 CloudWeGo Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package netHttp - -import ( - . "go/ast" - - "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" - - "golang.org/x/tools/go/ast/astutil" -) - -func ReplaceRequestURI(cur *astutil.Cursor) { - replaceAssignStmtRequestURI(cur) - replaceIfStmtRequestURI(cur) -} - -func replaceAssignStmtRequestURI(cur *astutil.Cursor) { - assignStmt, ok := cur.Node().(*AssignStmt) - if !ok { - return - } - if len(assignStmt.Rhs) == 1 { - selExpr, ok := assignStmt.Rhs[0].(*SelectorExpr) - if !ok { - return - } - if selExpr.Sel.Name == "RequestURI" { - if utils.CheckPtrStructName(selExpr, "Request") { - newExpr := &CallExpr{ - Fun: &Ident{Name: "string"}, - Args: []Expr{ - &CallExpr{ - Fun: &SelectorExpr{ - X: &SelectorExpr{ - X: &Ident{Name: "c"}, - Sel: &Ident{Name: "Request"}, - }, - Sel: &Ident{Name: "RequestURI"}, - }, - }, - }, - } - assignStmt.Rhs[0] = newExpr - } - } - } -} - -func replaceIfStmtRequestURI(cur *astutil.Cursor) { - ifStmt, ok := cur.Node().(*IfStmt) - if !ok { - return - } - binaryExpr, ok := ifStmt.Cond.(*BinaryExpr) - if !ok { - return - } - se, ok := binaryExpr.X.(*SelectorExpr) - if !ok { - return - } - if utils.CheckPtrStructName(se, "Request") && se.Sel.Name == "RequestURI" { - be := &BinaryExpr{ - X: &CallExpr{ - Fun: &Ident{Name: "string"}, - Args: []Expr{ - &CallExpr{ - Fun: &SelectorExpr{ - X: &SelectorExpr{ - X: &Ident{Name: "c"}, - Sel: &Ident{Name: "Request"}, - }, - Sel: &Ident{Name: "RequestURI"}, - }, - }, - }, - }, - Op: binaryExpr.Op, - Y: binaryExpr.Y, - } - ifStmt.Cond = be - } -} diff --git a/cmd/hertz_migrate/internal/logic/public.go b/cmd/hertz_migrate/internal/logic/public.go new file mode 100644 index 0000000..fa34e26 --- /dev/null +++ b/cmd/hertz_migrate/internal/logic/public.go @@ -0,0 +1,58 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logic + +import ( + . "go/ast" + + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/types" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/utils" + "golang.org/x/tools/go/ast/astutil" +) + +func collectOptions(elt Expr) { + if kve, ok := elt.(*KeyValueExpr); ok { + if ident := kve.Key.(*Ident); ok { + internal.Options = append(internal.Options, types.ExportServerOption("WithDisablePrintRoute", []Expr{NewIdent("true")})) + switch ident.Name { + case "Addr": + internal.Options = append(internal.Options, types.ExportServerOption("WithHostPorts", []Expr{kve.Value})) + case "WriteTimeout": + internal.Options = append(internal.Options, types.ExportServerOption("WithWriteTimeout", []Expr{kve.Value})) + case "ReadTimeout": + internal.Options = append(internal.Options, types.ExportServerOption("WithReadTimeout", []Expr{kve.Value})) + case "IdleTimeout": + internal.Options = append(internal.Options, types.ExportServerOption("WithIdleTimeout", []Expr{kve.Value})) + case "TLSConfig": + internal.Options = append(internal.Options, types.ExportServerOption("WithTLS", []Expr{kve.Value})) + } + } + } +} + +func GetHttpServerProps(cur *astutil.Cursor) { + if cLit, ok := cur.Node().(*CompositeLit); ok { + if sel, ok := cLit.Type.(*SelectorExpr); ok { + if utils.CheckSelPkgAndStruct(sel, "http", "Server") { + if len(cLit.Elts) > 0 { + for _, elt := range cLit.Elts { + collectOptions(elt) + } + } + } + } + } +} diff --git a/cmd/hertz_migrate/internal/logs/log.go b/cmd/hertz_migrate/internal/logs/log.go new file mode 100644 index 0000000..71cbc8e --- /dev/null +++ b/cmd/hertz_migrate/internal/logs/log.go @@ -0,0 +1,72 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logs + +func init() { + defaultLogger = NewStdLogger(LevelInfo) +} + +func SetLogger(logger Logger) { + defaultLogger = logger +} + +const ( + LevelDebug = 1 + iota + LevelInfo + LevelWarn + LevelError +) + +type Logger interface { + Debugf(format string, v ...interface{}) + Infof(format string, v ...interface{}) + Errorf(format string, v ...interface{}) + Flush() + SetLevel(level int) error +} + +var defaultLogger Logger + +func Errorf(format string, v ...interface{}) { + defaultLogger.Errorf(format, v...) +} + +func Infof(format string, v ...interface{}) { + defaultLogger.Infof(format, v...) +} + +func Debugf(format string, v ...interface{}) { + defaultLogger.Debugf(format, v...) +} + +func Error(format string, v ...interface{}) { + defaultLogger.Errorf(format, v...) +} + +func Info(format string, v ...interface{}) { + defaultLogger.Infof(format, v...) +} + +func Debug(format string, v ...interface{}) { + defaultLogger.Debugf(format, v...) +} + +func Flush() { + defaultLogger.Flush() +} + +func SetLevel(level int) { + defaultLogger.SetLevel(level) +} diff --git a/cmd/hertz_migrate/internal/logs/std.go b/cmd/hertz_migrate/internal/logs/std.go new file mode 100644 index 0000000..f5d1dd8 --- /dev/null +++ b/cmd/hertz_migrate/internal/logs/std.go @@ -0,0 +1,133 @@ +/* + * Copyright 2022 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package logs + +import ( + "bytes" + "errors" + "fmt" + "log" + "os" +) + +type StdLogger struct { + level int + outLogger *log.Logger + warnLogger *log.Logger + errLogger *log.Logger + out *bytes.Buffer + warn *bytes.Buffer + err *bytes.Buffer + Defer bool + ErrOnly bool +} + +func NewStdLogger(level int) *StdLogger { + out := bytes.NewBuffer(nil) + warn := bytes.NewBuffer(nil) + err := bytes.NewBuffer(nil) + + return &StdLogger{ + level: level, + outLogger: log.New(out, "[INFO]", log.Llongfile), + warnLogger: log.New(warn, "[WARN]", log.Llongfile), + errLogger: log.New(err, "[ERROR]", log.Llongfile), + out: out, + warn: warn, + err: err, + Defer: true, + } +} + +func (stdLogger *StdLogger) Debugf(format string, v ...interface{}) { + if stdLogger.level > LevelDebug { + return + } + stdLogger.outLogger.Output(3, fmt.Sprintf(format, v...)) + if !stdLogger.Defer { + stdLogger.FlushOut() + } +} + +func (stdLogger *StdLogger) Infof(format string, v ...interface{}) { + if stdLogger.level > LevelInfo { + return + } + stdLogger.outLogger.Output(3, fmt.Sprintf(format, v...)) + if !stdLogger.Defer { + stdLogger.FlushOut() + } +} + +func (stdLogger *StdLogger) Errorf(format string, v ...interface{}) { + if stdLogger.level > LevelError { + return + } + stdLogger.errLogger.Output(3, fmt.Sprintf(format, v...)) + if !stdLogger.Defer { + stdLogger.FlushErr() + } +} + +func (stdLogger *StdLogger) Flush() { + stdLogger.FlushErr() + if !stdLogger.ErrOnly { + stdLogger.FlushOut() + } +} + +func (stdLogger *StdLogger) FlushOut() { + os.Stderr.Write(stdLogger.out.Bytes()) + stdLogger.out.Reset() +} + +func (stdLogger *StdLogger) Err() string { + return string(stdLogger.err.Bytes()) +} + +func (stdLogger *StdLogger) Warn() string { + return string(stdLogger.warn.Bytes()) +} + +func (stdLogger *StdLogger) FlushErr() { + os.Stderr.Write(stdLogger.err.Bytes()) + stdLogger.err.Reset() +} + +func (stdLogger *StdLogger) OutLines() []string { + lines := bytes.Split(stdLogger.out.Bytes(), []byte("[INFO]")) + var rets []string + for _, line := range lines { + rets = append(rets, string(line)) + } + return rets +} + +func (stdLogger *StdLogger) Out() []byte { + return stdLogger.out.Bytes() +} + +func (stdLogger *StdLogger) SetLevel(level int) error { + switch level { + case LevelDebug, LevelInfo, LevelWarn, LevelError: + break + default: + return errors.New("invalid log level") + } + stdLogger.level = level + return nil +} diff --git a/cmd/hertz_migrate/internal/types/hertz.go b/cmd/hertz_migrate/internal/types/hertz.go new file mode 100644 index 0000000..1472776 --- /dev/null +++ b/cmd/hertz_migrate/internal/types/hertz.go @@ -0,0 +1,525 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + . "go/ast" + "go/token" +) + +var ( + StarServerHertz = &StarExpr{ + X: &SelectorExpr{ + X: NewIdent("hzserver"), + Sel: NewIdent("Hertz"), + }, + } + + StarRouteGroup = &StarExpr{ + X: &SelectorExpr{ + X: NewIdent("hzroute"), + Sel: NewIdent("RouterGroup"), + }, + } + + SelIRoutes = &SelectorExpr{ + X: NewIdent("hzroute"), + Sel: NewIdent("IRoutes"), + } + + StarCtx = &StarExpr{ + X: &SelectorExpr{ + X: NewIdent("hzapp"), + Sel: NewIdent("RequestContext"), + }, + } + + SelAppHandlerFunc = &SelectorExpr{ + X: NewIdent("hzapp"), + Sel: NewIdent("HandlerFunc"), + } + + SelWrite = &SelectorExpr{ + X: NewIdent("c"), + Sel: NewIdent("Write"), + } + CallNotFound = &CallExpr{ + Fun: &SelectorExpr{ + X: NewIdent("c"), + Sel: NewIdent("NotFound"), + }, + } + + CallRequestURI = &CallExpr{ + Fun: &Ident{Name: "string"}, + Args: []Expr{ + &CallExpr{ + Fun: &SelectorExpr{ + X: &SelectorExpr{ + X: &Ident{Name: "c"}, + Sel: &Ident{Name: "Request"}, + }, + Sel: &Ident{Name: "RequestURI"}, + }, + }, + }, + } + + SelRespStatusCode = &SelectorExpr{ + X: &SelectorExpr{ + X: &Ident{Name: "c"}, + Sel: &Ident{Name: "Response"}, + }, + Sel: NewIdent("StatusCode"), + } + + CallURIPath = &CallExpr{ + Fun: NewIdent("string"), + Args: []Expr{ + &CallExpr{ + Fun: &SelectorExpr{ + X: &CallExpr{ + Fun: &SelectorExpr{ + X: NewIdent("c"), + Sel: NewIdent("URI"), + }, + }, + Sel: &Ident{Name: "Path"}, + }, + }, + }, + } + + SelURIString = &SelectorExpr{ + X: &CallExpr{ + Fun: &SelectorExpr{ + X: &Ident{Name: "c"}, + Sel: &Ident{Name: "URI"}, + }, + }, + Sel: &Ident{Name: "String"}, + } + + CallURIQueryString = &CallExpr{ + Fun: NewIdent("string"), + Args: []Expr{ + &CallExpr{ + Fun: &SelectorExpr{ + X: &CallExpr{ + Fun: &SelectorExpr{ + X: &Ident{Name: "c"}, + Sel: &Ident{Name: "URI"}, + }, + }, + Sel: &Ident{Name: "QueryString"}, + }, + }, + }, + } + + CallUserAgent = &CallExpr{ + Fun: NewIdent("string"), + Args: []Expr{ + &CallExpr{ + Fun: &SelectorExpr{ + X: &SelectorExpr{ + X: &SelectorExpr{ + X: NewIdent("c"), + Sel: NewIdent("Request"), + }, + Sel: NewIdent("Header"), + }, + Sel: NewIdent("UserAgent"), + }, + }, + }, + } + + CallReqMethod = &CallExpr{ + Fun: NewIdent("string"), + Args: []Expr{ + &CallExpr{ + Fun: &SelectorExpr{ + X: &SelectorExpr{ + X: &Ident{Name: "c"}, + Sel: &Ident{Name: "Request"}, + }, + Sel: &Ident{Name: "Method"}, + }, + }, + }, + } + + CallReqHost = &CallExpr{ + Fun: &Ident{Name: "string"}, + Args: []Expr{ + &CallExpr{ + Fun: &SelectorExpr{ + X: &SelectorExpr{ + X: &Ident{Name: "c"}, + Sel: &Ident{Name: "Request"}, + }, + Sel: &Ident{Name: "Host"}, + }, + }, + }, + } + + SelRespHeader = &SelectorExpr{ + X: &SelectorExpr{ + X: NewIdent("c"), + Sel: NewIdent("Response"), + }, + Sel: NewIdent("Header"), + } + + CallContentLength = &CallExpr{ + Fun: &SelectorExpr{ + X: &SelectorExpr{ + X: &SelectorExpr{ + X: NewIdent("c"), + Sel: NewIdent("Request"), + }, + Sel: NewIdent("Header"), + }, + Sel: NewIdent("ContentLength"), + }, + } + + CallRemoteAddr = &CallExpr{ + Fun: &SelectorExpr{ + X: &CallExpr{ + Fun: &SelectorExpr{ + X: NewIdent("c"), + Sel: NewIdent("RemoteAddr"), + }, + }, + Sel: NewIdent("String"), + }, + } + + SelSetStatusCode = &SelectorExpr{ + X: NewIdent("c"), + Sel: NewIdent("SetStatusCode"), + } +) + +func ExportCtxGetHeader(ctx string, args []Expr) *CallExpr { + return &CallExpr{ + Fun: NewIdent("string"), + Args: []Expr{ + &CallExpr{ + Fun: &SelectorExpr{ + X: NewIdent(ctx), + Sel: NewIdent("GetHeader"), + }, + Args: args, + }, + }, + } +} + +func ExportCallRedirect(ctx string, args ...Expr) *CallExpr { + return &CallExpr{ + Fun: &SelectorExpr{ + X: NewIdent(ctx), + Sel: NewIdent("Redirect"), + }, + Args: []Expr{ + args[0], + &CallExpr{ + Fun: &ArrayType{ + Elt: &Ident{Name: "byte"}, + }, + Args: []Expr{ + args[1], + }, + }, + }, + } +} + +func ExportCtxOp(ctx, name string) *SelectorExpr { + return &SelectorExpr{ + X: NewIdent(ctx), + Sel: NewIdent(name), + } +} + +func ExportURIPath(ctx string) *CallExpr { + return &CallExpr{ + Fun: NewIdent("string"), + Args: []Expr{ + &CallExpr{ + Fun: ExportURIOp(ctx, "Path"), + }, + }, + } +} + +func ExportStringIncludeXXX(expr Expr) *CallExpr { + switch ty := expr.(type) { + case *CallExpr: + return &CallExpr{ + Fun: NewIdent("string"), + Args: []Expr{ty}, + } + case *SelectorExpr: + return &CallExpr{ + Fun: NewIdent("string"), + Args: []Expr{&CallExpr{Fun: ty}}, + } + } + return nil +} + +func ExportURIOp(ctx, op string) *SelectorExpr { + return ExportCtxXXXOp(ctx, "URI", op) +} + +func ExportCtxXXXOp(ctx, xxx, op string) *SelectorExpr { + return &SelectorExpr{ + X: &CallExpr{ + Fun: &SelectorExpr{ + X: NewIdent(ctx), + Sel: NewIdent(xxx), + }, + }, + Sel: NewIdent(op), + } +} + +func ExportURIString(ctx string) *SelectorExpr { + return &SelectorExpr{ + X: &CallExpr{ + Fun: &SelectorExpr{ + X: &Ident{Name: ctx}, + Sel: &Ident{Name: "URI"}, + }, + }, + Sel: &Ident{Name: "String"}, + } +} + +func ExportReqMethod(ctx string) *CallExpr { + return &CallExpr{ + Fun: NewIdent("string"), + Args: []Expr{ + &CallExpr{ + Fun: &SelectorExpr{ + X: &SelectorExpr{ + X: &Ident{Name: ctx}, + Sel: &Ident{Name: "Request"}, + }, + Sel: &Ident{Name: "Method"}, + }, + }, + }, + } +} + +func ExportCtxNext(ctx string) *CallExpr { + return &CallExpr{ + Fun: &SelectorExpr{ + X: &Ident{Name: ctx}, + Sel: &Ident{Name: "Next"}, + }, + Args: []Expr{ + NewIdent("_ctx"), + }, + } +} + +func ExportURIQueryString(ctx string) *CallExpr { + return &CallExpr{ + Fun: NewIdent("string"), + Args: []Expr{ + &CallExpr{ + Fun: &SelectorExpr{ + X: &CallExpr{ + Fun: &SelectorExpr{ + X: &Ident{Name: ctx}, + Sel: &Ident{Name: "URI"}, + }, + }, + Sel: &Ident{Name: "QueryString"}, + }, + }, + }, + } +} + +func ExportRequestURI(ctx string) *CallExpr { + return &CallExpr{ + Fun: &Ident{Name: "string"}, + Args: []Expr{ + &CallExpr{ + Fun: &SelectorExpr{ + X: &SelectorExpr{ + X: &Ident{Name: ctx}, + Sel: &Ident{Name: "Request"}, + }, + Sel: &Ident{Name: "RequestURI"}, + }, + }, + }, + } +} + +func ExportReqHost(ctx string) *CallExpr { + return &CallExpr{ + Fun: &Ident{Name: "string"}, + Args: []Expr{ + &CallExpr{ + Fun: &SelectorExpr{ + X: &SelectorExpr{ + X: &Ident{Name: ctx}, + Sel: &Ident{Name: "Request"}, + }, + Sel: &Ident{Name: "Host"}, + }, + }, + }, + } +} + +func ExportReqHeaderGetAll(ctx string) *SelectorExpr { + return &SelectorExpr{ + X: &SelectorExpr{ + X: &SelectorExpr{ + X: NewIdent(ctx), + Sel: NewIdent("Request"), + }, + Sel: NewIdent("Header"), + }, + Sel: &Ident{Name: "GetAll"}, + } +} + +func ExportStatusCode(ctx string) *SelectorExpr { + return &SelectorExpr{ + X: &SelectorExpr{ + X: &Ident{Name: ctx}, + Sel: &Ident{Name: "Response"}, + }, + Sel: NewIdent("StatusCode"), + } +} + +func ExportServerOption(optionName string, args []Expr) *CallExpr { + return &CallExpr{ + Fun: &SelectorExpr{ + X: NewIdent("hzserver"), + Sel: NewIdent(optionName), + }, + Args: args, + } +} + +func ExportRespHeader(ctx string) *SelectorExpr { + return &SelectorExpr{ + X: &SelectorExpr{ + X: NewIdent(ctx), + Sel: NewIdent("Response"), + }, + Sel: NewIdent("Header"), + } +} + +func ExportCtxCookie(ctx string, args []Expr) *CallExpr { + return &CallExpr{ + Fun: NewIdent("string"), + Args: []Expr{ + &CallExpr{ + Fun: &SelectorExpr{ + X: NewIdent(ctx), + Sel: NewIdent("Cookie"), + }, + Args: args, + }, + }, + } +} + +func ExportUserAgent(ctx string) *CallExpr { + return &CallExpr{ + Fun: NewIdent("string"), + Args: []Expr{ + &CallExpr{ + Fun: &SelectorExpr{ + X: &SelectorExpr{ + X: &SelectorExpr{ + X: NewIdent(ctx), + Sel: NewIdent("Request"), + }, + Sel: NewIdent("Header"), + }, + Sel: NewIdent("UserAgent"), + }, + }, + }, + } +} + +func ExportedAppFSPtr(expr ...Expr) *UnaryExpr { + return &UnaryExpr{ + Op: token.AND, + X: &CompositeLit{ + Type: &SelectorExpr{ + X: NewIdent("hzapp"), + Sel: NewIdent("FS"), + }, + Elts: []Expr{ + &KeyValueExpr{ + Key: NewIdent("Root"), + Value: expr[0], + }, + &KeyValueExpr{ + Key: NewIdent("GenerateIndexPages"), + Value: expr[1], + }, + &KeyValueExpr{ + Key: NewIdent("PathRewrite"), + Value: &CallExpr{ + Fun: &SelectorExpr{ + X: NewIdent("hzapp"), + Sel: NewIdent("NewPathSlashesStripper"), + }, + Args: []Expr{ + &BasicLit{ + Kind: token.INT, + Value: "100", + }, + }, + }, + }, + &KeyValueExpr{ + Key: NewIdent("IndexNames"), + Value: &CompositeLit{ + Type: &ArrayType{ + Elt: &Ident{Name: "string"}, + }, + Elts: []Expr{ + &BasicLit{ + Kind: token.STRING, + Value: "\"index.html\"", + }, + }, + }, + }, + }, + }, + } +} diff --git a/cmd/hertz_migrate/internal/utils/cli.go b/cmd/hertz_migrate/internal/utils/cli.go index abf18b4..f104ec4 100644 --- a/cmd/hertz_migrate/internal/utils/cli.go +++ b/cmd/hertz_migrate/internal/utils/cli.go @@ -16,6 +16,7 @@ package utils import ( "fmt" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/logs" "log" "os" "os/exec" @@ -55,3 +56,19 @@ func RunGoGet(path, repo string) { return } } + +func RunGoModTidy(path string) { + err := os.Chdir(path) + if err != nil { + logs.Error("Error changing directory:", err) + return + } + cmd := exec.Command("go", "mod", "tidy") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + if err != nil { + logs.Error("Error running command:", err) + return + } +} diff --git a/cmd/hertz_migrate/internal/utils/new_utils.go b/cmd/hertz_migrate/internal/utils/new_utils.go new file mode 100644 index 0000000..b68d9d6 --- /dev/null +++ b/cmd/hertz_migrate/internal/utils/new_utils.go @@ -0,0 +1,135 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + . "go/ast" +) + +func CheckPtrPkgAndStructName(s *StarExpr, packageName, structName string) bool { + if se, ok := s.X.(*SelectorExpr); ok { + return CheckSelPkgAndStruct(se, packageName, structName) + } + return false +} + +func CheckCallPkgAndMethodName(c *CallExpr, packageName, methodName string) bool { + if fs, ok := c.Fun.(*SelectorExpr); ok { + return CheckSelPkgAndStruct(fs, packageName, methodName) + } + return false +} + +func CheckSelPkgAndStruct(s *SelectorExpr, packageName, structName string) bool { + if i, ok := s.X.(*Ident); ok { + if packageName == "" { + return s.Sel.Name == structName + } + if i.Name != packageName { + return false + } + if s.Sel.Name == structName { + return true + } + } + return false +} + +func CheckSelObj(s *SelectorExpr, packageName, structName string) bool { + if ident, ok := s.X.(*Ident); ok { + if ident.Obj != nil { + if field, ok := ident.Obj.Decl.(*Field); ok { + if ss, ok := field.Type.(*SelectorExpr); ok { + return CheckSelPkgAndStruct(ss, packageName, structName) + } + } + } + } + return false +} + +func CheckFuncDeclReturnOne(fun *FuncDecl, pkg, name string) bool { + if fun.Type.Results == nil { + return false + } + if len(fun.Type.Results.List) != 1 { + return false + } + starField := fun.Type.Results.List[0] + if s, ok := starField.Type.(*StarExpr); ok { + if sel, ok := s.X.(*SelectorExpr); ok { + return CheckSelPkgAndStruct(sel, pkg, name) + } + } + return true +} + +func CheckObjStarExpr(obj *Object, pkg, name string) bool { + if obj == nil || obj.Decl == nil { + return false + } + + if field, ok := obj.Decl.(*Field); ok { + if starExpr, ok := field.Type.(*StarExpr); ok { + if selExpr, ok := starExpr.X.(*SelectorExpr); ok { + return CheckSelPkgAndStruct(selExpr, pkg, name) + } + } + } + + if assignStmt, ok := obj.Decl.(*AssignStmt); ok { + if call, ok := assignStmt.Rhs[0].(*CallExpr); ok { + if i, ok := call.Fun.(*Ident); ok { + if i.Obj == nil { + return false + } + if i.Obj.Kind == Fun { + return CheckFuncDeclReturnOne(i.Obj.Decl.(*FuncDecl), pkg, name) + } + } + + //if se, ok := call.Fun.(*SelectorExpr); ok { + // if i, ok := se.X.(*Ident); ok { + // return CheckObjStarExpr(i.Obj, pkg, name) + // } + //} + } + } + return false +} + +func CheckObjSelExpr(obj *Object, pkg, name string) bool { + if obj == nil || obj.Decl == nil { + return false + } + + if assignStmt, ok := obj.Decl.(*AssignStmt); ok { + + if call, ok := assignStmt.Rhs[0].(*CallExpr); ok { + if se, ok := call.Fun.(*SelectorExpr); ok { + return CheckSelPkgAndStruct(se, pkg, name) + } + } + + if ue, ok := assignStmt.Rhs[0].(*UnaryExpr); ok { + if clit, ok := ue.X.(*CompositeLit); ok { + if sel, ok := clit.Type.(*SelectorExpr); ok { + return CheckSelPkgAndStruct(sel, pkg, name) + } + } + } + } + return false +} diff --git a/cmd/hertz_migrate/internal/utils/utils.go b/cmd/hertz_migrate/internal/utils/utils.go index 4f2d8da..15ca1be 100644 --- a/cmd/hertz_migrate/internal/utils/utils.go +++ b/cmd/hertz_migrate/internal/utils/utils.go @@ -130,7 +130,7 @@ func CollectGoFiles(directory string, ignoreDirs []string) ([]string, error) { return goFiles, err } -func SearchAllDirHasGoMod(path string) (dirs []string) { +func SearchAllDirHasGoMod(path string) (dirs []string, err error) { abs, err := filepath.Abs(path) if err != nil { fmt.Println("[Error] search go.mod dir fail, error ", err) @@ -146,7 +146,7 @@ func SearchAllDirHasGoMod(path string) (dirs []string) { return nil }) if err != nil { - fmt.Println("Error:", err) + return nil, err } - return dirs + return dirs, nil } diff --git a/cmd/hertz_migrate/main.go b/cmd/hertz_migrate/main.go index 66e219a..1403b06 100644 --- a/cmd/hertz_migrate/main.go +++ b/cmd/hertz_migrate/main.go @@ -15,16 +15,21 @@ package main import ( - "log" "os" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/logs" + "github.com/hertz-contrib/migrate/cmd/hertz_migrate/internal/cli" ) func main() { + defer func() { + logs.Flush() + }() + app := cli.Init() err := app.Run(os.Args) if err != nil { - log.Printf("[Error] %v\n", err) + logs.Errorf("[Error] %v\n", err) } } diff --git a/cmd/hertz_migrate/readme.md b/cmd/hertz_migrate/readme.md index 277647d..f899afc 100644 --- a/cmd/hertz_migrate/readme.md +++ b/cmd/hertz_migrate/readme.md @@ -2,7 +2,10 @@ ### Introduction -Migrate go web code by analyzing ast, currently `hertz_migrate` supports migrating the following frameworks chi (only `func (http.ResponseWriter, *http.Request)` net/http migrations are supported for now) +Migrate go web code by analyzing ast, currently `hertz_migrate` supports migrating the following frameworks: + +- chi (only `func (http.ResponseWriter, *http.Request)` net/http migrations are supported for now) +- gin ### How to install @@ -43,11 +46,17 @@ GLOBAL OPTIONS: ```bash # example hertz_migration -target-dir ./haha hertz_migrate -target-dir ${dir-name} +# ignore some dirs +hertz_migrate -target-dir ${dir-name} -I=kitex_gen -I=hz_gen +# migrate with gin +hertz_migrate -target-dir ${dir-name} -use-gin ``` ### Adapt progress + [readme](./adapt.md) ### Warn -1. You must make sure that each subproject in the `target-dir` has a `go.mod` file, but you can continue to use it even if it doesn't +1. You must make sure that each subproject in the `target-dir` has a `go.mod` file, but you can continue to use it even + if it doesn't