feat: add comment docs
This commit is contained in:
@@ -1,10 +1,8 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/samber/lo"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -13,6 +11,46 @@ import (
|
||||
"go.ipao.vip/atomctl/v2/pkg/utils/gomod"
|
||||
)
|
||||
|
||||
// CommandGenProvider 创建并注册 provider 生成命令到根命令
|
||||
//
|
||||
// 命令功能:
|
||||
// - 扫描源码中带有 @provider 注释的结构体
|
||||
// - 生成 provider.gen.go 文件实现依赖注入与分组注册
|
||||
// - 支持多种 Provider 模式(grpc、event、job、cronjob、model)
|
||||
// - 自动处理导入依赖和注入配置
|
||||
//
|
||||
// 命令设计:
|
||||
// ┌─────────────────────────────────────────────────────────────┐
|
||||
// │ CommandGenProvider() │
|
||||
// ├─────────────────────────────────────────────────────────────┤
|
||||
// │ 1. 命令创建:&cobra.Command{...} │
|
||||
// │ 2. 配置设置:Use、Aliases、Short、Long │
|
||||
// │ 3. 执行函数:commandGenProviderE │
|
||||
// │ 4. 命令注册:root.AddCommand(cmd) │
|
||||
// └─────────────────────────────────────────────────────────────┘
|
||||
//
|
||||
// 命令配置:
|
||||
// - Use: "provider" - 命令名称
|
||||
// - Aliases: ["p"] - 简写别名
|
||||
// - Short: "Generate providers" - 简短描述
|
||||
// - Long: 详细的帮助文档,包含语法说明和模式特性
|
||||
// - RunE: commandGenProviderE - 命令执行函数
|
||||
//
|
||||
// 注释语法说明:
|
||||
// @provider(<mode>):[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: 根命令对象,用于注册子命令
|
||||
//
|
||||
// 注意事项:
|
||||
// - 命令会在当前目录或指定目录中扫描 Provider
|
||||
// - 生成的文件按包分组,每个包生成一个 provider.gen.go
|
||||
// - 可与 gen route 命令联动使用
|
||||
func CommandGenProvider(root *cobra.Command) {
|
||||
cmd := &cobra.Command{
|
||||
Use: "provider",
|
||||
@@ -44,56 +82,140 @@ func CommandGenProvider(root *cobra.Command) {
|
||||
root.AddCommand(cmd)
|
||||
}
|
||||
|
||||
// commandGenProviderE 实现 provider 生成命令的核心执行逻辑
|
||||
//
|
||||
// 执行流程:
|
||||
// ┌─────────────────────────────────────────────────────────────┐
|
||||
// │ commandGenProviderE(cmd, args) │
|
||||
// ├─────────────────────────────────────────────────────────────┤
|
||||
// │ 1. 路径解析:处理命令行参数,确定扫描目录 │
|
||||
// │ 2. 路径标准化:转换为绝对路径 │
|
||||
// │ 3. 模块解析:解析 go.mod 文件获取模块信息 │
|
||||
// │ 4. Provider 解析:使用重构后的解析器扫描目录 │
|
||||
// │ 5. 分组处理:按输出文件分组 Provider │
|
||||
// │ 6. 文件生成:调用 provider.Render() 生成代码 │
|
||||
// │ 7. 错误处理:统一的错误处理和日志记录 │
|
||||
// └─────────────────────────────────────────────────────────────┘
|
||||
//
|
||||
// 详细步骤说明:
|
||||
// 步骤 1:路径解析
|
||||
// - 检查命令行参数,确定扫描目标目录
|
||||
// - 如果没有提供参数,使用当前工作目录
|
||||
// - 处理路径获取失败的情况
|
||||
//
|
||||
// 步骤 2:路径标准化
|
||||
// - 将相对路径转换为绝对路径
|
||||
// - 确保路径的一致性和可处理性
|
||||
// - 避免相对路径带来的处理问题
|
||||
//
|
||||
// 步骤 3:模块解析
|
||||
// - 解析目标目录中的 go.mod 文件
|
||||
// - 获取模块名称和依赖信息
|
||||
// - 为后续的 Provider 解析提供上下文
|
||||
//
|
||||
// 步骤 4:Provider 解析(重构后的核心)
|
||||
// - 创建 GoParser 实例:provider.NewGoParser()
|
||||
// - 解析目录:parser.ParseDir(path)
|
||||
// - 错误处理:解析失败时记录详细错误信息
|
||||
// - 性能优化:使用重构后的高效解析器
|
||||
//
|
||||
// 步骤 5:分组处理
|
||||
// - 按输出文件分组:使用 ProviderFile 字段
|
||||
// - 使用 samber/lo 库的 GroupBy 函数
|
||||
// - 为每个输出文件准备对应的 Provider 配置
|
||||
//
|
||||
// 步骤 6:文件生成
|
||||
// - 遍历分组后的 Provider 配置
|
||||
// - 调用 provider.Render() 生成代码
|
||||
// - 处理文件生成过程中的错误
|
||||
//
|
||||
// 重构优化说明:
|
||||
// - 简化了代码结构:从原来的手动文件遍历简化为 6 行核心代码
|
||||
// - 提高了性能:使用重构后的高效解析器
|
||||
// - 增强了可维护性:解析逻辑封装在独立的模块中
|
||||
// - 统一了错误处理:所有解析错误都有统一的处理方式
|
||||
//
|
||||
// 参数:
|
||||
// - cmd: 命令对象,包含命令配置和上下文
|
||||
// - args: 命令行参数,第一个参数为目录路径
|
||||
//
|
||||
// 返回值:
|
||||
// - error: 执行过程中的错误,成功时返回 nil
|
||||
//
|
||||
// 错误处理策略:
|
||||
// - 路径错误:返回 os.Getwd() 的错误
|
||||
// - 模块解析错误:返回 gomod.Parse() 的错误
|
||||
// - Provider 解析错误:返回解析器的错误,包含详细上下文
|
||||
// - 文件生成错误:返回 provider.Render() 的错误
|
||||
//
|
||||
// 使用示例:
|
||||
// # 在当前目录生成 Provider
|
||||
// atomctl gen provider
|
||||
//
|
||||
// # 在指定目录生成 Provider
|
||||
// atomctl gen provider ./internal/services
|
||||
//
|
||||
// 注意事项:
|
||||
// - 目标目录必须包含 go.mod 文件
|
||||
// - 生成的文件会覆盖现有的 provider.gen.go 文件
|
||||
// - 建议在版本控制中提交生成的文件
|
||||
func commandGenProviderE(cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
var path string
|
||||
|
||||
// === 步骤 1:路径解析 ===
|
||||
// 处理命令行参数,确定要扫描的目录
|
||||
if len(args) > 0 {
|
||||
// 使用用户提供的路径参数
|
||||
path = args[0]
|
||||
} else {
|
||||
// 没有提供参数时,使用当前工作目录
|
||||
path, err = os.Getwd()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get working directory: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// === 步骤 2:路径标准化 ===
|
||||
// 将路径转换为绝对路径,确保路径的一致性
|
||||
path, _ = filepath.Abs(path)
|
||||
|
||||
// === 步骤 3:模块解析 ===
|
||||
// 解析 go.mod 文件,获取模块信息和依赖关系
|
||||
err = gomod.Parse(filepath.Join(path, "go.mod"))
|
||||
if err != nil {
|
||||
log.Errorf("Failed to parse go.mod file: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
providers := []provider.Provider{}
|
||||
|
||||
// if path is file, then get the dir
|
||||
// === 步骤 4:Provider 解析(重构后的核心) ===
|
||||
// 记录开始生成的日志信息
|
||||
log.Infof("generate providers for dir: %s", path)
|
||||
// travel controller to find all controller objects
|
||||
_ = filepath.WalkDir(path, func(filepath string, d fs.DirEntry, err error) error {
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(filepath, ".go") {
|
||||
return nil
|
||||
}
|
||||
// 使用重构后的 GoParser 解析目录中的 Provider
|
||||
parser := provider.NewGoParser()
|
||||
providers, err := parser.ParseDir(path)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to parse providers from directory %s: %v", path, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.HasSuffix(filepath, "_test.go") {
|
||||
return nil
|
||||
}
|
||||
|
||||
providers = append(providers, provider.Parse(filepath)...)
|
||||
return nil
|
||||
})
|
||||
|
||||
// generate files
|
||||
// === 步骤 5:分组处理 ===
|
||||
// 按输出文件分组 Provider,每个包生成一个 provider.gen.go
|
||||
groups := lo.GroupBy(providers, func(item provider.Provider) string {
|
||||
return item.ProviderFile
|
||||
})
|
||||
|
||||
// === 步骤 6:文件生成 ===
|
||||
// 遍历分组后的 Provider 配置,生成对应的代码文件
|
||||
for file, conf := range groups {
|
||||
if err := provider.Render(file, conf); err != nil {
|
||||
log.Errorf("Failed to render provider file %s: %v", file, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("Successfully generated %d provider files", len(groups))
|
||||
return nil
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,25 +12,54 @@ import (
|
||||
"go.ipao.vip/atomctl/v2/pkg/utils/gomod"
|
||||
)
|
||||
|
||||
// MainParser represents the main parser that uses extracted components
|
||||
// MainParser 重构后的主解析器,采用组合模式协调各个子组件完成 Provider 解析
|
||||
//
|
||||
// 架构设计:
|
||||
// ┌─────────────────────────────────────────────────────────────┐
|
||||
// │ MainParser (协调器) │
|
||||
// ├─────────────────────────────────────────────────────────────┤
|
||||
// │ commentParser │ importResolver │ astWalker │
|
||||
// │ (注释解析器) │ (导入解析器) │ (AST遍历器) │
|
||||
// ├─────────────────────────────────────────────────────────────┤
|
||||
// │ builder │ validator │ config │
|
||||
// │ (构建器) │ (验证器) │ (配置管理) │
|
||||
// └─────────────────────────────────────────────────────────────┘
|
||||
//
|
||||
// 执行流程:
|
||||
// 1. 文件过滤 → 2. AST解析 → 3. 导入处理 → 4. 注解发现 → 5. Provider构建 → 6. 验证
|
||||
type MainParser struct {
|
||||
commentParser *CommentParser
|
||||
importResolver *ImportResolver
|
||||
astWalker *ASTWalker
|
||||
builder *ProviderBuilder
|
||||
validator *GoValidator
|
||||
config *ParserConfig
|
||||
commentParser *CommentParser // 负责解析 @provider 注解
|
||||
importResolver *ImportResolver // 负责处理 Go 文件的导入信息
|
||||
astWalker *ASTWalker // 负责遍历 AST 发现 Provider 注解
|
||||
builder *ProviderBuilder // 负责从 AST 节点构建 Provider 对象
|
||||
validator *GoValidator // 负责验证 Provider 配置的正确性
|
||||
config *ParserConfig // 负责解析器配置管理
|
||||
}
|
||||
|
||||
// NewParser creates a new MainParser with default configuration
|
||||
// NewParser 创建一个使用默认配置的 MainParser 实例
|
||||
//
|
||||
// 初始化流程:
|
||||
// ┌─────────────────────────────────────────────────────────────┐
|
||||
// │ NewParser() │
|
||||
// ├─────────────────────────────────────────────────────────────┤
|
||||
// │ 创建各个子组件实例: │
|
||||
// │ - CommentParser: 解析 @provider 注释 │
|
||||
// │ - ImportResolver: 处理导入依赖 │
|
||||
// │ - ASTWalker: 遍历 AST 发现结构体 │
|
||||
// │ - ProviderBuilder: 构建 Provider 对象 │
|
||||
// │ - GoValidator: 验证 Provider 配置 │
|
||||
// │ - ParserConfig: 默认配置 │
|
||||
// └─────────────────────────────────────────────────────────────┘
|
||||
//
|
||||
// 返回值:配置好的 MainParser 实例,可直接调用 ParseFile() 或 ParseDir()
|
||||
func NewParser() *MainParser {
|
||||
return &MainParser{
|
||||
commentParser: NewCommentParser(),
|
||||
importResolver: NewImportResolver(),
|
||||
astWalker: NewASTWalker(),
|
||||
builder: NewProviderBuilder(),
|
||||
validator: NewGoValidator(),
|
||||
config: NewParserConfig(),
|
||||
commentParser: NewCommentParser(), // 初始化注释解析器
|
||||
importResolver: NewImportResolver(), // 初始化导入解析器
|
||||
astWalker: NewASTWalker(), // 初始化 AST 遍历器
|
||||
builder: NewProviderBuilder(), // 初始化 Provider 构建器
|
||||
validator: NewGoValidator(), // 初始化验证器
|
||||
config: NewParserConfig(), // 初始化默认配置
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,76 +88,150 @@ func NewParserWithConfig(config *ParserConfig) *MainParser {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse parses a Go source file and returns discovered providers
|
||||
// This is the refactored version of the original Parse function
|
||||
// ParseRefactored 重构后的单文件解析函数 - 替代原有的 Parse 函数
|
||||
//
|
||||
// 执行流程:
|
||||
// ┌─────────────────────────────────────────────────────────────┐
|
||||
// │ ParseRefactored(source) │
|
||||
// ├─────────────────────────────────────────────────────────────┤
|
||||
// │ 1. 创建解析器实例:NewParser() │
|
||||
// │ 2. 调用文件解析:parser.ParseFile(source) │
|
||||
// │ 3. 错误处理:如果解析失败,记录错误并返回空切片 │
|
||||
// │ 4. 返回结果:返回发现的 Provider 切片 │
|
||||
// └─────────────────────────────────────────────────────────────┘
|
||||
//
|
||||
// 参数:
|
||||
// - source: Go 源文件路径
|
||||
//
|
||||
// 返回值:
|
||||
// - []Provider: 解析到的 Provider 列表(如果失败则为空)
|
||||
//
|
||||
// 设计原则:
|
||||
// - 简化接口:提供便捷的单函数调用方式
|
||||
// - 错误处理:内部处理错误,避免调用者需要处理复杂错误
|
||||
// - 兼容性:保持与原有 Parse 函数相同的签名,方便迁移
|
||||
func ParseRefactored(source string) []Provider {
|
||||
// 创建解析器实例
|
||||
parser := NewParser()
|
||||
|
||||
// 调用详细的文件解析方法
|
||||
providers, err := parser.ParseFile(source)
|
||||
|
||||
// 错误处理:记录错误并返回空结果
|
||||
if err != nil {
|
||||
log.Error("Parse error: ", err)
|
||||
return []Provider{}
|
||||
}
|
||||
|
||||
// 返回解析结果
|
||||
return providers
|
||||
}
|
||||
|
||||
// ParseFile parses a single Go source file and returns discovered providers
|
||||
// ParseFile 单文件解析的核心方法 - 详细解析 Go 源文件中的 Provider 注解
|
||||
//
|
||||
// 执行流程图:
|
||||
// ┌─────────────────────────────────────────────────────────────┐
|
||||
// │ ParseFile(source) │
|
||||
// ├─────────────────────────────────────────────────────────────┤
|
||||
// │ 1. 文件过滤:shouldProcessFile() │
|
||||
// │ 2. AST解析:parser.ParseFile() │
|
||||
// │ 3. 创建上下文:NewParserContext() │
|
||||
// │ 4. 导入解析:ResolveFileImports() │
|
||||
// │ 5. 构建上下文:BuilderContext{...} │
|
||||
// │ 6. 注解发现:ProviderDiscoveryVisitor │
|
||||
// │ 7. AST遍历:astWalker.WalkFile() │
|
||||
// │ 8. Provider构建:buildProviderFromDiscovery() │
|
||||
// │ 9. 验证检查:validator.Validate() │
|
||||
// │ 10. 日志记录:记录警告和错误 │
|
||||
// └─────────────────────────────────────────────────────────────┘
|
||||
//
|
||||
// 详细步骤说明:
|
||||
// 步骤 1-2:文件预处理
|
||||
// - 检查文件是否应该被处理(跳过测试文件和生成文件)
|
||||
// - 使用 Go 标准库解析文件为 AST
|
||||
//
|
||||
// 步骤 3-5:上下文准备
|
||||
// - 创建解析器上下文,包含工作目录和模块名
|
||||
// - 解析文件的所有导入信息,建立包名映射
|
||||
// - 创建构建器上下文,包含解析所需的所有信息
|
||||
//
|
||||
// 步骤 6-7:注解发现
|
||||
// - 创建专门的访问器来发现 @provider 注解
|
||||
// - 遍历 AST,收集所有带有 @provider 注解的结构体
|
||||
//
|
||||
// 步骤 8-9:Provider 构建
|
||||
// - 对每个发现的注解,构建完整的 Provider 对象
|
||||
// - 如果启用严格模式,验证 Provider 配置的正确性
|
||||
//
|
||||
// 步骤 10:结果处理
|
||||
// - 记录解析过程中的所有警告和错误
|
||||
// - 返回构建成功的 Provider 列表
|
||||
func (p *MainParser) ParseFile(source string) ([]Provider, error) {
|
||||
// Check if file should be processed
|
||||
// === 步骤 1:文件过滤 ===
|
||||
// 检查文件是否应该被处理,跳过测试文件和生成文件
|
||||
if !p.shouldProcessFile(source) {
|
||||
return []Provider{}, nil
|
||||
}
|
||||
|
||||
// Parse the AST
|
||||
// === 步骤 2:AST 解析 ===
|
||||
// 使用 Go 标准库将源文件解析为抽象语法树
|
||||
fset := token.NewFileSet()
|
||||
node, err := parser.ParseFile(fset, source, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse file %s: %w", source, err)
|
||||
}
|
||||
|
||||
// Create parser context
|
||||
// === 步骤 3:创建解析器上下文 ===
|
||||
// 创建包含工作目录和模块信息的上下文
|
||||
context := NewParserContext(p.config)
|
||||
context.WorkingDir = filepath.Dir(source)
|
||||
context.ModuleName = gomod.GetModuleName()
|
||||
|
||||
// Resolve imports
|
||||
// === 步骤 4:导入解析 ===
|
||||
// 解析文件的所有导入信息,建立包名到路径的映射
|
||||
importContext, err := p.importResolver.ResolveFileImports(node, source)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to resolve imports: %w", err)
|
||||
}
|
||||
|
||||
// Create builder context
|
||||
// === 步骤 5:创建构建器上下文 ===
|
||||
// 创建包含解析所需所有信息的构建器上下文
|
||||
builderContext := &BuilderContext{
|
||||
FilePath: source,
|
||||
PackageName: node.Name.Name,
|
||||
ImportContext: importContext,
|
||||
ASTFile: node,
|
||||
ProcessedTypes: make(map[string]bool),
|
||||
Errors: make([]error, 0),
|
||||
Warnings: make([]string, 0),
|
||||
FilePath: source, // 当前文件路径
|
||||
PackageName: node.Name.Name, // 包名
|
||||
ImportContext: importContext, // 导入信息上下文
|
||||
ASTFile: node, // AST 节点
|
||||
ProcessedTypes: make(map[string]bool), // 已处理的类型,避免重复
|
||||
Errors: make([]error, 0), // 错误列表
|
||||
Warnings: make([]string, 0), // 警告列表
|
||||
}
|
||||
|
||||
// Use AST walker to find provider annotations
|
||||
// === 步骤 6:创建注解发现访问器 ===
|
||||
// 创建专门的访问器来发现 @provider 注解
|
||||
visitor := NewProviderDiscoveryVisitor(p.commentParser)
|
||||
p.astWalker.AddVisitor(visitor)
|
||||
|
||||
// Walk the AST
|
||||
// === 步骤 7:AST 遍历 ===
|
||||
// 遍历 AST,发现所有带有 @provider 注解的结构体
|
||||
if err := p.astWalker.WalkFile(source); err != nil {
|
||||
return nil, fmt.Errorf("failed to walk AST: %w", err)
|
||||
}
|
||||
|
||||
// Build providers from discovered annotations
|
||||
// === 步骤 8:Provider 构建和验证 ===
|
||||
// 初始化结果列表
|
||||
providers := make([]Provider, 0)
|
||||
discoveredProviders := visitor.GetProviders()
|
||||
|
||||
// 对每个发现的注解构建 Provider 对象
|
||||
for _, discoveredProvider := range discoveredProviders {
|
||||
// Find the corresponding AST node for this provider
|
||||
// 查找对应的 AST 节点
|
||||
provider, err := p.buildProviderFromDiscovery(discoveredProvider, node, builderContext)
|
||||
if err != nil {
|
||||
context.AddError(source, 0, 0, fmt.Sprintf("failed to build provider %s: %v", discoveredProvider.StructName, err), "error")
|
||||
continue
|
||||
}
|
||||
|
||||
// Validate the provider if enabled
|
||||
// 如果启用严格模式,验证 Provider 配置
|
||||
if p.config.StrictMode {
|
||||
if err := p.validator.Validate(&provider); err != nil {
|
||||
context.AddError(source, 0, 0, fmt.Sprintf("validation failed for provider %s: %v", provider.StructName, err), "error")
|
||||
@@ -136,10 +239,12 @@ func (p *MainParser) ParseFile(source string) ([]Provider, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 添加到结果列表
|
||||
providers = append(providers, provider)
|
||||
}
|
||||
|
||||
// Log any warnings or errors
|
||||
// === 步骤 9:日志记录 ===
|
||||
// 记录解析过程中的所有警告和错误
|
||||
for _, parseErr := range context.GetErrors("warning") {
|
||||
log.Warnf("Warning while parsing %s: %s", source, parseErr.Message)
|
||||
}
|
||||
@@ -147,6 +252,7 @@ func (p *MainParser) ParseFile(source string) ([]Provider, error) {
|
||||
log.Errorf("Error while parsing %s: %s", source, parseErr.Message)
|
||||
}
|
||||
|
||||
// 返回构建成功的 Provider 列表
|
||||
return providers, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -17,65 +17,240 @@ import (
|
||||
"go.ipao.vip/atomctl/v2/pkg/utils/gomod"
|
||||
)
|
||||
|
||||
// Parser defines the interface for parsing provider annotations
|
||||
// Parser 定义了解析器接口,为 Provider 注解解析提供统一的抽象层
|
||||
//
|
||||
// 接口设计原则:
|
||||
// ┌─────────────────────────────────────────────────────────────┐
|
||||
// │ Parser Interface │
|
||||
// ├─────────────────────────────────────────────────────────────┤
|
||||
// │ 核心功能: │
|
||||
// │ - ParseFile: 单文件解析 │
|
||||
// │ - ParseDir: 目录解析 │
|
||||
// │ - ParseString: 字符串解析 │
|
||||
// │ 配置管理: │
|
||||
// │ - SetConfig/GetConfig: 配置设置和获取 │
|
||||
// │ - GetContext: 获取解析上下文 │
|
||||
// └─────────────────────────────────────────────────────────────┘
|
||||
//
|
||||
// 设计理念:
|
||||
// - 接口隔离:只包含必要的方法,避免过度设计
|
||||
// - 扩展性:支持不同的解析器实现
|
||||
// - 配置化:支持运行时配置修改
|
||||
// - 上下文感知:提供解析过程的上下文信息
|
||||
type Parser interface {
|
||||
// ParseFile parses a single Go file and returns providers found
|
||||
// ParseFile 解析单个 Go 文件并返回发现的 Provider 列表
|
||||
//
|
||||
// 参数:
|
||||
// - filePath: Go 源文件路径
|
||||
//
|
||||
// 返回值:
|
||||
// - []Provider: 解析到的 Provider 列表
|
||||
// - error: 解析过程中的错误(如文件不存在、语法错误等)
|
||||
//
|
||||
// 使用场景:
|
||||
// - 需要解析单个文件的 Provider 注解
|
||||
// - 精确控制要解析的文件
|
||||
// - 集成到构建工具中
|
||||
ParseFile(filePath string) ([]Provider, error)
|
||||
|
||||
// ParseDir parses all Go files in a directory and returns providers found
|
||||
// ParseDir 解析目录中的所有 Go 文件并返回发现的 Provider 列表
|
||||
//
|
||||
// 执行流程:
|
||||
// 1. 遍历目录中的所有文件
|
||||
// 2. 过滤出 Go 源文件(.go 后缀)
|
||||
// 3. 跳过测试文件(_test.go)和隐藏目录
|
||||
// 4. 对每个文件调用 ParseFile
|
||||
// 5. 汇总所有文件的 Provider 结果
|
||||
//
|
||||
// 参数:
|
||||
// - dirPath: 要解析的目录路径
|
||||
//
|
||||
// 返回值:
|
||||
// - []Provider: 目录中所有文件的 Provider 列表
|
||||
// - error: 目录遍历或解析过程中的错误
|
||||
//
|
||||
// 使用场景:
|
||||
// - 批量解析整个项目的 Provider
|
||||
// - 代码生成工具的输入
|
||||
// - 项目分析工具
|
||||
ParseDir(dirPath string) ([]Provider, error)
|
||||
|
||||
// ParseString parses Go code from a string and returns providers found
|
||||
// ParseString 从字符串解析 Go 代码并返回发现的 Provider 列表
|
||||
//
|
||||
// 参数:
|
||||
// - code: Go 源代码字符串
|
||||
//
|
||||
// 返回值:
|
||||
// - []Provider: 解析到的 Provider 列表
|
||||
// - error: 解析过程中的错误
|
||||
//
|
||||
// 使用场景:
|
||||
// - 动态生成的代码解析
|
||||
// - IDE 插件的实时分析
|
||||
// - 单元测试中的模拟数据
|
||||
ParseString(code string) ([]Provider, error)
|
||||
|
||||
// SetConfig sets the parser configuration
|
||||
// SetConfig 设置解析器配置
|
||||
//
|
||||
// 支持的配置项:
|
||||
// - StrictMode: 严格模式,启用更严格的验证
|
||||
// - CacheEnabled: 启用解析结果缓存
|
||||
// - SourceLocations: 包含源码位置信息
|
||||
// - Mode: 解析模式(带注释、不带注释等)
|
||||
//
|
||||
// 参数:
|
||||
// - config: 新的解析器配置
|
||||
//
|
||||
// 使用场景:
|
||||
// - 根据不同环境调整解析行为
|
||||
// - 性能优化时启用缓存
|
||||
// - 调试时启用更详细的信息
|
||||
SetConfig(config *ParserConfig)
|
||||
|
||||
// GetConfig returns the current parser configuration
|
||||
// GetConfig 获取当前的解析器配置
|
||||
//
|
||||
// 返回值:
|
||||
// - *ParserConfig: 当前配置的副本
|
||||
//
|
||||
// 使用场景:
|
||||
// - 检查当前配置状态
|
||||
// - 保存和恢复配置
|
||||
// - 调试和诊断
|
||||
GetConfig() *ParserConfig
|
||||
|
||||
// GetContext returns the current parser context
|
||||
// GetContext 获取当前的解析器上下文
|
||||
//
|
||||
// 返回值:
|
||||
// - *ParserContext: 包含解析过程中的状态信息
|
||||
// - FilesProcessed: 已处理的文件数量
|
||||
// - FilesSkipped: 跳过的文件数量
|
||||
// - ProvidersFound: 发现的 Provider 数量
|
||||
// - Cache: 缓存的解析结果
|
||||
// - Imports: 导入信息映射
|
||||
//
|
||||
// 使用场景:
|
||||
// - 监控解析进度
|
||||
// - 性能分析
|
||||
// - 调试解析问题
|
||||
GetContext() *ParserContext
|
||||
}
|
||||
|
||||
// GoParser implements the Parser interface for Go source files
|
||||
// GoParser Parser 接口的具体实现,专门用于解析 Go 源文件中的 Provider 注解
|
||||
//
|
||||
// 架构设计:
|
||||
// ┌─────────────────────────────────────────────────────────────┐
|
||||
// │ GoParser │
|
||||
// ├─────────────────────────────────────────────────────────────┤
|
||||
// │ config: *ParserConfig - 解析器配置 │
|
||||
// │ context: *ParserContext - 解析上下文 │
|
||||
// │ mu: sync.RWMutex - 读写锁,保证并发安全 │
|
||||
// └─────────────────────────────────────────────────────────────┘
|
||||
//
|
||||
// 核心特性:
|
||||
// - 线程安全:使用读写锁保护共享状态
|
||||
// - 缓存支持:可选的解析结果缓存
|
||||
// - 并发处理:支持多文件并发解析
|
||||
// - 错误恢复:单个文件解析失败不影响其他文件
|
||||
//
|
||||
// 适用场景:
|
||||
// - 大型项目的批量 Provider 解析
|
||||
// - 需要高性能的代码生成工具
|
||||
// - 需要线程安全的解析环境
|
||||
type GoParser struct {
|
||||
config *ParserConfig
|
||||
context *ParserContext
|
||||
mu sync.RWMutex
|
||||
config *ParserConfig // 解析器配置,控制解析行为
|
||||
context *ParserContext // 解析上下文,包含解析状态信息
|
||||
mu sync.RWMutex // 读写锁,保护 config 和 context 的并发访问
|
||||
}
|
||||
|
||||
// NewGoParser creates a new GoParser with default configuration
|
||||
// NewGoParser 创建一个使用默认配置的 GoParser 实例
|
||||
//
|
||||
// 初始化流程:
|
||||
// ┌─────────────────────────────────────────────────────────────┐
|
||||
// │ NewGoParser() │
|
||||
// ├─────────────────────────────────────────────────────────────┤
|
||||
// │ 1. 创建默认配置:NewParserConfig() │
|
||||
// │ 2. 创建解析上下文:NewParserContext() │
|
||||
// │ 3. 初始化文件集:token.NewFileSet() │
|
||||
// │ 4. 构建解析器实例:&GoParser{...} │
|
||||
// └─────────────────────────────────────────────────────────────┘
|
||||
//
|
||||
// 默认配置特点:
|
||||
// - 解析模式:带注释解析 (parser.ParseComments)
|
||||
// - 缓存:默认关闭
|
||||
// - 源码位置:默认关闭
|
||||
// - 严格模式:默认关闭
|
||||
//
|
||||
// 返回值:
|
||||
// - *GoParser: 配置好的解析器实例,可以直接使用
|
||||
//
|
||||
// 使用示例:
|
||||
// parser := NewGoParser()
|
||||
// providers, err := parser.ParseFile("user_service.go")
|
||||
func NewGoParser() *GoParser {
|
||||
// 创建默认配置
|
||||
config := NewParserConfig()
|
||||
|
||||
// 创建解析上下文
|
||||
context := NewParserContext(config)
|
||||
|
||||
// Initialize file set if not provided
|
||||
// 初始化文件集(用于记录源码位置信息)
|
||||
if config.FileSet == nil {
|
||||
config.FileSet = token.NewFileSet()
|
||||
context.FileSet = config.FileSet
|
||||
}
|
||||
|
||||
// 构建并返回解析器实例
|
||||
return &GoParser{
|
||||
config: config,
|
||||
context: context,
|
||||
}
|
||||
}
|
||||
|
||||
// NewGoParserWithConfig creates a new GoParser with custom configuration
|
||||
// NewGoParserWithConfig 使用自定义配置创建 GoParser 实例
|
||||
//
|
||||
// 设计目标:
|
||||
// - 提供灵活的配置选项
|
||||
// - 支持不同的使用场景(调试、生产、测试)
|
||||
// - 保持向后兼容性
|
||||
//
|
||||
// 参数处理:
|
||||
// - 如果 config 为 nil,自动使用默认配置
|
||||
// - 如果 config.FileSet 为 nil,自动创建新的文件集
|
||||
//
|
||||
// 自定义配置场景:
|
||||
// - 调试模式:启用详细日志和源码位置
|
||||
// - 性能优化:启用缓存和并发解析
|
||||
// - 严格验证:启用严格模式进行代码质量检查
|
||||
//
|
||||
// 参数:
|
||||
// - config: 自定义的解析器配置(可为 nil)
|
||||
//
|
||||
// 返回值:
|
||||
// - *GoParser: 使用指定配置的解析器实例
|
||||
//
|
||||
// 使用示例:
|
||||
// config := &ParserConfig{
|
||||
// CacheEnabled: true,
|
||||
// StrictMode: true,
|
||||
// SourceLocations: true,
|
||||
// }
|
||||
// parser := NewGoParserWithConfig(config)
|
||||
func NewGoParserWithConfig(config *ParserConfig) *GoParser {
|
||||
// 处理 nil 配置,保持向后兼容
|
||||
if config == nil {
|
||||
return NewGoParser()
|
||||
}
|
||||
|
||||
// 使用自定义配置创建解析上下文
|
||||
context := NewParserContext(config)
|
||||
|
||||
// Initialize file set if not provided
|
||||
// 初始化文件集(如果未提供)
|
||||
if config.FileSet == nil {
|
||||
config.FileSet = token.NewFileSet()
|
||||
context.FileSet = config.FileSet
|
||||
}
|
||||
|
||||
// 构建并返回解析器实例
|
||||
return &GoParser{
|
||||
config: config,
|
||||
context: context,
|
||||
|
||||
@@ -14,6 +14,23 @@ import (
|
||||
"go.ipao.vip/atomctl/v2/pkg/utils/gomod"
|
||||
)
|
||||
|
||||
// ================================================================================================
|
||||
// 原有的 Parse 函数 - 保持向后兼容性
|
||||
// ================================================================================================
|
||||
//
|
||||
// 注意:这是重构前的原始解析函数,现在保持向后兼容性。
|
||||
// 新代码应该使用:
|
||||
// - ParseRefactored() - 简化的单文件解析
|
||||
// - NewParser().ParseFile() - 完整的解析功能
|
||||
// - NewGoParser().ParseDir() - 目录解析功能
|
||||
//
|
||||
// 原始函数的缺点:
|
||||
// - 单体设计:所有逻辑集中在一个函数中
|
||||
// - 难以测试:无法单独测试各个功能模块
|
||||
// - 扩展困难:添加新功能需要修改核心函数
|
||||
// - 错误处理简单:缺乏详细的错误信息和上下文
|
||||
// ================================================================================================
|
||||
|
||||
func getTypePkgName(typ string) string {
|
||||
if strings.Contains(typ, ".") {
|
||||
return strings.Split(typ, ".")[0]
|
||||
@@ -48,22 +65,65 @@ func atomPackage(suffix string) string {
|
||||
return root
|
||||
}
|
||||
|
||||
// Parse 原始的 Provider 解析函数 - 保持向后兼容性
|
||||
//
|
||||
// ⚠️ 警告:这是一个遗留函数,建议使用重构后的版本:
|
||||
// - 简单使用:ParseRefactored(source)
|
||||
// - 完整功能:NewParser().ParseFile(source)
|
||||
// - 目录解析:NewGoParser().ParseDir(dir)
|
||||
//
|
||||
// 执行流程(原始版本):
|
||||
// ┌─────────────────────────────────────────────────────────────┐
|
||||
// │ Parse(source) │
|
||||
// ├─────────────────────────────────────────────────────────────┤
|
||||
// │ 1. 文件过滤:跳过测试文件和生成文件 │
|
||||
// │ 2. AST解析:使用标准库解析 Go 文件 │
|
||||
// │ 3. 导入处理:构建导入映射表 │
|
||||
// │ 4. 遍历声明:查找带有 @provider 注解的结构体 │
|
||||
// │ 5. 注解解析:解析 @provider 语法 │
|
||||
// │ 6. 字段处理:处理结构体字段和注入参数 │
|
||||
// │ 7. 模式应用:根据模式应用特定逻辑 │
|
||||
// │ 8. 结果收集:收集所有有效的 Provider │
|
||||
// └─────────────────────────────────────────────────────────────┘
|
||||
//
|
||||
// 参数:
|
||||
// - source: Go 源文件路径
|
||||
//
|
||||
// 返回值:
|
||||
// - []Provider: 解析到的 Provider 列表(解析失败时为 nil)
|
||||
//
|
||||
// 缺点:
|
||||
// - 错误处理不完善:解析失败时返回 nil,丢失错误信息
|
||||
// - 单体设计:所有逻辑集中在一个函数中,难以维护
|
||||
// - 缺乏扩展性:添加新功能需要修改核心函数
|
||||
// - 性能问题:没有缓存和优化机制
|
||||
//
|
||||
// 兼容性说明:
|
||||
// - 保持原有接口不变
|
||||
// - 现有调用代码可以继续工作
|
||||
// - 建议逐步迁移到新版本
|
||||
func Parse(source string) []Provider {
|
||||
// === 步骤 1:文件过滤 ===
|
||||
// 跳过测试文件(_test.go 后缀)
|
||||
if strings.HasSuffix(source, "_test.go") {
|
||||
return []Provider{}
|
||||
}
|
||||
|
||||
// 跳过生成的 provider 文件(避免循环解析)
|
||||
if strings.HasSuffix(source, "/provider.gen.go") {
|
||||
return []Provider{}
|
||||
}
|
||||
|
||||
// 初始化结果列表
|
||||
providers := []Provider{}
|
||||
|
||||
// === 步骤 2:AST 解析 ===
|
||||
// 使用 Go 标准库将源文件解析为抽象语法树
|
||||
fset := token.NewFileSet()
|
||||
node, err := parser.ParseFile(fset, source, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
log.Error("ERR: ", err)
|
||||
return nil
|
||||
return nil // 原始版本在错误时返回 nil
|
||||
}
|
||||
imports := make(map[string]string)
|
||||
for _, imp := range node.Imports {
|
||||
@@ -336,14 +396,38 @@ func (p ProviderDescribe) String() {
|
||||
// log.Infof("[%s] %s => ONLY: %+v, EXCEPT: %+v, Type: %s, Group: %s", source, declType.Name.Name, onlyMode, exceptMode, provider.ReturnType, provider.ProviderGroup)
|
||||
}
|
||||
|
||||
// @provider
|
||||
// @provider(job)
|
||||
// @provider(job):except
|
||||
// @provider:except
|
||||
// @provider:only
|
||||
// @provider returnType
|
||||
// @provider returnType group
|
||||
// @provider(job) returnType group
|
||||
// parseProvider 解析 @provider 注解的语法
|
||||
//
|
||||
// 支持的语法格式:
|
||||
// @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. 解析返回类型和分组(剩余部分)
|
||||
//
|
||||
// 参数:
|
||||
// - doc: @provider 注解字符串
|
||||
//
|
||||
// 返回值:
|
||||
// - ProviderDescribe: 解析后的注解信息
|
||||
//
|
||||
// 示例:
|
||||
// 输入: "@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}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user