cmdr 03 - 用流式介面定義命令列引數處理選項
基於 v0.2.17
轉眼已經來到了 cmdr
v0.2.17 了,為了解決此前版本中關於子命令和選項定義語句的太多巢狀的問題,我們實現了流式呼叫介面(Fluent APIs)。
cmdr
是我釋出的一個開源的 golang 命令列引數處理器。它是 golang flags
的替代品。之所以釋出它,是因為已有的 command line UI 三方包無法滿足我的日常要求,迫不得己自己造一個。如果尚未有了解 cmdr
怎麼使用的,不妨抽空瀏覽我的早前文章,以求獲得一些基本概念:
稍後我會繼續針對 cmdr
的用法做介紹文章。
至於本文呢 ,只是簡單講述一下如何使用 cmdr
的流式介面(Fluent API)來完成定義。
定義 RootCommand
root := cmdr.Root("aa", "1.0.1").Header("aa - test for cmdr - no version")
rootCmd = root.RootCommand()
複製程式碼
第二句是拿到一個 *cmdr.Command
指標,稍後可以做一下相關的其它操作。
此外,rootCmd
作為函式返回值,也便於被用到向 cmdr.Exec()
做傳遞引數。
func buildCmds() *cmdr.Command {
root := ...
rootCmdr = root.RootCommand()
...
return rootCmdr
}
func main() {
if err := cmdr.Exec(buildCmds()); err != nil {
logrus.Fatal(err)
}
}
複製程式碼
定義命令 Command
頂級的命令其實就是 RootCommand 的 子命令,所以:
co := root.NewSubCommand().
Titles("ms", "micro-service").
Description("", "").
Group("")
複製程式碼
在這裡,你可以做的定義基本上和 cmdr.Command
結構定義是相匹配的,所以你可以使用 OptCmd
介面所支援的方法來完成一條命令的定義:
// OptCmd to support fluent api of cmdr.
// see also: cmdr.Root().NewSubCommand()/.NewFlag()
OptCmd interface {
Titles(short, long string, aliases ...string) (opt OptCmd)
Short(short string) (opt OptCmd)
Long(long string) (opt OptCmd)
Aliases(ss ...string) (opt OptCmd)
Description(oneLine, long string) (opt OptCmd)
Examples(examples string) (opt OptCmd)
Group(group string) (opt OptCmd)
Hidden(hidden bool) (opt OptCmd)
Deprecated(deprecation string) (opt OptCmd)
Action(action func(cmd *Command, args []string) (err error)) (opt OptCmd)
// FlagAdd(flg *Flag) (opt OptCmd)
// SubCommand(cmd *Command) (opt OptCmd)
PreAction(pre func(cmd *Command, args []string) (err error)) (opt OptCmd)
PostAction(post func(cmd *Command, args []string)) (opt OptCmd)
TailPlaceholder(placeholder string) (opt OptCmd)
NewFlag(typ OptFlagType) (opt OptFlag)
NewSubCommand() (opt OptCmd)
OwnerCommand() (opt OptCmd)
SetOwner(opt OptCmd)
RootCommand() *RootCommand
}
複製程式碼
定義選項 Flag
對於每條命令,你都可以為其附著一系列的選項,這是通過 NewFlag
來完成的:
co.NewFlag(cmdr.OptFlagTypeUint).
Titles("t", "retry").
Description("", "").
Group("").
DefaultValue(3, "RETRY")
複製程式碼
類似的,所有 OptFlag
介面支援的方法都可以用在這裡:
// OptFlag to support fluent api of cmdr.
// see also: cmdr.Root().NewSubCommand()/.NewFlag()
OptFlag interface {
Titles(short, long string, aliases ...string) (opt OptFlag)
Short(short string) (opt OptFlag)
Long(long string) (opt OptFlag)
Aliases(ss ...string) (opt OptFlag)
Description(oneLine, long string) (opt OptFlag)
Examples(examples string) (opt OptFlag)
Group(group string) (opt OptFlag)
Hidden(hidden bool) (opt OptFlag)
Deprecated(deprecation string) (opt OptFlag)
Action(action func(cmd *Command, args []string) (err error)) (opt OptFlag)
ToggleGroup(group string) (opt OptFlag)
DefaultValue(val interface{}, placeholder string) (opt OptFlag)
ExternalTool(envKeyName string) (opt OptFlag)
OwnerCommand() (opt OptCmd)
SetOwner(opt OptCmd)
RootCommand() *RootCommand
}
複製程式碼
重複以上步驟
按照遞迴的定義方案,反覆重複,你就可以得到一套完整的命令列介面定義了。
我得承認,這個方式避免了傳統方式的結構巢狀問題,可讀性上是要好很多的了。但它的問題也很明顯,你需要在程式啟動時額外消耗一點點 CPU 來完成上述定義指令的執行,相比而言,這比傳統方式略微費事了 a little bit。但我還要承認,這個消耗,人是感受不出來的。
小結
流式介面並未帶來任何新鮮東西。它只是改善了定義 Command Line UI 的友善性。
cmdr
同時支援兩種方式以支援你的命令列引數定義。
版本計劃和規劃
v0.2.17:在這個版本中,我們計劃做一系列 gocov 自測工作,以便掃蕩此前功能性推進過程中的潛在隱患。在某些臨界條件滿足的場景下,cmdr
也許會工作的不令人滿意,因此是時候自檢一下下了。
新的版本很快就會發布以覆蓋 v0.2.17 的一系列子版本。
總的來說,如無意外,我們遵循古老的傳統,奇數版本號程式碼著 stable 釋出。如果有,偶數版本屬於臨時性的、又或是試驗性的釋出。
更多情況下,我們會在奇數版本號上加以字尾以完成線上測試,例如 v0.2.17-rel01。它們往往是為了配合開源 CI/CD 而產生的。
如果我們有實驗性的想法,那麼通常會在 devel
的基礎上展開特殊分支來進行測試。