Files
Odin/core/flags/conversion.odin
gingerBill f4dd48aa5d Add core:flags
Based on the Feoramund's original package
2024-06-04 19:08:03 +01:00

158 lines
3.7 KiB
Odin

package flags
import "base:intrinsics"
import "base:runtime"
import "core:fmt"
import "core:mem"
import "core:reflect"
_, _, _, _, _ :: intrinsics, runtime, fmt, mem, reflect
// Add a positional argument to a data struct, checking for specified
// positionals first before adding it to a fallback field.
add_positional :: proc(data: ^$T, index: int, arg: string) -> Error {
field, has_pos_assigned := get_field_by_pos(data, index)
if !has_pos_assigned {
when !intrinsics.type_has_field(T, SUBTAG_POS) {
return Parse_Error {
.Extra_Pos,
fmt.tprintf("got extra positional argument `%s` with nowhere to store it", arg),
}
}
// Fall back to adding it to a dynamic array named `pos`.
field = reflect.struct_field_by_name(T, SUBTAG_POS)
assert(field.type != nil, "this should never happen")
}
ptr := cast(rawptr)(uintptr(data) + field.offset)
if !parse_and_set_pointer_by_type(ptr, arg, field.type) {
return Parse_Error {
.Bad_Type,
fmt.tprintf("unable to set positional %i (%s) of type %v to `%s`", index, field.name, field.type, arg),
}
}
return nil
}
// Set a `-flag` argument.
set_flag :: proc(data: ^$T, name: string) -> Error {
// We make a special case for help requests.
switch name {
case HARD_CODED_HELP_FLAG:
fallthrough
case HARD_CODED_HELP_FLAG_SHORT:
return Help_Request{}
}
field := get_field_by_name(data, name) or_return
#partial switch t in field.type.variant {
case runtime.Type_Info_Boolean:
ptr := cast(^bool)(uintptr(data) + field.offset)
ptr^ = true
case:
return Parse_Error {
.Bad_Type,
fmt.tprintf("unable to set `%s` of type %v to true", name, field.type),
}
}
return nil
}
// Set a `-flag:option` argument.
set_option :: proc(data: ^$T, name, option: string) -> Error {
field := get_field_by_name(data, name) or_return
// Guard against incorrect syntax.
#partial switch t in field.type.variant {
case runtime.Type_Info_Map:
return Parse_Error {
.Missing_Value,
fmt.tprintf("unable to set `%s` of type %v to `%s`, are you missing an `=`?", name, field.type, option),
}
}
ptr := rawptr(uintptr(data) + field.offset)
if !parse_and_set_pointer_by_type(ptr, option, field.type) {
return Parse_Error {
.Bad_Type,
fmt.tprintf("unable to set `%s` of type %v to `%s`", name, field.type, option),
}
}
return nil
}
// Set a `-map:key=value` argument.
set_key_value :: proc(data: ^$T, name, key, value: string) -> Error {
field := get_field_by_name(data, name) or_return
#partial switch t in field.type.variant {
case runtime.Type_Info_Map:
if !reflect.is_string(t.key) {
return Parse_Error {
.Bad_Type,
fmt.tprintf("`%s` must be a map[string]", name),
}
}
key := key
key_ptr := rawptr(&key)
key_cstr: cstring
if reflect.is_cstring(t.key) {
key_cstr = cstring(raw_data(key))
key_ptr = &key_cstr
}
raw_map := (^runtime.Raw_Map)(uintptr(data) + field.offset)
hash := t.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,
t.map_info,
hash,
key_ptr,
)
}
if value_ptr == nil {
elem_backing = mem.alloc_bytes(t.value.size, t.value.align) or_return
backing_alloc = true
value_ptr = raw_data(elem_backing)
}
if !parse_and_set_pointer_by_type(value_ptr, value, t.value) {
break
}
if backing_alloc {
runtime.__dynamic_map_set(raw_map,
t.map_info,
hash,
key_ptr,
value_ptr,
)
delete(elem_backing)
}
return nil
}
return Parse_Error {
.Bad_Type,
fmt.tprintf("unable to set `%s` of type %v with key=value `%s` = `%s`", name, field.type, key, value),
}
}