// 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 }