Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4fb641094 |
+36
-3
@@ -150,10 +150,31 @@ var globalFlags = map[string]bool{
|
|||||||
"--timeout": true,
|
"--timeout": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reservedFlags 回傳 dispatch 不得搶走的旗標名稱(含 -- 前綴):這些
|
||||||
|
// 旗標屬於目前命令(或其子命令),即使與全域選項同名也必須原樣留給
|
||||||
|
// 命令剖析。目前唯一與全域同名的是 login add 的 --url/--token
|
||||||
|
// (README「teai login add --url … --token …」)。其餘命令的旗標
|
||||||
|
// (--repo、--has-work、--mine……)本來就不在 globalFlags 內,不受影響。
|
||||||
|
func reservedFlags(name string) map[string]bool {
|
||||||
|
reserved := make(map[string]bool)
|
||||||
|
for _, f := range commandReservedFlags[name] {
|
||||||
|
reserved[f] = true
|
||||||
|
}
|
||||||
|
return reserved
|
||||||
|
}
|
||||||
|
|
||||||
|
// commandReservedFlags 登記每個命令「與全域選項同名」的自有旗標。
|
||||||
|
// 命令旗標優先留給命令;新命令有同姓旗標時在這裡登記(#18)。
|
||||||
|
var commandReservedFlags = map[string][]string{
|
||||||
|
"login": {"--url", "--token"},
|
||||||
|
}
|
||||||
|
|
||||||
// extractGlobals 從 args 中「抽取」全域選項(可出現在命令之後、與命令
|
// extractGlobals 從 args 中「抽取」全域選項(可出現在命令之後、與命令
|
||||||
// 旗標交錯),其餘引數依原順序保留給子命令剖析。未知的選項一律留給
|
// 旗標交錯),其餘引數依原順序保留給子命令剖析。未知的選項一律留給
|
||||||
// 子命令處理(可能是命令旗標,如 --has-work),不在這裡報錯。
|
// 子命令處理(可能是命令旗標,如 --has-work),不在這裡報錯。
|
||||||
func extractGlobals(g *Globals, args []string) ([]string, error) {
|
// reserved 是目前命令保留的旗標名稱(含 -- 前綴,見 reservedFlags):
|
||||||
|
// 列於其中的選項即使與全域同名也原樣留給命令,命令旗標優先(#18)。
|
||||||
|
func extractGlobals(g *Globals, args []string, reserved map[string]bool) ([]string, error) {
|
||||||
rest := make([]string, 0, len(args))
|
rest := make([]string, 0, len(args))
|
||||||
applyGlobal := func(name, value string) error {
|
applyGlobal := func(name, value string) error {
|
||||||
switch name {
|
switch name {
|
||||||
@@ -197,6 +218,17 @@ func extractGlobals(g *Globals, args []string) ([]string, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
name, inline, hasInline := strings.Cut(arg, "=")
|
name, inline, hasInline := strings.Cut(arg, "=")
|
||||||
|
if reserved[name] {
|
||||||
|
// 命令保留旗標:即使與全域選項同名也留給命令剖析(#18)。
|
||||||
|
rest = append(rest, arg)
|
||||||
|
if !hasInline && i+1 < len(args) {
|
||||||
|
// 旗標值跟在下一個引數;一併保留,不當成全域值。
|
||||||
|
rest = append(rest, args[i+1])
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
_, ok := globalFlags[name]
|
_, ok := globalFlags[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
// 不是全域選項:留給子命令(可能是 --has-work 這類命令旗標)。
|
// 不是全域選項:留給子命令(可能是 --has-work 這類命令旗標)。
|
||||||
@@ -293,11 +325,12 @@ func parseGlobals(g *Globals, args []string) ([]string, error) {
|
|||||||
// 旗標剖析交由各命令自行處理(不同命令有不同旗標,如 pulls --mine);
|
// 旗標剖析交由各命令自行處理(不同命令有不同旗標,如 pulls --mine);
|
||||||
// 全域選項允許出現在命令之後(README 範例:teai next --output table),
|
// 全域選項允許出現在命令之後(README 範例:teai next --output table),
|
||||||
// dispatch 先從 args 剝離全域選項併入 Globals,其餘(含命令自己的旗標,
|
// dispatch 先從 args 剝離全域選項併入 Globals,其餘(含命令自己的旗標,
|
||||||
// 如 --has-work)原樣交給命令,不會誤判為未知全域選項。
|
// 如 --has-work)原樣交給命令,不會誤判為未知全域選項。與全域選項
|
||||||
|
// 同名的命令旗標(登記於 commandReservedFlags)優先留給命令(#18)。
|
||||||
func dispatch(env *Env, name string, args []string) int {
|
func dispatch(env *Env, name string, args []string) int {
|
||||||
cmd := commands[name]
|
cmd := commands[name]
|
||||||
g := env.Globals
|
g := env.Globals
|
||||||
rest, err := extractGlobals(&g, args)
|
rest, err := extractGlobals(&g, args, reservedFlags(name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(env.Err, "teai: %v\n\n", err)
|
fmt.Fprintf(env.Err, "teai: %v\n\n", err)
|
||||||
printUsage(env.Err)
|
printUsage(env.Err)
|
||||||
|
|||||||
@@ -107,6 +107,56 @@ func TestParseGlobalsStopsAtCommand(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestExtractGlobalsReservedFlags 驗證 #18:與全域同名的命令旗標
|
||||||
|
// (登記於 commandReservedFlags)必須留給命令,其餘全域選項照常抽取。
|
||||||
|
func TestExtractGlobalsReservedFlags(t *testing.T) {
|
||||||
|
g := defaultGlobals()
|
||||||
|
rest, err := extractGlobals(&g, []string{
|
||||||
|
"add",
|
||||||
|
"--url", "https://srv.example.com", // login add 自有旗標:保留
|
||||||
|
"--token=tok", // 同上(inline 形式):保留
|
||||||
|
"--name", "test-site",
|
||||||
|
"--output", "table", // 非保留:仍是全域
|
||||||
|
}, reservedFlags("login"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
want := []string{"add", "--url", "https://srv.example.com", "--token=tok", "--name", "test-site"}
|
||||||
|
if len(rest) != len(want) {
|
||||||
|
t.Fatalf("rest = %v, want %v", rest, want)
|
||||||
|
}
|
||||||
|
for i := range want {
|
||||||
|
if rest[i] != want[i] {
|
||||||
|
t.Fatalf("rest = %v, want %v", rest, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if g.Output != gitea.FormatTable {
|
||||||
|
t.Fatalf("output = %v, want table", g.Output)
|
||||||
|
}
|
||||||
|
if g.URL != defaultGlobals().URL || g.Token != "" {
|
||||||
|
t.Fatalf("reserved flags must not leak into globals: %+v", g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExtractGlobalsNonReservedCommand 驗證 #13 行為不回歸:未登記保留
|
||||||
|
// 旗標的命令(如 next/members/pulls),命令後的全域選項照常抽取、
|
||||||
|
// 命令自己的旗標(--has-work 等)原樣保留。
|
||||||
|
func TestExtractGlobalsNonReservedCommand(t *testing.T) {
|
||||||
|
g := defaultGlobals()
|
||||||
|
rest, err := extractGlobals(&g, []string{
|
||||||
|
"members", "--has-work", "--output", "table",
|
||||||
|
}, reservedFlags("members"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(rest) != 2 || rest[0] != "members" || rest[1] != "--has-work" {
|
||||||
|
t.Fatalf("rest = %v, want [members --has-work]", rest)
|
||||||
|
}
|
||||||
|
if g.Output != gitea.FormatTable {
|
||||||
|
t.Fatalf("output = %v, want table", g.Output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// errString 把字串轉成 error(測試輔助)。
|
// errString 把字串轉成 error(測試輔助)。
|
||||||
type errStr string
|
type errStr string
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user