123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- package flags
- import "core:fmt"
- @require import "core:os"
- @require import "core:path/filepath"
- import "core:strings"
- /*
- Parse any arguments into an annotated struct or exit if there was an error.
- *Allocates Using Provided Allocator*
- This is a convenience wrapper over `parse` and `print_errors`.
- Inputs:
- - model: A pointer to an annotated struct.
- - program_args: A slice of strings, usually `os.args`.
- - style: The argument parsing style.
- - allocator: (default: context.allocator)
- - loc: The caller location for debugging purposes (default: #caller_location)
- */
- @(optimization_mode="favor_size")
- parse_or_exit :: proc(
- model: ^$T,
- program_args: []string,
- style: Parsing_Style = .Odin,
- allocator := context.allocator,
- loc := #caller_location,
- ) {
- assert(len(program_args) > 0, "Program arguments slice is empty.", loc)
- program := filepath.base(program_args[0])
- args: []string
- if len(program_args) > 1 {
- args = program_args[1:]
- }
- error := parse(model, args, style)
- if error != nil {
- stderr := os.stream_from_handle(os.stderr)
- if len(args) == 0 {
- // No arguments entered, and there was an error; show the usage,
- // specifically on STDERR.
- write_usage(stderr, T, program, style)
- fmt.wprintln(stderr)
- }
- print_errors(T, error, program, style)
- _, was_help_request := error.(Help_Request)
- os.exit(0 if was_help_request else 1)
- }
- }
- /*
- Print out any errors that may have resulted from parsing.
- All error messages print to STDERR, while usage goes to STDOUT, if requested.
- Inputs:
- - data_type: The typeid of the data structure to describe, if usage is requested.
- - error: The error returned from `parse`.
- - style: The argument parsing style, required to show flags in the proper style, when usage is shown.
- */
- @(optimization_mode="favor_size")
- print_errors :: proc(data_type: typeid, error: Error, program: string, style: Parsing_Style = .Odin) {
- stderr := os.stream_from_handle(os.stderr)
- stdout := os.stream_from_handle(os.stdout)
- switch specific_error in error {
- case Parse_Error:
- fmt.wprintfln(stderr, "[%T.%v] %s", specific_error, specific_error.reason, specific_error.message)
- case Open_File_Error:
- fmt.wprintfln(stderr, "[%T#%i] Unable to open file with perms 0o%o in mode 0x%x: %s",
- specific_error,
- specific_error.errno,
- specific_error.perms,
- specific_error.mode,
- specific_error.filename)
- case Validation_Error:
- fmt.wprintfln(stderr, "[%T] %s", specific_error, specific_error.message)
- case Help_Request:
- write_usage(stdout, data_type, program, style)
- }
- }
- /*
- Get the value for a subtag.
- This is useful if you need to parse through the `args` tag for a struct field
- on a custom type setter or custom flag checker.
- Example:
- import "core:flags"
- import "core:fmt"
- subtag_example :: proc() {
- args_tag := "precision=3,signed"
- precision, has_precision := flags.get_subtag(args_tag, "precision")
- signed, is_signed := flags.get_subtag(args_tag, "signed")
- fmt.printfln("precision = %q, %t", precision, has_precision)
- fmt.printfln("signed = %q, %t", signed, is_signed)
- }
- Output:
- precision = "3", true
- signed = "", true
- */
- get_subtag :: proc(tag, id: string) -> (value: string, ok: bool) {
- // This proc was initially private in `internal_rtti.odin`, but given how
- // useful it would be to custom type setters and flag checkers, it lives
- // here now.
- tag := tag
- for subtag in strings.split_iterator(&tag, ",") {
- if equals := strings.index_byte(subtag, '='); equals != -1 && id == subtag[:equals] {
- return subtag[1 + equals:], true
- } else if id == subtag {
- return "", true
- }
- }
- return
- }
|