package flags @require import "core:container/bit_array" @require import "core:fmt" Parsing_Style :: enum { // Odin-style: `-flag`, `-flag:option`, `-map:key=value` Odin, // UNIX-style: `-flag` or `--flag`, `--flag=argument`, `--flag argument repeating-argument` Unix, } /* Parse a slice of command-line arguments into an annotated struct. *Allocates Using Provided Allocator* By default, this proc will only allocate memory outside of its lifetime if it has to append to a dynamic array, set a map value, or set a cstring. The program is expected to free any allocations on `model` as a result of parsing. Inputs: - model: A pointer to an annotated struct with flag definitions. - args: A slice of strings, usually `os.args[1:]`. - style: The argument parsing style. - validate_args: If `true`, will ensure that all required arguments are set if no errors occurred. - strict: If `true`, will return on first error. Otherwise, parsing continues. - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: #caller_location) Returns: - error: A union of errors; parsing, file open, a help request, or validation. */ @(optimization_mode="favor_size") parse :: proc( model: ^$T, args: []string, style: Parsing_Style = .Odin, validate_args: bool = true, strict: bool = true, allocator := context.allocator, loc := #caller_location, ) -> (error: Error) { context.allocator = allocator validate_structure(model^, style, loc) parser: Parser defer { bit_array.destroy(&parser.filled_pos) bit_array.destroy(&parser.fields_set) } switch style { case .Odin: for arg in args { error = parse_one_odin_arg(model, &parser, arg) if strict && error != nil { return } } case .Unix: // Support for `-flag argument (repeating-argument ...)` future_args: int current_flag: string for i := 0; i < len(args); i += 1 { #no_bounds_check arg := args[i] future_args, current_flag, error = parse_one_unix_arg(model, &parser, arg) if strict && error != nil { return } for starting_future_args := future_args; future_args > 0; future_args -= 1 { i += 1 if i == len(args) { if future_args == starting_future_args { return Parse_Error { .No_Value, fmt.tprintf("Expected a value for `%s` but none was given.", current_flag), } } break } #no_bounds_check arg = args[i] error = set_option(model, &parser, current_flag, arg) if strict && error != nil { return } } } } if error == nil && validate_args { return validate_arguments(model, &parser) } return }