// 套件 cli 提供 teai 的命令列架構:引數剖析、子命令分派與輸出。 // // 設計目標是讓之後新增子命令時只需註冊一個 command 結構, // 不需要改動分派邏輯;也讓核心功能可以被測試(Run 接受 io.Writer)。 package cli import ( "flag" "fmt" "io" "sort" ) // Version 是目前開發中的版本號。採用語意化版本;正式發佈前以 0 開頭。 var Version = "0.1.0-dev" // ExitCode 是 Run 回傳的行程結束碼。 type ExitCode int // 常見的結束碼定義。0 表示成功,其餘對應常見的命令列錯誤情境。 const ( ExitOK ExitCode = iota // 成功 ExitUsage // 引數或子命令錯誤 ExitInternal // 內部錯誤(不該發生) ) // command 定義一個子命令:名稱、一行說明與實作。 type command struct { name string usage string run func(env *Env, args []string) error } // Env 聚集一次執行所需的輸出目標,便於測試時替換。 type Env struct { // Out 是一般輸出(命令結果)。 Out io.Writer // Err 是診斷輸出(錯誤、警告)。 Err io.Writer } // commands 是已註冊的子命令表。新增功能時在這裡註冊即可。 var commands = map[string]*command{ "version": { name: "version", usage: "顯示版本資訊", run: runVersion, }, } // Run 剖析引數並分派到對應子命令,回傳行程結束碼。 // // 無引數或要求說明(-h/--help)時印出用法;未知子命令回 ExitUsage。 func Run(stdout, stderr io.Writer, args []string) int { env := &Env{Out: stdout, Err: stderr} if len(args) == 0 { printUsage(env.Out) return int(ExitOK) } switch args[0] { case "-h", "--help", "help": printUsage(env.Out) return int(ExitOK) case "-v", "--version", "version": return dispatch(env, "version", args[1:]) default: name := args[0] if _, ok := commands[name]; !ok { fmt.Fprintf(stderr, "teai: unknown command %q\n\n", name) printUsage(stderr) return int(ExitUsage) } return dispatch(env, name, args[1:]) } } // dispatch 執行已註冊的子命令,把錯誤轉成結束碼並輸出。 func dispatch(env *Env, name string, args []string) int { cmd := commands[name] fs := flag.NewFlagSet("teai "+name, flag.ContinueOnError) fs.SetOutput(env.Err) if err := fs.Parse(args); err != nil { return int(ExitUsage) } if err := cmd.run(env, fs.Args()); err != nil { fmt.Fprintf(env.Err, "teai %s: %v\n", name, err) return int(ExitInternal) } return int(ExitOK) } // runVersion 輸出版本資訊。 func runVersion(env *Env, args []string) error { fmt.Fprintf(env.Out, "teai version %s\n", Version) return nil } // printUsage 印出用法與已註冊的子命令清單(依名稱排序)。 func printUsage(w io.Writer) { fmt.Fprintf(w, "teai — Gitea CLI 輔助工具(tea + AI)\n\n") fmt.Fprintf(w, "用法:\n teai [命令] [參數]\n\n命令:\n") names := make([]string, 0, len(commands)) for name := range commands { names = append(names, name) } sort.Strings(names) for _, name := range names { fmt.Fprintf(w, " %-10s %s\n", name, commands[name].usage) } fmt.Fprintf(w, "\n說明:\n -h, --help 顯示說明\n -v, --version 顯示版本\n") fmt.Fprintf(w, "\n更多資訊:https://gitea.alterminal.com/alterminal/teai\n") }