diff --git a/cmd/internal/migrations/v3/logger_generics.go b/cmd/internal/migrations/v3/logger_generics.go index 2246b28..9423aa8 100644 --- a/cmd/internal/migrations/v3/logger_generics.go +++ b/cmd/internal/migrations/v3/logger_generics.go @@ -2,7 +2,11 @@ package v3 import ( "fmt" + "go/parser" + "go/token" "regexp" + "strconv" + "strings" semver "github.com/Masterminds/semver/v3" "github.com/spf13/cobra" @@ -11,18 +15,79 @@ import ( ) func MigrateLoggerGenerics(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { - reAllLogger := regexp.MustCompile(`(\w+)\.AllLogger([^\w\[]|$)`) - reConfigurableLogger := regexp.MustCompile(`(\w+)\.ConfigurableLogger([^\w\[]|$)`) - reDefaultLogger := regexp.MustCompile(`(\w+)\.DefaultLogger\(\)`) - reSetLogger := regexp.MustCompile(`(\w+)\.SetLogger\(`) - reLoggerToWriter := regexp.MustCompile(`(\w+)\.LoggerToWriter\(`) + fiberImport := regexp.MustCompile(`^github\.com/gofiber/fiber/v\d+/log$`) + reLoggerType := regexp.MustCompile(`(?m)^func\s+(?:\(\w+\s+\*?\w+\)\s+)?Logger\(\)\s*(\*?\w+(?:\.\w+)*)\s*{`) changed, err := internal.ChangeFileContent(cwd, func(content string) string { - content = reAllLogger.ReplaceAllString(content, `$1.AllLogger[any]$2`) - content = reConfigurableLogger.ReplaceAllString(content, `$1.ConfigurableLogger[any]$2`) - content = reDefaultLogger.ReplaceAllString(content, `$1.DefaultLogger[any]()`) - content = reSetLogger.ReplaceAllString(content, `$1.SetLogger[any](`) - content = reLoggerToWriter.ReplaceAllString(content, `$1.LoggerToWriter[any](`) + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, "", content, parser.ImportsOnly) + if err != nil { + return content + } + + aliases := make([]string, 0, len(file.Imports)) + for _, imp := range file.Imports { + path, err := strconv.Unquote(imp.Path.Value) + if err != nil { + continue + } + if !fiberImport.MatchString(path) { + continue + } + alias := "log" + if imp.Name != nil { + alias = imp.Name.Name + } else if idx := strings.LastIndex(path, "/"); idx != -1 { + alias = path[idx+1:] + } + aliases = append(aliases, regexp.QuoteMeta(alias)) + } + + if len(aliases) == 0 { + return content + } + + aliasPattern := strings.Join(aliases, "|") + reAllLogger := regexp.MustCompile(fmt.Sprintf("(%s)\\.AllLogger([^\\w\\[]|$)", aliasPattern)) + reConfigurableLogger := regexp.MustCompile(fmt.Sprintf("(%s)\\.ConfigurableLogger([^\\w\\[]|$)", aliasPattern)) + reDefaultLogger := regexp.MustCompile(fmt.Sprintf("(%s)\\.DefaultLogger\\(\\)", aliasPattern)) + reSetLogger := regexp.MustCompile(fmt.Sprintf("(%s)\\.SetLogger\\(", aliasPattern)) + reLoggerToWriter := regexp.MustCompile(fmt.Sprintf("(%s)\\.LoggerToWriter\\(", aliasPattern)) + + loggerType := "any" + if m := reLoggerType.FindStringSubmatch(content); len(m) == 2 { + loggerType = m[1] + } + + type replacement struct { + re *regexp.Regexp + format string + expectedSubmatch int + } + + replacements := []replacement{ + {reAllLogger, "%s.AllLogger[%s]%s", 3}, + {reConfigurableLogger, "%s.ConfigurableLogger[%s]%s", 3}, + {reDefaultLogger, "%s.DefaultLogger[%s]()", 2}, + {reSetLogger, "%s.SetLogger[%s](", 2}, + {reLoggerToWriter, "%s.LoggerToWriter[%s](", 2}, + } + + for _, r := range replacements { + content = r.re.ReplaceAllStringFunc(content, func(m string) string { + sub := r.re.FindStringSubmatch(m) + if len(sub) != r.expectedSubmatch { + return m + } + + args := []any{sub[1], loggerType} + if len(sub) > 2 { + args = append(args, sub[2]) + } + + return fmt.Sprintf(r.format, args...) + }) + } return content }) if err != nil { diff --git a/cmd/internal/migrations/v3/logger_generics_test.go b/cmd/internal/migrations/v3/logger_generics_test.go index d9cc347..da23d91 100644 --- a/cmd/internal/migrations/v3/logger_generics_test.go +++ b/cmd/internal/migrations/v3/logger_generics_test.go @@ -20,14 +20,53 @@ func Test_MigrateLoggerGenerics(t *testing.T) { file := writeTempFile(t, dir, `package main import ( + "github.com/gofiber/fiber/v2/log" +) +var _ log.AllLogger = (*customLogger)(nil) +var _ log.ConfigurableLogger = (*customLogger)(nil) +func main() { + logger := log.DefaultLogger() + log.SetLogger(logger) + _ = log.LoggerToWriter(logger, log.LevelInfo) +} +`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, v3.MigrateLoggerGenerics(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.NotContains(t, content, "AllLogger =") + assert.NotContains(t, content, "ConfigurableLogger =") + assert.Contains(t, content, "AllLogger[any]") + assert.Contains(t, content, "ConfigurableLogger[any]") + assert.Contains(t, content, "log.DefaultLogger[any]()") + assert.Contains(t, content, "log.SetLogger[any](") + assert.Contains(t, content, "log.LoggerToWriter[any](") + assert.Contains(t, buf.String(), "Migrating logger generics") +} + +func Test_MigrateLoggerGenerics_Zap(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mloggenericstestzap") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( fiberlog "github.com/gofiber/fiber/v2/log" + "go.uber.org/zap" ) +type customLogger struct{} +func (c *customLogger) Logger() *zap.Logger { return nil } var _ fiberlog.AllLogger = (*customLogger)(nil) var _ fiberlog.ConfigurableLogger = (*customLogger)(nil) func main() { - logger := fiberlog.DefaultLogger() + logger := &customLogger{} fiberlog.SetLogger(logger) _ = fiberlog.LoggerToWriter(logger, fiberlog.LevelInfo) + _ = fiberlog.DefaultLogger().Logger() } `) @@ -38,10 +77,41 @@ func main() { content := readFile(t, file) assert.NotContains(t, content, "AllLogger =") assert.NotContains(t, content, "ConfigurableLogger =") - assert.Contains(t, content, "AllLogger[any]") - assert.Contains(t, content, "ConfigurableLogger[any]") - assert.Contains(t, content, "DefaultLogger[any]()") - assert.Contains(t, content, "SetLogger[any](") - assert.Contains(t, content, "LoggerToWriter[any](") + assert.Contains(t, content, "AllLogger[*zap.Logger]") + assert.Contains(t, content, "ConfigurableLogger[*zap.Logger]") + assert.Contains(t, content, "SetLogger[*zap.Logger](") + assert.Contains(t, content, "LoggerToWriter[*zap.Logger](") + assert.Contains(t, content, "DefaultLogger[*zap.Logger]()") assert.Contains(t, buf.String(), "Migrating logger generics") } + +func Test_MigrateLoggerGenerics_SkipNonFiber(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mloggenericstestskip") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + otherlog "example.com/other/log" +) +var _ otherlog.AllLogger = (*customLogger)(nil) +var _ otherlog.ConfigurableLogger = (*customLogger)(nil) +func main() { + logger := otherlog.DefaultLogger() + otherlog.SetLogger(logger) + _ = otherlog.LoggerToWriter(logger, otherlog.LevelInfo) +} +`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, v3.MigrateLoggerGenerics(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "otherlog.DefaultLogger()") + assert.NotContains(t, content, "AllLogger[") + assert.NotContains(t, content, "ConfigurableLogger[") + assert.NotContains(t, buf.String(), "Migrating logger generics") +}