mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-08-17 19:47:10 +08:00
342 lines
10 KiB
Go
342 lines
10 KiB
Go
// generator.go 计算跨包依赖并为各协议包准备完整声明集合。
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
// generateProtos 按协议包聚合声明并生成对应文件。
|
|
func generateProtos(messages []Message, enums []Enum, services []Service, resolver *TypeResolver, outputDir string) {
|
|
os.MkdirAll(outputDir, 0755)
|
|
|
|
// 按协议包聚合声明。
|
|
packages := make(map[string]struct {
|
|
messages []Message
|
|
enums []Enum
|
|
services []Service
|
|
})
|
|
|
|
for _, msg := range messages {
|
|
pkg := packages[msg.Package]
|
|
pkg.messages = append(pkg.messages, msg)
|
|
packages[msg.Package] = pkg
|
|
}
|
|
|
|
for _, enum := range enums {
|
|
pkg := packages[enum.Package]
|
|
pkg.enums = append(pkg.enums, enum)
|
|
packages[enum.Package] = pkg
|
|
}
|
|
|
|
for _, svc := range services {
|
|
pkg := packages[svc.Package]
|
|
pkg.services = append(pkg.services, svc)
|
|
packages[svc.Package] = pkg
|
|
}
|
|
|
|
// 建立跨包复制使用的全局类型索引。
|
|
allMessages := make(map[string]*Message)
|
|
allEnums := make(map[string]*Enum)
|
|
|
|
for pkgName, pkg := range packages {
|
|
if isGooglePkg(pkgName) {
|
|
continue
|
|
}
|
|
for i := range pkg.messages {
|
|
msg := &pkg.messages[i]
|
|
allMessages[msg.TypeName] = msg
|
|
}
|
|
for i := range pkg.enums {
|
|
enum := &pkg.enums[i]
|
|
allEnums[enum.TypeName] = enum
|
|
}
|
|
}
|
|
|
|
// 每轮生成前重置已复制类型索引。
|
|
copiedTypes = make(map[string]map[string]string)
|
|
|
|
for pkgName, pkg := range packages {
|
|
// Google 标准包直接使用官方协议文件。
|
|
if isGooglePkg(pkgName) {
|
|
fmt.Printf("跳过: %s (使用官方 proto 文件)\n", pkgName)
|
|
continue
|
|
}
|
|
|
|
// 把当前包引用的外部类型复制到本地。
|
|
augmentedPkg := copyAllExternalTypes(pkgName, pkg, resolver, allMessages, allEnums)
|
|
generateProtoFile(pkgName, augmentedPkg.messages, augmentedPkg.enums, pkg.services, resolver, outputDir)
|
|
}
|
|
}
|
|
|
|
// copyAllExternalTypes 递归复制当前包引用的全部外部类型。
|
|
func copyAllExternalTypes(pkgName string, pkg struct {
|
|
messages []Message
|
|
enums []Enum
|
|
services []Service
|
|
}, resolver *TypeResolver, allMessages map[string]*Message, allEnums map[string]*Enum) struct {
|
|
messages []Message
|
|
enums []Enum
|
|
services []Service
|
|
} {
|
|
if copiedTypes[pkgName] == nil {
|
|
copiedTypes[pkgName] = make(map[string]string)
|
|
}
|
|
|
|
// 建立当前包已有类型集合,并登记本地名称供字段解析使用。
|
|
localTypes := make(map[string]bool)
|
|
for _, msg := range pkg.messages {
|
|
localTypes[msg.ShortName] = true
|
|
// 空来源名表示该类型原本就在当前包。
|
|
if copiedTypes[pkgName][msg.ShortName] == "" {
|
|
copiedTypes[pkgName][msg.ShortName] = "local:" + msg.TypeName
|
|
}
|
|
}
|
|
for _, enum := range pkg.enums {
|
|
localTypes[enum.ShortName] = true
|
|
if copiedTypes[pkgName][enum.ShortName] == "" {
|
|
copiedTypes[pkgName][enum.ShortName] = "local:" + enum.TypeName
|
|
}
|
|
}
|
|
|
|
// 结果先保留当前包原始声明。
|
|
result := struct {
|
|
messages []Message
|
|
enums []Enum
|
|
services []Service
|
|
}{
|
|
messages: append([]Message{}, pkg.messages...),
|
|
enums: append([]Enum{}, pkg.enums...),
|
|
services: pkg.services,
|
|
}
|
|
|
|
totalCopied := 0
|
|
|
|
// 持续迭代,直到不再发现新的外部依赖。
|
|
for round := 1; ; round++ {
|
|
// 收集当前消息中的外部类型引用。
|
|
neededTypes := make(map[string]bool)
|
|
|
|
for _, msg := range result.messages {
|
|
preferredPkg, _ := parseTypeName(msg.TypeName)
|
|
for _, f := range msg.Fields {
|
|
collectFieldRefsSimple(f, pkgName, preferredPkg, msg.Pos, msg.ModuleStart, resolver, neededTypes, localTypes)
|
|
}
|
|
}
|
|
for _, svc := range result.services {
|
|
for _, m := range svc.Methods {
|
|
collectMethodRefsSimple(m.InputType, pkgName, svc.Pos, svc.ModuleStart, resolver, neededTypes, localTypes)
|
|
collectMethodRefsSimple(m.OutputType, pkgName, svc.Pos, svc.ModuleStart, resolver, neededTypes, localTypes)
|
|
}
|
|
}
|
|
|
|
// 复制本轮新增依赖类型。
|
|
copiedThisRound := 0
|
|
for typeName := range neededTypes {
|
|
refPkg, shortName := parseTypeName(typeName)
|
|
if refPkg == pkgName || isGooglePkg(refPkg) {
|
|
continue
|
|
}
|
|
|
|
// 已存在于本地时无需重复复制。
|
|
if localTypes[shortName] {
|
|
continue
|
|
}
|
|
|
|
// 复制消息声明。
|
|
if msg, ok := allMessages[typeName]; ok {
|
|
msgCopy := *msg
|
|
msgCopy.Package = pkgName
|
|
// 保留原始完整类型名,用于生成来源注释。
|
|
result.messages = append(result.messages, msgCopy)
|
|
copiedTypes[pkgName][shortName] = typeName // 保存原始完整类型名。
|
|
localTypes[shortName] = true
|
|
copiedThisRound++
|
|
fmt.Printf(" [%s] 轮%d 复制: %s\n", pkgName, round, typeName)
|
|
} else if enum, ok := allEnums[typeName]; ok {
|
|
// 复制枚举声明。
|
|
enumCopy := *enum
|
|
enumCopy.Package = pkgName
|
|
result.enums = append(result.enums, enumCopy)
|
|
copiedTypes[pkgName][shortName] = typeName
|
|
localTypes[shortName] = true
|
|
copiedThisRound++
|
|
fmt.Printf(" [%s] 轮%d 复制枚举: %s\n", pkgName, round, typeName)
|
|
} else {
|
|
// 未找到声明时仍登记本地引用,兼容提取结果缺少但 bundle 实际存在的类型。
|
|
copiedTypes[pkgName][shortName] = typeName
|
|
localTypes[shortName] = true
|
|
fmt.Printf(" [%s] 轮%d 警告: 类型未找到 %s,标记为本地引用\n", pkgName, round, typeName)
|
|
}
|
|
}
|
|
|
|
totalCopied += copiedThisRound
|
|
|
|
if copiedThisRound == 0 {
|
|
break // 没有新增依赖时结束迭代。
|
|
}
|
|
|
|
if round > 20 {
|
|
fmt.Printf(" [%s] 警告: 复制轮次超过20,可能存在问题\n", pkgName)
|
|
break
|
|
}
|
|
}
|
|
|
|
if totalCopied > 0 {
|
|
fmt.Printf(" [%s] 共复制 %d 个外部类型\n", pkgName, totalCopied)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// collectFieldRefsSimple 收集单个字段直接引用的外部类型。
|
|
func collectFieldRefsSimple(f Field, currentPkg string, preferredPkg string, contextPos int, contextModuleStart int, resolver *TypeResolver,
|
|
neededTypes map[string]bool, localTypes map[string]bool) {
|
|
|
|
type refWithKind struct {
|
|
ref string
|
|
kind string
|
|
}
|
|
|
|
var refs []refWithKind
|
|
if f.Kind == "message" || f.Kind == "enum" {
|
|
if v, ok := f.T.(string); ok {
|
|
refs = append(refs, refWithKind{ref: v, kind: f.Kind})
|
|
}
|
|
}
|
|
if f.Kind == "map" && (f.MapValueKind == "message" || f.MapValueKind == "enum") {
|
|
if v, ok := f.MapValueT.(string); ok {
|
|
refs = append(refs, refWithKind{ref: v, kind: f.MapValueKind})
|
|
}
|
|
}
|
|
|
|
for _, item := range refs {
|
|
typeName, ok := resolver.ResolveTypeName(item.ref, contextPos, contextModuleStart, preferredPkg, item.kind)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
refPkg, shortName := parseTypeName(typeName)
|
|
if refPkg == "" || refPkg == currentPkg || isGooglePkg(refPkg) {
|
|
continue
|
|
}
|
|
|
|
// 已在当前包中的类型无需收集。
|
|
if localTypes[shortName] {
|
|
continue
|
|
}
|
|
|
|
neededTypes[typeName] = true
|
|
}
|
|
}
|
|
|
|
// collectMethodRefsSimple 收集服务方法输入或输出引用的外部类型。
|
|
func collectMethodRefsSimple(ref string, currentPkg string, contextPos int, contextModuleStart int, resolver *TypeResolver,
|
|
neededTypes map[string]bool, localTypes map[string]bool) {
|
|
|
|
typeName, ok := resolver.ResolveTypeName(ref, contextPos, contextModuleStart, currentPkg, "message")
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
refPkg, shortName := parseTypeName(typeName)
|
|
if refPkg == "" || refPkg == currentPkg || isGooglePkg(refPkg) {
|
|
return
|
|
}
|
|
|
|
if localTypes[shortName] {
|
|
return
|
|
}
|
|
|
|
neededTypes[typeName] = true
|
|
}
|
|
|
|
// copiedTypes 按目标包和短名称记录被复制类型的原始全限定名。
|
|
var copiedTypes = make(map[string]map[string]string)
|
|
|
|
// TypeNode 表示嵌套消息与枚举组成的类型树节点。
|
|
type TypeNode struct {
|
|
// Name 是当前嵌套层级的类型名。
|
|
Name string
|
|
// Message 保存当前节点的消息声明。
|
|
Message *Message
|
|
// Enum 保存当前节点的枚举声明。
|
|
Enum *Enum
|
|
// Children 保存下一层嵌套类型。
|
|
Children map[string]*TypeNode
|
|
}
|
|
|
|
// collectImports 只收集 Google 标准依赖,其余类型会复制到本地。
|
|
func collectImports(currentPkg string, messages []Message, services []Service, resolver *TypeResolver) map[string]bool {
|
|
imports := make(map[string]bool)
|
|
|
|
addImport := func(ref string, contextPos int, contextModuleStart int, expectedKind string) {
|
|
typeName, ok := resolver.ResolveTypeName(ref, contextPos, contextModuleStart, currentPkg, expectedKind)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
refPkg, shortName := parseTypeName(typeName)
|
|
// 仅导入 Google 标准类型。
|
|
if refPkg == "google.protobuf" {
|
|
var importFile string
|
|
switch shortName {
|
|
case "Struct", "Value", "ListValue", "NullValue":
|
|
importFile = "google/protobuf/struct.proto"
|
|
case "Timestamp":
|
|
importFile = "google/protobuf/timestamp.proto"
|
|
case "Duration":
|
|
importFile = "google/protobuf/duration.proto"
|
|
case "Any":
|
|
importFile = "google/protobuf/any.proto"
|
|
case "Empty":
|
|
importFile = "google/protobuf/empty.proto"
|
|
case "FieldMask":
|
|
importFile = "google/protobuf/field_mask.proto"
|
|
case "BoolValue", "BytesValue", "DoubleValue", "FloatValue",
|
|
"Int32Value", "Int64Value", "StringValue", "UInt32Value", "UInt64Value":
|
|
importFile = "google/protobuf/wrappers.proto"
|
|
default:
|
|
importFile = "google/protobuf/descriptor.proto"
|
|
}
|
|
imports[importFile] = true
|
|
} else if refPkg == "google.rpc" {
|
|
var importFile string
|
|
switch shortName {
|
|
case "Status":
|
|
importFile = "google/rpc/status.proto"
|
|
case "Code":
|
|
importFile = "google/rpc/code.proto"
|
|
default:
|
|
importFile = "google/rpc/status.proto"
|
|
}
|
|
imports[importFile] = true
|
|
}
|
|
}
|
|
|
|
for _, msg := range messages {
|
|
for _, f := range msg.Fields {
|
|
if f.Kind == "message" || f.Kind == "enum" {
|
|
if ref, ok := f.T.(string); ok {
|
|
addImport(ref, msg.Pos, msg.ModuleStart, f.Kind)
|
|
}
|
|
}
|
|
// map 值类型也可能引用标准包。
|
|
if f.Kind == "map" && (f.MapValueKind == "message" || f.MapValueKind == "enum") {
|
|
if ref, ok := f.MapValueT.(string); ok {
|
|
addImport(ref, msg.Pos, msg.ModuleStart, f.MapValueKind)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, svc := range services {
|
|
for _, m := range svc.Methods {
|
|
addImport(m.InputType, svc.Pos, svc.ModuleStart, "message")
|
|
addImport(m.OutputType, svc.Pos, svc.ModuleStart, "message")
|
|
}
|
|
}
|
|
|
|
return imports
|
|
}
|