cmdr 03 - 用流式接口定義命令行參數處理選項

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 的基礎上展開特殊分支來進行測試。

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章