- go.mod(module gitea.alterminal.com/alterminal/teai,Go 1.26) - cmd/teai/main.go:程式進入點,結束碼交由 internal/cli.Run 處理 - internal/cli:命令列架構(引數剖析、子命令分派、version 命令) - internal/cli/cli_test.go:用法輸出、version、未知命令等基本測試 - README.md:專案說明、建置與開發慣例(繁體中文) - .gitignore:建置產物、測試輸出、環境檔 - 僅使用標準庫,可離線建置;授權待維護者選定 Refs #1
115 lines
3.3 KiB
Go
115 lines
3.3 KiB
Go
// 套件 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")
|
||
}
|