diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 258ac67..e612451 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,6 +1,10 @@ name: Lint -on: [push] +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] jobs: lint: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3ebd241..0a2f984 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,4 +22,4 @@ jobs: version: latest args: release --clean env: - GITHUB_TOKEN: ${{ secrets.TOKEN }} + GITHUB_TOKEN: ${{ secrets.RELEASER_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 4d28f2f..3a8a19d 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -15,17 +15,6 @@ builds: - -X 'github.com/pubgo/funk/version.project=protobuild' - -X 'github.com/pubgo/funk/version.buildTime={{ .CommitDate }}' - -X 'github.com/pubgo/funk/version.commitID={{ .ShortCommit }}' - - main: ./cmd/protoc-gen-retag/main.go - id: protoc-gen-retag - binary: protoc-gen-retag - skip: false - goos: - - linux - - darwin - - windows - goarch: - - amd64 - - arm64 archives: - name_template: "{{ .Binary }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}" format: binary diff --git a/.version/VERSION b/.version/VERSION index a2588c5..8308b63 100644 --- a/.version/VERSION +++ b/.version/VERSION @@ -1 +1 @@ -v0.0.29 +v0.1.1 diff --git a/README.md b/README.md index 44b7202..d235f24 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,9 @@ - ⚙️ **Configuration-driven** - YAML-based project configuration - 📊 **Progress Display** - Visual progress bars and detailed error messages - 🗑️ **Cache Management** - Clean and manage dependency cache +- 🌐 **Web UI** - Visual configuration editor with proto file browser +- 🏥 **Environment Check** - Doctor command to diagnose development environment +- 🎯 **Project Initialization** - Quick project setup with templates ## Installation @@ -71,8 +74,14 @@ protobuild gen | `format -w` | Format and write changes to files | | `format --diff` | Show diff of formatting changes | | `format --builtin` | Use builtin formatter instead of buf | +| `web` | Start web-based configuration UI | +| `web --port 9090` | Start web UI on custom port | | `clean` | Clean dependency cache | | `clean --dry-run` | Show what would be cleaned without deleting | +| `init` | Initialize a new protobuild project | +| `init --template grpc` | Initialize with specific template (basic, grpc, minimal) | +| `doctor` | Check development environment and dependencies | +| `doctor --fix` | Auto-install missing Go plugins | | `version` | Show version information | ## Configuration @@ -238,6 +247,69 @@ protobuild format --builtin protobuild format -w proto/ api/ ``` +### Web Configuration UI + +```bash +# Start web UI on default port (8080) +protobuild web + +# Start web UI on custom port +protobuild web --port 9090 +``` + +The web interface provides: +- 📝 Visual configuration editor +- 📦 Dependency management +- 🔌 Plugin configuration +- 🚀 One-click build, lint, format operations +- 📄 Real-time YAML preview +- 📊 Project statistics dashboard +- 🔍 Proto file browser with syntax highlighting +- 📚 Configuration examples reference + +### Initialize New Project + +```bash +# Interactive initialization +protobuild init + +# Use specific template +protobuild init --template basic # Basic Go + gRPC project +protobuild init --template grpc # Full gRPC-Gateway project +protobuild init --template minimal # Minimal configuration + +# Specify output directory +protobuild init -o ./my-project +``` + +### Check Development Environment + +```bash +# Diagnose environment issues +protobuild doctor + +# Auto-install missing Go plugins +protobuild doctor --fix +``` + +Example output: +``` +🏥 Protobuild Doctor + + Checking development environment... + + ✅ protoc installed (v25.1) + ✅ protoc-gen-go installed + ✅ protoc-gen-go-grpc installed + ✅ buf installed (v1.28.1) + ✅ api-linter installed + ✅ go installed (go1.21.5) + ✅ Configuration protobuf.yaml found + ⚠️ Vendor directory not found (run 'protobuild vendor') + + ✅ Environment check passed! +``` + ### Force Vendor Update ```bash @@ -339,7 +411,11 @@ protobuild │ │ └── yaml_types.go # YAML type definitions │ ├── format/ # Proto file formatting (builtin) │ ├── formatcmd/ # Format command (buf integration) -│ └── linters/ # AIP linting rules +│ ├── linters/ # AIP linting rules +│ └── webcmd/ # Web configuration UI +│ ├── cmd.go # Web command entry +│ ├── server.go # HTTP server and API +│ └── templates/ # HTML templates (Alpine.js + Tailwind) └── internal/ ├── depresolver/ # Multi-source dependency resolver ├── modutil/ # Go module utilities @@ -355,6 +431,20 @@ protobuild - [Multi-Source Dependencies](./docs/MULTI_SOURCE_DEPS.md) - Design document for multi-source dependency resolution - [Design Document](./docs/DESIGN.md) - Architecture and design documentation +## Roadmap + +Upcoming features planned for future releases: + +| Feature | Description | Status | +|---------|-------------|--------| +| 🔗 **Dependency Graph** | Visualize proto file import dependencies | Planned | +| ⚠️ **Breaking Change Detection** | Detect incompatible changes between versions | Planned | +| 📚 **API Documentation Generator** | Auto-generate Markdown/HTML docs from proto comments | Planned | +| 🎭 **Mock Server** | Auto-start mock gRPC/HTTP server for testing | Planned | +| 📝 **Proto Templates** | Quick generation of common proto patterns (CRUD, pagination) | Planned | +| 📊 **Field Statistics** | Analyze field naming conventions and type distribution | Planned | +| ✏️ **Online Editor** | Edit proto files directly in Web UI | Planned | + ## License [MIT License](LICENSE) diff --git a/README_CN.md b/README_CN.md index de06146..645803b 100644 --- a/README_CN.md +++ b/README_CN.md @@ -17,6 +17,9 @@ - ⚙️ **配置驱动** - 基于 YAML 的项目配置 - 📊 **进度显示** - 可视化进度条和详细错误信息 - 🗑️ **缓存管理** - 清理和管理依赖缓存 +- 🌐 **Web 界面** - 可视化配置编辑器,支持 Proto 文件浏览 +- 🏥 **环境诊断** - Doctor 命令检查开发环境配置 +- 🎯 **项目初始化** - 快速项目设置,支持多种模板 ## 安装 @@ -72,8 +75,14 @@ protobuild gen | `format --diff` | 显示格式化差异 | | `format --exit-code` | 需要格式化时返回错误码(CI 适用)| | `format --builtin` | 使用内置格式化器 | +| `web` | 启动 Web 配置管理界面 | +| `web --port 9090` | 指定端口启动 Web 界面 | | `clean` | 清理依赖缓存 | | `clean --dry-run` | 预览将被清理的内容 | +| `init` | 初始化新的 protobuild 项目 | +| `init --template grpc` | 使用指定模板初始化(basic、grpc、minimal)| +| `doctor` | 检查开发环境和依赖配置 | +| `doctor --fix` | 自动安装缺失的 Go 插件 | | `version` | 显示版本信息 | ## 配置说明 @@ -239,6 +248,69 @@ protobuild format --builtin protobuild format -w proto/ api/ ``` +### Web 配置管理界面 + +```bash +# 在默认端口 (8080) 启动 Web 界面 +protobuild web + +# 在指定端口启动 Web 界面 +protobuild web --port 9090 +``` + +Web 界面提供: +- 📝 可视化配置编辑器 +- 📦 依赖管理 +- 🔌 插件配置 +- 🚀 一键执行构建、检查、格式化等操作 +- 📄 实时 YAML 配置预览 +- 📊 项目统计仪表盘 +- 🔍 Proto 文件浏览器(支持语法高亮) +- 📚 配置示例参考 + +### 初始化新项目 + +```bash +# 交互式初始化 +protobuild init + +# 使用指定模板 +protobuild init --template basic # 基础 Go + gRPC 项目 +protobuild init --template grpc # 完整 gRPC-Gateway 项目 +protobuild init --template minimal # 最小化配置 + +# 指定输出目录 +protobuild init -o ./my-project +``` + +### 检查开发环境 + +```bash +# 诊断环境问题 +protobuild doctor + +# 自动安装缺失的 Go 插件 +protobuild doctor --fix +``` + +输出示例: +``` +🏥 Protobuild Doctor + + 正在检查开发环境... + + ✅ protoc 已安装 (v25.1) + ✅ protoc-gen-go 已安装 + ✅ protoc-gen-go-grpc 已安装 + ✅ buf 已安装 (v1.28.1) + ✅ api-linter 已安装 + ✅ go 已安装 (go1.21.5) + ✅ 配置文件 已找到 protobuf.yaml + ⚠️ Vendor 目录 未找到(请运行 'protobuild vendor') + + ✅ 环境检查通过! +``` + ### 强制更新 Vendor ```bash @@ -330,6 +402,20 @@ plugins: - [多源依赖设计](./docs/MULTI_SOURCE_DEPS.md) - 多源依赖解析设计文档 - [设计文档](./docs/DESIGN_CN.md) - 架构和设计文档 +## 路线图 + +以下是计划在未来版本中实现的功能: + +| 功能 | 描述 | 状态 | +|------|------|------| +| 🔗 **依赖关系图** | 可视化 proto 文件的 import 依赖关系 | 计划中 | +| ⚠️ **Breaking Change 检测** | 检测版本间的不兼容变更 | 计划中 | +| 📚 **API 文档生成** | 从 proto 注释自动生成 Markdown/HTML 文档 | 计划中 | +| 🎭 **Mock 服务器** | 自动启动用于测试的 mock gRPC/HTTP 服务器 | 计划中 | +| 📝 **Proto 模板** | 快速生成常用 proto 模式(CRUD、分页等)| 计划中 | +| 📊 **字段统计分析** | 分析字段命名规范和类型分布 | 计划中 | +| ✏️ **在线编辑器** | 在 Web 界面直接编辑 proto 文件 | 计划中 | + ## 项目架构 ``` @@ -346,7 +432,11 @@ protobuild │ │ └── yaml_types.go # YAML 类型定义 │ ├── format/ # Proto 文件格式化(内置) │ ├── formatcmd/ # 格式化命令(buf 集成) -│ └── linters/ # AIP 检查规则 +│ ├── linters/ # AIP 检查规则 +│ └── webcmd/ # Web 配置管理界面 +│ ├── cmd.go # Web 命令入口 +│ ├── server.go # HTTP 服务器和 API +│ └── templates/ # HTML 模板 (Alpine.js + Tailwind) └── internal/ ├── depresolver/ # 多源依赖解析器 ├── modutil/ # Go 模块工具 diff --git a/Taskfile.yml b/Taskfile.yml index 9bc80ff..7ba7b60 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -128,3 +128,7 @@ tasks: - task: lint - task: test - task: build + web: + desc: Run web server + cmds: + - go run -v *.go web diff --git a/cmd/protobuild/cmd.go b/cmd/protobuild/cmd.go index 6da1d2e..f068b75 100644 --- a/cmd/protobuild/cmd.go +++ b/cmd/protobuild/cmd.go @@ -17,6 +17,7 @@ import ( "github.com/pubgo/funk/running" "github.com/pubgo/protobuild/cmd/formatcmd" "github.com/pubgo/protobuild/cmd/linters" + "github.com/pubgo/protobuild/cmd/webcmd" "github.com/pubgo/protobuild/internal/shutil" "github.com/pubgo/protobuild/internal/typex" "github.com/pubgo/redant" @@ -72,6 +73,8 @@ func Main(ver string) *redant.Command { }, Handler: handleStdinPlugin, Children: typex.Commands{ + newInitCommand(), + newDoctorCommand(), newGenCommand(), newVendorCommand(&force, &update), newInstallCommand(&force), @@ -79,6 +82,7 @@ func Main(ver string) *redant.Command { newFormatCommand(), newDepsCommand(), newCleanCommand(&dryRun), + webcmd.New(&protoCfg), newVersionCommand(), }, } @@ -201,7 +205,7 @@ func installPlugin(plg string, force bool) { slog.Error("command not found", slog.Any("name", plgName)) } - if err == nil && !globalCfg.changed && !force { + if err == nil && !globalCfg.Changed && !force { slog.Info("no changes", slog.Any("path", path)) return } @@ -256,7 +260,8 @@ func newLintCommand(cliArgs *linters.CliArgs, options typex.Options) *redant.Com } includes := lo.Uniq(append(globalCfg.Includes, globalCfg.Vendor)) - if err := linters.Linter(cliArgs, globalCfg.Linter, includes, protoFiles); err != nil { + linterCfg := toLinterConfig(globalCfg.Linter) + if err := linters.Linter(cliArgs, linterCfg, includes, protoFiles); err != nil { return err } } diff --git a/cmd/protobuild/cmd_doctor.go b/cmd/protobuild/cmd_doctor.go new file mode 100644 index 0000000..615478c --- /dev/null +++ b/cmd/protobuild/cmd_doctor.go @@ -0,0 +1,399 @@ +package protobuild + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/pubgo/funk/recovery" + "github.com/pubgo/protobuild/internal/typex" + "github.com/pubgo/redant" +) + +// checkItem represents a single environment check. +type checkItem struct { + Name string + Description string + Check func() checkResult +} + +// checkResult represents the result of a check. +type checkResult struct { + OK bool + Message string + Help string +} + +// newDoctorCommand creates the doctor command. +func newDoctorCommand() *redant.Command { + var fix bool + + return &redant.Command{ + Use: "doctor", + Short: "检查开发环境配置", + Options: typex.Options{ + redant.Option{ + Flag: "fix", + Description: "尝试自动修复问题", + Value: redant.BoolOf(&fix), + }, + }, + Handler: func(ctx context.Context, inv *redant.Invocation) error { + defer recovery.Exit() + return runDoctor(fix) + }, + } +} + +// runDoctor executes the doctor command logic. +func runDoctor(fix bool) error { + fmt.Println("🩺 Protobuild 环境检查") + fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + fmt.Println() + + checks := []checkItem{ + { + Name: "protoc", + Description: "Protocol Buffers 编译器", + Check: checkProtoc, + }, + { + Name: "protoc-gen-go", + Description: "Go Protobuf 插件", + Check: checkProtocGenGo, + }, + { + Name: "protoc-gen-go-grpc", + Description: "Go gRPC 插件", + Check: checkProtocGenGoGrpc, + }, + { + Name: "buf", + Description: "Buf CLI (可选,用于格式化)", + Check: checkBuf, + }, + { + Name: "api-linter", + Description: "API Linter (可选,用于代码检查)", + Check: checkApiLinter, + }, + { + Name: "go", + Description: "Go 编译器", + Check: checkGo, + }, + { + Name: "config", + Description: "项目配置文件", + Check: checkConfig, + }, + { + Name: "vendor", + Description: "Proto 依赖目录", + Check: checkVendor, + }, + } + + var issues []checkItem + var warnings []checkItem + + for _, item := range checks { + result := item.Check() + printCheckResult(item.Name, item.Description, result) + + if !result.OK { + if isRequired(item.Name) { + issues = append(issues, item) + } else { + warnings = append(warnings, item) + } + } + } + + fmt.Println() + fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + + if len(issues) == 0 && len(warnings) == 0 { + fmt.Println("✅ 所有检查通过!环境配置正确。") + return nil + } + + if len(issues) > 0 { + fmt.Printf("❌ 发现 %d 个问题需要修复:\n", len(issues)) + for _, item := range issues { + result := item.Check() + fmt.Printf(" • %s: %s\n", item.Name, result.Message) + if result.Help != "" { + fmt.Printf(" 💡 %s\n", result.Help) + } + } + } + + if len(warnings) > 0 { + fmt.Printf("⚠️ 发现 %d 个可选组件未安装:\n", len(warnings)) + for _, item := range warnings { + result := item.Check() + fmt.Printf(" • %s: %s\n", item.Name, result.Message) + if result.Help != "" { + fmt.Printf(" 💡 %s\n", result.Help) + } + } + } + + if fix && len(issues) > 0 { + fmt.Println("\n🔧 尝试自动修复...") + autoFix() + } + + return nil +} + +// printCheckResult prints a formatted check result. +func printCheckResult(name, desc string, result checkResult) { + status := "✅" + if !result.OK { + if isRequired(name) { + status = "❌" + } else { + status = "⚠️ " + } + } + + fmt.Printf("%s %-20s %s\n", status, name, result.Message) +} + +// isRequired returns true if the check is required (not optional). +func isRequired(name string) bool { + optional := map[string]bool{ + "buf": true, + "api-linter": true, + } + return !optional[name] +} + +// checkProtoc checks if protoc is installed. +func checkProtoc() checkResult { + path, err := exec.LookPath("protoc") + if err != nil { + return checkResult{ + OK: false, + Message: "未安装", + Help: getProtocInstallHelp(), + } + } + + // Get version + out, err := exec.Command("protoc", "--version").Output() + if err != nil { + return checkResult{OK: true, Message: fmt.Sprintf("已安装 (%s)", path)} + } + + version := strings.TrimSpace(string(out)) + return checkResult{OK: true, Message: version} +} + +// checkProtocGenGo checks if protoc-gen-go is installed. +func checkProtocGenGo() checkResult { + path, err := exec.LookPath("protoc-gen-go") + if err != nil { + return checkResult{ + OK: false, + Message: "未安装", + Help: "go install google.golang.org/protobuf/cmd/protoc-gen-go@latest", + } + } + + return checkResult{OK: true, Message: fmt.Sprintf("已安装 (%s)", path)} +} + +// checkProtocGenGoGrpc checks if protoc-gen-go-grpc is installed. +func checkProtocGenGoGrpc() checkResult { + path, err := exec.LookPath("protoc-gen-go-grpc") + if err != nil { + return checkResult{ + OK: false, + Message: "未安装", + Help: "go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest", + } + } + + return checkResult{OK: true, Message: fmt.Sprintf("已安装 (%s)", path)} +} + +// checkBuf checks if buf is installed. +func checkBuf() checkResult { + path, err := exec.LookPath("buf") + if err != nil { + return checkResult{ + OK: false, + Message: "未安装 (可选)", + Help: "go install github.com/bufbuild/buf/cmd/buf@latest", + } + } + + out, err := exec.Command("buf", "--version").Output() + if err != nil { + return checkResult{OK: true, Message: fmt.Sprintf("已安装 (%s)", path)} + } + + version := strings.TrimSpace(string(out)) + return checkResult{OK: true, Message: fmt.Sprintf("v%s", version)} +} + +// checkApiLinter checks if api-linter is installed. +func checkApiLinter() checkResult { + path, err := exec.LookPath("api-linter") + if err != nil { + return checkResult{ + OK: false, + Message: "未安装 (可选)", + Help: "go install github.com/googleapis/api-linter/cmd/api-linter@latest", + } + } + + return checkResult{OK: true, Message: fmt.Sprintf("已安装 (%s)", path)} +} + +// checkGo checks if Go is installed. +func checkGo() checkResult { + path, err := exec.LookPath("go") + if err != nil { + return checkResult{ + OK: false, + Message: "未安装", + Help: "请从 https://go.dev/dl/ 下载安装", + } + } + + out, err := exec.Command("go", "version").Output() + if err != nil { + return checkResult{OK: true, Message: fmt.Sprintf("已安装 (%s)", path)} + } + + // Extract version from "go version go1.21.0 darwin/amd64" + parts := strings.Split(string(out), " ") + if len(parts) >= 3 { + return checkResult{OK: true, Message: parts[2]} + } + + return checkResult{OK: true, Message: "已安装"} +} + +// checkConfig checks if project config file exists. +func checkConfig() checkResult { + if _, err := os.Stat(protoCfg); os.IsNotExist(err) { + return checkResult{ + OK: false, + Message: fmt.Sprintf("%s 不存在", protoCfg), + Help: "运行 'protobuild init' 初始化项目", + } + } + + // Try to parse config + if err := parseConfig(); err != nil { + return checkResult{ + OK: false, + Message: fmt.Sprintf("配置文件解析错误: %v", err), + Help: "检查 YAML 语法是否正确", + } + } + + return checkResult{OK: true, Message: fmt.Sprintf("已配置 (%s)", protoCfg)} +} + +// checkVendor checks if vendor directory exists and has dependencies. +func checkVendor() checkResult { + if globalCfg.Vendor == "" { + return checkResult{ + OK: false, + Message: "未配置 vendor 目录", + Help: "在配置文件中设置 vendor 字段", + } + } + + if _, err := os.Stat(globalCfg.Vendor); os.IsNotExist(err) { + return checkResult{ + OK: false, + Message: fmt.Sprintf("%s 目录不存在", globalCfg.Vendor), + Help: "运行 'protobuild vendor' 同步依赖", + } + } + + // Count proto files in vendor + count := 0 + filepath.Walk(globalCfg.Vendor, func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil + } + if !info.IsDir() && strings.HasSuffix(path, ".proto") { + count++ + } + return nil + }) + + if count == 0 { + return checkResult{ + OK: false, + Message: fmt.Sprintf("%s 目录为空", globalCfg.Vendor), + Help: "运行 'protobuild vendor' 同步依赖", + } + } + + return checkResult{OK: true, Message: fmt.Sprintf("%s (%d 个 proto 文件)", globalCfg.Vendor, count)} +} + +// getProtocInstallHelp returns platform-specific install instructions for protoc. +func getProtocInstallHelp() string { + switch runtime.GOOS { + case "darwin": + return "brew install protobuf" + case "linux": + return "apt install -y protobuf-compiler 或从 https://github.com/protocolbuffers/protobuf/releases 下载" + case "windows": + return "从 https://github.com/protocolbuffers/protobuf/releases 下载" + default: + return "从 https://github.com/protocolbuffers/protobuf/releases 下载" + } +} + +// autoFix attempts to automatically fix common issues. +func autoFix() { + // Check and install protoc-gen-go + if _, err := exec.LookPath("protoc-gen-go"); err != nil { + fmt.Println(" 安装 protoc-gen-go...") + cmd := exec.Command("go", "install", "google.golang.org/protobuf/cmd/protoc-gen-go@latest") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + fmt.Printf(" ❌ 安装失败: %v\n", err) + } else { + fmt.Println(" ✅ protoc-gen-go 安装成功") + } + } + + // Check and install protoc-gen-go-grpc + if _, err := exec.LookPath("protoc-gen-go-grpc"); err != nil { + fmt.Println(" 安装 protoc-gen-go-grpc...") + cmd := exec.Command("go", "install", "google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + fmt.Printf(" ❌ 安装失败: %v\n", err) + } else { + fmt.Println(" ✅ protoc-gen-go-grpc 安装成功") + } + } + + // Run vendor if needed + if globalCfg.Vendor != "" { + if _, err := os.Stat(globalCfg.Vendor); os.IsNotExist(err) { + fmt.Println(" 同步依赖...") + // This would need to call the vendor command + fmt.Println(" 💡 请手动运行 'protobuild vendor'") + } + } +} diff --git a/cmd/protobuild/cmd_init.go b/cmd/protobuild/cmd_init.go new file mode 100644 index 0000000..ea3a637 --- /dev/null +++ b/cmd/protobuild/cmd_init.go @@ -0,0 +1,288 @@ +package protobuild + +import ( + "bufio" + "context" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/pubgo/funk/recovery" + "github.com/pubgo/protobuild/internal/config" + "github.com/pubgo/protobuild/internal/typex" + "github.com/pubgo/redant" +) + +// initTemplates defines available project templates. +var initTemplates = map[string]*config.Config{ + "basic": { + Vendor: ".proto", + Root: []string{"proto"}, + Includes: []string{"proto"}, + Depends: []*config.Depend{ + {Name: "google/protobuf", Url: "https://github.com/protocolbuffers/protobuf", Path: "src/google/protobuf"}, + }, + Plugins: []*config.Plugin{ + {Name: "go"}, + {Name: "go-grpc"}, + }, + }, + "grpc-gateway": { + Vendor: ".proto", + BasePlugin: &config.BasePluginCfg{ + Out: "./pkg", + Paths: "import", + Module: "", + }, + Root: []string{"proto"}, + Includes: []string{"proto"}, + Depends: []*config.Depend{ + {Name: "google/protobuf", Url: "https://github.com/protocolbuffers/protobuf", Path: "src/google/protobuf"}, + {Name: "google/api", Url: "https://github.com/googleapis/googleapis", Path: "google/api"}, + {Name: "protoc-gen-openapiv2/options", Url: "https://github.com/grpc-ecosystem/grpc-gateway", Path: "protoc-gen-openapiv2/options"}, + }, + Plugins: []*config.Plugin{ + {Name: "go"}, + {Name: "go-grpc"}, + {Name: "grpc-gateway", Opts: config.PluginOpts{"generate_unbound_methods=true"}}, + {Name: "openapiv2", Out: "./docs/swagger"}, + }, + Installers: []string{ + "go install google.golang.org/protobuf/cmd/protoc-gen-go@latest", + "go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest", + "go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest", + "go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest", + }, + }, + "minimal": { + Vendor: ".proto", + Root: []string{"proto"}, + Includes: []string{"proto"}, + Plugins: []*config.Plugin{ + {Name: "go"}, + }, + }, +} + +// newInitCommand creates the init command. +func newInitCommand() *redant.Command { + var template string + var force bool + + return &redant.Command{ + Use: "init", + Short: "初始化 protobuf 项目配置", + Options: typex.Options{ + redant.Option{ + Flag: "template", + Shorthand: "t", + Description: "项目模板 (basic, grpc-gateway, minimal)", + Default: "", + Value: redant.StringOf(&template), + }, + redant.Option{ + Flag: "force", + Shorthand: "f", + Description: "覆盖已存在的配置文件", + Value: redant.BoolOf(&force), + }, + }, + Handler: func(ctx context.Context, inv *redant.Invocation) error { + defer recovery.Exit() + return runInit(template, force) + }, + } +} + +// runInit executes the init command logic. +func runInit(template string, force bool) error { + // Check if config already exists + if _, err := os.Stat(protoCfg); err == nil && !force { + return fmt.Errorf("配置文件 %s 已存在,使用 --force 覆盖", protoCfg) + } + + var cfg *config.Config + + if template != "" { + // Use specified template + tmpl, ok := initTemplates[template] + if !ok { + fmt.Println("可用的模板:") + for name := range initTemplates { + fmt.Printf(" - %s\n", name) + } + return fmt.Errorf("未知的模板: %s", template) + } + cfg = tmpl + fmt.Printf("📦 使用模板: %s\n", template) + } else { + // Interactive mode + cfg = interactiveInit() + } + + // Try to detect Go module + if cfg.BasePlugin != nil && cfg.BasePlugin.Module == "" { + if mod := detectGoModule(); mod != "" { + cfg.BasePlugin.Module = mod + "/pkg" + fmt.Printf("🔍 检测到 Go 模块: %s\n", mod) + } + } + + // Create proto directory if not exists + for _, dir := range cfg.Root { + if err := os.MkdirAll(dir, 0755); err != nil { + return fmt.Errorf("创建目录 %s 失败: %w", dir, err) + } + fmt.Printf("📁 创建目录: %s\n", dir) + } + + // Save config + if err := config.Save(protoCfg, cfg); err != nil { + return fmt.Errorf("保存配置失败: %w", err) + } + + fmt.Printf("✅ 配置文件已创建: %s\n", protoCfg) + fmt.Println("\n下一步:") + fmt.Println(" 1. 运行 'protobuild vendor' 同步依赖") + fmt.Println(" 2. 在 proto/ 目录下创建 .proto 文件") + fmt.Println(" 3. 运行 'protobuild gen' 生成代码") + + return nil +} + +// interactiveInit runs interactive configuration setup. +func interactiveInit() *config.Config { + reader := bufio.NewReader(os.Stdin) + + fmt.Println("🚀 Protobuild 项目初始化") + fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + + // Proto source directory + fmt.Print("Proto 源目录 [proto]: ") + protoDir, _ := reader.ReadString('\n') + protoDir = strings.TrimSpace(protoDir) + if protoDir == "" { + protoDir = "proto" + } + + // Output directory + fmt.Print("代码输出目录 [./pkg]: ") + outDir, _ := reader.ReadString('\n') + outDir = strings.TrimSpace(outDir) + if outDir == "" { + outDir = "./pkg" + } + + // Path mode + fmt.Print("路径模式 (source_relative/import) [source_relative]: ") + pathMode, _ := reader.ReadString('\n') + pathMode = strings.TrimSpace(pathMode) + if pathMode == "" { + pathMode = "source_relative" + } + + // gRPC support + fmt.Print("是否需要 gRPC 支持? (y/n) [y]: ") + grpcInput, _ := reader.ReadString('\n') + grpcInput = strings.TrimSpace(strings.ToLower(grpcInput)) + needGrpc := grpcInput == "" || grpcInput == "y" || grpcInput == "yes" + + // Build config + cfg := &config.Config{ + Vendor: ".proto", + BasePlugin: &config.BasePluginCfg{ + Out: outDir, + Paths: pathMode, + }, + Root: []string{protoDir}, + Includes: []string{protoDir}, + Depends: []*config.Depend{ + {Name: "google/protobuf", Url: "https://github.com/protocolbuffers/protobuf", Path: "src/google/protobuf"}, + }, + Plugins: []*config.Plugin{ + {Name: "go"}, + }, + } + + if needGrpc { + cfg.Plugins = append(cfg.Plugins, &config.Plugin{Name: "go-grpc"}) + } + + return cfg +} + +// detectGoModule tries to detect the Go module name from go.mod. +func detectGoModule() string { + data, err := os.ReadFile("go.mod") + if err != nil { + return "" + } + + lines := strings.Split(string(data), "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "module ") { + return strings.TrimSpace(strings.TrimPrefix(line, "module ")) + } + } + + return "" +} + +// createExampleProto creates an example proto file if the directory is empty. +func createExampleProto(protoDir string) error { + // Check if directory is empty + entries, err := os.ReadDir(protoDir) + if err != nil { + return err + } + + for _, e := range entries { + if strings.HasSuffix(e.Name(), ".proto") { + return nil // Already has proto files + } + } + + // Create example proto + exampleDir := filepath.Join(protoDir, "example", "v1") + if err := os.MkdirAll(exampleDir, 0755); err != nil { + return err + } + + module := detectGoModule() + if module == "" { + module = "github.com/yourorg/yourproject" + } + + example := fmt.Sprintf(`syntax = "proto3"; + +package example.v1; + +option go_package = "%s/pkg/example/v1;examplev1"; + +// HelloRequest is the request message for Hello. +message HelloRequest { + string name = 1; +} + +// HelloResponse is the response message for Hello. +message HelloResponse { + string message = 1; +} + +// GreeterService is a simple greeting service. +service GreeterService { + // SayHello returns a greeting. + rpc SayHello(HelloRequest) returns (HelloResponse); +} +`, module) + + exampleFile := filepath.Join(exampleDir, "greeter.proto") + if err := os.WriteFile(exampleFile, []byte(example), 0644); err != nil { + return err + } + + fmt.Printf("📝 创建示例文件: %s\n", exampleFile) + return nil +} diff --git a/cmd/protobuild/commands.go b/cmd/protobuild/commands.go index be223e5..03d4330 100644 --- a/cmd/protobuild/commands.go +++ b/cmd/protobuild/commands.go @@ -86,7 +86,7 @@ func newVendorCommand(force, update *bool) *redant.Command { return nil } - if !result.Changed && !globalCfg.changed && !*force { + if !result.Changed && !globalCfg.Changed && !*force { fmt.Println("\n✨ No changes detected") return nil } diff --git a/cmd/protobuild/config.go b/cmd/protobuild/config.go index 8a21c1e..25c2d54 100644 --- a/cmd/protobuild/config.go +++ b/cmd/protobuild/config.go @@ -1,64 +1,14 @@ package protobuild import ( - "github.com/pubgo/protobuild/cmd/linters" + "github.com/pubgo/protobuild/internal/config" ) -type Config struct { - Checksum string `yaml:"checksum,omitempty" hash:"-"` - Vendor string `yaml:"vendor,omitempty"` - BasePlugin *basePluginCfg `yaml:"base,omitempty" hash:"-"` - - // Root path, default is proto path - // source path - Root []string `yaml:"root,omitempty" hash:"-"` - - // Includes protoc include path, default is proto path and .proto path - Includes []string `yaml:"includes,omitempty" hash:"-"` - Excludes []string `yaml:"excludes,omitempty" hash:"-"` - Depends []*depend `yaml:"deps,omitempty"` - Plugins []*plugin `yaml:"plugins,omitempty" hash:"-"` - changed bool - Installers []string `yaml:"installers,omitempty" hash:"-"` - Linter linters.LinterConfig `yaml:"linter,omitempty" hash:"-"` -} - -type basePluginCfg struct { - Out string `yaml:"out,omitempty"` - Paths string `yaml:"paths,omitempty"` - Module string `yaml:"module,omitempty"` -} - -type plugin struct { - // Name protoc plugin name - Name string `yaml:"name,omitempty"` - - // Path protoc plugin path - Path string `yaml:"path,omitempty"` - - Out string `yaml:"out,omitempty"` - Shell string `yaml:"shell,omitempty"` - Docker string `yaml:"docker,omitempty"` - Remote string `yaml:"remote,omitempty"` - - // SkipBase skip base config - SkipBase bool `yaml:"skip_base,omitempty"` - - // SkipRun skip run plugin - SkipRun bool `yaml:"skip_run,omitempty"` - - // ExcludeOpts exclude plugin opts - ExcludeOpts pluginOpts `yaml:"exclude_opts,omitempty"` - Opt pluginOpts `yaml:"opt,omitempty"` - Opts pluginOpts `yaml:"opts,omitempty"` -} - -type depend struct { - Name string `yaml:"name,omitempty"` - Source string `yaml:"source,omitempty"` // gomod(default), git, http, s3, gcs, local - Url string `yaml:"url,omitempty"` - Path string `yaml:"path,omitempty"` - Version *string `yaml:"version,omitempty"` // for gomod - Ref string `yaml:"ref,omitempty"` // for git: tag/branch/commit - Optional *bool `yaml:"optional,omitempty"` -} +// Type aliases for backward compatibility +type ( + Config = config.Config + basePluginCfg = config.BasePluginCfg + plugin = config.Plugin + depend = config.Depend + pluginOpts = config.PluginOpts +) diff --git a/cmd/protobuild/protoc_builder.go b/cmd/protobuild/protoc_builder.go index 9876198..061022a 100644 --- a/cmd/protobuild/protoc_builder.go +++ b/cmd/protobuild/protoc_builder.go @@ -135,14 +135,14 @@ func (c *ProtocCommand) buildPluginArgs(plg *plugin) string { args.WriteString(fmt.Sprintf(" --plugin=protoc-gen-%s=%s", name, wrapperPath)) } - // Handle retag plugin specially + // Handle retag plugin specially - run after main compilation to modify generated files if name == reTagPluginName { - opts = append(opts, "__out="+out) args.WriteString(fmt.Sprintf(" --%s_out=%s", name, out)) - args.WriteString(fmt.Sprintf(" --%s_opt=%s", name, strings.Join(opts, ","))) - if plg.Path != "" { - plgPath := assert.Must1(exec.LookPath(plg.Path)) - args.WriteString(fmt.Sprintf(" --plugin=protoc-gen-%s=%s", name, plgPath)) + if len(opts) > 0 { + filteredOpts := c.filterExcludedOpts(opts, plg.ExcludeOpts) + if len(filteredOpts) > 0 { + args.WriteString(fmt.Sprintf(" --%s_opt=%s", name, strings.Join(filteredOpts, ","))) + } } return args.String() } diff --git a/cmd/protobuild/util.go b/cmd/protobuild/util.go index 1a75076..19affa0 100644 --- a/cmd/protobuild/util.go +++ b/cmd/protobuild/util.go @@ -8,14 +8,38 @@ import ( "github.com/a8m/envsubst" "github.com/cnf/structhash" + "github.com/googleapis/api-linter/v2/lint" "github.com/huandu/go-clone" "github.com/pubgo/funk/assert" "github.com/pubgo/funk/errors" "github.com/pubgo/funk/pathutil" "github.com/pubgo/funk/strutil" + "github.com/pubgo/protobuild/cmd/linters" + "github.com/pubgo/protobuild/internal/config" "gopkg.in/yaml.v3" ) +// toLinterConfig converts the shared config Linter to linters.LinterConfig. +func toLinterConfig(l *config.Linter) linters.LinterConfig { + if l == nil { + return linters.LinterConfig{} + } + + cfg := linters.LinterConfig{ + FormatType: l.FormatType, + IgnoreCommentDisablesFlag: l.IgnoreCommentDisablesFlag, + } + + if l.Rules != nil { + cfg.Rules = lint.Config{ + EnabledRules: l.Rules.EnabledRules, + DisabledRules: l.Rules.DisabledRules, + } + } + + return cfg +} + func mergePluginConfig(base *Config, pluginConfigs ...*Config) *Config { base = clone.Clone(base).(*Config) for _, cfg := range pluginConfigs { @@ -83,7 +107,7 @@ func parseConfig() error { checksum := fmt.Sprintf("%x", structhash.Sha1(globalCfg, 1)) if globalCfg.Checksum != checksum { globalCfg.Checksum = checksum - globalCfg.changed = true + globalCfg.Changed = true } oldChecksum, err := getChecksumData(globalCfg.Vendor) @@ -91,7 +115,7 @@ func parseConfig() error { slog.Warn("failed to get checksum data", slog.Any("err", err.Error())) } if oldChecksum != checksum { - globalCfg.changed = true + globalCfg.Changed = true } return nil diff --git a/cmd/protobuild/yaml_types.go b/cmd/protobuild/yaml_types.go index 2e15c4c..4bca88e 100644 --- a/cmd/protobuild/yaml_types.go +++ b/cmd/protobuild/yaml_types.go @@ -6,33 +6,8 @@ import ( yaml "gopkg.in/yaml.v3" ) -var _ yaml.Unmarshaler = (*pluginOpts)(nil) - -type pluginOpts []string - -func (p *pluginOpts) UnmarshalYAML(value *yaml.Node) error { - if value.IsZero() { - return nil - } - - switch value.Kind { - case yaml.ScalarNode: - if value.Value != "" { - *p = []string{value.Value} - return nil - } - return nil - case yaml.SequenceNode: - var data []string - if err := value.Decode(&data); err != nil { - return err - } - *p = data - return nil - default: - return errors.Format("yaml kind type error, kind=%v data=%s", value.Kind, value.Value) - } -} +// Note: pluginOpts is now defined in internal/config/yaml_types.go +// and aliased in config.go type YamlListType[T any] []T diff --git a/cmd/webcmd/cmd.go b/cmd/webcmd/cmd.go new file mode 100644 index 0000000..3d6b8bc --- /dev/null +++ b/cmd/webcmd/cmd.go @@ -0,0 +1,69 @@ +// Package webcmd provides CLI command for the web UI. +package webcmd + +import ( + "context" + "os" + "os/signal" + "strconv" + "syscall" + + "github.com/pubgo/protobuild/internal/typex" + "github.com/pubgo/redant" +) + +// New creates the web command. +func New(configPath *string) *redant.Command { + var portStr string + + return &redant.Command{ + Use: "web", + Short: "启动 Web 配置管理界面", + Long: `启动一个本地 Web 服务器,提供可视化的配置管理界面。 + +通过浏览器可以: + - 查看和编辑项目配置 + - 管理 Proto 依赖 + - 配置 Protoc 插件 + - 执行构建、检查、格式化等操作 + - 实时预览 YAML 配置 + +示例: + protobuild web + protobuild web --port 9090`, + Options: typex.Options{ + redant.Option{ + Flag: "port", + Shorthand: "p", + Description: "Web 服务器端口", + Default: "8080", + Value: redant.StringOf(&portStr), + }, + }, + Handler: func(ctx context.Context, inv *redant.Invocation) error { + port, _ := strconv.Atoi(portStr) + if port == 0 { + port = 8080 + } + + server, err := NewServer(*configPath) + if err != nil { + return err + } + + // Handle signals for graceful shutdown + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) + + go func() { + <-sigCh + cancel() + }() + + return server.Start(ctx, port) + }, + } +} diff --git a/cmd/webcmd/server.go b/cmd/webcmd/server.go new file mode 100644 index 0000000..44acd7b --- /dev/null +++ b/cmd/webcmd/server.go @@ -0,0 +1,554 @@ +// Package webcmd provides a web-based configuration UI for protobuild. +package webcmd + +import ( + "bufio" + "context" + "embed" + "encoding/json" + "fmt" + "html/template" + "io/fs" + "log/slog" + "net" + "net/http" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "sync" + "time" + + "github.com/pubgo/protobuild/internal/config" +) + +//go:embed templates/* +var templateFS embed.FS + +// CommandResult represents the result of a command execution. +type CommandResult struct { + Success bool `json:"success"` + Output string `json:"output"` + Error string `json:"error,omitempty"` +} + +// Server represents the web server. +type Server struct { + configPath string + config *config.Config + mu sync.RWMutex + server *http.Server + templates *template.Template +} + +// NewServer creates a new web server. +func NewServer(configPath string) (*Server, error) { + s := &Server{ + configPath: configPath, + } + + // Load templates + tmpl, err := template.ParseFS(templateFS, "templates/*.html") + if err != nil { + return nil, fmt.Errorf("failed to parse templates: %w", err) + } + s.templates = tmpl + + // Load initial config + if err := s.loadConfig(); err != nil { + // Create default config if not exists + s.config = config.Default() + } + + return s, nil +} + +// loadConfig loads the configuration from file. +func (s *Server) loadConfig() error { + s.mu.Lock() + defer s.mu.Unlock() + + cfg, err := config.Load(s.configPath) + if err != nil { + return err + } + + s.config = cfg + return nil +} + +// saveConfig saves the configuration to file. +func (s *Server) saveConfig() error { + s.mu.RLock() + cfg := s.config + s.mu.RUnlock() + + return config.Save(s.configPath, cfg) +} + +// Start starts the web server. +func (s *Server) Start(ctx context.Context, port int) error { + mux := http.NewServeMux() + + // Static files + mux.HandleFunc("/", s.handleIndex) + + // API endpoints + mux.HandleFunc("/api/config", s.handleConfig) + mux.HandleFunc("/api/config/save", s.handleSaveConfig) + mux.HandleFunc("/api/command/", s.handleCommand) + mux.HandleFunc("/api/command-stream/", s.handleCommandStream) + mux.HandleFunc("/api/proto-files", s.handleProtoFiles) + mux.HandleFunc("/api/proto-content", s.handleProtoContent) + mux.HandleFunc("/api/deps/status", s.handleDepsStatus) + mux.HandleFunc("/api/project/stats", s.handleProjectStats) + + addr := fmt.Sprintf(":%d", port) + s.server = &http.Server{ + Addr: addr, + Handler: mux, + } + + // Find available port if default is in use + listener, err := net.Listen("tcp", addr) + if err != nil { + // Try to find an available port + listener, err = net.Listen("tcp", ":0") + if err != nil { + return fmt.Errorf("failed to find available port: %w", err) + } + } + + actualPort := listener.Addr().(*net.TCPAddr).Port + url := fmt.Sprintf("http://localhost:%d", actualPort) + + slog.Info("Starting web server", "url", url) + fmt.Printf("\n🌐 Web UI available at: %s\n\n", url) + + // Open browser + go func() { + time.Sleep(500 * time.Millisecond) + openBrowser(url) + }() + + // Handle graceful shutdown + go func() { + <-ctx.Done() + shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + s.server.Shutdown(shutdownCtx) + }() + + return s.server.Serve(listener) +} + +// handleIndex serves the main page. +func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } + + s.mu.RLock() + cfg := s.config + s.mu.RUnlock() + + data := map[string]interface{}{ + "Config": cfg, + "ConfigPath": s.configPath, + } + + w.Header().Set("Content-Type", "text/html; charset=utf-8") + if err := s.templates.ExecuteTemplate(w, "index.html", data); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + +// handleConfig returns the current configuration. +func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet { + // Reload config from file + s.loadConfig() + + s.mu.RLock() + cfg := s.config + s.mu.RUnlock() + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(cfg) + return + } + + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) +} + +// handleSaveConfig saves the configuration. +func (s *Server) handleSaveConfig(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + var cfg config.Config + if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + s.mu.Lock() + s.config = &cfg + s.mu.Unlock() + + if err := s.saveConfig(); err != nil { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(CommandResult{ + Success: false, + Error: err.Error(), + }) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(CommandResult{ + Success: true, + Output: "Configuration saved successfully", + }) +} + +// handleCommand executes protobuild commands. +func (s *Server) handleCommand(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + // Extract command from URL path + cmdName := strings.TrimPrefix(r.URL.Path, "/api/command/") + if cmdName == "" { + http.Error(w, "Command name required", http.StatusBadRequest) + return + } + + // Build command arguments + args := []string{"-c", s.configPath, cmdName} + + // Parse additional flags from request body + var flags map[string]interface{} + if err := json.NewDecoder(r.Body).Decode(&flags); err == nil { + for key, val := range flags { + switch v := val.(type) { + case bool: + if v { + args = append(args, "--"+key) + } + case string: + if v != "" { + args = append(args, "--"+key, v) + } + } + } + } + + // Get executable path + executable, err := os.Executable() + if err != nil { + executable = "protobuild" + } + + // Execute command + ctx, cancel := context.WithTimeout(r.Context(), 60*time.Second) + defer cancel() + + cmd := exec.CommandContext(ctx, executable, args...) + cmd.Dir = filepath.Dir(s.configPath) + + output, err := cmd.CombinedOutput() + + result := CommandResult{ + Success: err == nil, + Output: string(output), + } + if err != nil { + result.Error = err.Error() + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(result) +} + +// handleProtoFiles returns a list of proto files in the project. +func (s *Server) handleProtoFiles(w http.ResponseWriter, r *http.Request) { + s.mu.RLock() + cfg := s.config + s.mu.RUnlock() + + var files []string + baseDir := filepath.Dir(s.configPath) + + for _, root := range cfg.Root { + rootPath := filepath.Join(baseDir, root) + filepath.Walk(rootPath, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return nil + } + if !info.IsDir() && strings.HasSuffix(path, ".proto") { + relPath, _ := filepath.Rel(baseDir, path) + files = append(files, relPath) + } + return nil + }) + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(files) +} + +// handleDepsStatus returns the status of dependencies. +func (s *Server) handleDepsStatus(w http.ResponseWriter, r *http.Request) { + // Get executable path + executable, err := os.Executable() + if err != nil { + executable = "protobuild" + } + + cmd := exec.Command(executable, "-c", s.configPath, "deps") + cmd.Dir = filepath.Dir(s.configPath) + output, _ := cmd.CombinedOutput() + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]string{ + "output": string(output), + }) +} + +// openBrowser opens the URL in the default browser. +func openBrowser(url string) error { + var cmd string + var args []string + + switch runtime.GOOS { + case "darwin": + cmd = "open" + args = []string{url} + case "linux": + cmd = "xdg-open" + args = []string{url} + case "windows": + cmd = "rundll32" + args = []string{"url.dll,FileProtocolHandler", url} + default: + return fmt.Errorf("unsupported platform") + } + + return exec.Command(cmd, args...).Start() +} + +// handleCommandStream executes a command and streams output via SSE. +func (s *Server) handleCommandStream(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + // Extract command from URL path + cmdName := strings.TrimPrefix(r.URL.Path, "/api/command-stream/") + if cmdName == "" { + http.Error(w, "Command name required", http.StatusBadRequest) + return + } + + // Set SSE headers + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + w.Header().Set("Access-Control-Allow-Origin", "*") + + flusher, ok := w.(http.Flusher) + if !ok { + http.Error(w, "Streaming not supported", http.StatusInternalServerError) + return + } + + // Build command arguments + args := []string{"-c", s.configPath, cmdName} + + // Get executable path + executable, err := os.Executable() + if err != nil { + executable = "protobuild" + } + + // Execute command with streaming output + ctx, cancel := context.WithTimeout(r.Context(), 120*time.Second) + defer cancel() + + cmd := exec.CommandContext(ctx, executable, args...) + cmd.Dir = filepath.Dir(s.configPath) + + // Create pipes for stdout and stderr + stdout, _ := cmd.StdoutPipe() + stderr, _ := cmd.StderrPipe() + + if err := cmd.Start(); err != nil { + fmt.Fprintf(w, "data: {\"type\":\"error\",\"data\":\"%s\"}\n\n", err.Error()) + flusher.Flush() + return + } + + // Stream output + go func() { + scanner := bufio.NewScanner(stdout) + for scanner.Scan() { + line := scanner.Text() + fmt.Fprintf(w, "data: {\"type\":\"stdout\",\"data\":\"%s\"}\n\n", escapeJSON(line)) + flusher.Flush() + } + }() + + go func() { + scanner := bufio.NewScanner(stderr) + for scanner.Scan() { + line := scanner.Text() + fmt.Fprintf(w, "data: {\"type\":\"stderr\",\"data\":\"%s\"}\n\n", escapeJSON(line)) + flusher.Flush() + } + }() + + err = cmd.Wait() + if err != nil { + fmt.Fprintf(w, "data: {\"type\":\"error\",\"data\":\"%s\"}\n\n", err.Error()) + } else { + fmt.Fprintf(w, "data: {\"type\":\"done\",\"data\":\"Command completed successfully\"}\n\n") + } + flusher.Flush() +} + +// escapeJSON escapes special characters for JSON string. +func escapeJSON(s string) string { + s = strings.ReplaceAll(s, "\\", "\\\\") + s = strings.ReplaceAll(s, "\"", "\\\"") + s = strings.ReplaceAll(s, "\n", "\\n") + s = strings.ReplaceAll(s, "\r", "\\r") + s = strings.ReplaceAll(s, "\t", "\\t") + return s +} + +// handleProtoContent returns the content of a specific proto file. +func (s *Server) handleProtoContent(w http.ResponseWriter, r *http.Request) { + filePath := r.URL.Query().Get("file") + if filePath == "" { + http.Error(w, "File path required", http.StatusBadRequest) + return + } + + baseDir := filepath.Dir(s.configPath) + fullPath := filepath.Join(baseDir, filePath) + + // Security check: ensure the path is within the project + absBase, _ := filepath.Abs(baseDir) + absPath, _ := filepath.Abs(fullPath) + if !strings.HasPrefix(absPath, absBase) { + http.Error(w, "Invalid file path", http.StatusForbidden) + return + } + + // Check file extension + if !strings.HasSuffix(fullPath, ".proto") { + http.Error(w, "Only .proto files allowed", http.StatusForbidden) + return + } + + content, err := os.ReadFile(fullPath) + if err != nil { + http.Error(w, err.Error(), http.StatusNotFound) + return + } + + // Get file info + info, _ := os.Stat(fullPath) + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]interface{}{ + "path": filePath, + "content": string(content), + "size": info.Size(), + "modified": info.ModTime().Format(time.RFC3339), + }) +} + +// ProjectStats represents project statistics. +type ProjectStats struct { + ProtoFiles int `json:"proto_files"` + TotalLines int `json:"total_lines"` + MessageCount int `json:"message_count"` + ServiceCount int `json:"service_count"` + DependencyCount int `json:"dependency_count"` + PluginCount int `json:"plugin_count"` + ProtoRoots []string `json:"proto_roots"` + VendorDir string `json:"vendor_dir"` + VendorFiles int `json:"vendor_files"` +} + +// handleProjectStats returns project statistics. +func (s *Server) handleProjectStats(w http.ResponseWriter, r *http.Request) { + s.mu.RLock() + cfg := s.config + s.mu.RUnlock() + + stats := ProjectStats{ + ProtoRoots: cfg.Root, + VendorDir: cfg.Vendor, + DependencyCount: len(cfg.Depends), + PluginCount: len(cfg.Plugins), + } + + baseDir := filepath.Dir(s.configPath) + + // Count proto files in root directories + for _, root := range cfg.Root { + rootPath := filepath.Join(baseDir, root) + filepath.Walk(rootPath, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return nil + } + if !info.IsDir() && strings.HasSuffix(path, ".proto") { + stats.ProtoFiles++ + + // Count lines, messages, and services + content, err := os.ReadFile(path) + if err == nil { + lines := strings.Split(string(content), "\n") + stats.TotalLines += len(lines) + + for _, line := range lines { + trimmed := strings.TrimSpace(line) + if strings.HasPrefix(trimmed, "message ") { + stats.MessageCount++ + } else if strings.HasPrefix(trimmed, "service ") { + stats.ServiceCount++ + } + } + } + } + return nil + }) + } + + // Count vendor files + if cfg.Vendor != "" { + vendorPath := filepath.Join(baseDir, cfg.Vendor) + filepath.Walk(vendorPath, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return nil + } + if !info.IsDir() && strings.HasSuffix(path, ".proto") { + stats.VendorFiles++ + } + return nil + }) + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(stats) +} diff --git a/cmd/webcmd/templates/index.html b/cmd/webcmd/templates/index.html new file mode 100644 index 0000000..c4933ce --- /dev/null +++ b/cmd/webcmd/templates/index.html @@ -0,0 +1,1605 @@ + + + + + + Protobuild - 配置管理 + + + + + +
+ +
+
+
+
+

🔧 Protobuild

+

Protocol Buffers 构建与管理工具

+
+
+ 配置文件: {{.ConfigPath}} + +
+
+
+
+ + +
+ +
+ + +
+ +
+
+
+
Proto 文件
+
+
+
+
Message
+
+
+
+
Service
+
+
+
+
依赖
+
+
+
+
插件
+
+
+
+
总行数
+
+
+ + +
+

⚡ 快速操作

+
+ + + + + + + + +
+
+ + +
+
+ + + + + + + 运行中... + + 命令输出 + + +
+

+            
+ + +
+ +
+ + +
+ +
+
+ +
+ + +

依赖存放的目录

+
+ + +
+ + +

插件生成代码的默认目录

+
+ + +
+ + +

protoc 路径模式

+
+ + +
+ + +

Go 模块路径

+
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ + +
+
+
+ + +
+
+

Proto 依赖

+ +
+ +
+ + +
+ 暂无依赖配置,点击"添加依赖"开始 +
+
+
+ + +
+
+

Protoc 插件

+
+ + + + +
+
+ +
+ + +
+ 暂无插件配置,点击上方按钮添加 +
+
+ + +
+

插件安装器

+

通过 go install 安装的插件列表

+
+ + +
+
+
+ + +
+

Linter 配置

+ +
+ + +
+ +
+ +
+ +
+ + +
+
+ + +
+ +
+ + +
+
+
+ +
+

💡 常用规则

+
+ + + +
+
+
+ + +
+
+ +
+
+ 📁 文件浏览器 + +
+
+ +
+ + + +

暂无 Proto 文件

+
+
+
+ + +
+ +
+
+ + +
+
+ messages + services + imports + lines +
+
+ + +
+
+
+ + + +

👈 从左侧选择文件查看

+
+
+
+ + + + +
+
+ +
+ +
+ +

+                                
+
+
+
+
+ + +
+

YAML 配置预览

+

+                
+ + +
+

📚 配置示例参考

+

以下是常用的配置示例,点击「应用此配置」可直接使用。

+ +
+ +
+
+
+

🚀 基础 Go 项目

+

适合简单的 Go + gRPC 项目

+
+ +
+
vendor: .proto
+base:
+  out: ./gen
+  paths: source_relative
+root:
+  - proto
+includes:
+  - proto
+deps:
+  - name: google/protobuf
+    url: https://github.com/protocolbuffers/protobuf
+    path: src/google/protobuf
+plugins:
+  - name: go
+  - name: go-grpc
+
+ + +
+
+
+

⚡ 完整项目配置

+

包含 gRPC-Gateway、Validate、OpenAPI 等

+
+ +
+
vendor: .proto
+base:
+  out: ./pkg
+  paths: import
+  module: github.com/yourorg/yourproject/pkg
+root:
+  - proto
+  - api
+includes:
+  - proto
+  - third_party
+deps:
+  - name: google/protobuf
+    url: https://github.com/protocolbuffers/protobuf
+    path: src/google/protobuf
+  - name: google/api
+    url: https://github.com/googleapis/googleapis
+    path: google/api
+  - name: protoc-gen-openapiv2/options
+    url: https://github.com/grpc-ecosystem/grpc-gateway
+    path: protoc-gen-openapiv2/options
+  - name: validate
+    url: https://github.com/bufbuild/protovalidate
+    path: proto/protovalidate/buf/validate
+plugins:
+  - name: go
+  - name: go-grpc
+  - name: grpc-gateway
+    opt:
+      - generate_unbound_methods=true
+  - name: openapiv2
+    out: ./docs/swagger
+  - name: validate-go
+linter:
+  format_type: github_actions
+  rules:
+    enabled_rules:
+      - core
+      - naming
+    disabled_rules: []
+
+ + +
+
+
+

🔧 微服务项目

+

适合多模块微服务架构

+
+ +
+
vendor: .proto
+base:
+  out: ./internal/pb
+  paths: import
+  module: github.com/yourorg/service/internal/pb
+root:
+  - api/proto
+includes:
+  - api/proto
+  - vendor/proto
+excludes:
+  - vendor
+  - third_party
+deps:
+  - name: google/protobuf
+    url: https://github.com/protocolbuffers/protobuf
+    path: src/google/protobuf
+  - name: google/api
+    url: https://github.com/googleapis/googleapis
+    path: google/api
+plugins:
+  - name: go
+    opt:
+      - paths=source_relative
+  - name: go-grpc
+    opt:
+      - paths=source_relative
+      - require_unimplemented_servers=false
+  - name: connect-go
+    out: ./internal/connect
+linter:
+  format_type: yaml
+  rules:
+    enabled_rules:
+      - core::0131::http-method
+      - core::0131::http-body
+    disabled_rules:
+      - all
+
+ + +
+
+
+

🏷️ 自定义结构体标签

+

使用 retag 添加 JSON/XML/Validate 标签

+
+ +
+
vendor: .proto
+base:
+  out: ./pkg
+  paths: import
+  module: github.com/yourorg/yourproject/pkg
+root:
+  - proto
+includes:
+  - proto
+deps:
+  - name: google/protobuf
+    url: https://github.com/protocolbuffers/protobuf
+    path: src/google/protobuf
+  - name: retag
+    url: https://github.com/pubgo/protoc-gen-retag
+    path: proto/retag
+plugins:
+  - name: go
+  - name: retag
+# Proto 文件中使用示例:
+# message User {
+#   string id = 1 [(retag.tags) = {name: "json", value: "user_id"}];
+#   string email = 2 [
+#     (retag.tags) = {name: "json", value: "email"},
+#     (retag.tags) = {name: "xml", value: "email,attr"},
+#     (retag.tags) = {name: "validate", value: "required,email"}
+#   ];
+# }
+
+ + +
+
+
+

📦 Buf 兼容配置

+

与 Buf Schema Registry 集成

+
+ +
+
vendor: .proto
+base:
+  out: ./gen/go
+  paths: source_relative
+root:
+  - proto
+includes:
+  - proto
+deps:
+  - name: buf/validate
+    source: buf.build
+    url: buf.build/bufbuild/protovalidate
+  - name: googleapis
+    source: buf.build
+    url: buf.build/googleapis/googleapis
+plugins:
+  - name: go
+  - name: go-grpc
+  - name: buf-validate-go
+installers:
+  - go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
+  - go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
+  - go install github.com/bufbuild/buf/cmd/buf@latest
+
+ + +
+
+
+

🌐 多语言生成

+

同时生成 Go、TypeScript、Python 代码

+
+ +
+
vendor: .proto
+root:
+  - proto
+includes:
+  - proto
+deps:
+  - name: google/protobuf
+    url: https://github.com/protocolbuffers/protobuf
+    path: src/google/protobuf
+plugins:
+  # Go
+  - name: go
+    out: ./gen/go
+    opt:
+      - paths=source_relative
+  - name: go-grpc
+    out: ./gen/go
+    opt:
+      - paths=source_relative
+  # TypeScript (使用 ts-proto)
+  - name: ts_proto
+    out: ./gen/ts
+    opt:
+      - esModuleInterop=true
+      - outputServices=grpc-js
+  # Python
+  - name: python
+    out: ./gen/python
+  - name: grpc_python
+    out: ./gen/python
+installers:
+  - go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
+  - go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
+  - npm install -g ts-proto
+  - pip install grpcio-tools
+
+
+
+
+
+ + + +
+ + + + + diff --git a/go.mod b/go.mod index 4b714c2..7cf7416 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/hashicorp/go-version v1.8.0 github.com/huandu/go-clone v1.7.3 github.com/pubgo/funk v0.5.68 + github.com/pubgo/protoc-gen-retag v0.0.5 github.com/pubgo/redant v0.0.5 github.com/samber/lo v1.51.0 github.com/schollz/progressbar/v3 v3.19.0 diff --git a/go.sum b/go.sum index d4582b2..dfc7c62 100644 --- a/go.sum +++ b/go.sum @@ -195,6 +195,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pubgo/funk v0.5.68 h1:3fDJAt+QHhPnbAxUr8kLLAh6vra/C3vb7/LoSMQl788= github.com/pubgo/funk v0.5.68/go.mod h1:CQDKnci4zmCyb0LSD9YiKx/6QBh3Z+PRyCLmJb6ZTOg= +github.com/pubgo/protoc-gen-retag v0.0.5 h1:tF++mxmAUILb3IkVvSM0el+ww0jcLyPTkIXYzwgiv1A= +github.com/pubgo/protoc-gen-retag v0.0.5/go.mod h1:/TD5yIvhQpb6ttvpmhpQPUnQ73Rtj23QlXeoczGyAHs= github.com/pubgo/redant v0.0.5 h1:iDq0cQJNtST8pu9bFSgxZ78JoQ0aVW+svZyOMouWjfM= github.com/pubgo/redant v0.0.5/go.mod h1:FOBNjL8pPLOBcZS3SL2R5GusFz/bNBwDJzSinGuKs7A= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..cf62cc1 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,127 @@ +// Package config provides shared configuration types for protobuild. +package config + +// Config represents the protobuild project configuration. +type Config struct { + Checksum string `yaml:"checksum,omitempty" json:"checksum,omitempty" hash:"-"` + Vendor string `yaml:"vendor,omitempty" json:"vendor"` + BasePlugin *BasePluginCfg `yaml:"base,omitempty" json:"base,omitempty" hash:"-"` + + // Root path, default is proto path (source path) + Root []string `yaml:"root,omitempty" json:"root" hash:"-"` + + // Includes protoc include path, default is proto path and .proto path + Includes []string `yaml:"includes,omitempty" json:"includes" hash:"-"` + Excludes []string `yaml:"excludes,omitempty" json:"excludes" hash:"-"` + Depends []*Depend `yaml:"deps,omitempty" json:"deps"` + Plugins []*Plugin `yaml:"plugins,omitempty" json:"plugins" hash:"-"` + Installers []string `yaml:"installers,omitempty" json:"installers" hash:"-"` + Linter *Linter `yaml:"linter,omitempty" json:"linter,omitempty" hash:"-"` + + // Changed is used internally to track if config has been modified (lowercase for internal use) + Changed bool `yaml:"-" json:"-"` +} + +// BasePluginCfg represents base plugin configuration applied to all plugins. +type BasePluginCfg struct { + Out string `yaml:"out,omitempty" json:"out"` + Paths string `yaml:"paths,omitempty" json:"paths"` + Module string `yaml:"module,omitempty" json:"module"` +} + +// Plugin represents a protoc plugin configuration. +type Plugin struct { + // Name protoc plugin name (used as protoc-gen-{name}) + Name string `yaml:"name,omitempty" json:"name"` + + // Path custom plugin binary path + Path string `yaml:"path,omitempty" json:"path,omitempty"` + + // Out output directory + Out string `yaml:"out,omitempty" json:"out,omitempty"` + + // Shell run via shell command + Shell string `yaml:"shell,omitempty" json:"shell,omitempty"` + + // Docker run via Docker container + Docker string `yaml:"docker,omitempty" json:"docker,omitempty"` + + // Remote remote plugin URL + Remote string `yaml:"remote,omitempty" json:"remote,omitempty"` + + // SkipBase skip base config + SkipBase bool `yaml:"skip_base,omitempty" json:"skip_base,omitempty"` + + // SkipRun skip run plugin + SkipRun bool `yaml:"skip_run,omitempty" json:"skip_run,omitempty"` + + // ExcludeOpts options to exclude + ExcludeOpts PluginOpts `yaml:"exclude_opts,omitempty" json:"exclude_opts,omitempty"` + + // Opt plugin options + Opt PluginOpts `yaml:"opt,omitempty" json:"opt,omitempty"` + + // Opts alias for Opt + Opts PluginOpts `yaml:"opts,omitempty" json:"opts,omitempty"` +} + +// Depend represents a proto dependency. +type Depend struct { + // Name local name/path in vendor directory + Name string `yaml:"name,omitempty" json:"name"` + + // Source type: gomod(default), git, http, s3, gcs, local + Source string `yaml:"source,omitempty" json:"source,omitempty"` + + // Url source URL + Url string `yaml:"url,omitempty" json:"url"` + + // Path subdirectory within the source + Path string `yaml:"path,omitempty" json:"path,omitempty"` + + // Version specific version (for Go modules) + Version *string `yaml:"version,omitempty" json:"version,omitempty"` + + // Ref git ref (branch, tag, commit) for Git sources + Ref string `yaml:"ref,omitempty" json:"ref,omitempty"` + + // Optional skip if not found + Optional *bool `yaml:"optional,omitempty" json:"optional,omitempty"` +} + +// Linter represents linter configuration. +type Linter struct { + Rules *LinterRules `yaml:"rules,omitempty" json:"rules,omitempty" hash:"-"` + FormatType string `yaml:"format_type,omitempty" json:"format_type,omitempty"` + IgnoreCommentDisablesFlag bool `yaml:"ignore_comment_disables_flag,omitempty" json:"ignore_comment_disables_flag,omitempty"` +} + +// LinterRules represents linter rules configuration. +type LinterRules struct { + EnabledRules []string `yaml:"enabled_rules,omitempty" json:"enabled_rules,omitempty"` + DisabledRules []string `yaml:"disabled_rules,omitempty" json:"disabled_rules,omitempty"` +} + +// GetVersion returns the version string or empty if nil. +func (d *Depend) GetVersion() string { + if d.Version == nil { + return "" + } + return *d.Version +} + +// IsOptional returns true if the dependency is optional. +func (d *Depend) IsOptional() bool { + if d.Optional == nil { + return false + } + return *d.Optional +} + +// GetAllOpts returns combined Opt and Opts. +func (p *Plugin) GetAllOpts() []string { + result := make([]string, 0, len(p.Opt)+len(p.Opts)) + result = append(result, p.Opt...) + result = append(result, p.Opts...) + return result +} diff --git a/internal/config/loader.go b/internal/config/loader.go new file mode 100644 index 0000000..8616178 --- /dev/null +++ b/internal/config/loader.go @@ -0,0 +1,42 @@ +// Package config provides file loading and saving utilities. +package config + +import ( + "os" + + "gopkg.in/yaml.v3" +) + +// Load loads configuration from a YAML file. +func Load(path string) (*Config, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var cfg Config + if err := yaml.Unmarshal(data, &cfg); err != nil { + return nil, err + } + + return &cfg, nil +} + +// Save saves configuration to a YAML file. +func Save(path string, cfg *Config) error { + data, err := yaml.Marshal(cfg) + if err != nil { + return err + } + + return os.WriteFile(path, data, 0644) +} + +// Default returns a default configuration. +func Default() *Config { + return &Config{ + Vendor: ".proto", + Root: []string{"proto"}, + Includes: []string{"proto", ".proto"}, + } +} diff --git a/internal/config/yaml_types.go b/internal/config/yaml_types.go new file mode 100644 index 0000000..01882a2 --- /dev/null +++ b/internal/config/yaml_types.go @@ -0,0 +1,37 @@ +// Package config provides YAML type helpers for configuration parsing. +package config + +import ( + "github.com/pubgo/funk/errors" + "gopkg.in/yaml.v3" +) + +var _ yaml.Unmarshaler = (*PluginOpts)(nil) + +// PluginOpts is a list of plugin options that can be unmarshaled from string or list. +type PluginOpts []string + +// UnmarshalYAML implements yaml.Unmarshaler. +func (p *PluginOpts) UnmarshalYAML(value *yaml.Node) error { + if value.IsZero() { + return nil + } + + switch value.Kind { + case yaml.ScalarNode: + if value.Value != "" { + *p = []string{value.Value} + return nil + } + return nil + case yaml.SequenceNode: + var data []string + if err := value.Decode(&data); err != nil { + return err + } + *p = data + return nil + default: + return errors.Format("yaml kind type error, kind=%v data=%s", value.Kind, value.Value) + } +} diff --git a/pkg/example/v1/order.pb.go b/pkg/example/v1/order.pb.go new file mode 100644 index 0000000..d486b90 --- /dev/null +++ b/pkg/example/v1/order.pb.go @@ -0,0 +1,707 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc v5.29.3 +// source: example/v1/order.proto + +package examplev1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// OrderStatus represents the status of an order. +type OrderStatus int32 + +const ( + OrderStatus_ORDER_STATUS_UNSPECIFIED OrderStatus = 0 + OrderStatus_ORDER_STATUS_PENDING OrderStatus = 1 + OrderStatus_ORDER_STATUS_CONFIRMED OrderStatus = 2 + OrderStatus_ORDER_STATUS_SHIPPED OrderStatus = 3 + OrderStatus_ORDER_STATUS_DELIVERED OrderStatus = 4 + OrderStatus_ORDER_STATUS_CANCELLED OrderStatus = 5 +) + +// Enum value maps for OrderStatus. +var ( + OrderStatus_name = map[int32]string{ + 0: "ORDER_STATUS_UNSPECIFIED", + 1: "ORDER_STATUS_PENDING", + 2: "ORDER_STATUS_CONFIRMED", + 3: "ORDER_STATUS_SHIPPED", + 4: "ORDER_STATUS_DELIVERED", + 5: "ORDER_STATUS_CANCELLED", + } + OrderStatus_value = map[string]int32{ + "ORDER_STATUS_UNSPECIFIED": 0, + "ORDER_STATUS_PENDING": 1, + "ORDER_STATUS_CONFIRMED": 2, + "ORDER_STATUS_SHIPPED": 3, + "ORDER_STATUS_DELIVERED": 4, + "ORDER_STATUS_CANCELLED": 5, + } +) + +func (x OrderStatus) Enum() *OrderStatus { + p := new(OrderStatus) + *p = x + return p +} + +func (x OrderStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (OrderStatus) Descriptor() protoreflect.EnumDescriptor { + return file_example_v1_order_proto_enumTypes[0].Descriptor() +} + +func (OrderStatus) Type() protoreflect.EnumType { + return &file_example_v1_order_proto_enumTypes[0] +} + +func (x OrderStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use OrderStatus.Descriptor instead. +func (OrderStatus) EnumDescriptor() ([]byte, []int) { + return file_example_v1_order_proto_rawDescGZIP(), []int{0} +} + +// Order represents an order in the system. +type Order struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Items []*OrderItem `protobuf:"bytes,3,rep,name=items,proto3" json:"items,omitempty"` + Status OrderStatus `protobuf:"varint,4,opt,name=status,proto3,enum=example.v1.OrderStatus" json:"status,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + Total *Money `protobuf:"bytes,7,opt,name=total,proto3" json:"total,omitempty"` + ShippingAddress *ShippingAddress `protobuf:"bytes,8,opt,name=shipping_address,json=shippingAddress,proto3" json:"shipping_address,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Order) Reset() { + *x = Order{} + mi := &file_example_v1_order_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Order) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Order) ProtoMessage() {} + +func (x *Order) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_order_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Order.ProtoReflect.Descriptor instead. +func (*Order) Descriptor() ([]byte, []int) { + return file_example_v1_order_proto_rawDescGZIP(), []int{0} +} + +func (x *Order) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Order) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *Order) GetItems() []*OrderItem { + if x != nil { + return x.Items + } + return nil +} + +func (x *Order) GetStatus() OrderStatus { + if x != nil { + return x.Status + } + return OrderStatus_ORDER_STATUS_UNSPECIFIED +} + +func (x *Order) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *Order) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +func (x *Order) GetTotal() *Money { + if x != nil { + return x.Total + } + return nil +} + +func (x *Order) GetShippingAddress() *ShippingAddress { + if x != nil { + return x.ShippingAddress + } + return nil +} + +// OrderItem represents an item in an order. +type OrderItem struct { + state protoimpl.MessageState `protogen:"open.v1"` + ProductId string `protobuf:"bytes,1,opt,name=product_id,json=productId,proto3" json:"product_id,omitempty"` + ProductName string `protobuf:"bytes,2,opt,name=product_name,json=productName,proto3" json:"product_name,omitempty"` + Quantity int32 `protobuf:"varint,3,opt,name=quantity,proto3" json:"quantity,omitempty"` + UnitPrice *Money `protobuf:"bytes,4,opt,name=unit_price,json=unitPrice,proto3" json:"unit_price,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *OrderItem) Reset() { + *x = OrderItem{} + mi := &file_example_v1_order_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *OrderItem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OrderItem) ProtoMessage() {} + +func (x *OrderItem) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_order_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OrderItem.ProtoReflect.Descriptor instead. +func (*OrderItem) Descriptor() ([]byte, []int) { + return file_example_v1_order_proto_rawDescGZIP(), []int{1} +} + +func (x *OrderItem) GetProductId() string { + if x != nil { + return x.ProductId + } + return "" +} + +func (x *OrderItem) GetProductName() string { + if x != nil { + return x.ProductName + } + return "" +} + +func (x *OrderItem) GetQuantity() int32 { + if x != nil { + return x.Quantity + } + return 0 +} + +func (x *OrderItem) GetUnitPrice() *Money { + if x != nil { + return x.UnitPrice + } + return nil +} + +// Money represents a monetary value. +type Money struct { + state protoimpl.MessageState `protogen:"open.v1"` + CurrencyCode string `protobuf:"bytes,1,opt,name=currency_code,json=currencyCode,proto3" json:"currency_code,omitempty"` + Units int64 `protobuf:"varint,2,opt,name=units,proto3" json:"units,omitempty"` + Nanos int32 `protobuf:"varint,3,opt,name=nanos,proto3" json:"nanos,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Money) Reset() { + *x = Money{} + mi := &file_example_v1_order_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Money) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Money) ProtoMessage() {} + +func (x *Money) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_order_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Money.ProtoReflect.Descriptor instead. +func (*Money) Descriptor() ([]byte, []int) { + return file_example_v1_order_proto_rawDescGZIP(), []int{2} +} + +func (x *Money) GetCurrencyCode() string { + if x != nil { + return x.CurrencyCode + } + return "" +} + +func (x *Money) GetUnits() int64 { + if x != nil { + return x.Units + } + return 0 +} + +func (x *Money) GetNanos() int32 { + if x != nil { + return x.Nanos + } + return 0 +} + +// ShippingAddress represents a shipping address. +type ShippingAddress struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Street string `protobuf:"bytes,2,opt,name=street,proto3" json:"street,omitempty"` + City string `protobuf:"bytes,3,opt,name=city,proto3" json:"city,omitempty"` + State string `protobuf:"bytes,4,opt,name=state,proto3" json:"state,omitempty"` + PostalCode string `protobuf:"bytes,5,opt,name=postal_code,json=postalCode,proto3" json:"postal_code,omitempty"` + Country string `protobuf:"bytes,6,opt,name=country,proto3" json:"country,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ShippingAddress) Reset() { + *x = ShippingAddress{} + mi := &file_example_v1_order_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ShippingAddress) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ShippingAddress) ProtoMessage() {} + +func (x *ShippingAddress) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_order_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ShippingAddress.ProtoReflect.Descriptor instead. +func (*ShippingAddress) Descriptor() ([]byte, []int) { + return file_example_v1_order_proto_rawDescGZIP(), []int{3} +} + +func (x *ShippingAddress) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ShippingAddress) GetStreet() string { + if x != nil { + return x.Street + } + return "" +} + +func (x *ShippingAddress) GetCity() string { + if x != nil { + return x.City + } + return "" +} + +func (x *ShippingAddress) GetState() string { + if x != nil { + return x.State + } + return "" +} + +func (x *ShippingAddress) GetPostalCode() string { + if x != nil { + return x.PostalCode + } + return "" +} + +func (x *ShippingAddress) GetCountry() string { + if x != nil { + return x.Country + } + return "" +} + +// CreateOrderRequest is the request for creating an order. +type CreateOrderRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Order *Order `protobuf:"bytes,1,opt,name=order,proto3" json:"order,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateOrderRequest) Reset() { + *x = CreateOrderRequest{} + mi := &file_example_v1_order_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateOrderRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateOrderRequest) ProtoMessage() {} + +func (x *CreateOrderRequest) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_order_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateOrderRequest.ProtoReflect.Descriptor instead. +func (*CreateOrderRequest) Descriptor() ([]byte, []int) { + return file_example_v1_order_proto_rawDescGZIP(), []int{4} +} + +func (x *CreateOrderRequest) GetOrder() *Order { + if x != nil { + return x.Order + } + return nil +} + +// CreateOrderResponse is the response for creating an order. +type CreateOrderResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Order *Order `protobuf:"bytes,1,opt,name=order,proto3" json:"order,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateOrderResponse) Reset() { + *x = CreateOrderResponse{} + mi := &file_example_v1_order_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateOrderResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateOrderResponse) ProtoMessage() {} + +func (x *CreateOrderResponse) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_order_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateOrderResponse.ProtoReflect.Descriptor instead. +func (*CreateOrderResponse) Descriptor() ([]byte, []int) { + return file_example_v1_order_proto_rawDescGZIP(), []int{5} +} + +func (x *CreateOrderResponse) GetOrder() *Order { + if x != nil { + return x.Order + } + return nil +} + +// GetOrderRequest is the request for getting an order. +type GetOrderRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetOrderRequest) Reset() { + *x = GetOrderRequest{} + mi := &file_example_v1_order_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetOrderRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetOrderRequest) ProtoMessage() {} + +func (x *GetOrderRequest) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_order_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetOrderRequest.ProtoReflect.Descriptor instead. +func (*GetOrderRequest) Descriptor() ([]byte, []int) { + return file_example_v1_order_proto_rawDescGZIP(), []int{6} +} + +func (x *GetOrderRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +// GetOrderResponse is the response for getting an order. +type GetOrderResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Order *Order `protobuf:"bytes,1,opt,name=order,proto3" json:"order,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetOrderResponse) Reset() { + *x = GetOrderResponse{} + mi := &file_example_v1_order_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetOrderResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetOrderResponse) ProtoMessage() {} + +func (x *GetOrderResponse) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_order_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetOrderResponse.ProtoReflect.Descriptor instead. +func (*GetOrderResponse) Descriptor() ([]byte, []int) { + return file_example_v1_order_proto_rawDescGZIP(), []int{7} +} + +func (x *GetOrderResponse) GetOrder() *Order { + if x != nil { + return x.Order + } + return nil +} + +var File_example_v1_order_proto protoreflect.FileDescriptor + +const file_example_v1_order_proto_rawDesc = "" + + "\n" + + "\x16example/v1/order.proto\x12\n" + + "example.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"\xf5\x02\n" + + "\x05Order\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x17\n" + + "\auser_id\x18\x02 \x01(\tR\x06userId\x12+\n" + + "\x05items\x18\x03 \x03(\v2\x15.example.v1.OrderItemR\x05items\x12/\n" + + "\x06status\x18\x04 \x01(\x0e2\x17.example.v1.OrderStatusR\x06status\x129\n" + + "\n" + + "created_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\x12'\n" + + "\x05total\x18\a \x01(\v2\x11.example.v1.MoneyR\x05total\x12F\n" + + "\x10shipping_address\x18\b \x01(\v2\x1b.example.v1.ShippingAddressR\x0fshippingAddress\"\x9b\x01\n" + + "\tOrderItem\x12\x1d\n" + + "\n" + + "product_id\x18\x01 \x01(\tR\tproductId\x12!\n" + + "\fproduct_name\x18\x02 \x01(\tR\vproductName\x12\x1a\n" + + "\bquantity\x18\x03 \x01(\x05R\bquantity\x120\n" + + "\n" + + "unit_price\x18\x04 \x01(\v2\x11.example.v1.MoneyR\tunitPrice\"X\n" + + "\x05Money\x12#\n" + + "\rcurrency_code\x18\x01 \x01(\tR\fcurrencyCode\x12\x14\n" + + "\x05units\x18\x02 \x01(\x03R\x05units\x12\x14\n" + + "\x05nanos\x18\x03 \x01(\x05R\x05nanos\"\xa2\x01\n" + + "\x0fShippingAddress\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n" + + "\x06street\x18\x02 \x01(\tR\x06street\x12\x12\n" + + "\x04city\x18\x03 \x01(\tR\x04city\x12\x14\n" + + "\x05state\x18\x04 \x01(\tR\x05state\x12\x1f\n" + + "\vpostal_code\x18\x05 \x01(\tR\n" + + "postalCode\x12\x18\n" + + "\acountry\x18\x06 \x01(\tR\acountry\"=\n" + + "\x12CreateOrderRequest\x12'\n" + + "\x05order\x18\x01 \x01(\v2\x11.example.v1.OrderR\x05order\">\n" + + "\x13CreateOrderResponse\x12'\n" + + "\x05order\x18\x01 \x01(\v2\x11.example.v1.OrderR\x05order\"!\n" + + "\x0fGetOrderRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\";\n" + + "\x10GetOrderResponse\x12'\n" + + "\x05order\x18\x01 \x01(\v2\x11.example.v1.OrderR\x05order*\xb3\x01\n" + + "\vOrderStatus\x12\x1c\n" + + "\x18ORDER_STATUS_UNSPECIFIED\x10\x00\x12\x18\n" + + "\x14ORDER_STATUS_PENDING\x10\x01\x12\x1a\n" + + "\x16ORDER_STATUS_CONFIRMED\x10\x02\x12\x18\n" + + "\x14ORDER_STATUS_SHIPPED\x10\x03\x12\x1a\n" + + "\x16ORDER_STATUS_DELIVERED\x10\x04\x12\x1a\n" + + "\x16ORDER_STATUS_CANCELLED\x10\x052\xa5\x01\n" + + "\fOrderService\x12N\n" + + "\vCreateOrder\x12\x1e.example.v1.CreateOrderRequest\x1a\x1f.example.v1.CreateOrderResponse\x12E\n" + + "\bGetOrder\x12\x1b.example.v1.GetOrderRequest\x1a\x1c.example.v1.GetOrderResponseBH\n" + + "\x0ecom.example.v1P\x01Z4github.com/pubgo/protobuild/pkg/example/v1;examplev1b\x06proto3" + +var ( + file_example_v1_order_proto_rawDescOnce sync.Once + file_example_v1_order_proto_rawDescData []byte +) + +func file_example_v1_order_proto_rawDescGZIP() []byte { + file_example_v1_order_proto_rawDescOnce.Do(func() { + file_example_v1_order_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_example_v1_order_proto_rawDesc), len(file_example_v1_order_proto_rawDesc))) + }) + return file_example_v1_order_proto_rawDescData +} + +var file_example_v1_order_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_example_v1_order_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_example_v1_order_proto_goTypes = []any{ + (OrderStatus)(0), // 0: example.v1.OrderStatus + (*Order)(nil), // 1: example.v1.Order + (*OrderItem)(nil), // 2: example.v1.OrderItem + (*Money)(nil), // 3: example.v1.Money + (*ShippingAddress)(nil), // 4: example.v1.ShippingAddress + (*CreateOrderRequest)(nil), // 5: example.v1.CreateOrderRequest + (*CreateOrderResponse)(nil), // 6: example.v1.CreateOrderResponse + (*GetOrderRequest)(nil), // 7: example.v1.GetOrderRequest + (*GetOrderResponse)(nil), // 8: example.v1.GetOrderResponse + (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp +} +var file_example_v1_order_proto_depIdxs = []int32{ + 2, // 0: example.v1.Order.items:type_name -> example.v1.OrderItem + 0, // 1: example.v1.Order.status:type_name -> example.v1.OrderStatus + 9, // 2: example.v1.Order.created_at:type_name -> google.protobuf.Timestamp + 9, // 3: example.v1.Order.updated_at:type_name -> google.protobuf.Timestamp + 3, // 4: example.v1.Order.total:type_name -> example.v1.Money + 4, // 5: example.v1.Order.shipping_address:type_name -> example.v1.ShippingAddress + 3, // 6: example.v1.OrderItem.unit_price:type_name -> example.v1.Money + 1, // 7: example.v1.CreateOrderRequest.order:type_name -> example.v1.Order + 1, // 8: example.v1.CreateOrderResponse.order:type_name -> example.v1.Order + 1, // 9: example.v1.GetOrderResponse.order:type_name -> example.v1.Order + 5, // 10: example.v1.OrderService.CreateOrder:input_type -> example.v1.CreateOrderRequest + 7, // 11: example.v1.OrderService.GetOrder:input_type -> example.v1.GetOrderRequest + 6, // 12: example.v1.OrderService.CreateOrder:output_type -> example.v1.CreateOrderResponse + 8, // 13: example.v1.OrderService.GetOrder:output_type -> example.v1.GetOrderResponse + 12, // [12:14] is the sub-list for method output_type + 10, // [10:12] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name +} + +func init() { file_example_v1_order_proto_init() } +func file_example_v1_order_proto_init() { + if File_example_v1_order_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_example_v1_order_proto_rawDesc), len(file_example_v1_order_proto_rawDesc)), + NumEnums: 1, + NumMessages: 8, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_example_v1_order_proto_goTypes, + DependencyIndexes: file_example_v1_order_proto_depIdxs, + EnumInfos: file_example_v1_order_proto_enumTypes, + MessageInfos: file_example_v1_order_proto_msgTypes, + }.Build() + File_example_v1_order_proto = out.File + file_example_v1_order_proto_goTypes = nil + file_example_v1_order_proto_depIdxs = nil +} diff --git a/pkg/example/v1/user.pb.go b/pkg/example/v1/user.pb.go new file mode 100644 index 0000000..7512265 --- /dev/null +++ b/pkg/example/v1/user.pb.go @@ -0,0 +1,882 @@ +// Code generated by protoc-gen-retag. DO NOT EDIT. +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc v5.29.3 +// source: example/v1/user.proto + +package examplev1 + +import ( + _ "github.com/pubgo/protoc-gen-retag/pkg/retag" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// UserStatus represents the status of a user. +type UserStatus int32 + +const ( + UserStatus_USER_STATUS_UNSPECIFIED UserStatus = 0 + UserStatus_USER_STATUS_ACTIVE UserStatus = 1 + UserStatus_USER_STATUS_INACTIVE UserStatus = 2 + UserStatus_USER_STATUS_BANNED UserStatus = 3 +) + +// Enum value maps for UserStatus. +var ( + UserStatus_name = map[int32]string{ + 0: "USER_STATUS_UNSPECIFIED", + 1: "USER_STATUS_ACTIVE", + 2: "USER_STATUS_INACTIVE", + 3: "USER_STATUS_BANNED", + } + UserStatus_value = map[string]int32{ + "USER_STATUS_UNSPECIFIED": 0, + "USER_STATUS_ACTIVE": 1, + "USER_STATUS_INACTIVE": 2, + "USER_STATUS_BANNED": 3, + } +) + +func (x UserStatus) Enum() *UserStatus { + p := new(UserStatus) + *p = x + return p +} + +func (x UserStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (UserStatus) Descriptor() protoreflect.EnumDescriptor { + return file_example_v1_user_proto_enumTypes[0].Descriptor() +} + +func (UserStatus) Type() protoreflect.EnumType { + return &file_example_v1_user_proto_enumTypes[0] +} + +func (x UserStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use UserStatus.Descriptor instead. +func (UserStatus) EnumDescriptor() ([]byte, []int) { + return file_example_v1_user_proto_rawDescGZIP(), []int{0} +} + +// User represents a user in the system. +type User struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Unique identifier for the user. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" xml:"id,attr"` + // User's display name. + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty" xml:"name"` + // User's email address. + Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty" xml:"email" validate:"required,email"` + // User's age. + Age int32 `protobuf:"varint,4,opt,name=age,proto3" json:"age,omitempty" xml:"age,omitempty"` + // When the user was created. + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + // When the user was last updated. + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + // User's status. + Status UserStatus `protobuf:"varint,7,opt,name=status,proto3,enum=example.v1.UserStatus" json:"status,omitempty"` + // User's profile. + Profile *Profile `protobuf:"bytes,8,opt,name=profile,proto3" json:"profile,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *User) Reset() { + *x = User{} + mi := &file_example_v1_user_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *User) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*User) ProtoMessage() {} + +func (x *User) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_user_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use User.ProtoReflect.Descriptor instead. +func (*User) Descriptor() ([]byte, []int) { + return file_example_v1_user_proto_rawDescGZIP(), []int{0} +} + +func (x *User) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *User) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *User) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *User) GetAge() int32 { + if x != nil { + return x.Age + } + return 0 +} + +func (x *User) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *User) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +func (x *User) GetStatus() UserStatus { + if x != nil { + return x.Status + } + return UserStatus_USER_STATUS_UNSPECIFIED +} + +func (x *User) GetProfile() *Profile { + if x != nil { + return x.Profile + } + return nil +} + +// Profile contains user profile information. +type Profile struct { + state protoimpl.MessageState `protogen:"open.v1"` + AvatarUrl string `protobuf:"bytes,1,opt,name=avatar_url,json=avatarUrl,proto3" json:"avatar_url,omitempty" xml:"avatarUrl"` + Bio string `protobuf:"bytes,2,opt,name=bio,proto3" json:"bio,omitempty" xml:"bio,omitempty"` + Location string `protobuf:"bytes,3,opt,name=location,proto3" json:"location,omitempty" xml:"location,omitempty"` + Website string `protobuf:"bytes,4,opt,name=website,proto3" json:"website,omitempty" xml:"website,omitempty" validate:"omitempty,url"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Profile) Reset() { + *x = Profile{} + mi := &file_example_v1_user_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Profile) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Profile) ProtoMessage() {} + +func (x *Profile) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_user_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Profile.ProtoReflect.Descriptor instead. +func (*Profile) Descriptor() ([]byte, []int) { + return file_example_v1_user_proto_rawDescGZIP(), []int{1} +} + +func (x *Profile) GetAvatarUrl() string { + if x != nil { + return x.AvatarUrl + } + return "" +} + +func (x *Profile) GetBio() string { + if x != nil { + return x.Bio + } + return "" +} + +func (x *Profile) GetLocation() string { + if x != nil { + return x.Location + } + return "" +} + +func (x *Profile) GetWebsite() string { + if x != nil { + return x.Website + } + return "" +} + +// CreateUserRequest is the request for creating a user. +type CreateUserRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateUserRequest) Reset() { + *x = CreateUserRequest{} + mi := &file_example_v1_user_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateUserRequest) ProtoMessage() {} + +func (x *CreateUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_user_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateUserRequest.ProtoReflect.Descriptor instead. +func (*CreateUserRequest) Descriptor() ([]byte, []int) { + return file_example_v1_user_proto_rawDescGZIP(), []int{2} +} + +func (x *CreateUserRequest) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +// CreateUserResponse is the response for creating a user. +type CreateUserResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateUserResponse) Reset() { + *x = CreateUserResponse{} + mi := &file_example_v1_user_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateUserResponse) ProtoMessage() {} + +func (x *CreateUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_user_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateUserResponse.ProtoReflect.Descriptor instead. +func (*CreateUserResponse) Descriptor() ([]byte, []int) { + return file_example_v1_user_proto_rawDescGZIP(), []int{3} +} + +func (x *CreateUserResponse) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +// GetUserRequest is the request for getting a user. +type GetUserRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetUserRequest) Reset() { + *x = GetUserRequest{} + mi := &file_example_v1_user_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUserRequest) ProtoMessage() {} + +func (x *GetUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_user_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetUserRequest.ProtoReflect.Descriptor instead. +func (*GetUserRequest) Descriptor() ([]byte, []int) { + return file_example_v1_user_proto_rawDescGZIP(), []int{4} +} + +func (x *GetUserRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +// GetUserResponse is the response for getting a user. +type GetUserResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetUserResponse) Reset() { + *x = GetUserResponse{} + mi := &file_example_v1_user_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUserResponse) ProtoMessage() {} + +func (x *GetUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_user_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetUserResponse.ProtoReflect.Descriptor instead. +func (*GetUserResponse) Descriptor() ([]byte, []int) { + return file_example_v1_user_proto_rawDescGZIP(), []int{5} +} + +func (x *GetUserResponse) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +// UpdateUserRequest is the request for updating a user. +type UpdateUserRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateUserRequest) Reset() { + *x = UpdateUserRequest{} + mi := &file_example_v1_user_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateUserRequest) ProtoMessage() {} + +func (x *UpdateUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_user_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateUserRequest.ProtoReflect.Descriptor instead. +func (*UpdateUserRequest) Descriptor() ([]byte, []int) { + return file_example_v1_user_proto_rawDescGZIP(), []int{6} +} + +func (x *UpdateUserRequest) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +func (x *UpdateUserRequest) GetUpdateMask() *fieldmaskpb.FieldMask { + if x != nil { + return x.UpdateMask + } + return nil +} + +// UpdateUserResponse is the response for updating a user. +type UpdateUserResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateUserResponse) Reset() { + *x = UpdateUserResponse{} + mi := &file_example_v1_user_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateUserResponse) ProtoMessage() {} + +func (x *UpdateUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_user_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateUserResponse.ProtoReflect.Descriptor instead. +func (*UpdateUserResponse) Descriptor() ([]byte, []int) { + return file_example_v1_user_proto_rawDescGZIP(), []int{7} +} + +func (x *UpdateUserResponse) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +// DeleteUserRequest is the request for deleting a user. +type DeleteUserRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteUserRequest) Reset() { + *x = DeleteUserRequest{} + mi := &file_example_v1_user_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteUserRequest) ProtoMessage() {} + +func (x *DeleteUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_user_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteUserRequest.ProtoReflect.Descriptor instead. +func (*DeleteUserRequest) Descriptor() ([]byte, []int) { + return file_example_v1_user_proto_rawDescGZIP(), []int{8} +} + +func (x *DeleteUserRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +// DeleteUserResponse is the response for deleting a user. +type DeleteUserResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteUserResponse) Reset() { + *x = DeleteUserResponse{} + mi := &file_example_v1_user_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteUserResponse) ProtoMessage() {} + +func (x *DeleteUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_user_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteUserResponse.ProtoReflect.Descriptor instead. +func (*DeleteUserResponse) Descriptor() ([]byte, []int) { + return file_example_v1_user_proto_rawDescGZIP(), []int{9} +} + +// ListUsersRequest is the request for listing users. +type ListUsersRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListUsersRequest) Reset() { + *x = ListUsersRequest{} + mi := &file_example_v1_user_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListUsersRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListUsersRequest) ProtoMessage() {} + +func (x *ListUsersRequest) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_user_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListUsersRequest.ProtoReflect.Descriptor instead. +func (*ListUsersRequest) Descriptor() ([]byte, []int) { + return file_example_v1_user_proto_rawDescGZIP(), []int{10} +} + +func (x *ListUsersRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +func (x *ListUsersRequest) GetPageToken() string { + if x != nil { + return x.PageToken + } + return "" +} + +// ListUsersResponse is the response for listing users. +type ListUsersResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Users []*User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"` + NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListUsersResponse) Reset() { + *x = ListUsersResponse{} + mi := &file_example_v1_user_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListUsersResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListUsersResponse) ProtoMessage() {} + +func (x *ListUsersResponse) ProtoReflect() protoreflect.Message { + mi := &file_example_v1_user_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListUsersResponse.ProtoReflect.Descriptor instead. +func (*ListUsersResponse) Descriptor() ([]byte, []int) { + return file_example_v1_user_proto_rawDescGZIP(), []int{11} +} + +func (x *ListUsersResponse) GetUsers() []*User { + if x != nil { + return x.Users + } + return nil +} + +func (x *ListUsersResponse) GetNextPageToken() string { + if x != nil { + return x.NextPageToken + } + return "" +} + +var File_example_v1_user_proto protoreflect.FileDescriptor + +const file_example_v1_user_proto_rawDesc = "" + + "\n" + + "\x15example/v1/user.proto\x12\n" + + "example.v1\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x11retag/retag.proto\"\x96\x03\n" + + "\x04User\x12\"\n" + + "\x02id\x18\x01 \x01(\tB\x12\x82\xea0\x0e\n" + + "\x03xml\x12\aid,attrR\x02id\x12#\n" + + "\x04name\x18\x02 \x01(\tB\x0f\x82\xea0\v\n" + + "\x03xml\x12\x04nameR\x04name\x12D\n" + + "\x05email\x18\x03 \x01(\tB.\x82\xea0\f\n" + + "\x03xml\x12\x05email\x82\xea0\x1a\n" + + "\bvalidate\x12\x0erequired,emailR\x05email\x12*\n" + + "\x03age\x18\x04 \x01(\x05B\x18\x82\xea0\x14\n" + + "\x03xml\x12\rage,omitemptyR\x03age\x129\n" + + "\n" + + "created_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\x12.\n" + + "\x06status\x18\a \x01(\x0e2\x16.example.v1.UserStatusR\x06status\x12-\n" + + "\aprofile\x18\b \x01(\v2\x13.example.v1.ProfileR\aprofile\"\xfa\x01\n" + + "\aProfile\x123\n" + + "\n" + + "avatar_url\x18\x01 \x01(\tB\x14\x82\xea0\x10\n" + + "\x03xml\x12\tavatarUrlR\tavatarUrl\x12*\n" + + "\x03bio\x18\x02 \x01(\tB\x18\x82\xea0\x14\n" + + "\x03xml\x12\rbio,omitemptyR\x03bio\x129\n" + + "\blocation\x18\x03 \x01(\tB\x1d\x82\xea0\x19\n" + + "\x03xml\x12\x12location,omitemptyR\blocation\x12S\n" + + "\awebsite\x18\x04 \x01(\tB9\x82\xea0\x18\n" + + "\x03xml\x12\x11website,omitempty\x82\xea0\x19\n" + + "\bvalidate\x12\romitempty,urlR\awebsite\"9\n" + + "\x11CreateUserRequest\x12$\n" + + "\x04user\x18\x01 \x01(\v2\x10.example.v1.UserR\x04user\":\n" + + "\x12CreateUserResponse\x12$\n" + + "\x04user\x18\x01 \x01(\v2\x10.example.v1.UserR\x04user\" \n" + + "\x0eGetUserRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"7\n" + + "\x0fGetUserResponse\x12$\n" + + "\x04user\x18\x01 \x01(\v2\x10.example.v1.UserR\x04user\"v\n" + + "\x11UpdateUserRequest\x12$\n" + + "\x04user\x18\x01 \x01(\v2\x10.example.v1.UserR\x04user\x12;\n" + + "\vupdate_mask\x18\x02 \x01(\v2\x1a.google.protobuf.FieldMaskR\n" + + "updateMask\":\n" + + "\x12UpdateUserResponse\x12$\n" + + "\x04user\x18\x01 \x01(\v2\x10.example.v1.UserR\x04user\"#\n" + + "\x11DeleteUserRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"\x14\n" + + "\x12DeleteUserResponse\"N\n" + + "\x10ListUsersRequest\x12\x1b\n" + + "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" + + "\n" + + "page_token\x18\x02 \x01(\tR\tpageToken\"c\n" + + "\x11ListUsersResponse\x12&\n" + + "\x05users\x18\x01 \x03(\v2\x10.example.v1.UserR\x05users\x12&\n" + + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken*s\n" + + "\n" + + "UserStatus\x12\x1b\n" + + "\x17USER_STATUS_UNSPECIFIED\x10\x00\x12\x16\n" + + "\x12USER_STATUS_ACTIVE\x10\x01\x12\x18\n" + + "\x14USER_STATUS_INACTIVE\x10\x02\x12\x16\n" + + "\x12USER_STATUS_BANNED\x10\x032\x82\x03\n" + + "\vUserService\x12K\n" + + "\n" + + "CreateUser\x12\x1d.example.v1.CreateUserRequest\x1a\x1e.example.v1.CreateUserResponse\x12B\n" + + "\aGetUser\x12\x1a.example.v1.GetUserRequest\x1a\x1b.example.v1.GetUserResponse\x12K\n" + + "\n" + + "UpdateUser\x12\x1d.example.v1.UpdateUserRequest\x1a\x1e.example.v1.UpdateUserResponse\x12K\n" + + "\n" + + "DeleteUser\x12\x1d.example.v1.DeleteUserRequest\x1a\x1e.example.v1.DeleteUserResponse\x12H\n" + + "\tListUsers\x12\x1c.example.v1.ListUsersRequest\x1a\x1d.example.v1.ListUsersResponseBH\n" + + "\x0ecom.example.v1P\x01Z4github.com/pubgo/protobuild/pkg/example/v1;examplev1b\x06proto3" + +var ( + file_example_v1_user_proto_rawDescOnce sync.Once + file_example_v1_user_proto_rawDescData []byte +) + +func file_example_v1_user_proto_rawDescGZIP() []byte { + file_example_v1_user_proto_rawDescOnce.Do(func() { + file_example_v1_user_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_example_v1_user_proto_rawDesc), len(file_example_v1_user_proto_rawDesc))) + }) + return file_example_v1_user_proto_rawDescData +} + +var file_example_v1_user_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_example_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_example_v1_user_proto_goTypes = []any{ + (UserStatus)(0), // 0: example.v1.UserStatus + (*User)(nil), // 1: example.v1.User + (*Profile)(nil), // 2: example.v1.Profile + (*CreateUserRequest)(nil), // 3: example.v1.CreateUserRequest + (*CreateUserResponse)(nil), // 4: example.v1.CreateUserResponse + (*GetUserRequest)(nil), // 5: example.v1.GetUserRequest + (*GetUserResponse)(nil), // 6: example.v1.GetUserResponse + (*UpdateUserRequest)(nil), // 7: example.v1.UpdateUserRequest + (*UpdateUserResponse)(nil), // 8: example.v1.UpdateUserResponse + (*DeleteUserRequest)(nil), // 9: example.v1.DeleteUserRequest + (*DeleteUserResponse)(nil), // 10: example.v1.DeleteUserResponse + (*ListUsersRequest)(nil), // 11: example.v1.ListUsersRequest + (*ListUsersResponse)(nil), // 12: example.v1.ListUsersResponse + (*timestamppb.Timestamp)(nil), // 13: google.protobuf.Timestamp + (*fieldmaskpb.FieldMask)(nil), // 14: google.protobuf.FieldMask +} +var file_example_v1_user_proto_depIdxs = []int32{ + 13, // 0: example.v1.User.created_at:type_name -> google.protobuf.Timestamp + 13, // 1: example.v1.User.updated_at:type_name -> google.protobuf.Timestamp + 0, // 2: example.v1.User.status:type_name -> example.v1.UserStatus + 2, // 3: example.v1.User.profile:type_name -> example.v1.Profile + 1, // 4: example.v1.CreateUserRequest.user:type_name -> example.v1.User + 1, // 5: example.v1.CreateUserResponse.user:type_name -> example.v1.User + 1, // 6: example.v1.GetUserResponse.user:type_name -> example.v1.User + 1, // 7: example.v1.UpdateUserRequest.user:type_name -> example.v1.User + 14, // 8: example.v1.UpdateUserRequest.update_mask:type_name -> google.protobuf.FieldMask + 1, // 9: example.v1.UpdateUserResponse.user:type_name -> example.v1.User + 1, // 10: example.v1.ListUsersResponse.users:type_name -> example.v1.User + 3, // 11: example.v1.UserService.CreateUser:input_type -> example.v1.CreateUserRequest + 5, // 12: example.v1.UserService.GetUser:input_type -> example.v1.GetUserRequest + 7, // 13: example.v1.UserService.UpdateUser:input_type -> example.v1.UpdateUserRequest + 9, // 14: example.v1.UserService.DeleteUser:input_type -> example.v1.DeleteUserRequest + 11, // 15: example.v1.UserService.ListUsers:input_type -> example.v1.ListUsersRequest + 4, // 16: example.v1.UserService.CreateUser:output_type -> example.v1.CreateUserResponse + 6, // 17: example.v1.UserService.GetUser:output_type -> example.v1.GetUserResponse + 8, // 18: example.v1.UserService.UpdateUser:output_type -> example.v1.UpdateUserResponse + 10, // 19: example.v1.UserService.DeleteUser:output_type -> example.v1.DeleteUserResponse + 12, // 20: example.v1.UserService.ListUsers:output_type -> example.v1.ListUsersResponse + 16, // [16:21] is the sub-list for method output_type + 11, // [11:16] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name +} + +func init() { file_example_v1_user_proto_init() } +func file_example_v1_user_proto_init() { + if File_example_v1_user_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_example_v1_user_proto_rawDesc), len(file_example_v1_user_proto_rawDesc)), + NumEnums: 1, + NumMessages: 12, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_example_v1_user_proto_goTypes, + DependencyIndexes: file_example_v1_user_proto_depIdxs, + EnumInfos: file_example_v1_user_proto_enumTypes, + MessageInfos: file_example_v1_user_proto_msgTypes, + }.Build() + File_example_v1_user_proto = out.File + file_example_v1_user_proto_goTypes = nil + file_example_v1_user_proto_depIdxs = nil +} diff --git a/proto/example/v1/order.proto b/proto/example/v1/order.proto index 2c6cdaa..f13fff2 100644 --- a/proto/example/v1/order.proto +++ b/proto/example/v1/order.proto @@ -4,7 +4,7 @@ package example.v1; import "google/protobuf/timestamp.proto"; -option go_package = "github.com/pubgo/protobuild/proto/example/v1;examplev1"; +option go_package = "github.com/pubgo/protobuild/pkg/example/v1;examplev1"; option java_multiple_files = true; option java_package = "com.example.v1"; diff --git a/proto/example/v1/user.proto b/proto/example/v1/user.proto index 330023c..baa54d9 100644 --- a/proto/example/v1/user.proto +++ b/proto/example/v1/user.proto @@ -4,21 +4,40 @@ package example.v1; import "google/protobuf/field_mask.proto"; import "google/protobuf/timestamp.proto"; +import "retag/retag.proto"; -option go_package = "github.com/pubgo/protobuild/proto/example/v1;examplev1"; +option go_package = "github.com/pubgo/protobuild/pkg/example/v1;examplev1"; option java_multiple_files = true; option java_package = "com.example.v1"; // User represents a user in the system. message User { // Unique identifier for the user. - string id = 1; + string id = 1 [(retag.tags) = { + name: "xml" + value: "id,attr" + }]; // User's display name. - string name = 2; + string name = 2 [(retag.tags) = { + name: "xml" + value: "name" + }]; // User's email address. - string email = 3; + string email = 3 [ + (retag.tags) = { + name: "xml" + value: "email" + }, + (retag.tags) = { + name: "validate" + value: "required,email" + } + ]; // User's age. - int32 age = 4; + int32 age = 4 [(retag.tags) = { + name: "xml" + value: "age,omitempty" + }]; // When the user was created. google.protobuf.Timestamp created_at = 5; // When the user was last updated. @@ -39,10 +58,28 @@ enum UserStatus { // Profile contains user profile information. message Profile { - string avatar_url = 1; - string bio = 2; - string location = 3; - string website = 4; + string avatar_url = 1 [(retag.tags) = { + name: "xml" + value: "avatarUrl" + }]; + string bio = 2 [(retag.tags) = { + name: "xml" + value: "bio,omitempty" + }]; + string location = 3 [(retag.tags) = { + name: "xml" + value: "location,omitempty" + }]; + string website = 4 [ + (retag.tags) = { + name: "xml" + value: "website,omitempty" + }, + (retag.tags) = { + name: "validate" + value: "omitempty,url" + } + ]; } // CreateUserRequest is the request for creating a user. diff --git a/protobuf.yaml b/protobuf.yaml index 2694587..4921e07 100644 --- a/protobuf.yaml +++ b/protobuf.yaml @@ -1,5 +1,9 @@ -checksum: ae1e52134aaa1770db37e83bf3cea605874942cb +checksum: db5860630b46e9b8b0c59d190bf5639ab2da2b17 vendor: .proto +base: + out: ./pkg + paths: import + module: github.com/pubgo/protobuild/pkg root: - proto includes: @@ -7,20 +11,15 @@ includes: deps: - name: google/protobuf url: /usr/local/include/google/protobuf + - name: retag + url: github.com/pubgo/protoc-gen-retag + path: proto/retag + version: v0.0.5 plugins: - name: go - out: pkg - opt: - - paths=source_relative - name: retag - opt: - - paths=source_relative - - output=pkg - - name: test linter: rules: - included_paths: [] - excluded_paths: [] enabled_rules: - core::0131::http-method - core::0131::http-body @@ -28,4 +27,3 @@ linter: disabled_rules: - all format_type: yaml - ignore_comment_disables_flag: false