Last active
March 17, 2026 05:38
-
-
Save miguelmartin75/525cedfad798f9f3f541eb929706a7a0 to your computer and use it in GitHub Desktop.
commands for core:flags
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| 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