Kong, The Beautiful CLI Framework

There is a unique elegance to a well designed command-line interface (CLI), and the joy of using one is surpassed only by the satisfaction of creating it. Today I'm going to talk about my favourite Go CLI library, Kong, by Alec Thomas.

I’ll preface this by saying that while Kong is now my favourite CLI framework, it wasn’t always my first choice. Early on, I built many of my CLI tools using Cobra, inspired by Ricardo Gerardi’s excellent book, Powerful Command-Line Applications in Go, which I still highly recommend. For smaller-scale CLIs, I usually turn to urfave/cli or Peter Bourgon’s Flags First package, both of which are excellent libraries as well.


Why Kong is King

Declarative approach

Kong’s approach, leveraging Go structs and tags, eases the transition from CLI concept to code. For the purposes of 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:

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:

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:

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 matches the format typically expected in audio CLIs.

Highly extensible

Being a well designed library it supports all kinds of plugins, here are two of my favourites:

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!


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:

Subscribe to this blog's RSS feed