From a8eb9e002518b91ac9a8e045d5e17aa2902f9a88 Mon Sep 17 00:00:00 2001 From: Rogee Date: Mon, 22 Sep 2025 14:16:22 +0800 Subject: [PATCH] fix: gen provider --- cmd/gen_provider.go | 22 ++++--- pkg/ast/provider/ast_walker.go | 66 ++++++++++--------- pkg/ast/provider/config.go | 4 +- pkg/ast/provider/parser.go | 19 +++--- pkg/ast/provider/parser_interface.go | 33 ++++++---- pkg/ast/provider/provider.go | 42 ++++++------ pkg/ast/route/errors.go | 2 +- pkg/ast/route/render.go | 1 - pkg/ast/route/renderer.go | 14 ++-- pkg/ast/route/route.go | 29 ++++---- pkg/ast/route/route_test.go | 22 +++---- .../contracts/route_builder_test.go | 2 +- .../contracts/route_parser_test.go | 12 ++-- 13 files changed, 142 insertions(+), 126 deletions(-) diff --git a/cmd/gen_provider.go b/cmd/gen_provider.go index 875b80d..74eeb37 100644 --- a/cmd/gen_provider.go +++ b/cmd/gen_provider.go @@ -37,12 +37,13 @@ import ( // - RunE: commandGenProviderE - 命令执行函数 // // 注释语法说明: -// @provider():[except|only] [returnType] [group] -// - mode: grpc|event|job|cronjob|model(可选) -// - :only: 仅注入字段 tag 为 inject:"true" 的依赖 -// - :except: 注入除标注 inject:"false" 之外的非标量依赖 -// - returnType: Provide 返回类型(如 contracts.Initial) -// - group: 分组(如 atom.GroupInitial) +// +// @provider():[except|only] [returnType] [group] +// - mode: grpc|event|job|cronjob|model(可选) +// - :only: 仅注入字段 tag 为 inject:"true" 的依赖 +// - :except: 注入除标注 inject:"false" 之外的非标量依赖 +// - returnType: Provide 返回类型(如 contracts.Initial) +// - group: 分组(如 atom.GroupInitial) // // 参数: // - root: 根命令对象,用于注册子命令 @@ -149,11 +150,12 @@ func CommandGenProvider(root *cobra.Command) { // - 文件生成错误:返回 provider.Render() 的错误 // // 使用示例: -// # 在当前目录生成 Provider -// atomctl gen provider // -// # 在指定目录生成 Provider -// atomctl gen provider ./internal/services +// # 在当前目录生成 Provider +// atomctl gen provider +// +// # 在指定目录生成 Provider +// atomctl gen provider ./internal/services // // 注意事项: // - 目标目录必须包含 go.mod 文件 diff --git a/pkg/ast/provider/ast_walker.go b/pkg/ast/provider/ast_walker.go index 9b7a55d..85db0a3 100644 --- a/pkg/ast/provider/ast_walker.go +++ b/pkg/ast/provider/ast_walker.go @@ -30,7 +30,7 @@ import ( // - 错误处理:提供完善的错误处理机制 // // 执行流程: -// 1. 文件过滤 → 2. AST 解析 → 3. 遍历声明 → 4. 访问者通知 → 5. 结果收集 +// 1. 文件过滤 → 2. AST 解析 → 3. 遍历声明 → 4. 访问者通知 → 5. 结果收集 // // 设计原则: // - 单一职责:专注于 AST 遍历,不涉及具体的业务逻辑 @@ -84,12 +84,12 @@ type WalkerConfig struct { // └─────────────────────────────────────────────────────────────┘ // // 调用时机: -// 1. VisitFile: 开始处理文件时调用,用于初始化文件级上下文 -// 2. VisitGenDecl: 遇到通用声明时调用(type、var、const) -// 3. VisitTypeSpec: 遇到类型规范时调用(具体的类型定义) -// 4. VisitStructType: 遇到结构体类型时调用 -// 5. VisitStructField: 遍历结构体字段时调用 -// 6. Complete: 文件处理完成时调用,用于清理和结果收集 +// 1. VisitFile: 开始处理文件时调用,用于初始化文件级上下文 +// 2. VisitGenDecl: 遇到通用声明时调用(type、var、const) +// 3. VisitTypeSpec: 遇到类型规范时调用(具体的类型定义) +// 4. VisitStructType: 遇到结构体类型时调用 +// 5. VisitStructField: 遍历结构体字段时调用 +// 6. Complete: 文件处理完成时调用,用于清理和结果收集 // // 错误处理: // - 如果任何方法返回错误,遍历过程会立即停止 @@ -218,10 +218,11 @@ type NodeVisitor interface { // - *ASTWalker: 配置好的遍历器实例,可以直接使用 // // 使用示例: -// walker := NewASTWalker() -// visitor := NewProviderDiscoveryVisitor() -// walker.AddVisitor(visitor) -// err := walker.WalkFile("user_service.go") +// +// walker := NewASTWalker() +// visitor := NewProviderDiscoveryVisitor() +// walker.AddVisitor(visitor) +// err := walker.WalkFile("user_service.go") // // 注意事项: // - 必须添加至少一个访问者才能进行有效的分析 @@ -264,13 +265,14 @@ func NewASTWalker() *ASTWalker { // - *ASTWalker: 使用指定配置的遍历器实例 // // 使用示例: -// config := &WalkerConfig{ -// IncludeTestFiles: true, -// IncludeGeneratedFiles: true, -// StrictMode: true, -// MaxFileSize: 5 * 1024 * 1024, // 5MB -// } -// walker := NewASTWalkerWithConfig(config) +// +// config := &WalkerConfig{ +// IncludeTestFiles: true, +// IncludeGeneratedFiles: true, +// StrictMode: true, +// MaxFileSize: 5 * 1024 * 1024, // 5MB +// } +// walker := NewASTWalkerWithConfig(config) // // 注意事项: // - 自定义配置会覆盖所有默认设置 @@ -392,12 +394,13 @@ func (aw *ASTWalker) RemoveVisitor(visitor NodeVisitor) { // - 访问者错误:返回访问者产生的错误,停止后续处理 // // 使用示例: -// walker := NewASTWalker() -// walker.AddVisitor(NewProviderDiscoveryVisitor()) -// err := walker.WalkFile("user_service.go") -// if err != nil { -// log.Fatal("Failed to walk file: ", err) -// } +// +// walker := NewASTWalker() +// walker.AddVisitor(NewProviderDiscoveryVisitor()) +// err := walker.WalkFile("user_service.go") +// if err != nil { +// log.Fatal("Failed to walk file: ", err) +// } // // 注意事项: // - 必须至少添加一个访问者才能进行有效分析 @@ -933,9 +936,9 @@ func (aw *ASTWalker) GetCommentParser() *CommentParser { // - 代码生成:为代码生成工具提供输入数据 // - 项目分析:分析项目中的 Provider 定义 type ProviderDiscoveryVisitor struct { - commentParser *CommentParser // 注释解析器,用于解析 @provider 注解 + commentParser *CommentParser // 注释解析器,用于解析 @provider 注解 providers []Provider // 发现的 Provider 列表 - currentFile string // 当前处理的文件路径 + currentFile string // 当前处理的文件路径 } // NewProviderDiscoveryVisitor 创建新的 ProviderDiscoveryVisitor 实例 @@ -964,10 +967,11 @@ type ProviderDiscoveryVisitor struct { // - *ProviderDiscoveryVisitor: 配置好的访问者实例 // // 使用示例: -// commentParser := NewCommentParser() -// visitor := NewProviderDiscoveryVisitor(commentParser) -// walker := NewASTWalker() -// walker.AddVisitor(visitor) +// +// commentParser := NewCommentParser() +// visitor := NewProviderDiscoveryVisitor(commentParser) +// walker := NewASTWalker() +// walker.AddVisitor(visitor) // // 注意事项: // - 注释解析器必须提前初始化 @@ -1150,7 +1154,7 @@ func (pdv *ProviderDiscoveryVisitor) VisitStructType(filePath string, structType // === Provider 构建 === // 创建 Provider 对象,设置基本信息 provider := Provider{ - StructName: typeSpec.Name.Name, // 结构体名称 + StructName: typeSpec.Name.Name, // 结构体名称 Mode: providerComment.Mode, // Provider 模式 ProviderGroup: providerComment.Group, // Provider 分组 ReturnType: providerComment.ReturnType, // 返回类型 diff --git a/pkg/ast/provider/config.go b/pkg/ast/provider/config.go index a609893..d7b222b 100644 --- a/pkg/ast/provider/config.go +++ b/pkg/ast/provider/config.go @@ -114,8 +114,8 @@ func (c *ParserContext) ShouldIncludeFile(filePath string) bool { return false } - // Skip generated files if not allowed - if !c.Config.AllowGenFiles && strings.HasSuffix(filePath, ".gen.go") { + // Skip generated files if not allowed, but allow routes.gen.go since it may contain providers + if !c.Config.AllowGenFiles && strings.HasSuffix(filePath, ".gen.go") && !strings.HasSuffix(filePath, "routes.gen.go") { return false } diff --git a/pkg/ast/provider/parser.go b/pkg/ast/provider/parser.go index e5ff47c..294d78a 100644 --- a/pkg/ast/provider/parser.go +++ b/pkg/ast/provider/parser.go @@ -28,10 +28,10 @@ import ( // 执行流程: // 1. 文件过滤 → 2. AST解析 → 3. 导入处理 → 4. 注解发现 → 5. Provider构建 → 6. 验证 type MainParser struct { - commentParser *CommentParser // 负责解析 @provider 注解 + commentParser *CommentParser // 负责解析 @provider 注解 importResolver *ImportResolver // 负责处理 Go 文件的导入信息 astWalker *ASTWalker // 负责遍历 AST 发现 Provider 注解 - builder *ProviderBuilder // 负责从 AST 节点构建 Provider 对象 + builder *ProviderBuilder // 负责从 AST 节点构建 Provider 对象 validator *GoValidator // 负责验证 Provider 配置的正确性 config *ParserConfig // 负责解析器配置管理 } @@ -54,9 +54,9 @@ type MainParser struct { // 返回值:配置好的 MainParser 实例,可直接调用 ParseFile() 或 ParseDir() func NewParser() *MainParser { return &MainParser{ - commentParser: NewCommentParser(), // 初始化注释解析器 - importResolver: NewImportResolver(), // 初始化导入解析器 - astWalker: NewASTWalker(), // 初始化 AST 遍历器 + commentParser: NewCommentParser(), // 初始化注释解析器 + importResolver: NewImportResolver(), // 初始化导入解析器 + astWalker: NewASTWalker(), // 初始化 AST 遍历器 builder: NewProviderBuilder(), // 初始化 Provider 构建器 validator: NewGoValidator(), // 初始化验证器 config: NewParserConfig(), // 初始化默认配置 @@ -116,7 +116,6 @@ func ParseRefactored(source string) []Provider { // 调用详细的文件解析方法 providers, err := parser.ParseFile(source) - // 错误处理:记录错误并返回空结果 if err != nil { log.Error("Parse error: ", err) @@ -197,10 +196,10 @@ func (p *MainParser) ParseFile(source string) ([]Provider, error) { // === 步骤 5:创建构建器上下文 === // 创建包含解析所需所有信息的构建器上下文 builderContext := &BuilderContext{ - FilePath: source, // 当前文件路径 - PackageName: node.Name.Name, // 包名 - ImportContext: importContext, // 导入信息上下文 - ASTFile: node, // AST 节点 + FilePath: source, // 当前文件路径 + PackageName: node.Name.Name, // 包名 + ImportContext: importContext, // 导入信息上下文 + ASTFile: node, // AST 节点 ProcessedTypes: make(map[string]bool), // 已处理的类型,避免重复 Errors: make([]error, 0), // 错误列表 Warnings: make([]string, 0), // 警告列表 diff --git a/pkg/ast/provider/parser_interface.go b/pkg/ast/provider/parser_interface.go index 5ad8f62..94dbf44 100644 --- a/pkg/ast/provider/parser_interface.go +++ b/pkg/ast/provider/parser_interface.go @@ -159,7 +159,7 @@ type Parser interface { type GoParser struct { config *ParserConfig // 解析器配置,控制解析行为 context *ParserContext // 解析上下文,包含解析状态信息 - mu sync.RWMutex // 读写锁,保护 config 和 context 的并发访问 + mu sync.RWMutex // 读写锁,保护 config 和 context 的并发访问 } // NewGoParser 创建一个使用默认配置的 GoParser 实例 @@ -184,8 +184,9 @@ type GoParser struct { // - *GoParser: 配置好的解析器实例,可以直接使用 // // 使用示例: -// parser := NewGoParser() -// providers, err := parser.ParseFile("user_service.go") +// +// parser := NewGoParser() +// providers, err := parser.ParseFile("user_service.go") func NewGoParser() *GoParser { // 创建默认配置 config := NewParserConfig() @@ -229,12 +230,13 @@ func NewGoParser() *GoParser { // - *GoParser: 使用指定配置的解析器实例 // // 使用示例: -// config := &ParserConfig{ -// CacheEnabled: true, -// StrictMode: true, -// SourceLocations: true, -// } -// parser := NewGoParserWithConfig(config) +// +// config := &ParserConfig{ +// CacheEnabled: true, +// StrictMode: true, +// SourceLocations: true, +// } +// parser := NewGoParserWithConfig(config) func NewGoParserWithConfig(config *ParserConfig) *GoParser { // 处理 nil 配置,保持向后兼容 if config == nil { @@ -552,12 +554,13 @@ func (p *GoParser) parseStructFields(structType *ast.StructType, imports map[str // Add injection parameter for _, name := range field.Names { - provider.InjectParams[name.Name] = InjectParam{ + param := InjectParam{ Star: star, Type: typ, Package: pkg, PackageAlias: pkgAlias, } + provider.InjectParams[name.Name] = param // Add to imports if pkg != "" && pkgAlias != "" { @@ -576,7 +579,15 @@ func (p *GoParser) parseFieldType(expr ast.Expr, imports map[string]string) (sta typ = t.Name case *ast.StarExpr: star = "*" - return p.parseFieldType(t.X, imports) + _, innerPkg, innerPkgAlias, innerTyp, innerErr := p.parseFieldType(t.X, imports) + if innerErr != nil { + return "", "", "", "", innerErr + } + // Use inner package info but keep star + pkg = innerPkg + pkgAlias = innerPkgAlias + typ = innerTyp + return star, pkg, pkgAlias, typ, nil case *ast.SelectorExpr: if x, ok := t.X.(*ast.Ident); ok { pkgAlias = x.Name diff --git a/pkg/ast/provider/provider.go b/pkg/ast/provider/provider.go index 470aa1a..f1ab0be 100644 --- a/pkg/ast/provider/provider.go +++ b/pkg/ast/provider/provider.go @@ -123,7 +123,7 @@ func Parse(source string) []Provider { node, err := parser.ParseFile(fset, source, nil, parser.ParseComments) if err != nil { log.Error("ERR: ", err) - return nil // 原始版本在错误时返回 nil + return nil // 原始版本在错误时返回 nil } imports := make(map[string]string) for _, imp := range node.Imports { @@ -415,20 +415,21 @@ func (p ProviderDescribe) String() { // parseProvider 解析 @provider 注解的语法 // // 支持的语法格式: -// @provider - 基本格式 -// @provider(job) - 指定模式 -// @provider(job):except - 排除模式 -// @provider:except - 排除模式(无模式) -// @provider:only - 仅包含模式 -// @provider returnType - 指定返回类型 -// @provider returnType group - 指定返回类型和分组 -// @provider(job) returnType group - 完整格式 +// +// @provider - 基本格式 +// @provider(job) - 指定模式 +// @provider(job):except - 排除模式 +// @provider:except - 排除模式(无模式) +// @provider:only - 仅包含模式 +// @provider returnType - 指定返回类型 +// @provider returnType group - 指定返回类型和分组 +// @provider(job) returnType group - 完整格式 // // 解析规则: -// 1. 移除 "@provider" 前缀 -// 2. 处理模式(括号内的内容) -// 3. 处理注入模式(:except 或 :only) -// 4. 解析返回类型和分组(剩余部分) +// 1. 移除 "@provider" 前缀 +// 2. 处理模式(括号内的内容) +// 3. 处理注入模式(:except 或 :only) +// 4. 解析返回类型和分组(剩余部分) // // 参数: // - doc: @provider 注解字符串 @@ -437,13 +438,14 @@ func (p ProviderDescribe) String() { // - ProviderDescribe: 解析后的注解信息 // // 示例: -// 输入: "@provider(job) contracts.Initial atom.GroupInitial" -// 输出: ProviderDescribe{ -// Mode: "job", -// ReturnType: "contracts.Initial", -// Group: "atom.GroupInitial", -// IsOnly: false -// } +// +// 输入: "@provider(job) contracts.Initial atom.GroupInitial" +// 输出: ProviderDescribe{ +// Mode: "job", +// ReturnType: "contracts.Initial", +// Group: "atom.GroupInitial", +// IsOnly: false +// } func parseProvider(doc string) ProviderDescribe { result := ProviderDescribe{IsOnly: false} diff --git a/pkg/ast/route/errors.go b/pkg/ast/route/errors.go index 32a275a..fa92564 100644 --- a/pkg/ast/route/errors.go +++ b/pkg/ast/route/errors.go @@ -55,4 +55,4 @@ func WrapError(err error, format string, args ...interface{}) error { // Wrap other errors with a generic parse error return NewRouteError(ErrParseFailed, format, args...).WithCause(err) -} \ No newline at end of file +} diff --git a/pkg/ast/route/render.go b/pkg/ast/route/render.go index 9851fa4..c3de222 100644 --- a/pkg/ast/route/render.go +++ b/pkg/ast/route/render.go @@ -8,7 +8,6 @@ import ( "go.ipao.vip/atomctl/v2/pkg/utils/gomod" ) - //go:embed router.go.tpl var routeTpl string diff --git a/pkg/ast/route/renderer.go b/pkg/ast/route/renderer.go index 84a0d41..c354c56 100644 --- a/pkg/ast/route/renderer.go +++ b/pkg/ast/route/renderer.go @@ -17,11 +17,11 @@ type TemplateRenderer interface { // TemplateInfo provides metadata about the template type TemplateInfo struct { - Name string - Version string - Functions []string - Options []string - Size int + Name string + Version string + Functions []string + Options []string + Size int } // RouteRenderer implements TemplateRenderer for route generation @@ -108,8 +108,8 @@ func (r *RouteRenderer) Render(data RenderData) ([]byte, error) { } r.logger.WithFields(log.Fields{ - "package_name": data.PackageName, - "routes_count": len(data.Routes), + "package_name": data.PackageName, + "routes_count": len(data.Routes), "content_length": len(result), }).Debug("Template rendered successfully") diff --git a/pkg/ast/route/route.go b/pkg/ast/route/route.go index 28d8a41..5008513 100644 --- a/pkg/ast/route/route.go +++ b/pkg/ast/route/route.go @@ -12,7 +12,6 @@ import ( log "github.com/sirupsen/logrus" ) - type RouteDefinition struct { FilePath string Path string @@ -80,24 +79,24 @@ func ParseFile(file string) []RouteDefinition { imports := extractImports(node) parser := &routeParser{ - file: file, - node: node, - imports: imports, - routes: make(map[string]RouteDefinition), - actions: make(map[string][]ActionDefinition), - usedImports: make(map[string][]string), + file: file, + node: node, + imports: imports, + routes: make(map[string]RouteDefinition), + actions: make(map[string][]ActionDefinition), + usedImports: make(map[string][]string), } return parser.parse() } type routeParser struct { - file string - node *ast.File - imports map[string]string - routes map[string]RouteDefinition - actions map[string][]ActionDefinition - usedImports map[string][]string + file string + node *ast.File + imports map[string]string + routes map[string]RouteDefinition + actions map[string][]ActionDefinition + usedImports map[string][]string } func (p *routeParser) parse() []RouteDefinition { @@ -127,8 +126,8 @@ func (p *routeParser) parseFunctionDeclarations() { func (p *routeParser) isValidFunctionDeclaration(decl *ast.FuncDecl, ok bool) bool { return ok && - decl.Recv != nil && - decl.Doc != nil + decl.Recv != nil && + decl.Doc != nil } func (p *routeParser) extractReceiverType(decl *ast.FuncDecl) string { diff --git a/pkg/ast/route/route_test.go b/pkg/ast/route/route_test.go index acd52cf..60f5dca 100644 --- a/pkg/ast/route/route_test.go +++ b/pkg/ast/route/route_test.go @@ -26,7 +26,7 @@ func (c *UserController) GetUser() error { // Create a temporary file for testing tmpFile := "/tmp/test_route.go" - err := os.WriteFile(tmpFile, []byte(code), 0644) + err := os.WriteFile(tmpFile, []byte(code), 0o644) assert.NoError(t, err, "Should create temp file") defer os.Remove(tmpFile) @@ -63,7 +63,7 @@ func (c *UserController) GetUser(id string, limit int) { // Create a temporary file for testing tmpFile := "/tmp/test_params.go" - err := os.WriteFile(tmpFile, []byte(code), 0644) + err := os.WriteFile(tmpFile, []byte(code), 0o644) assert.NoError(t, err, "Should create temp file") defer os.Remove(tmpFile) @@ -104,7 +104,7 @@ func (c *UserController) GetUser() { // Create a temporary file for testing tmpFile := "/tmp/test_invalid.go" - err := os.WriteFile(tmpFile, []byte(code), 0644) + err := os.WriteFile(tmpFile, []byte(code), 0o644) assert.NoError(t, err, "Should create temp file") defer os.Remove(tmpFile) @@ -130,7 +130,7 @@ func (c *UserController) GetUser() { // Create a temporary file for testing tmpFile := "/tmp/test_empty.go" - err := os.WriteFile(tmpFile, []byte(code), 0644) + err := os.WriteFile(tmpFile, []byte(code), 0o644) assert.NoError(t, err, "Should create temp file") defer os.Remove(tmpFile) @@ -196,7 +196,7 @@ func (c *HealthController) Check() error { ` // Create a temporary file for testing tmpFile := "/tmp/test_compatibility.go" - err := os.WriteFile(tmpFile, []byte(code), 0644) + err := os.WriteFile(tmpFile, []byte(code), 0o644) assert.NoError(t, err, "Should create temp file") defer os.Remove(tmpFile) @@ -251,7 +251,7 @@ func (c *ApiController) DownloadFile(filename string) error { } ` tmpFile := "/tmp/test_special_paths.go" - err := os.WriteFile(tmpFile, []byte(code), 0644) + err := os.WriteFile(tmpFile, []byte(code), 0o644) assert.NoError(t, err, "Should create temp file") defer os.Remove(tmpFile) @@ -352,13 +352,13 @@ func (c *HealthController) Check() error { // Create a realistic file structure tmpDir := "/tmp/test_app" httpDir := tmpDir + "/app/http" - err := os.MkdirAll(httpDir, 0755) + err := os.MkdirAll(httpDir, 0o755) assert.NoError(t, err, "Should create directory structure") defer os.RemoveAll(tmpDir) // Write controller file controllerFile := httpDir + "/user_controller.go" - err = os.WriteFile(controllerFile, []byte(code), 0644) + err = os.WriteFile(controllerFile, []byte(code), 0o644) assert.NoError(t, err, "Should write controller file") // WHEN parsing using the same method as CLI @@ -398,7 +398,7 @@ type EmptyController struct { } ` tmpFile := "/tmp/test_empty.go" - err := os.WriteFile(tmpFile, []byte(code), 0644) + err := os.WriteFile(tmpFile, []byte(code), 0o644) assert.NoError(t, err, "Should create temp file") defer os.Remove(tmpFile) @@ -419,7 +419,7 @@ package main type BrokenController struct { ` tmpFile := "/tmp/test_broken.go" - err := os.WriteFile(tmpFile, []byte(code), 0644) + err := os.WriteFile(tmpFile, []byte(code), 0o644) assert.NoError(t, err, "Should create temp file") defer os.Remove(tmpFile) @@ -538,4 +538,4 @@ func TestRouteRenderer(t *testing.T) { assert.NotEmpty(t, content) assert.Contains(t, string(content), "package main") }) -} \ No newline at end of file +} diff --git a/specs/002-refactor-ast-gen/contracts/route_builder_test.go b/specs/002-refactor-ast-gen/contracts/route_builder_test.go index d928a1b..8e60fae 100644 --- a/specs/002-refactor-ast-gen/contracts/route_builder_test.go +++ b/specs/002-refactor-ast-gen/contracts/route_builder_test.go @@ -236,4 +236,4 @@ func findParameterByPosition(params []ParamDefinition, position string) *ParamDe } } return nil -} \ No newline at end of file +} diff --git a/specs/002-refactor-ast-gen/contracts/route_parser_test.go b/specs/002-refactor-ast-gen/contracts/route_parser_test.go index 0663994..7fbed05 100644 --- a/specs/002-refactor-ast-gen/contracts/route_parser_test.go +++ b/specs/002-refactor-ast-gen/contracts/route_parser_test.go @@ -24,11 +24,11 @@ type RouteParser interface { // RouteDefinition represents a route definition (simplified for testing) type RouteDefinition struct { - StructName string - Path string - Methods []string - Parameters []ParamDefinition - Imports map[string]string + StructName string + Path string + Methods []string + Parameters []ParamDefinition + Imports map[string]string } // ParamDefinition represents a parameter definition (simplified for testing) @@ -258,4 +258,4 @@ func findParameterByPosition(params []ParamDefinition, position string) *ParamDe } } return nil -} \ No newline at end of file +}