|
@@ -1,211 +0,0 @@
|
|
-package flag
|
|
|
|
-
|
|
|
|
-import "core:runtime"
|
|
|
|
-import "core:strings"
|
|
|
|
-import "core:reflect"
|
|
|
|
-import "core:fmt"
|
|
|
|
-import "core:mem"
|
|
|
|
-import "core:strconv"
|
|
|
|
-
|
|
|
|
-Flag_Error :: enum {
|
|
|
|
- None,
|
|
|
|
- No_Base_Struct,
|
|
|
|
- Arg_Error,
|
|
|
|
- Arg_Unsupported_Field_Type,
|
|
|
|
- Arg_Not_Defined,
|
|
|
|
- Arg_Non_Optional,
|
|
|
|
- Value_Parse_Error,
|
|
|
|
- Tag_Error,
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-Flag :: struct {
|
|
|
|
- optional: bool,
|
|
|
|
- type: ^runtime.Type_Info,
|
|
|
|
- data: rawptr,
|
|
|
|
- tag_ptr: rawptr,
|
|
|
|
- parsed: bool,
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-Flag_Context :: struct {
|
|
|
|
- seen_flags: map[string]Flag,
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-parse_args :: proc(ctx: ^Flag_Context, args: []string) -> Flag_Error {
|
|
|
|
-
|
|
|
|
- using runtime;
|
|
|
|
-
|
|
|
|
- args := args;
|
|
|
|
-
|
|
|
|
- for {
|
|
|
|
- if len(args) == 0 {
|
|
|
|
- return .None;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- arg := args[0];
|
|
|
|
-
|
|
|
|
- if len(arg) < 2 || arg[0] != '-' {
|
|
|
|
- return .Arg_Error;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- minus_count := 1;
|
|
|
|
-
|
|
|
|
- if arg[1] == '-' {
|
|
|
|
- minus_count += 1;
|
|
|
|
-
|
|
|
|
- if len(arg) == 2 {
|
|
|
|
- return .Arg_Error;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- name := arg[minus_count:];
|
|
|
|
-
|
|
|
|
- if len(name) == 0 {
|
|
|
|
- return .Arg_Error;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- args = args[1:];
|
|
|
|
-
|
|
|
|
- assign_index := strings.index(name, "=");
|
|
|
|
-
|
|
|
|
- value := "";
|
|
|
|
-
|
|
|
|
- if assign_index > 0 {
|
|
|
|
- value = name[assign_index + 1:];
|
|
|
|
- name = name[0:assign_index];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- flag := &ctx.seen_flags[name];
|
|
|
|
-
|
|
|
|
- if flag == nil {
|
|
|
|
- return .Arg_Not_Defined;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if reflect.is_boolean(flag.type) {
|
|
|
|
- tmp: b64 = true;
|
|
|
|
- mem.copy(flag.data, &tmp, flag.type.size);
|
|
|
|
- flag.parsed = true;
|
|
|
|
- continue;
|
|
|
|
- } else if value == "" { // must be in the next argument
|
|
|
|
- if len(args) == 0 {
|
|
|
|
- return .Arg_Error;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- value = args[0];
|
|
|
|
- args = args[1:];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- #partial switch _ in flag.type.variant {
|
|
|
|
- case Type_Info_Integer:
|
|
|
|
- if v, ok := strconv.parse_int(value); ok {
|
|
|
|
- mem.copy(flag.data, &v, flag.type.size);
|
|
|
|
- } else {
|
|
|
|
- return .Value_Parse_Error;
|
|
|
|
- }
|
|
|
|
- case Type_Info_String:
|
|
|
|
- raw_string := cast(^mem.Raw_String)flag.data;
|
|
|
|
- raw_string.data = strings.ptr_from_string(value);
|
|
|
|
- raw_string.len = len(value);
|
|
|
|
- case Type_Info_Float:
|
|
|
|
- switch flag.type.size {
|
|
|
|
- case 32:
|
|
|
|
- if v, ok := strconv.parse_f32(value); ok {
|
|
|
|
- mem.copy(flag.data, &v, flag.type.size);
|
|
|
|
- } else {
|
|
|
|
- return .Value_Parse_Error;
|
|
|
|
- }
|
|
|
|
- case 64:
|
|
|
|
- if v, ok := strconv.parse_f64(value); ok {
|
|
|
|
- mem.copy(flag.data, &v, flag.type.size);
|
|
|
|
- } else {
|
|
|
|
- return .Value_Parse_Error;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- flag.parsed = true;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return .None;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-reflect_args_structure :: proc(ctx: ^Flag_Context, v: any) -> Flag_Error {
|
|
|
|
- using runtime;
|
|
|
|
-
|
|
|
|
- if !reflect.is_struct(type_info_of(v.id)) {
|
|
|
|
- return .No_Base_Struct;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- names := reflect.struct_field_names(v.id);
|
|
|
|
- types := reflect.struct_field_types(v.id);
|
|
|
|
- offsets := reflect.struct_field_offsets(v.id);
|
|
|
|
- tags := reflect.struct_field_tags(v.id);
|
|
|
|
-
|
|
|
|
- for name, i in names {
|
|
|
|
- flag: Flag;
|
|
|
|
-
|
|
|
|
- type := types[i];
|
|
|
|
-
|
|
|
|
- if named_type, ok := type.variant.(Type_Info_Named); ok {
|
|
|
|
- if union_type, ok := named_type.base.variant.(Type_Info_Union); ok && len(union_type.variants) == 1 {
|
|
|
|
- flag.optional = true;
|
|
|
|
- flag.tag_ptr = rawptr(uintptr(union_type.tag_offset) + uintptr(v.data) + uintptr(offsets[i]));
|
|
|
|
- type = union_type.variants[0];
|
|
|
|
- } else {
|
|
|
|
- return .Arg_Unsupported_Field_Type;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- #partial switch _ in type.variant {
|
|
|
|
- case Type_Info_Integer, Type_Info_String, Type_Info_Boolean, Type_Info_Float:
|
|
|
|
- flag.type = type;
|
|
|
|
- flag.data = rawptr(uintptr(v.data) + uintptr(offsets[i]));
|
|
|
|
- case:
|
|
|
|
- return .Arg_Unsupported_Field_Type;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- flag_name: string;
|
|
|
|
-
|
|
|
|
- if value, ok := reflect.struct_tag_lookup(tags[i], "flag"); ok {
|
|
|
|
- flag_name = cast(string)value;
|
|
|
|
- } else {
|
|
|
|
- return .Tag_Error;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ctx.seen_flags[flag_name] = flag;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return .None;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-parse :: proc(v: any, args: []string) -> Flag_Error {
|
|
|
|
-
|
|
|
|
- if v == nil {
|
|
|
|
- return .None;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ctx: Flag_Context;
|
|
|
|
-
|
|
|
|
- if res := reflect_args_structure(&ctx, v); res != .None {
|
|
|
|
- return res;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if res := parse_args(&ctx, args); res != .None {
|
|
|
|
- return res;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- //validate that the required flags were actually set
|
|
|
|
- for k, v in ctx.seen_flags {
|
|
|
|
- if v.optional && v.parsed {
|
|
|
|
- tag_value: i32 = 1;
|
|
|
|
- mem.copy(v.tag_ptr, &tag_value, 4); //4 constant is probably not portable, but it works for me currently
|
|
|
|
- } else if !v.parsed && !v.optional {
|
|
|
|
- return .Arg_Non_Optional;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return .None;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-usage :: proc(v: any) -> string {
|
|
|
|
- return "failed";
|
|
|
|
-}
|
|
|