Skip to content

Instantly share code, notes, and snippets.

@miguelmartin75
Last active March 17, 2026 05:38
Show Gist options
  • Select an option

  • Save miguelmartin75/525cedfad798f9f3f541eb929706a7a0 to your computer and use it in GitHub Desktop.

Select an option

Save miguelmartin75/525cedfad798f9f3f541eb929706a7a0 to your computer and use it in GitHub Desktop.
commands for core:flags
package commandflags
import "core:flags"
import "core:fmt"
import "core:io"
import "core:os"
import "core:strings"
Command :: struct {
name: string,
arg_type: typeid,
fn_ptr: rawptr,
parsing_style: flags.Parsing_Style,
run: proc(cmd: ^Command, argv: []string),
}
make_cmd :: proc(
$T: typeid,
name: string,
f: proc(_: T),
parsing_style: flags.Parsing_Style = .Odin,
) -> Command {
return Command {
name = name,
arg_type = typeid_of(T),
fn_ptr = rawptr(f),
parsing_style = parsing_style,
run = proc(cmd: ^Command, argv: []string) {
args: T
flags.parse_or_exit(&args, argv, cmd.parsing_style)
fn := cast(proc(_: T))cmd.fn_ptr
fn(args)
},
}
}
print_usage :: proc(cmds: []Command, description: string, writer: io.Writer, show_description: bool) {
if show_description {
fmt.wprintfln(writer, "%v", description)
fmt.wprintln(writer)
}
fmt.wprintln(writer, "Usage by command:\n")
for cmd in cmds {
command_program_name := fmt.tprintf("%s %s", os.args[0], cmd.name)
fmt.wprintfln(writer, "command %v", cmd.name)
flags.write_usage(writer, cmd.arg_type, command_program_name)
fmt.wprintln(writer)
}
}
run :: proc(cmds: []Command, description: string, args: []string = os.args) -> int {
if len(args) < 2 {
fmt.eprintln("No command provided. Expected one of:")
for cmd in cmds {
fmt.eprintfln("\t%v", cmd.name)
}
print_usage(cmds, description, os.to_stream(os.stderr), false)
return 1
}
cmd_name := args[1]
switch cmd_name := strings.to_lower(cmd_name); cmd_name {
case "-h", "--help", "-help":
{
print_usage(cmds, description, os.to_stream(os.stdout), true)
return 0
}
case:
{
for &cmd in cmds {
if cmd.name == cmd_name {
cmd.run(&cmd, args[1:])
return 0
}
}
}
}
fmt.eprintfln("Unknown command: %q, expected one of:", cmd_name)
for cmd in cmds {
fmt.eprintfln("\t%v", cmd.name)
}
return 2
}
package example
import cf "commandflags"
import "core:flags"
import "core:fmt"
import "core:os"
Foo :: struct {
name: string `usage:"name"`,
count: int `usage:"count"`,
}
Bar :: struct {
baz: string `usage:"baz"`,
}
foo :: proc(args: Foo) {
fmt.printfln("in foo, args: %v", args);
}
bar :: proc(args: Bar) {
fmt.printfln("in bar, args: %v", args);
}
main :: proc() {
parsing_style := flags.Parsing_Style.Odin
cmds := []cf.Command{
cf.make_cmd(Foo, "foo", foo, parsing_style),
cf.make_cmd(Bar, "bar", bar, parsing_style),
}
os.exit(cf.run(cmds, description = "this is an example CLI tool that has multiple commands"))
}
/*
This is the above example.odin, but manually switching on the command string
and parsing the associated argument type with core:flags. It's not a bad way of
doing this & for the example this approach is less LOC in total, if we include
the LOC of commandflags.odin
However, once the number of commands increases, the commandflags package approach will
produce less LOC overall.
*/
package manualcommands
import "core:flags"
import "core:fmt"
import "core:os"
import "core:strings"
Foo :: struct {
name: string `usage:"name"`,
count: int `usage:"count"`,
}
Bar :: struct {
baz: string `usage:"baz"`,
}
foo :: proc(args: Foo) {
fmt.printfln("in foo, args: %v", args);
}
bar :: proc(args: Bar) {
fmt.printfln("in bar, args: %v", args);
}
main :: proc() {
if len(os.args) == 1 {
fmt.eprintln("expected command argument");
os.exit(1)
}
cmd := os.args[1]
switch strings.to_lower(cmd) {
case "foo": {
args: Foo
flags.parse_or_exit(&args, os.args[1:], .Odin)
foo(args)
}
case "bar": {
args: Bar
flags.parse_or_exit(&args, os.args[1:], .Odin)
bar(args)
}
case "-h", "-help", "--help": {
fmt.eprintfln(`%v is a demo tool for a multi command CLI
Usage:
%v command [arguments]
Commands:
foo foo command
bar bar command
For further details on a command, invoke the command help:
e.g. %v foo -h or %v foo help
`, os.args[0], os.args[0], os.args[0], os.args[0])
}
case: {
fmt.eprintln("unexpected command: %v, expected one of 'foo', 'bar'", cmd);
os.exit(1)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment