123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- //+private
- package flags
- import "base:intrinsics"
- @require import "base:runtime"
- import "core:container/bit_array"
- @require import "core:fmt"
- @require import "core:mem"
- import "core:reflect"
- @require import "core:strconv"
- @require import "core:strings"
- // Push a positional argument onto a data struct, checking for specified
- // positionals first before adding it to a fallback field.
- @(optimization_mode="favor_size")
- push_positional :: #force_no_inline proc (model: ^$T, parser: ^Parser, arg: string) -> (error: Error) {
- if bit_array.get(&parser.filled_pos, parser.filled_pos.max_index) {
- // The max index is set, which means we're out of space.
- // Add one free bit by setting the index above to false.
- bit_array.set(&parser.filled_pos, 1 + parser.filled_pos.max_index, false)
- }
- pos: int = ---
- {
- iter := bit_array.make_iterator(&parser.filled_pos)
- ok: bool
- pos, ok = bit_array.iterate_by_unset(&iter)
- // This may be an allocator error.
- assert(ok, "Unable to find a free spot in the positional bit_array.")
- }
- field, index, has_pos_assigned := get_field_by_pos(model, pos)
- if !has_pos_assigned {
- when intrinsics.type_has_field(T, INTERNAL_VARIADIC_FLAG) {
- // Add it to the fallback array.
- field = reflect.struct_field_by_name(T, INTERNAL_VARIADIC_FLAG)
- } else {
- return Parse_Error {
- .Extra_Positional,
- fmt.tprintf("Got extra positional argument `%s` with nowhere to store it.", arg),
- }
- }
- }
- ptr := cast(rawptr)(cast(uintptr)model + field.offset)
- args_tag, _ := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
- field_name := get_field_name(field)
- error = parse_and_set_pointer_by_type(ptr, arg, field.type, args_tag)
- #partial switch &specific_error in error {
- case Parse_Error:
- specific_error.message = fmt.tprintf("Unable to set positional #%i (%s) of type %v to `%s`.%s%s",
- pos,
- field_name,
- field.type,
- arg,
- " " if len(specific_error.message) > 0 else "",
- specific_error.message)
- case nil:
- bit_array.set(&parser.filled_pos, pos)
- bit_array.set(&parser.fields_set, index)
- }
- return
- }
- register_field :: proc(parser: ^Parser, field: reflect.Struct_Field, index: int) {
- if pos, ok := get_field_pos(field); ok {
- bit_array.set(&parser.filled_pos, pos)
- }
- bit_array.set(&parser.fields_set, index)
- }
- // Set a `-flag` argument, Odin-style.
- @(optimization_mode="favor_size")
- set_odin_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (error: Error) {
- // We make a special case for help requests.
- switch name {
- case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
- return Help_Request{}
- }
- field, index := get_field_by_name(model, name) or_return
- #partial switch specific_type_info in field.type.variant {
- case runtime.Type_Info_Boolean:
- ptr := cast(^bool)(cast(uintptr)model + field.offset)
- ptr^ = true
- case:
- return Parse_Error {
- .Bad_Value,
- fmt.tprintf("Unable to set `%s` of type %v to true.", name, field.type),
- }
- }
- register_field(parser, field, index)
- return
- }
- // Set a `-flag` argument, UNIX-style.
- @(optimization_mode="favor_size")
- set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args: int, error: Error) {
- // We make a special case for help requests.
- switch name {
- case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
- return 0, Help_Request{}
- }
- field, index := get_field_by_name(model, name) or_return
- #partial switch specific_type_info in field.type.variant {
- case runtime.Type_Info_Boolean:
- ptr := cast(^bool)(cast(uintptr)model + field.offset)
- ptr^ = true
- case runtime.Type_Info_Dynamic_Array:
- future_args = 1
- if tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
- if length, is_variadic := get_struct_subtag(tag, SUBTAG_VARIADIC); is_variadic {
- // Variadic arrays may specify how many arguments they consume at once.
- // Otherwise, they take everything that's left.
- if value, value_ok := strconv.parse_u64_of_base(length, 10); value_ok {
- future_args = cast(int)value
- } else {
- future_args = max(int)
- }
- }
- }
- case:
- // `--flag`, waiting on its value.
- future_args = 1
- }
- register_field(parser, field, index)
- return
- }
- // Set a `-flag:option` argument.
- @(optimization_mode="favor_size")
- set_option :: proc(model: ^$T, parser: ^Parser, name, option: string) -> (error: Error) {
- field, index := get_field_by_name(model, name) or_return
- if len(option) == 0 {
- return Parse_Error {
- .No_Value,
- fmt.tprintf("Setting `%s` to an empty value is meaningless.", name),
- }
- }
- // Guard against incorrect syntax.
- #partial switch specific_type_info in field.type.variant {
- case runtime.Type_Info_Map:
- return Parse_Error {
- .No_Value,
- fmt.tprintf("Unable to set `%s` of type %v to `%s`. Are you missing an `=`? The correct format is `map:key=value`.", name, field.type, option),
- }
- }
- ptr := cast(rawptr)(cast(uintptr)model + field.offset)
- args_tag := reflect.struct_tag_get(field.tag, TAG_ARGS)
- error = parse_and_set_pointer_by_type(ptr, option, field.type, args_tag)
- #partial switch &specific_error in error {
- case Parse_Error:
- specific_error.message = fmt.tprintf("Unable to set `%s` of type %v to `%s`.%s%s",
- name,
- field.type,
- option,
- " " if len(specific_error.message) > 0 else "",
- specific_error.message)
- case nil:
- register_field(parser, field, index)
- }
- return
- }
- // Set a `-map:key=value` argument.
- @(optimization_mode="favor_size")
- set_key_value :: proc(model: ^$T, parser: ^Parser, name, key, value: string) -> (error: Error) {
- field, index := get_field_by_name(model, name) or_return
- #partial switch specific_type_info in field.type.variant {
- case runtime.Type_Info_Map:
- key := key
- key_ptr := cast(rawptr)&key
- key_cstr: cstring
- if reflect.is_cstring(specific_type_info.key) {
- // We clone the key here, because it's liable to be a slice of an
- // Odin string, and we need to put a NUL terminator in it.
- key_cstr = strings.clone_to_cstring(key)
- key_ptr = &key_cstr
- }
- defer if key_cstr != nil {
- delete(key_cstr)
- }
- raw_map := (^runtime.Raw_Map)(cast(uintptr)model + field.offset)
- hash := specific_type_info.map_info.key_hasher(key_ptr, runtime.map_seed(raw_map^))
- backing_alloc := false
- elem_backing: []byte
- value_ptr: rawptr
- if raw_map.allocator.procedure == nil {
- raw_map.allocator = context.allocator
- } else {
- value_ptr = runtime.__dynamic_map_get(raw_map,
- specific_type_info.map_info,
- hash,
- key_ptr,
- )
- }
- if value_ptr == nil {
- alloc_error: runtime.Allocator_Error = ---
- elem_backing, alloc_error = mem.alloc_bytes(specific_type_info.value.size, specific_type_info.value.align)
- if elem_backing == nil {
- return Parse_Error {
- alloc_error,
- "Failed to allocate element backing for map value.",
- }
- }
- backing_alloc = true
- value_ptr = raw_data(elem_backing)
- }
- args_tag, _ := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
- error = parse_and_set_pointer_by_type(value_ptr, value, specific_type_info.value, args_tag)
- #partial switch &specific_error in error {
- case Parse_Error:
- specific_error.message = fmt.tprintf("Unable to set `%s` of type %v with key=value: `%s`=`%s`.%s%s",
- name,
- field.type,
- key,
- value,
- " " if len(specific_error.message) > 0 else "",
- specific_error.message)
- }
- if backing_alloc {
- runtime.__dynamic_map_set(raw_map,
- specific_type_info.map_info,
- hash,
- key_ptr,
- value_ptr,
- )
- delete(elem_backing)
- }
- register_field(parser, field, index)
- return
- }
- return Parse_Error {
- .Bad_Value,
- fmt.tprintf("Unable to set `%s` of type %v with key=value: `%s`=`%s`.", name, field.type, key, value),
- }
- }
|