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:

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 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:

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:

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:

Subscribe to this blog's RSS feed