Kong, The Beautiful CLI Framework
There are few things more enjoyable than using a well designed command-line interface, except perhaps creating one. Today I'm going to talk about my favourite Go CLI library, Kong, by Alec Thomas.
Why Kong is King
Declarative approach
For these examples I use the xair-cli package. The CLI struct defines the root of the CLI and embeds a Config struct:
type CLI struct {
Config `embed:"" prefix:"" help:"The configuration for the CLI."`
}
That Config struct then uses tags to define:
- default values
- environment variables
- long and short versions of the flag
- help output
This is clean, declarative and easy to read.
Subcommands as structs
Many CLIs require complex subcommand structures and how a library allows you to layout your structure can make a big difference to readability.
By defining subcommands as struct fields tagged with cmd defining complex structures becomes possible:
type CLI struct {
Main MainCmdGroup `cmd:""`
Strip StripCmdGroup `cmd:""`
Bus BusCmdGroup `cmd:""`
Headamp HeadampCmdGroup `cmd:""`
Snapshot SnapshotCmdGroup `cmd:""`
Dca DCACmdGroup `cmd:""`
}
Where each one of these can be it's own command or subcommand group:
type MainCmdGroup struct {
Mute MainMuteCmd `cmd:""`
Fader MainFaderCmd `cmd:""`
Fadein MainFadeinCmd `cmd:""`
Fadeout MainFadeoutCmd `cmd:""`
Eq MainEqCmdGroup `cmd:""`
Comp MainCompCmdGroup `cmd:""`
}
This approach makes it straightforward to design CLI structures of arbitrary depth.
Supports all the goodies
Kong offers many features allowing developers to create powerful and intuitive interfaces to fit a variety of domains. These include:
- Custom validators
- Branching off positional arguments
- Argument definitions with slices, maps and pointers
- Help customisation at a granular level
For example, the xair-cli package models an 18-channel rack mixer consisting of 18 input strips, 6 auxiliary buses, a Main L/R bus and a wide variety of control types such as EQ, effects, and gain sliders. When we look at the OSC spec we see addresses such as /ch/01/mix/01/level and /bus/1/eq/1/f.
The pattern here is:
- select the channel type and index into it
- select the parameter type and index into it
- select the item you wish to control.
So ideally we want to represent these commands like so:
xair-cli strip <index> send <send-index> [<level>]
xair-cli bus <index> eq <band> freq [<freq>]
Since Kong allows us to branch off positional arguments we can achieve this by embedding structs tagged with arg into command structs:
type BusCmdGroup struct {
Index struct {
Index int `arg:""`
} `arg:"" help:"Control a specific bus by index."`
}
If we repeat this pattern:
type BusEqCmdGroup struct {
Band struct {
Band *int `arg:""`
} `arg:"" help:"Control a specific EQ band of the bus."`
}
The final result is an interface that closely aligns with the format of the original OSC spec.
Highly extensible
Being a well designed library it supports all kinds of plugins, here are two of my favourites:
- mango-kong for generating man pages.
- kong-completion for generating shell completion scripts.
Both integrate seamlessly with the library allowing a developer to extend their CLI by defining fields on the CLI struct:
type CLI struct {
Man mangokong.ManFlag `help:"Print man page."`
Completion kongcompletion.Completion `help:"Generate completions."`
}
Quick, simple and effective!
Other CLI frameworks
There are some other fantastic frameworks as well including:
- Cobra: Probably the most well known framework and the first I was introduced to through Ricardo Gerardi’s excellent book, Powerful Command-Line Applications in Go.
The following two also take a struct approach to command tree layout although they do not utilise tags as Kong does.
Conclusion
Kong is a beautiful, powerful and highly flexible library perfect for writing all kinds of command-line interfaces. It's my personal favourite but I encourage you to explore the different libraries available, they all offer their own unique take.
Further notes:
- Historically, Kong has lacked robust shell completion, but the recently released kong-completion plugin is compatible with the latest version. While this plugin works for bash, zsh, and fish, it does not currently extend to PowerShell.
Subscribe to this blog's RSS feed