Go: implementing subcommands with the Go flag package

It is not obvious, but it is possible to implement sub-commands using the Go flag package. Below is an example from the Simple IoT source code. This is setup in that it allows us to have our own set of flags in the top level main() function, and then the server can have its own flags in the server package. They key is to deliniate the two sets of flags with a sub command – in this case serve. This will be important for programs that want to use the SIOT server as a package, and yet have a significant amount of functionality beyond that – the main program will likely need its own flags.

There are many nice flag libraries for Go, but its nice to be able to stick with the stdlib a bit long for command line flags. If the NATS server is still using the Go flag package, it is probably good enough for most things.

func main() {
	// global options
	flags := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	flagVersion := flags.Bool("version", false, "Print app version")
	flags.Usage = func() {
		fmt.Println("usage: siot [OPTION]... COMMAND [OPTION]...")
		fmt.Println("Global options:")
		flags.PrintDefaults()
		fmt.Println()
		fmt.Println("Available commands:")
		fmt.Println("  * serve")
	}

	flags.Parse(os.Args[1:])

	if *flagVersion {
		fmt.Println(version)
		os.Exit(0)
	}

	log.Printf("SimpleIOT %v\n", version)

	// extract sub command and its arguments
	args := flags.Args()

	if len(args) < 1 {
		// run serve command by default
		args = []string{"serve"}
	}

	switch args[0] {
	case "serve":
		if err := runServer(args[1:], version); err != nil {
			log.Println("Simple IoT stopped, reason: ", err)
		}
	default:
		log.Fatal("Unknown command, options: serve")
	}
}