diff --git a/cli.odin b/cli.odin index df0616f..c97494b 100644 --- a/cli.odin +++ b/cli.odin @@ -97,14 +97,18 @@ key somewhere, otherwise your data could be lost forever.`, short = "Import a .env file into envr", aliases = {"add"}, flags = GLOBAL_FLAGS, - args = {{name = "path", completion = "untracked-paths", desc = "Path to .env file to backup"}}, + args = { + {name = "path", completion = "untracked-paths", desc = "Path to .env file to backup"}, + }, }, { name = "restore", usage = "envr restore ", short = "Restore a .env file from the database", flags = GLOBAL_FLAGS, - args = {{name = "path", completion = "tracked-paths", desc = "Path to .env file to restore"}}, + args = { + {name = "path", completion = "tracked-paths", desc = "Path to .env file to restore"}, + }, }, { name = "list", @@ -117,14 +121,22 @@ key somewhere, otherwise your data could be lost forever.`, usage = "envr remove ", short = "Remove a .env file from your database", flags = GLOBAL_FLAGS, - args = {{name = "path", completion = "tracked-paths", desc = "Path to .env file to remove"}}, + args = { + {name = "path", completion = "tracked-paths", desc = "Path to .env file to remove"}, + }, }, { name = "check", usage = "envr check [path]", short = "Check if files are backed up", flags = GLOBAL_FLAGS, - args = {{name = "path", optional = true, desc = "Path to check (defaults to current directory)"}}, + args = { + { + name = "path", + optional = true, + desc = "Path to check (defaults to current directory)", + }, + }, }, {name = "version", usage = "envr version", short = "Show envr's version", flags = {.Help}}, { @@ -142,7 +154,14 @@ key somewhere, otherwise your data could be lost forever.`, nushell bash`, flags = {.Help}, - args = {{name = "shell", ntype = "string", completion = "shells", desc = "Shell to generate completions for"}}, + args = { + { + name = "shell", + ntype = "string", + completion = "shells", + desc = "Shell to generate completions for", + }, + }, }, { name = "uninstall", @@ -271,48 +290,22 @@ Flag_Field :: struct { completion: string, } -flag_field :: proc(ft: Flag_Type) -> Flag_Field { - field := reflect.struct_field_at(Flags, int(ft)) - - args_tag := reflect.struct_tag_get(field.tag, "args") - long_name, _ := strings.replace(field.name, "_", "-", -1, context.temp_allocator) - if n, ok := get_subtag(args_tag, "name"); ok { - long_name = n - } - - short, has_short := get_subtag(args_tag, "short") - - base_ti := runtime.type_info_base(field.type) - kind: Flag_Kind - enum_values: string - - if _, is_bool := base_ti.variant.(runtime.Type_Info_Boolean); is_bool { - kind = .Bool - } else if _, is_string := base_ti.variant.(runtime.Type_Info_String); is_string { - kind = .String - } else if enum_ti, is_enum := base_ti.variant.(runtime.Type_Info_Enum); is_enum { - kind = .Enum - parts := make([dynamic]string, 0, len(enum_ti.names), context.temp_allocator) - for name in enum_ti.names { - lower := strings.to_lower(name, context.temp_allocator) - append(&parts, fmt.tprintf("'%s'", lower)) +write_flags_table :: proc(tbl: ^table.Table, flags: bit_set[Flag_Type]) { + table.caption(tbl, "Flags:") + for ft in Flag_Type { + if ft not_in flags do continue + names, hint, desc := flag_field_info(ft) + if len(hint) > 0 { + display := table.format( + tbl, + "%s%s", + colorize(.Flag, names, tbl.format_allocator), + hint, + ) + table.row(tbl, display, desc) + } else { + table.row(tbl, colorize(.Flag, names, tbl.format_allocator), desc) } - enum_values = strings.join(parts[:], "|", context.temp_allocator) - delete(parts) - } - - usage := reflect.struct_tag_get(field.tag, "usage") - default_val := reflect.struct_tag_get(field.tag, "default") - completion := reflect.struct_tag_get(field.tag, "completion") - - return { - long_name = long_name, - short_name = has_short ? short : "", - kind = kind, - usage = usage, - default_val = default_val, - enum_values = enum_values, - completion = completion, } } @@ -355,22 +348,48 @@ flag_field_info :: proc( return } -write_flags_table :: proc(tbl: ^table.Table, flags: bit_set[Flag_Type]) { - table.caption(tbl, "Flags:") - for ft in Flag_Type { - if ft not_in flags do continue - names, hint, desc := flag_field_info(ft) - if len(hint) > 0 { - display := table.format( - tbl, - "%s%s", - colorize(.Flag, names, tbl.format_allocator), - hint, - ) - table.row(tbl, display, desc) - } else { - table.row(tbl, colorize(.Flag, names, tbl.format_allocator), desc) +flag_field :: proc(ft: Flag_Type) -> Flag_Field { + field := reflect.struct_field_at(Flags, int(ft)) + + args_tag := reflect.struct_tag_get(field.tag, "args") + long_name, _ := strings.replace(field.name, "_", "-", -1, context.temp_allocator) + if n, ok := get_subtag(args_tag, "name"); ok { + long_name = n + } + + short, has_short := get_subtag(args_tag, "short") + + base_ti := runtime.type_info_base(field.type) + kind: Flag_Kind + enum_values: string + + if _, is_bool := base_ti.variant.(runtime.Type_Info_Boolean); is_bool { + kind = .Bool + } else if _, is_string := base_ti.variant.(runtime.Type_Info_String); is_string { + kind = .String + } else if enum_ti, is_enum := base_ti.variant.(runtime.Type_Info_Enum); is_enum { + kind = .Enum + parts := make([dynamic]string, 0, len(enum_ti.names), context.temp_allocator) + for name in enum_ti.names { + lower := strings.to_lower(name, context.temp_allocator) + append(&parts, fmt.tprintf("'%s'", lower)) } + enum_values = strings.join(parts[:], "|", context.temp_allocator) + delete(parts) + } + + usage := reflect.struct_tag_get(field.tag, "usage") + default_val := reflect.struct_tag_get(field.tag, "default") + completion := reflect.struct_tag_get(field.tag, "completion") + + return { + long_name = long_name, + short_name = has_short ? short : "", + kind = kind, + usage = usage, + default_val = default_val, + enum_values = enum_values, + completion = completion, } } diff --git a/config.odin b/config.odin index eabd26b..1ac9d89 100644 --- a/config.odin +++ b/config.odin @@ -231,10 +231,6 @@ search_paths :: proc(cfg: Config, allocator := context.allocator) -> [dynamic]st return result } -envr_dir :: proc(config_path: string) -> string { - return filepath.dir(config_path) -} - // User is responsible for freeing the path data_path :: proc( config_path: string, @@ -246,3 +242,7 @@ data_path :: proc( return filepath.join([]string{envr_dir(config_path), "data.envr"}, allocator) } +envr_dir :: proc(config_path: string) -> string { + return filepath.dir(config_path) +} + diff --git a/flags.odin b/flags.odin index 87fc28c..ccbce7e 100644 --- a/flags.odin +++ b/flags.odin @@ -4,64 +4,12 @@ import "base:runtime" import "core:reflect" import "core:strings" -get_subtag :: proc(tag: string, id: string) -> (value: string, ok: bool) { - parts := strings.split(tag, ",", context.temp_allocator) - for part in parts { - trimmed := strings.trim_space(part) - if strings.has_prefix(trimmed, id) && len(trimmed) > len(id) && trimmed[len(id)] == '=' { - return trimmed[len(id) + 1:], true - } - if trimmed == id { - return "", true - } - } - return "", false -} - -is_bool_type :: proc(field: reflect.Struct_Field) -> bool { - base_ti := runtime.type_info_base(field.type) - _, is_bool := base_ti.variant.(runtime.Type_Info_Boolean) - return is_bool -} - -set_field :: proc(model: rawptr, field: reflect.Struct_Field, value: string) -> bool { - ptr := rawptr(uintptr(model) + field.offset) - base_ti := runtime.type_info_base(field.type) - - if _, is_bool := base_ti.variant.(runtime.Type_Info_Boolean); is_bool { - (cast(^bool)ptr)^ = true - return true - } - - if _, is_string := base_ti.variant.(runtime.Type_Info_String); is_string { - (cast(^string)ptr)^ = value - return true - } - - if enum_ti, is_enum := base_ti.variant.(runtime.Type_Info_Enum); is_enum { - for name, i in enum_ti.names { - if strings.equal_fold(value, name) { - v := enum_ti.values[i] - switch base_ti.size { - case 1: (cast(^u8)ptr)^ = cast(u8)v - case 2: (cast(^u16)ptr)^ = cast(u16)v - case 4: (cast(^u32)ptr)^ = cast(u32)v - case 8: (cast(^u64)ptr)^ = cast(u64)v - } - return true - } - } - } - - return false -} - parse_flags :: proc(model: ^$T, args: []string) -> (overflow: []string) { field_count := reflect.struct_field_count(T) long_map := make(map[string]reflect.Struct_Field, field_count, context.temp_allocator) short_map := make(map[string]reflect.Struct_Field, field_count, context.temp_allocator) - for i in 0.. (overflow: []string) { return overflow_dyn[:] } + +get_subtag :: proc(tag: string, id: string) -> (value: string, ok: bool) { + parts := strings.split(tag, ",", context.temp_allocator) + for part in parts { + trimmed := strings.trim_space(part) + if strings.has_prefix(trimmed, id) && len(trimmed) > len(id) && trimmed[len(id)] == '=' { + return trimmed[len(id) + 1:], true + } + if trimmed == id { + return "", true + } + } + return "", false +} + +is_bool_type :: proc(field: reflect.Struct_Field) -> bool { + base_ti := runtime.type_info_base(field.type) + _, is_bool := base_ti.variant.(runtime.Type_Info_Boolean) + return is_bool +} + +set_field :: proc(model: rawptr, field: reflect.Struct_Field, value: string) -> bool { + ptr := rawptr(uintptr(model) + field.offset) + base_ti := runtime.type_info_base(field.type) + + if _, is_bool := base_ti.variant.(runtime.Type_Info_Boolean); is_bool { + (cast(^bool)ptr)^ = true + return true + } + + if _, is_string := base_ti.variant.(runtime.Type_Info_String); is_string { + (cast(^string)ptr)^ = value + return true + } + + if enum_ti, is_enum := base_ti.variant.(runtime.Type_Info_Enum); is_enum { + for name, i in enum_ti.names { + if strings.equal_fold(value, name) { + v := enum_ti.values[i] + switch base_ti.size { + case 1: + (cast(^u8)ptr)^ = cast(u8)v + case 2: + (cast(^u16)ptr)^ = cast(u16)v + case 4: + (cast(^u32)ptr)^ = cast(u32)v + case 8: + (cast(^u64)ptr)^ = cast(u64)v + } + return true + } + } + } + + return false +} + diff --git a/table.odin b/table.odin index 4f3de8d..078f6e0 100644 --- a/table.odin +++ b/table.odin @@ -1,6 +1,5 @@ package main -import "core:fmt" import "core:io" import "core:text/table" @@ -18,20 +17,6 @@ decorations := table.Decorations { "─", } -ansi_aware_width :: proc(str: string) -> int #no_bounds_check { - width := 0 - for i := 0; i < len(str); { - if i + 1 < len(str) && str[i] == 0x1b && str[i + 1] == '[' { - i += 2 - for i < len(str) {c := str[i]; i += 1; if c >= 0x40 && c <= 0x7E {break}} - } else { - width += 1 - i += 1 - } - } - return width -} - write_borderless_table :: proc(w: io.Writer, t: ^table.Table) { table.build(t, ansi_aware_width) @@ -64,6 +49,21 @@ write_borderless_table :: proc(w: io.Writer, t: ^table.Table) { write_table_separator(w, t) } +ansi_aware_width :: proc(str: string) -> int #no_bounds_check { + width := 0 + for i := 0; i < len(str); { + if i + 1 < len(str) && str[i] == 0x1b && str[i + 1] == '[' { + i += 2 + for i < len(str) {c := str[i]; i += 1; if c >= 0x40 && c <= 0x7E {break}} + } else { + width += 1 + i += 1 + } + } + return width +} + + table_reset :: proc(t: ^table.Table) { clear(&t.cells) clear(&t.colw)