aboutsummaryrefslogtreecommitdiff
path: root/core/flags/internal_assignment.odin
diff options
context:
space:
mode:
authorFeoramund <161657516+Feoramund@users.noreply.github.com>2024-06-06 18:35:43 -0400
committerFeoramund <161657516+Feoramund@users.noreply.github.com>2024-06-07 13:16:13 -0400
commitedb685f04be9aef407dc8abac7bb76fb51184f9f (patch)
tree63f48c951de7a9d751895a3cd72194e241bd38c0 /core/flags/internal_assignment.odin
parent08612423b9afffb7499d8db2c69715df1627bd50 (diff)
Add package `core:flags`
Diffstat (limited to 'core/flags/internal_assignment.odin')
-rw-r--r--core/flags/internal_assignment.odin262
1 files changed, 262 insertions, 0 deletions
diff --git a/core/flags/internal_assignment.odin b/core/flags/internal_assignment.odin
new file mode 100644
index 000000000..1e715998d
--- /dev/null
+++ b/core/flags/internal_assignment.odin
@@ -0,0 +1,262 @@
+//+private
+package flags
+
+import "base:intrinsics"
+import "base:runtime"
+import "core:container/bit_array"
+import "core:fmt"
+import "core:mem"
+import "core:reflect"
+import "core:strconv"
+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="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="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="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="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="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),
+ }
+}