Add a shared error-code catalog so failures can be traced by stable codes.
ci / vet-and-test (push) Successful in 3s
ci / docker-checkout-hostgateway (push) Successful in 3s

Services can resolve legacy aliases and expose the catalog over CLI and HTTP instead of ad-hoc error strings.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
weichen
2026-09-29 13:43:12 +00:00
co-authored by Cursor
commit bd53e9ba79
33 changed files with 2489 additions and 0 deletions
+176
View File
@@ -0,0 +1,176 @@
// Package cli 实现 errcode 命令:list / show / search / validate / export / serve。
package cli
import (
"encoding/json"
"flag"
"fmt"
"io"
"os"
"strings"
"error-code-system/errcode"
"error-code-system/server"
)
// Main 执行命令行并返回进程退出码。
func Main(args []string) int {
return Run(args, os.Stdout, os.Stderr)
}
// Run 执行命令。stdout 写结果,stderr 写用法与错误。
func Run(args []string, stdout, stderr io.Writer) int {
if len(args) == 0 {
fmt.Fprintln(stderr, usage)
return 2
}
switch args[0] {
case "list":
return cmdList(args[1:], stdout, stderr)
case "show":
return cmdShow(args[1:], stdout, stderr)
case "search":
return cmdSearch(args[1:], stdout, stderr)
case "validate":
return cmdValidate(args[1:], stdout, stderr)
case "export":
return cmdExport(args[1:], stdout, stderr)
case "serve":
return cmdServe(args[1:], stderr)
case "-h", "--help", "help":
fmt.Fprintln(stdout, usage)
return 0
default:
fmt.Fprintf(stderr, "未知命令 %q\n%s\n", args[0], usage)
return 2
}
}
const usage = `用法:
errcode list [--domain 域] [--json]
errcode show <码或别名>
errcode search <关键词>
errcode validate <码或别名>
errcode export [--domain 域]
errcode serve [--addr :8091]`
func registry() *errcode.Registry { return errcode.Default }
func cmdList(args []string, stdout, stderr io.Writer) int {
fs := flag.NewFlagSet("list", flag.ContinueOnError)
fs.SetOutput(stderr)
domain := fs.String("domain", "", "只列出该域")
asJSON := fs.Bool("json", false, "以 JSON 输出")
if err := fs.Parse(args); err != nil {
return 2
}
defs := registry().List(*domain)
if *asJSON {
enc := json.NewEncoder(stdout)
enc.SetEscapeHTML(false)
enc.SetIndent("", " ")
if err := enc.Encode(defs); err != nil {
fmt.Fprintln(stderr, err)
return 1
}
return 0
}
for _, def := range defs {
fmt.Fprintf(stdout, "%s\t%d\t%s\t%s\n", def.Code, def.HTTP, def.Retry, def.Summary)
}
return 0
}
func cmdShow(args []string, stdout, stderr io.Writer) int {
if len(args) != 1 {
fmt.Fprintln(stderr, "用法: errcode show <码或别名>")
return 2
}
def, ok := registry().Lookup(args[0])
if !ok {
fmt.Fprintf(stderr, "未登记: %s(回落 %s)\n", args[0], errcode.SysUnknownCode)
return 1
}
enc := json.NewEncoder(stdout)
enc.SetEscapeHTML(false)
enc.SetIndent("", " ")
if err := enc.Encode(def); err != nil {
fmt.Fprintln(stderr, err)
return 1
}
return 0
}
func cmdSearch(args []string, stdout, stderr io.Writer) int {
if len(args) < 1 {
fmt.Fprintln(stderr, "用法: errcode search <关键词>")
return 2
}
q := strings.Join(args, " ")
for _, def := range registry().Search(q) {
fmt.Fprintf(stdout, "%s\t%s\n", def.Code, def.Summary)
}
return 0
}
func cmdValidate(args []string, stdout, stderr io.Writer) int {
if len(args) != 1 {
fmt.Fprintln(stderr, "用法: errcode validate <码或别名>")
return 2
}
def, ok := registry().Lookup(args[0])
resolved := def
if !ok {
resolved = registry().Resolve(args[0])
}
fmt.Fprintf(stdout, "input=%s known=%t resolved=%s\n", args[0], ok, resolved.Code)
if !ok {
return 1
}
return 0
}
func cmdExport(args []string, stdout, stderr io.Writer) int {
fs := flag.NewFlagSet("export", flag.ContinueOnError)
fs.SetOutput(stderr)
domain := fs.String("domain", "", "只导出该域")
if err := fs.Parse(args); err != nil {
return 2
}
var data []byte
var err error
if *domain == "" {
data, err = registry().ExportJSON()
} else {
encBuf := &strings.Builder{}
enc := json.NewEncoder(encBuf)
enc.SetEscapeHTML(false)
enc.SetIndent("", " ")
err = enc.Encode(registry().List(*domain))
data = []byte(encBuf.String())
}
if err != nil {
fmt.Fprintln(stderr, err)
return 1
}
if _, err := stdout.Write(data); err != nil {
fmt.Fprintln(stderr, err)
return 1
}
return 0
}
func cmdServe(args []string, stderr io.Writer) int {
fs := flag.NewFlagSet("serve", flag.ContinueOnError)
fs.SetOutput(stderr)
addr := fs.String("addr", server.DefaultAddr, "监听地址")
if err := fs.Parse(args); err != nil {
return 2
}
fmt.Fprintf(stderr, "errcode serve %s\n", *addr)
if err := server.New(registry()).ListenAndServe(*addr); err != nil {
fmt.Fprintln(stderr, err)
return 1
}
return 0
}